summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRegis <boudinot.regis@yahoo.com>2017-01-02 16:24:37 -0700
committerRegis <boudinot.regis@yahoo.com>2017-01-02 16:24:37 -0700
commit0a074f2e091d8b2f1bce49e50ecf69b667c62dc2 (patch)
tree314027556f06514278a4f83f35813c00b2d5418f
parent588219352c99213d099aff72f32d6ad9ec4830d4 (diff)
parentde25604fbca2f7005754d821d571bbcb1cc510ac (diff)
downloadgitlab-ce-0a074f2e091d8b2f1bce49e50ecf69b667c62dc2.tar.gz
fix pipelines/index.html.haml merge conflict
-rw-r--r--.gitlab-ci.yml2
-rw-r--r--.haml-lint.yml16
-rw-r--r--CHANGELOG.md16
-rw-r--r--CONTRIBUTING.md5
-rw-r--r--Gemfile4
-rw-r--r--Gemfile.lock8
-rw-r--r--PROCESS.md3
-rw-r--r--app/assets/javascripts/application.js8
-rw-r--r--app/assets/javascripts/boards/boards_bundle.js.es62
-rw-r--r--app/assets/javascripts/boards/models/issue.js.es62
-rw-r--r--app/assets/javascripts/boards/models/label.js.es62
-rw-r--r--app/assets/javascripts/boards/models/list.js.es62
-rw-r--r--app/assets/javascripts/boards/models/milestone.js.es62
-rw-r--r--app/assets/javascripts/boards/models/user.js.es62
-rw-r--r--app/assets/javascripts/boards/services/board_service.js.es64
-rw-r--r--app/assets/javascripts/build.js4
-rw-r--r--app/assets/javascripts/diff_notes/components/resolve_btn.js.es610
-rw-r--r--app/assets/javascripts/diff_notes/models/discussion.js.es62
-rw-r--r--app/assets/javascripts/diff_notes/models/note.js.es62
-rw-r--r--app/assets/javascripts/dispatcher.js.es611
-rw-r--r--app/assets/javascripts/environments/services/environments_service.js.es62
-rw-r--r--app/assets/javascripts/gfm_auto_complete.js.es64
-rw-r--r--app/assets/javascripts/issue.js3
-rw-r--r--app/assets/javascripts/preview_markdown.js115
-rw-r--r--app/assets/javascripts/profile/profile.js.es69
-rw-r--r--app/assets/javascripts/project_select.js3
-rw-r--r--app/assets/javascripts/protected_branches/protected_branch_dropdown.js.es62
-rw-r--r--app/assets/javascripts/u2f/authenticate.js.es6 (renamed from app/assets/javascripts/u2f/authenticate.js)35
-rw-r--r--app/assets/stylesheets/framework/common.scss18
-rw-r--r--app/assets/stylesheets/framework/gitlab-theme.scss35
-rw-r--r--app/assets/stylesheets/framework/lists.scss7
-rw-r--r--app/assets/stylesheets/framework/nav.scss82
-rw-r--r--app/assets/stylesheets/framework/sidebar.scss11
-rw-r--r--app/assets/stylesheets/framework/tw_bootstrap.scss2
-rw-r--r--app/assets/stylesheets/framework/variables.scss6
-rw-r--r--app/assets/stylesheets/pages/boards.scss8
-rw-r--r--app/assets/stylesheets/pages/deploy_keys.scss13
-rw-r--r--app/assets/stylesheets/pages/issuable.scss47
-rw-r--r--app/assets/stylesheets/pages/labels.scss2
-rw-r--r--app/assets/stylesheets/pages/login.scss4
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss8
-rw-r--r--app/assets/stylesheets/pages/note_form.scss2
-rw-r--r--app/assets/stylesheets/pages/notes.scss8
-rw-r--r--app/assets/stylesheets/pages/pipelines.scss4
-rw-r--r--app/assets/stylesheets/pages/profiles/preferences.scss4
-rw-r--r--app/assets/stylesheets/pages/projects.scss6
-rw-r--r--app/controllers/admin/deploy_keys_controller.rb4
-rw-r--r--app/controllers/admin/groups_controller.rb7
-rw-r--r--app/controllers/admin/projects_controller.rb2
-rw-r--r--app/controllers/admin/users_controller.rb3
-rw-r--r--app/controllers/concerns/creates_commit.rb2
-rw-r--r--app/controllers/dashboard/todos_controller.rb3
-rw-r--r--app/controllers/groups_controller.rb4
-rw-r--r--app/controllers/projects/deploy_keys_controller.rb4
-rw-r--r--app/controllers/projects/issues_controller.rb3
-rw-r--r--app/controllers/projects/merge_requests_controller.rb3
-rw-r--r--app/controllers/projects/snippets_controller.rb3
-rw-r--r--app/controllers/projects/tags_controller.rb2
-rw-r--r--app/helpers/projects_helper.rb80
-rw-r--r--app/helpers/sorting_helper.rb11
-rw-r--r--app/helpers/storage_helper.rb7
-rw-r--r--app/models/ci/build.rb6
-rw-r--r--app/models/ci/pipeline.rb9
-rw-r--r--app/models/concerns/issuable.rb5
-rw-r--r--app/models/deploy_key.rb14
-rw-r--r--app/models/group.rb26
-rw-r--r--app/models/key.rb8
-rw-r--r--app/models/lfs_objects_project.rb9
-rw-r--r--app/models/merge_request.rb10
-rw-r--r--app/models/namespace.rb13
-rw-r--r--app/models/project.rb26
-rw-r--r--app/models/project_statistics.rb43
-rw-r--r--app/policies/group_policy.rb2
-rw-r--r--app/policies/project_policy.rb15
-rw-r--r--app/services/git_push_service.rb2
-rw-r--r--app/services/git_tag_push_service.rb2
-rw-r--r--app/services/groups/create_service.rb7
-rw-r--r--app/services/issuable_base_service.rb41
-rw-r--r--app/services/issues/base_service.rb4
-rw-r--r--app/services/merge_requests/base_service.rb4
-rw-r--r--app/services/notes/create_service.rb2
-rw-r--r--app/services/users/refresh_authorized_projects_service.rb2
-rw-r--r--app/views/abuse_report_mailer/notify.html.haml6
-rw-r--r--app/views/admin/application_settings/_form.html.haml4
-rw-r--r--app/views/admin/applications/_delete_form.html.haml2
-rw-r--r--app/views/admin/applications/index.html.haml2
-rw-r--r--app/views/admin/background_jobs/show.html.haml2
-rw-r--r--app/views/admin/broadcast_messages/index.html.haml2
-rw-r--r--app/views/admin/deploy_keys/index.html.haml57
-rw-r--r--app/views/admin/deploy_keys/new.html.haml8
-rw-r--r--app/views/admin/groups/_group.html.haml5
-rw-r--r--app/views/admin/groups/index.html.haml2
-rw-r--r--app/views/admin/groups/show.html.haml22
-rw-r--r--app/views/admin/hooks/index.html.haml5
-rw-r--r--app/views/admin/labels/_label.html.haml2
-rw-r--r--app/views/admin/projects/index.html.haml4
-rw-r--r--app/views/admin/projects/show.html.haml13
-rw-r--r--app/views/admin/runners/_runner.html.haml2
-rw-r--r--app/views/admin/users/_head.html.haml4
-rw-r--r--app/views/admin/users/_user.html.haml2
-rw-r--r--app/views/admin/users/groups.html.haml20
-rw-r--r--app/views/admin/users/projects.html.haml16
-rw-r--r--app/views/admin/users/show.html.haml2
-rw-r--r--app/views/ci/lints/_create.html.haml2
-rw-r--r--app/views/dashboard/projects/_zero_authorized_projects.html.haml2
-rw-r--r--app/views/dashboard/todos/_todo.html.haml2
-rw-r--r--app/views/dashboard/todos/index.html.haml6
-rw-r--r--app/views/devise/sessions/_new_base.html.haml8
-rw-r--r--app/views/devise/sessions/_new_crowd.html.haml4
-rw-r--r--app/views/devise/sessions/_new_ldap.html.haml4
-rw-r--r--app/views/devise/sessions/two_factor.html.haml2
-rw-r--r--app/views/devise/shared/_omniauth_box.html.haml2
-rw-r--r--app/views/devise/shared/_signin_box.html.haml8
-rw-r--r--app/views/devise/shared/_signup_box.html.haml8
-rw-r--r--app/views/devise/shared/_tabs_ldap.html.haml2
-rw-r--r--app/views/devise/shared/_tabs_normal.html.haml8
-rw-r--r--app/views/discussions/_diff_discussion.html.haml2
-rw-r--r--app/views/discussions/_jump_to_next.html.haml2
-rw-r--r--app/views/discussions/_parallel_diff_discussion.html.haml6
-rw-r--r--app/views/doorkeeper/applications/_delete_form.html.haml2
-rw-r--r--app/views/doorkeeper/applications/index.html.haml6
-rw-r--r--app/views/doorkeeper/authorizations/error.html.haml2
-rw-r--r--app/views/doorkeeper/authorizations/new.html.haml2
-rw-r--r--app/views/doorkeeper/authorizations/show.html.haml2
-rw-r--r--app/views/doorkeeper/authorized_applications/_delete_form.html.haml4
-rw-r--r--app/views/doorkeeper/authorized_applications/index.html.haml2
-rw-r--r--app/views/emojis/index.html.haml2
-rw-r--r--app/views/errors/access_denied.html.haml3
-rw-r--r--app/views/errors/encoding.html.haml3
-rw-r--r--app/views/errors/git_not_found.html.haml3
-rw-r--r--app/views/errors/not_found.html.haml3
-rw-r--r--app/views/errors/omniauth_error.html.haml3
-rw-r--r--app/views/events/_event_issue.atom.haml2
-rw-r--r--app/views/events/_event_merge_request.atom.haml2
-rw-r--r--app/views/events/_event_note.atom.haml2
-rw-r--r--app/views/events/_event_push.atom.haml2
-rw-r--r--app/views/events/event/_common.html.haml2
-rw-r--r--app/views/events/event/_created_project.html.haml2
-rw-r--r--app/views/explore/groups/index.html.haml2
-rw-r--r--app/views/explore/projects/_filter.html.haml4
-rw-r--r--app/views/groups/_group_lfs_settings.html.haml2
-rw-r--r--app/views/groups/projects.html.haml4
-rw-r--r--app/views/groups/show.html.haml11
-rw-r--r--app/views/help/_shortcuts.html.haml12
-rw-r--r--app/views/help/ui.html.haml124
-rw-r--r--app/views/import/_githubish_status.html.haml4
-rw-r--r--app/views/import/bitbucket/status.html.haml6
-rw-r--r--app/views/import/fogbugz/status.html.haml4
-rw-r--r--app/views/import/gitlab/status.html.haml4
-rw-r--r--app/views/import/google_code/new.html.haml2
-rw-r--r--app/views/import/google_code/status.html.haml6
-rw-r--r--app/views/kaminari/gitlab/_next_page.html.haml4
-rw-r--r--app/views/kaminari/gitlab/_page.html.haml4
-rw-r--r--app/views/kaminari/gitlab/_paginator.html.haml3
-rw-r--r--app/views/kaminari/gitlab/_prev_page.html.haml4
-rw-r--r--app/views/layouts/_head.html.haml40
-rw-r--r--app/views/layouts/_search.html.haml4
-rw-r--r--app/views/layouts/application.html.haml2
-rw-r--r--app/views/layouts/devise.html.haml4
-rw-r--r--app/views/layouts/devise_empty.html.haml2
-rw-r--r--app/views/layouts/devise_mailer.html.haml4
-rw-r--r--app/views/layouts/errors.html.haml4
-rw-r--r--app/views/layouts/header/_default.html.haml6
-rw-r--r--app/views/layouts/nav/_admin_settings.html.haml2
-rw-r--r--app/views/layouts/nav/_dashboard.html.haml4
-rw-r--r--app/views/layouts/nav/_group_settings.html.haml2
-rw-r--r--app/views/layouts/nav/_project.html.haml2
-rw-r--r--app/views/layouts/notify.html.haml8
-rw-r--r--app/views/notify/build_fail_email.html.haml2
-rw-r--r--app/views/notify/build_success_email.html.haml2
-rw-r--r--app/views/notify/links/ci/builds/_build.html.haml2
-rw-r--r--app/views/notify/new_issue_email.html.haml2
-rw-r--r--app/views/notify/new_mention_in_issue_email.html.haml2
-rw-r--r--app/views/notify/new_mention_in_merge_request_email.html.haml2
-rw-r--r--app/views/notify/new_merge_request_email.html.haml2
-rw-r--r--app/views/notify/pipeline_failed_email.html.haml136
-rw-r--r--app/views/notify/pipeline_success_email.html.haml112
-rw-r--r--app/views/notify/project_was_not_exported_email.text.haml2
-rw-r--r--app/views/notify/repository_push_email.html.haml6
-rw-r--r--app/views/profiles/chat_names/new.html.haml2
-rw-r--r--app/views/profiles/keys/index.html.haml2
-rw-r--r--app/views/profiles/notifications/show.html.haml2
-rw-r--r--app/views/profiles/preferences/show.html.haml4
-rw-r--r--app/views/profiles/show.html.haml10
-rw-r--r--app/views/projects/_activity.html.haml2
-rw-r--r--app/views/projects/_bitbucket_import_modal.html.haml6
-rw-r--r--app/views/projects/_customize_workflow.html.haml2
-rw-r--r--app/views/projects/_find_file_link.html.haml6
-rw-r--r--app/views/projects/_gitlab_import_modal.html.haml6
-rw-r--r--app/views/projects/_home_panel.html.haml2
-rw-r--r--app/views/projects/_md_preview.html.haml20
-rw-r--r--app/views/projects/_wiki.html.haml2
-rw-r--r--app/views/projects/artifacts/_tree_directory.html.haml2
-rw-r--r--app/views/projects/artifacts/browse.html.haml2
-rw-r--r--app/views/projects/blame/show.html.haml2
-rw-r--r--app/views/projects/blob/_blob.html.haml2
-rw-r--r--app/views/projects/blob/_image.html.haml4
-rw-r--r--app/views/projects/blob/_new_dir.html.haml2
-rw-r--r--app/views/projects/blob/_remove.html.haml2
-rw-r--r--app/views/projects/blob/_upload.html.haml4
-rw-r--r--app/views/projects/blob/diff.html.haml8
-rw-r--r--app/views/projects/blob/preview.html.haml2
-rw-r--r--app/views/projects/blob/show.html.haml2
-rw-r--r--app/views/projects/boards/components/_card.html.haml2
-rw-r--r--app/views/projects/boards/components/sidebar/_assignee.html.haml2
-rw-r--r--app/views/projects/branches/_branch.html.haml4
-rw-r--r--app/views/projects/branches/index.html.haml4
-rw-r--r--app/views/projects/branches/new.html.haml2
-rw-r--r--app/views/projects/builds/_sidebar.html.haml8
-rw-r--r--app/views/projects/builds/index.html.haml2
-rw-r--r--app/views/projects/builds/show.html.haml4
-rw-r--r--app/views/projects/buttons/_dropdown.html.haml2
-rw-r--r--app/views/projects/buttons/_fork.html.haml2
-rw-r--r--app/views/projects/buttons/_star.html.haml4
-rw-r--r--app/views/projects/ci/builds/_build.html.haml2
-rw-r--r--app/views/projects/ci/pipelines/_pipeline.html.haml6
-rw-r--r--app/views/projects/commit/_change.html.haml6
-rw-r--r--app/views/projects/commit/_commit_box.html.haml2
-rw-r--r--app/views/projects/commit/show.html.haml2
-rw-r--r--app/views/projects/commits/_commit_list.html.haml2
-rw-r--r--app/views/projects/commits/show.html.haml4
-rw-r--r--app/views/projects/cycle_analytics/show.html.haml8
-rw-r--r--app/views/projects/deploy_keys/_deploy_key.html.haml3
-rw-r--r--app/views/projects/deploy_keys/_form.html.haml9
-rw-r--r--app/views/projects/deployments/_actions.haml2
-rw-r--r--app/views/projects/deployments/_commit.html.haml2
-rw-r--r--app/views/projects/diffs/_content.html.haml2
-rw-r--r--app/views/projects/diffs/_file.html.haml4
-rw-r--r--app/views/projects/diffs/_file_header.html.haml2
-rw-r--r--app/views/projects/diffs/_image.html.haml34
-rw-r--r--app/views/projects/diffs/_line.html.haml4
-rw-r--r--app/views/projects/diffs/_parallel_view.html.haml14
-rw-r--r--app/views/projects/diffs/_stats.html.haml9
-rw-r--r--app/views/projects/empty.html.haml2
-rw-r--r--app/views/projects/environments/index.html.haml2
-rw-r--r--app/views/projects/environments/terminal.html.haml6
-rw-r--r--app/views/projects/find_file/show.html.haml6
-rw-r--r--app/views/projects/forks/index.html.haml4
-rw-r--r--app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml2
-rw-r--r--app/views/projects/graphs/ci/_build_times.haml2
-rw-r--r--app/views/projects/graphs/ci/_builds.haml6
-rw-r--r--app/views/projects/graphs/show.html.haml2
-rw-r--r--app/views/projects/issues/_issue.html.haml2
-rw-r--r--app/views/projects/issues/_new_branch.html.haml5
-rw-r--r--app/views/projects/mattermosts/_team_selection.html.haml4
-rw-r--r--app/views/projects/merge_requests/_merge_request.html.haml2
-rw-r--r--app/views/projects/merge_requests/_new_submit.html.haml2
-rw-r--r--app/views/projects/merge_requests/_show.html.haml12
-rw-r--r--app/views/projects/merge_requests/conflicts.html.haml18
-rw-r--r--app/views/projects/merge_requests/conflicts/_commit_stats.html.haml8
-rw-r--r--app/views/projects/merge_requests/conflicts/_file_actions.html.haml4
-rw-r--r--app/views/projects/merge_requests/conflicts/_submit_form.html.haml2
-rw-r--r--app/views/projects/merge_requests/conflicts/components/_diff_file_editor.html.haml2
-rw-r--r--app/views/projects/merge_requests/conflicts/components/_inline_conflict_lines.html.haml18
-rw-r--r--app/views/projects/merge_requests/show/_how_to_merge.html.haml4
-rw-r--r--app/views/projects/merge_requests/show/_mr_box.html.haml2
-rw-r--r--app/views/projects/merge_requests/show/_versions.html.haml6
-rw-r--r--app/views/projects/merge_requests/widget/_closed.html.haml2
-rw-r--r--app/views/projects/merge_requests/widget/_heading.html.haml6
-rw-r--r--app/views/projects/merge_requests/widget/_open.html.haml4
-rw-r--r--app/views/projects/merge_requests/widget/open/_archived.html.haml2
-rw-r--r--app/views/projects/merge_requests/widget/open/_conflicts.html.haml30
-rw-r--r--app/views/projects/merge_requests/widget/open/_nothing.html.haml2
-rw-r--r--app/views/projects/milestones/_form.html.haml2
-rw-r--r--app/views/projects/new.html.haml2
-rw-r--r--app/views/projects/notes/_form.html.haml2
-rw-r--r--app/views/projects/notes/_note.html.haml6
-rw-r--r--app/views/projects/pipelines/_info.html.haml2
-rw-r--r--app/views/projects/pipelines/index.html.haml8
-rw-r--r--app/views/projects/pipelines/show.html.haml2
-rw-r--r--app/views/projects/runners/_runner.html.haml2
-rw-r--r--app/views/projects/show.html.haml8
-rw-r--r--app/views/projects/snippets/show.html.haml2
-rw-r--r--app/views/projects/stage/_graph.html.haml5
-rw-r--r--app/views/projects/stage/_in_stage_group.html.haml2
-rw-r--r--app/views/projects/stage/_stage.html.haml6
-rw-r--r--app/views/projects/tags/_tag.html.haml41
-rw-r--r--app/views/projects/tags/index.html.haml12
-rw-r--r--app/views/projects/tags/new.html.haml2
-rw-r--r--app/views/projects/tags/show.html.haml16
-rw-r--r--app/views/projects/tree/_blob_item.html.haml2
-rw-r--r--app/views/projects/tree/_submodule_item.html.haml2
-rw-r--r--app/views/projects/tree/_tree_content.html.haml6
-rw-r--r--app/views/projects/tree/_tree_header.html.haml8
-rw-r--r--app/views/projects/tree/_tree_item.html.haml2
-rw-r--r--app/views/projects/variables/index.html.haml2
-rw-r--r--app/views/projects/wikis/_new.html.haml4
-rw-r--r--app/views/search/_category.html.haml26
-rw-r--r--app/views/shared/_clone_panel.html.haml4
-rw-r--r--app/views/shared/_confirm_modal.html.haml4
-rw-r--r--app/views/shared/_file_highlight.html.haml4
-rw-r--r--app/views/shared/_label.html.haml4
-rw-r--r--app/views/shared/_milestones_filter.html.haml6
-rw-r--r--app/views/shared/_nav_scroll.html.haml2
-rw-r--r--app/views/shared/_sort_dropdown.html.haml2
-rw-r--r--app/views/shared/groups/_group.html.haml2
-rw-r--r--app/views/shared/issuable/_filter.html.haml12
-rw-r--r--app/views/shared/issuable/_form.html.haml2
-rw-r--r--app/views/shared/issuable/_label_dropdown.html.haml2
-rw-r--r--app/views/shared/issuable/_label_page_default.html.haml2
-rw-r--r--app/views/shared/issuable/_nav.html.haml10
-rw-r--r--app/views/shared/issuable/_participants.html.haml4
-rw-r--r--app/views/shared/issuable/_sidebar.html.haml14
-rw-r--r--app/views/shared/members/_group.html.haml2
-rw-r--r--app/views/shared/milestones/_issuable.html.haml6
-rw-r--r--app/views/shared/milestones/_milestone.html.haml2
-rw-r--r--app/views/shared/notifications/_custom_notifications.html.haml2
-rw-r--r--app/views/shared/projects/_project.html.haml2
-rw-r--r--app/views/shared/snippets/_blob.html.haml2
-rw-r--r--app/views/sherlock/file_samples/show.html.haml2
-rw-r--r--app/views/sherlock/queries/_general.html.haml4
-rw-r--r--app/views/sherlock/queries/show.html.haml4
-rw-r--r--app/views/sherlock/transactions/index.html.haml2
-rw-r--r--app/views/sherlock/transactions/show.html.haml6
-rw-r--r--app/views/u2f/_authenticate.html.haml17
-rw-r--r--app/views/u2f/_register.html.haml2
-rw-r--r--app/views/users/calendar.html.haml2
-rw-r--r--app/views/users/show.html.haml8
-rw-r--r--app/workers/project_cache_worker.rb23
-rw-r--r--changelogs/unreleased/18786-go-to-a-project-order.yml4
-rw-r--r--changelogs/unreleased/19988-prevent-empty-pagination-when-list-not-empty.yml4
-rw-r--r--changelogs/unreleased/21135-resolve-these-conflicts-link-is-too-subtle.yml4
-rw-r--r--changelogs/unreleased/24820-buttons-in-the-branches-page-are-stacking-on-top-of-each-other-depending-on-the-selected-filter.yml4
-rw-r--r--changelogs/unreleased/24876-page-jumps-to-wrong-position-when-clicking-a-comment-anchor.yml4
-rw-r--r--changelogs/unreleased/24941-login-tabs-border.yml4
-rw-r--r--changelogs/unreleased/25430-make-colors-in-runner-status-more-colorblind-friendly.yml4
-rw-r--r--changelogs/unreleased/25705-your-commands-have-been-executed-is-overkill.yml3
-rw-r--r--changelogs/unreleased/25829-update-username-button-remains-disabled-upon-failure.yml4
-rw-r--r--changelogs/unreleased/25930-discussion-actions-overlap-header-text.yml4
-rw-r--r--changelogs/unreleased/25996-Move-award-emoji-out-of-the-discussion-tab-for-MR.yml4
-rw-r--r--changelogs/unreleased/26018-mini-pipeline-hover-cross-broswer.yml4
-rw-r--r--changelogs/unreleased/26126-cache-even-when-no-projects.yml4
-rw-r--r--changelogs/unreleased/26134-ctrl-enter-does-not-submit-a-new-merge-request.yml4
-rw-r--r--changelogs/unreleased/26192-fixes-too-short-input.yml4
-rw-r--r--changelogs/unreleased/26218-rety-button-pipeline-builds-name-drodown-broken.yml4
-rw-r--r--changelogs/unreleased/7898-fixes-issue-boards-list-colored-top-border-visual-glitch.yml4
-rw-r--r--changelogs/unreleased/dz-improve-admin-group-routing.yml4
-rw-r--r--changelogs/unreleased/dz-nested-group-misc.yml4
-rw-r--r--changelogs/unreleased/dz-rename-invalid-users.yml4
-rw-r--r--changelogs/unreleased/dz-rename-reserved-project-names.yml4
-rw-r--r--changelogs/unreleased/feature-1376-allow-write-access-deploy-keys.yml4
-rw-r--r--changelogs/unreleased/feature-admin-merge-groups-and-projects.yml4
-rw-r--r--changelogs/unreleased/feature-more-storage-statistics.yml4
-rw-r--r--changelogs/unreleased/filename-to-file-path.yml3
-rw-r--r--changelogs/unreleased/fix-api-deprecation.yml4
-rw-r--r--changelogs/unreleased/fix-boards-search-typo.yml4
-rw-r--r--changelogs/unreleased/fix-light-hr-in-descriptions.yml4
-rw-r--r--changelogs/unreleased/fix-mentioned-issue-text-grammar.yml4
-rw-r--r--changelogs/unreleased/gfm-new-line-fix.yml4
-rw-r--r--changelogs/unreleased/issue_22664.yml4
-rw-r--r--changelogs/unreleased/issue_25578.yml4
-rw-r--r--changelogs/unreleased/issue_25682.yml4
-rw-r--r--changelogs/unreleased/issues-8081.yml4
-rw-r--r--changelogs/unreleased/mentioned-but-not-closed-issues-messages.yml4
-rw-r--r--changelogs/unreleased/re-style-issue-new-branch.yml3
-rw-r--r--changelogs/unreleased/view-ce-vs-ee.yml4
-rw-r--r--config/initializers/inflections.rb2
-rw-r--r--config/routes/admin.rb3
-rw-r--r--db/migrate/20160811172945_add_can_push_to_keys.rb14
-rw-r--r--db/migrate/20161201155511_create_project_statistics.rb20
-rw-r--r--db/migrate/20161201160452_migrate_project_statistics.rb23
-rw-r--r--db/migrate/20161226122833_remove_dot_git_from_usernames.rb87
-rw-r--r--db/migrate/20161227192806_rename_slack_and_mattermost_notification_services.rb25
-rw-r--r--db/post_migrate/20161221140236_remove_unneeded_services.rb2
-rw-r--r--db/post_migrate/20161221153951_rename_reserved_project_names.rb135
-rw-r--r--db/schema.rb21
-rw-r--r--doc/administration/build_artifacts.md6
-rw-r--r--doc/api/deploy_keys.md19
-rw-r--r--doc/api/groups.md8
-rw-r--r--doc/api/projects.md4
-rw-r--r--doc/api/settings.md1
-rw-r--r--doc/ci/README.md2
-rw-r--r--doc/ci/autodeploy/img/auto_deploy_button.pngbin0 -> 43441 bytes
-rw-r--r--doc/ci/autodeploy/img/auto_deploy_dropdown.pngbin0 -> 44380 bytes
-rw-r--r--doc/ci/autodeploy/img/autodeploy_button.pngbin41799 -> 0 bytes
-rw-r--r--doc/ci/autodeploy/img/autodeploy_dropdown.pngbin51420 -> 0 bytes
-rw-r--r--doc/ci/autodeploy/index.md14
-rw-r--r--doc/development/sidekiq_debugging.md13
-rw-r--r--doc/development/ux_guide/animation.md17
-rw-r--r--doc/development/ux_guide/basics.md14
-rw-r--r--doc/development/ux_guide/copy.md8
-rw-r--r--doc/development/ux_guide/img/animation-autoscroll.gifbin0 -> 302217 bytes
-rw-r--r--doc/development/ux_guide/img/animation-reorder.gifbin0 -> 70515 bytes
-rw-r--r--doc/workflow/lfs/lfs_administration.md8
-rw-r--r--features/admin/deploy_keys.feature23
-rw-r--r--features/steps/admin/deploy_keys.rb59
-rw-r--r--lib/api/entities.rb41
-rw-r--r--lib/api/groups.rb31
-rw-r--r--lib/api/helpers.rb4
-rw-r--r--lib/api/notes.rb2
-rw-r--r--lib/api/projects.rb82
-rw-r--r--lib/api/settings.rb118
-rw-r--r--lib/gitlab/auth/result.rb3
-rw-r--r--lib/gitlab/checks/change_access.rb11
-rw-r--r--lib/gitlab/diff/file_collection/merge_request_diff.rb5
-rw-r--r--lib/gitlab/email/reply_parser.rb36
-rw-r--r--lib/gitlab/git_access.rb152
-rw-r--r--lib/gitlab/git_access_wiki.rb4
-rw-r--r--lib/gitlab/template/gitlab_ci_yml_template.rb4
-rw-r--r--lib/gitlab/themes.rb2
-rw-r--r--lib/gitlab/user_access.rb16
-rw-r--r--lib/tasks/gitlab/import.rake3
-rw-r--r--lib/tasks/gitlab/update_commit_count.rake20
-rw-r--r--spec/controllers/dashboard/todos_controller_spec.rb37
-rw-r--r--spec/controllers/projects/blob_controller_spec.rb32
-rw-r--r--spec/controllers/projects/issues_controller_spec.rb30
-rw-r--r--spec/controllers/projects/merge_requests_controller_spec.rb22
-rw-r--r--spec/controllers/projects/snippets_controller_spec.rb22
-rw-r--r--spec/factories/lfs_objects.rb2
-rw-r--r--spec/factories/project_statistics.rb6
-rw-r--r--spec/features/admin/admin_groups_spec.rb10
-rw-r--r--spec/features/admin/admin_users_spec.rb17
-rw-r--r--spec/features/auto_deploy_spec.rb14
-rw-r--r--spec/features/boards/boards_spec.rb6
-rw-r--r--spec/features/dashboard/datetime_on_tooltips_spec.rb2
-rw-r--r--spec/features/groups_spec.rb13
-rw-r--r--spec/features/issuables/default_sort_order_spec.rb10
-rw-r--r--spec/features/issues/gfm_autocomplete_spec.rb25
-rw-r--r--spec/features/issues/user_uses_slash_commands_spec.rb8
-rw-r--r--spec/features/merge_requests/closes_issues_spec.rb4
-rw-r--r--spec/features/merge_requests/merge_request_versions_spec.rb4
-rw-r--r--spec/features/merge_requests/user_uses_slash_commands_spec.rb6
-rw-r--r--spec/features/milestones/milestones_spec.rb6
-rw-r--r--spec/features/projects/features_visibility_spec.rb11
-rw-r--r--spec/features/tags/master_creates_tag_spec.rb2
-rw-r--r--spec/features/u2f_spec.rb119
-rw-r--r--spec/helpers/storage_helper_spec.rb21
-rw-r--r--spec/javascripts/fixtures/project_title.html.haml2
-rw-r--r--spec/javascripts/project_title_spec.js1
-rw-r--r--spec/javascripts/u2f/authenticate_spec.js23
-rw-r--r--spec/lib/banzai/reference_parser/external_issue_parser_spec.rb12
-rw-r--r--spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb19
-rw-r--r--spec/lib/gitlab/email/reply_parser_spec.rb2
-rw-r--r--spec/lib/gitlab/git_access_spec.rb40
-rw-r--r--spec/lib/gitlab/git_access_wiki_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml1
-rw-r--r--spec/lib/gitlab/import_export/safe_model_attributes.yml1
-rw-r--r--spec/migrations/remove_dot_git_from_usernames.rb29
-rw-r--r--spec/migrations/rename_reserved_project_names_spec.rb47
-rw-r--r--spec/models/ci/build_spec.rb26
-rw-r--r--spec/models/ci/pipeline_spec.rb13
-rw-r--r--spec/models/concerns/issuable_spec.rb39
-rw-r--r--spec/models/deploy_key_spec.rb14
-rw-r--r--spec/models/group_spec.rb11
-rw-r--r--spec/models/key_spec.rb14
-rw-r--r--spec/models/lfs_objects_project_spec.rb36
-rw-r--r--spec/models/merge_request_spec.rb10
-rw-r--r--spec/models/namespace_spec.rb45
-rw-r--r--spec/models/project_spec.rb21
-rw-r--r--spec/models/project_statistics_spec.rb160
-rw-r--r--spec/policies/group_policy_spec.rb66
-rw-r--r--spec/requests/api/groups_spec.rb33
-rw-r--r--spec/requests/api/helpers_spec.rb4
-rw-r--r--spec/requests/api/projects_spec.rb56
-rw-r--r--spec/requests/api/settings_spec.rb3
-rw-r--r--spec/requests/git_http_spec.rb16
-rw-r--r--spec/routing/admin_routing_spec.rb10
-rw-r--r--spec/services/git_push_service_spec.rb4
-rw-r--r--spec/services/groups/create_service_spec.rb31
-rw-r--r--spec/services/issuable/bulk_update_service_spec.rb12
-rw-r--r--spec/services/issues/create_service_spec.rb2
-rw-r--r--spec/services/issues/update_service_spec.rb11
-rw-r--r--spec/services/merge_requests/create_service_spec.rb2
-rw-r--r--spec/services/merge_requests/merge_request_diff_cache_service_spec.rb1
-rw-r--r--spec/services/notes/slash_commands_service_spec.rb2
-rw-r--r--spec/services/users/refresh_authorized_projects_service_spec.rb37
-rw-r--r--spec/support/fake_u2f_device.rb3
-rw-r--r--spec/support/features/issuable_slash_commands_shared_examples.rb22
-rw-r--r--spec/support/services/issuable_create_service_shared_examples.rb52
-rw-r--r--spec/support/services/issuable_create_service_slash_commands_shared_examples.rb4
-rw-r--r--spec/support/services/issuable_update_service_shared_examples.rb52
-rw-r--r--spec/workers/project_cache_worker_spec.rb42
-rw-r--r--[l---------]vendor/gitignore/Clojure.gitignore14
473 files changed, 3660 insertions, 1563 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index b256e8a2a5f..e0e780e1e6b 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -425,7 +425,7 @@ notify:slack:
SETUP_DB: "false"
USE_BUNDLE_INSTALL: "false"
script:
- - ./scripts/notify_slack.sh "#development" "Build on \`$CI_BUILD_REF_NAME\` failed! Commit \`$(git log -1 --oneline)\` See <https://gitlab.com/gitlab-org/$(basename "$PWD")/commit/"$CI_BUILD_REF"/builds>"
+ - ./scripts/notify_slack.sh "#development" "Build on \`$CI_BUILD_REF_NAME\` failed! Commit \`$(git log -1 --oneline)\` See <https://gitlab.com/gitlab-org/$(basename "$PWD")/commit/"$CI_BUILD_REF"/pipelines>"
when: on_failure
only:
- master@gitlab-org/gitlab-ce
diff --git a/.haml-lint.yml b/.haml-lint.yml
index da9a43d9c6d..7c8a9c4fd17 100644
--- a/.haml-lint.yml
+++ b/.haml-lint.yml
@@ -7,10 +7,10 @@ exclude:
linters:
AltText:
- enabled: false
+ enabled: true
ClassAttributeWithStaticValue:
- enabled: false
+ enabled: true
ClassesBeforeIds:
enabled: false
@@ -29,14 +29,14 @@ linters:
enabled: true
FinalNewline:
- enabled: false
+ enabled: true
present: true
HtmlAttributes:
- enabled: false
+ enabled: true
ImplicitDiv:
- enabled: false
+ enabled: true
LeadingCommentSpace:
enabled: false
@@ -80,10 +80,10 @@ linters:
enabled: false
SpaceBeforeScript:
- enabled: false
+ enabled: true
SpaceInsideHashAttributes:
- enabled: false
+ enabled: true
style: space
Indentation:
@@ -94,7 +94,7 @@ linters:
enabled: true
TrailingWhitespace:
- enabled: false
+ enabled: true
UnnecessaryInterpolation:
enabled: false
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c57ba82e38c..1baf5b3257c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,22 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 8.15.2 (2016-12-27)
+
+- Fix finding the latest pipeline. !8301
+- Fix mr list timestamp alignment. !8271
+- Fix discussion overlap text in regular screens. !8273
+- Fixes mini-pipeline-graph dropdown animation and stage position in chrome, firefox and safari. !8282
+- Fix line breaking in nodes of the pipeline graph in firefox. !8292
+- Fixes confendential warning text alignment. !8293
+- Hide Scroll Top button for failed build page. !8295
+- Fix finding the latest pipeline. !8301
+- Disable PostgreSQL statement timeouts when removing unneeded services. !8322
+- Fix timeout when MR contains large files marked as binary by .gitattributes.
+- Rename "autodeploy" to "auto deploy".
+- Fixed GFM autocomplete error when no data exists.
+- Fixed resolve discussion note button color.
+
## 8.15.1 (2016-12-23)
- Push payloads schedule at most 100 commits, instead of all commits.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 659871a06a4..b68c4a67826 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -217,8 +217,8 @@ We welcome merge requests with fixes and improvements to GitLab code, tests,
and/or documentation. The features we would really like a merge request for are
listed with the label [`Accepting Merge Requests` on our issue tracker for CE][accepting-mrs-ce]
and [EE][accepting-mrs-ee] but other improvements are also welcome. Please note
-that if an issue is marked for the current milestone either before or while you
-are working on it, a team member may take over the merge request in order to
+that if an issue is marked for the current milestone either before or while you
+are working on it, a team member may take over the merge request in order to
ensure the work is finished before the release date.
If you want to add a new feature that is not labeled it is best to first create
@@ -300,6 +300,7 @@ you start with a very simple UI? Can you do part of the refactor? The increased
reviewability of small MRs that leads to higher code quality is more important
to us than having a minimal commit log. The smaller an MR is the more likely it
is it will be merged (quickly). After that you can send more MRs to enhance it.
+The ['How to get faster PR reviews' document of Kubernetes](https://github.com/kubernetes/community/blob/master/contributors/devel/faster_reviews.md) also has some great points regarding this.
For examples of feedback on merge requests please look at already
[closed merge requests][closed-merge-requests]. If you would like quick feedback
diff --git a/Gemfile b/Gemfile
index 9dfaf7a48a2..eadb67405ee 100644
--- a/Gemfile
+++ b/Gemfile
@@ -332,7 +332,7 @@ gem 'octokit', '~> 4.3.0'
gem 'mail_room', '~> 0.9.0'
-gem 'email_reply_parser', '~> 0.5.8'
+gem 'email_reply_trimmer', '~> 0.1'
gem 'html2text'
gem 'ruby-prof', '~> 0.16.2'
@@ -347,5 +347,5 @@ gem 'paranoia', '~> 2.2'
gem 'health_check', '~> 2.2.0'
# System information
-gem 'vmstat', '~> 2.2'
+gem 'vmstat', '~> 2.3.0'
gem 'sys-filesystem', '~> 1.1.6'
diff --git a/Gemfile.lock b/Gemfile.lock
index 9f8367b420a..1c192d2fc6e 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -167,7 +167,7 @@ GEM
railties (>= 4.2)
dropzonejs-rails (0.7.2)
rails (> 3.1)
- email_reply_parser (0.5.8)
+ email_reply_trimmer (0.1.6)
email_spec (1.6.0)
launchy (~> 2.1)
mail (~> 2.2)
@@ -773,7 +773,7 @@ GEM
coercible (~> 1.0)
descendants_tracker (~> 0.0, >= 0.0.3)
equalizer (~> 0.0, >= 0.0.9)
- vmstat (2.2.0)
+ vmstat (2.3.0)
warden (1.2.6)
rack (>= 1.0)
web-console (2.3.0)
@@ -839,7 +839,7 @@ DEPENDENCIES
diffy (~> 3.1.0)
doorkeeper (~> 4.2.0)
dropzonejs-rails (~> 0.7.1)
- email_reply_parser (~> 0.5.8)
+ email_reply_trimmer (~> 0.1)
email_spec (~> 1.6.0)
factory_girl_rails (~> 4.7.0)
ffaker (~> 2.0.0)
@@ -982,7 +982,7 @@ DEPENDENCIES
unicorn-worker-killer (~> 0.4.4)
version_sorter (~> 2.1.0)
virtus (~> 1.0.1)
- vmstat (~> 2.2)
+ vmstat (~> 2.3.0)
web-console (~> 2.0)
webmock (~> 1.21.0)
wikicloth (= 0.8.1)
diff --git a/PROCESS.md b/PROCESS.md
index 8af660fbdd1..cbeb781cd3c 100644
--- a/PROCESS.md
+++ b/PROCESS.md
@@ -69,7 +69,8 @@ to add details to the issue.
- ~UX needs help from a UX designer
- ~Frontend needs help from a Front-end engineer. Please follow the
["Implement design & UI elements" guidelines].
-- ~up-for-grabs is an issue suitable for first-time contributors, of reasonable difficulty and size. Not exclusive with other labels.
+- ~"Accepting Merge Requests" is a low priority, well-defined issue that we
+ encourage people to contribute to. Not exclusive with other labels.
- ~"feature proposal" is a proposal for a new feature for GitLab. People are encouraged to vote
in support or comment for further detail. Do not use `feature request`.
- ~bug is an issue reporting undesirable or incorrect behavior.
diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js
index 043c6a11c4f..e43afbb4cc9 100644
--- a/app/assets/javascripts/application.js
+++ b/app/assets/javascripts/application.js
@@ -89,6 +89,14 @@
// Set the default path for all cookies to GitLab's root directory
Cookies.defaults.path = gon.relative_url_root || '/';
+ // `hashchange` is not triggered when link target is already in window.location
+ $body.on('click', 'a[href^="#"]', function() {
+ var href = this.getAttribute('href');
+ if (href.substr(1) === gl.utils.getLocationHash()) {
+ setTimeout(gl.utils.handleLocationHash, 1);
+ }
+ });
+
// prevent default action for disabled buttons
$('.btn').click(function(e) {
if ($(this).hasClass('disabled')) {
diff --git a/app/assets/javascripts/boards/boards_bundle.js.es6 b/app/assets/javascripts/boards/boards_bundle.js.es6
index ab2343c72fc..2c9ab61c94d 100644
--- a/app/assets/javascripts/boards/boards_bundle.js.es6
+++ b/app/assets/javascripts/boards/boards_bundle.js.es6
@@ -73,7 +73,7 @@ $(() => {
});
gl.IssueBoardsSearch = new Vue({
- el: '#js-boards-seach',
+ el: '#js-boards-search',
data: {
filters: Store.state.filters
},
diff --git a/app/assets/javascripts/boards/models/issue.js.es6 b/app/assets/javascripts/boards/models/issue.js.es6
index 1199e022ff1..cd942c8332b 100644
--- a/app/assets/javascripts/boards/models/issue.js.es6
+++ b/app/assets/javascripts/boards/models/issue.js.es6
@@ -71,3 +71,5 @@ class ListIssue {
return Vue.http.patch(url, data);
}
}
+
+window.ListIssue = ListIssue;
diff --git a/app/assets/javascripts/boards/models/label.js.es6 b/app/assets/javascripts/boards/models/label.js.es6
index 8f20a1bbec7..9af88d167d6 100644
--- a/app/assets/javascripts/boards/models/label.js.es6
+++ b/app/assets/javascripts/boards/models/label.js.es6
@@ -10,3 +10,5 @@ class ListLabel {
this.priority = (obj.priority !== null) ? obj.priority : Infinity;
}
}
+
+window.ListLabel = ListLabel;
diff --git a/app/assets/javascripts/boards/models/list.js.es6 b/app/assets/javascripts/boards/models/list.js.es6
index a8d38c16485..fb13f529b11 100644
--- a/app/assets/javascripts/boards/models/list.js.es6
+++ b/app/assets/javascripts/boards/models/list.js.es6
@@ -148,3 +148,5 @@ class List {
});
}
}
+
+window.List = List;
diff --git a/app/assets/javascripts/boards/models/milestone.js.es6 b/app/assets/javascripts/boards/models/milestone.js.es6
index 9c173c1b70b..c867b06d320 100644
--- a/app/assets/javascripts/boards/models/milestone.js.es6
+++ b/app/assets/javascripts/boards/models/milestone.js.es6
@@ -6,3 +6,5 @@ class ListMilestone {
this.title = obj.title;
}
}
+
+window.ListMilestone = ListMilestone;
diff --git a/app/assets/javascripts/boards/models/user.js.es6 b/app/assets/javascripts/boards/models/user.js.es6
index a8a3892e2ad..8e9de4d4cbb 100644
--- a/app/assets/javascripts/boards/models/user.js.es6
+++ b/app/assets/javascripts/boards/models/user.js.es6
@@ -8,3 +8,5 @@ class ListUser {
this.avatar = user.avatar_url;
}
}
+
+window.ListUser = ListUser;
diff --git a/app/assets/javascripts/boards/services/board_service.js.es6 b/app/assets/javascripts/boards/services/board_service.js.es6
index 189a8703198..f18633f0913 100644
--- a/app/assets/javascripts/boards/services/board_service.js.es6
+++ b/app/assets/javascripts/boards/services/board_service.js.es6
@@ -65,4 +65,6 @@ class BoardService {
issue
});
}
-};
+}
+
+window.BoardService = BoardService;
diff --git a/app/assets/javascripts/build.js b/app/assets/javascripts/build.js
index 5e449170cd3..bc13c46443a 100644
--- a/app/assets/javascripts/build.js
+++ b/app/assets/javascripts/build.js
@@ -92,8 +92,8 @@
success: function(buildData) {
$('.js-build-output').html(buildData.trace_html);
if (removeRefreshStatuses.indexOf(buildData.status) >= 0) {
- this.initScrollMonitor();
- return this.$buildRefreshAnimation.remove();
+ this.$buildRefreshAnimation.remove();
+ return this.initScrollMonitor();
}
}.bind(this)
});
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 88a19fc6e1d..5852b8bbdb7 100644
--- a/app/assets/javascripts/diff_notes/components/resolve_btn.js.es6
+++ b/app/assets/javascripts/diff_notes/components/resolve_btn.js.es6
@@ -59,9 +59,11 @@
},
methods: {
updateTooltip: function () {
- $(this.$refs.button)
- .tooltip('hide')
- .tooltip('fixTitle');
+ this.$nextTick(() => {
+ $(this.$refs.button)
+ .tooltip('hide')
+ .tooltip('fixTitle');
+ });
},
resolve: function () {
if (!this.canResolve) return;
@@ -90,7 +92,7 @@
new Flash('An error occurred when trying to resolve a comment. Please try again.', 'alert');
}
- this.$nextTick(this.updateTooltip);
+ this.updateTooltip();
});
}
},
diff --git a/app/assets/javascripts/diff_notes/models/discussion.js.es6 b/app/assets/javascripts/diff_notes/models/discussion.js.es6
index efcd46680a7..fa518ba4d33 100644
--- a/app/assets/javascripts/diff_notes/models/discussion.js.es6
+++ b/app/assets/javascripts/diff_notes/models/discussion.js.es6
@@ -92,3 +92,5 @@ class DiscussionModel {
return false;
}
}
+
+window.DiscussionModel = DiscussionModel;
diff --git a/app/assets/javascripts/diff_notes/models/note.js.es6 b/app/assets/javascripts/diff_notes/models/note.js.es6
index e3bce1d2038..f3a7cba5ef6 100644
--- a/app/assets/javascripts/diff_notes/models/note.js.es6
+++ b/app/assets/javascripts/diff_notes/models/note.js.es6
@@ -9,3 +9,5 @@ class NoteModel {
this.resolved_by = resolved_by;
}
}
+
+window.NoteModel = NoteModel;
diff --git a/app/assets/javascripts/dispatcher.js.es6 b/app/assets/javascripts/dispatcher.js.es6
index 6a84f52d8df..f09f496710f 100644
--- a/app/assets/javascripts/dispatcher.js.es6
+++ b/app/assets/javascripts/dispatcher.js.es6
@@ -64,6 +64,17 @@
new UsernameValidator();
new ActiveTabMemoizer();
break;
+ case 'sessions:create':
+ if (!gon.u2f) break;
+ window.gl.u2fAuthenticate = new gl.U2FAuthenticate(
+ $("#js-authenticate-u2f"),
+ '#js-login-u2f-form',
+ gon.u2f,
+ document.querySelector('#js-login-2fa-device'),
+ document.querySelector('.js-2fa-form'),
+ );
+ window.gl.u2fAuthenticate.start();
+ break;
case 'projects:boards:show':
case 'projects:boards:index':
shortcut_handler = new ShortcutsNavigation();
diff --git a/app/assets/javascripts/environments/services/environments_service.js.es6 b/app/assets/javascripts/environments/services/environments_service.js.es6
index 15ec7b76c3d..575a45d9802 100644
--- a/app/assets/javascripts/environments/services/environments_service.js.es6
+++ b/app/assets/javascripts/environments/services/environments_service.js.es6
@@ -20,3 +20,5 @@ class EnvironmentsService {
return this.environments.get();
}
}
+
+window.EnvironmentsService = EnvironmentsService;
diff --git a/app/assets/javascripts/gfm_auto_complete.js.es6 b/app/assets/javascripts/gfm_auto_complete.js.es6
index 12875eaa1c3..87c579ac757 100644
--- a/app/assets/javascripts/gfm_auto_complete.js.es6
+++ b/app/assets/javascripts/gfm_auto_complete.js.es6
@@ -77,7 +77,7 @@
var _a, _y, regexp, match, atSymbolsWithBar, atSymbolsWithoutBar;
atSymbolsWithBar = Object.keys(this.app.controllers).join('|');
atSymbolsWithoutBar = Object.keys(this.app.controllers).join('');
- subtext = subtext.split(' ').pop();
+ subtext = subtext.split(/\s+/g).pop();
flag = flag.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
_a = decodeURI("%C3%80");
@@ -367,7 +367,7 @@
return $input.trigger('keyup');
},
isLoading(data) {
- if (!data) return false;
+ if (!data || !data.length) return false;
if (Array.isArray(data)) data = data[0];
return data === this.defaultLoadingData[0] || data.name === this.defaultLoadingData[0];
},
diff --git a/app/assets/javascripts/issue.js b/app/assets/javascripts/issue.js
index 63b70d4be17..61e8531153b 100644
--- a/app/assets/javascripts/issue.js
+++ b/app/assets/javascripts/issue.js
@@ -139,15 +139,12 @@
return;
}
return $.getJSON($container.data('path')).error(function() {
- $container.find('.checking').hide();
$container.find('.unavailable').show();
return new Flash('Failed to check if a new branch can be created.', 'alert');
}).success(function(data) {
if (data.can_create_branch) {
- $container.find('.checking').hide();
$container.find('.available').show();
} else {
- $container.find('.checking').hide();
return $container.find('.unavailable').show();
}
});
diff --git a/app/assets/javascripts/preview_markdown.js b/app/assets/javascripts/preview_markdown.js
index 1e261cd49c2..89f7e976934 100644
--- a/app/assets/javascripts/preview_markdown.js
+++ b/app/assets/javascripts/preview_markdown.js
@@ -1,14 +1,17 @@
-/* eslint-disable func-names, space-before-function-paren, no-var, one-var, one-var-declaration-per-line, wrap-iife, no-else-return, consistent-return, object-shorthand, comma-dangle, no-param-reassign, padded-blocks, camelcase, prefer-arrow-callback, max-len */
+/* eslint-disable func-names, no-var, object-shorthand, comma-dangle, prefer-arrow-callback */
// MarkdownPreview
//
// Handles toggling the "Write" and "Preview" tab clicks, rendering the preview,
// and showing a warning when more than `x` users are referenced.
//
-(function() {
- var lastTextareaPreviewed, markdownPreview, previewButtonSelector, writeButtonSelector;
+(function () {
+ var lastTextareaPreviewed;
+ var markdownPreview;
+ var previewButtonSelector;
+ var writeButtonSelector;
- window.MarkdownPreview = (function() {
+ window.MarkdownPreview = (function () {
function MarkdownPreview() {}
// Minimum number of users referenced before triggering a warning
@@ -16,73 +19,71 @@
MarkdownPreview.prototype.ajaxCache = {};
- MarkdownPreview.prototype.showPreview = function(form) {
- var mdText, preview;
- preview = form.find('.js-md-preview');
- mdText = form.find('textarea.markdown-area').val();
+ MarkdownPreview.prototype.showPreview = function ($form) {
+ var mdText;
+ var preview = $form.find('.js-md-preview');
+ if (preview.hasClass('md-preview-loading')) {
+ return;
+ }
+ mdText = $form.find('textarea.markdown-area').val();
+
if (mdText.trim().length === 0) {
preview.text('Nothing to preview.');
- return this.hideReferencedUsers(form);
+ this.hideReferencedUsers($form);
} else {
- preview.text('Loading...');
- return this.renderMarkdown(mdText, (function(_this) {
- return function(response) {
- preview.html(response.body);
- preview.renderGFM();
- return _this.renderReferencedUsers(response.references.users, form);
- };
- })(this));
+ preview.addClass('md-preview-loading').text('Loading...');
+ this.fetchMarkdownPreview(mdText, (function (response) {
+ preview.removeClass('md-preview-loading').html(response.body);
+ preview.renderGFM();
+ this.renderReferencedUsers(response.references.users, $form);
+ }).bind(this));
}
};
- MarkdownPreview.prototype.renderMarkdown = function(text, success) {
+ MarkdownPreview.prototype.fetchMarkdownPreview = function (text, success) {
if (!window.preview_markdown_path) {
return;
}
if (text === this.ajaxCache.text) {
- return success(this.ajaxCache.response);
+ success(this.ajaxCache.response);
+ return;
}
- return $.ajax({
+ $.ajax({
type: 'POST',
url: window.preview_markdown_path,
data: {
text: text
},
dataType: 'json',
- success: (function(_this) {
- return function(response) {
- _this.ajaxCache = {
- text: text,
- response: response
- };
- return success(response);
+ success: (function (response) {
+ this.ajaxCache = {
+ text: text,
+ response: response
};
- })(this)
+ success(response);
+ }).bind(this)
});
};
- MarkdownPreview.prototype.hideReferencedUsers = function(form) {
- var referencedUsers;
- referencedUsers = form.find('.referenced-users');
- return referencedUsers.hide();
+ MarkdownPreview.prototype.hideReferencedUsers = function ($form) {
+ $form.find('.referenced-users').hide();
};
- MarkdownPreview.prototype.renderReferencedUsers = function(users, form) {
+ MarkdownPreview.prototype.renderReferencedUsers = function (users, $form) {
var referencedUsers;
- referencedUsers = form.find('.referenced-users');
+ referencedUsers = $form.find('.referenced-users');
if (referencedUsers.length) {
if (users.length >= this.referenceThreshold) {
referencedUsers.show();
- return referencedUsers.find('.js-referenced-users-count').text(users.length);
+ referencedUsers.find('.js-referenced-users-count').text(users.length);
} else {
- return referencedUsers.hide();
+ referencedUsers.hide();
}
}
};
return MarkdownPreview;
-
- })();
+ }());
markdownPreview = new window.MarkdownPreview();
@@ -92,19 +93,14 @@
lastTextareaPreviewed = null;
- $.fn.setupMarkdownPreview = function() {
- var $form, form_textarea;
- $form = $(this);
- form_textarea = $form.find('textarea.markdown-area');
- form_textarea.on('input', function() {
- return markdownPreview.hideReferencedUsers($form);
- });
- return form_textarea.on('blur', function() {
- return markdownPreview.showPreview($form);
+ $.fn.setupMarkdownPreview = function () {
+ var $form = $(this);
+ $form.find('textarea.markdown-area').on('input', function () {
+ markdownPreview.hideReferencedUsers($form);
});
};
- $(document).on('markdown-preview:show', function(e, $form) {
+ $(document).on('markdown-preview:show', function (e, $form) {
if (!$form) {
return;
}
@@ -115,10 +111,10 @@
// toggle content
$form.find('.md-write-holder').hide();
$form.find('.md-preview-holder').show();
- return markdownPreview.showPreview($form);
+ markdownPreview.showPreview($form);
});
- $(document).on('markdown-preview:hide', function(e, $form) {
+ $(document).on('markdown-preview:hide', function (e, $form) {
if (!$form) {
return;
}
@@ -129,34 +125,33 @@
// toggle content
$form.find('.md-write-holder').show();
$form.find('textarea.markdown-area').focus();
- return $form.find('.md-preview-holder').hide();
+ $form.find('.md-preview-holder').hide();
});
- $(document).on('markdown-preview:toggle', function(e, keyboardEvent) {
+ $(document).on('markdown-preview:toggle', function (e, keyboardEvent) {
var $target;
$target = $(keyboardEvent.target);
if ($target.is('textarea.markdown-area')) {
$(document).triggerHandler('markdown-preview:show', [$target.closest('form')]);
- return keyboardEvent.preventDefault();
+ keyboardEvent.preventDefault();
} else if (lastTextareaPreviewed) {
$target = lastTextareaPreviewed;
$(document).triggerHandler('markdown-preview:hide', [$target.closest('form')]);
- return keyboardEvent.preventDefault();
+ keyboardEvent.preventDefault();
}
});
- $(document).on('click', previewButtonSelector, function(e) {
+ $(document).on('click', previewButtonSelector, function (e) {
var $form;
e.preventDefault();
$form = $(this).closest('form');
- return $(document).triggerHandler('markdown-preview:show', [$form]);
+ $(document).triggerHandler('markdown-preview:show', [$form]);
});
- $(document).on('click', writeButtonSelector, function(e) {
+ $(document).on('click', writeButtonSelector, function (e) {
var $form;
e.preventDefault();
$form = $(this).closest('form');
- return $(document).triggerHandler('markdown-preview:hide', [$form]);
+ $(document).triggerHandler('markdown-preview:hide', [$form]);
});
-
-}).call(this);
+}());
diff --git a/app/assets/javascripts/profile/profile.js.es6 b/app/assets/javascripts/profile/profile.js.es6
index eb2fe3477a2..aef2e3a3fa8 100644
--- a/app/assets/javascripts/profile/profile.js.es6
+++ b/app/assets/javascripts/profile/profile.js.es6
@@ -41,15 +41,12 @@
}
beforeUpdateUsername() {
- $('.loading-username').show();
- $(this).find('.update-success').hide();
- return $(this).find('.update-failed').hide();
+ $('.loading-username', this).removeClass('hidden');
}
afterUpdateUsername() {
- $('.loading-username').hide();
- $(this).find('.btn-save').enable();
- return $(this).find('.loading-gif').hide();
+ $('.loading-username', this).addClass('hidden');
+ $('button[type=submit]', this).enable();
}
onUpdateNotifs(e, data) {
diff --git a/app/assets/javascripts/project_select.js b/app/assets/javascripts/project_select.js
index 650996700ba..38bc2e1c3a0 100644
--- a/app/assets/javascripts/project_select.js
+++ b/app/assets/javascripts/project_select.js
@@ -15,6 +15,7 @@
},
data: function(term, callback) {
var finalCallback, projectsCallback;
+ var orderBy = $dropdown.data('order-by');
finalCallback = function(projects) {
return callback(projects);
};
@@ -34,7 +35,7 @@
if (this.groupId) {
return Api.groupProjects(this.groupId, term, projectsCallback);
} else {
- return Api.projects(term, this.orderBy, projectsCallback);
+ return Api.projects(term, orderBy, projectsCallback);
}
},
url: function(project) {
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 08264ad3d2f..03f4531abf5 100644
--- a/app/assets/javascripts/protected_branches/protected_branch_dropdown.js.es6
+++ b/app/assets/javascripts/protected_branches/protected_branch_dropdown.js.es6
@@ -76,3 +76,5 @@ class ProtectedBranchDropdown {
this.$dropdownFooter.toggleClass('hidden', !branchName);
}
}
+
+window.ProtectedBranchDropdown = ProtectedBranchDropdown;
diff --git a/app/assets/javascripts/u2f/authenticate.js b/app/assets/javascripts/u2f/authenticate.js.es6
index e407b856e10..2b992109a8c 100644
--- a/app/assets/javascripts/u2f/authenticate.js
+++ b/app/assets/javascripts/u2f/authenticate.js.es6
@@ -8,21 +8,26 @@
// State Flow #1: setup -> in_progress -> authenticated -> POST to server
// State Flow #2: setup -> in_progress -> error -> setup
(function() {
+ const global = window.gl || (window.gl = {});
+
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
- this.U2FAuthenticate = (function() {
- function U2FAuthenticate(container, u2fParams) {
+ global.U2FAuthenticate = (function() {
+ function U2FAuthenticate(container, form, u2fParams, fallbackButton, fallbackUI) {
this.container = container;
this.renderNotSupported = bind(this.renderNotSupported, this);
this.renderAuthenticated = bind(this.renderAuthenticated, this);
this.renderError = bind(this.renderError, this);
this.renderInProgress = bind(this.renderInProgress, this);
- this.renderSetup = bind(this.renderSetup, this);
this.renderTemplate = bind(this.renderTemplate, this);
this.authenticate = bind(this.authenticate, this);
this.start = bind(this.start, this);
this.appId = u2fParams.app_id;
this.challenge = u2fParams.challenge;
+ this.form = form;
+ this.fallbackButton = fallbackButton;
+ this.fallbackUI = fallbackUI;
+ if (this.fallbackButton) this.fallbackButton.addEventListener('click', this.switchToFallbackUI.bind(this));
this.signRequests = u2fParams.sign_requests.map(function(request) {
// The U2F Javascript API v1.1 requires a single challenge, with
// _no challenges per-request_. The U2F Javascript API v1.0 requires a
@@ -41,7 +46,7 @@
U2FAuthenticate.prototype.start = function() {
if (U2FUtil.isU2FSupported()) {
- return this.renderSetup();
+ return this.renderInProgress();
} else {
return this.renderNotSupported();
}
@@ -77,11 +82,6 @@
return this.container.html(template(params));
};
- U2FAuthenticate.prototype.renderSetup = function() {
- this.renderTemplate('setup');
- return this.container.find('#js-login-u2f-device').on('click', this.renderInProgress);
- };
-
U2FAuthenticate.prototype.renderInProgress = function() {
this.renderTemplate('inProgress');
return this.authenticate();
@@ -92,22 +92,29 @@
error_message: error.message(),
error_code: error.errorCode
});
- return this.container.find('#js-u2f-try-again').on('click', this.renderSetup);
+ return this.container.find('#js-u2f-try-again').on('click', this.renderInProgress);
};
U2FAuthenticate.prototype.renderAuthenticated = function(deviceResponse) {
this.renderTemplate('authenticated');
- // Prefer to do this instead of interpolating using Underscore templates
- // because of JSON escaping issues.
- return this.container.find("#js-device-response").val(deviceResponse);
+ const container = this.container[0];
+ container.querySelector('#js-device-response').value = deviceResponse;
+ container.querySelector(this.form).submit();
+ this.fallbackButton.classList.add('hidden');
};
U2FAuthenticate.prototype.renderNotSupported = function() {
return this.renderTemplate('notSupported');
};
+ U2FAuthenticate.prototype.switchToFallbackUI = function() {
+ this.fallbackButton.classList.add('hidden');
+ this.container[0].classList.add('hidden');
+ this.fallbackUI.classList.remove('hidden');
+ };
+
return U2FAuthenticate;
})();
-}).call(this);
+})();
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index 251e43d2edd..67b5aa37ae7 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -26,6 +26,7 @@
.append-bottom-default { margin-bottom: $gl-padding; }
.inline { display: inline-block; }
.center { text-align: center; }
+.vertical-align-middle { vertical-align: middle; }
.underlined-link { text-decoration: underline; }
.hint { font-style: italic; color: $hint-color; }
@@ -57,16 +58,33 @@ pre {
border-radius: 0;
color: $well-pre-color;
}
+
+ &.wrap {
+ word-break: break-word;
+ white-space: pre-wrap;
+ }
}
hr {
margin: $gl-padding 0;
+ border-top: 1px solid darken($gray-normal, 8%);
}
.str-truncated {
@include str-truncated;
}
+.block-truncated {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+
+ > div,
+ .str-truncated {
+ display: inline;
+ }
+}
+
.item-title { font-weight: 600; }
/** FLASH message **/
diff --git a/app/assets/stylesheets/framework/gitlab-theme.scss b/app/assets/stylesheets/framework/gitlab-theme.scss
index 5cd242af91d..d6566dc4ec9 100644
--- a/app/assets/stylesheets/framework/gitlab-theme.scss
+++ b/app/assets/stylesheets/framework/gitlab-theme.scss
@@ -80,29 +80,32 @@
}
}
+ .about-gitlab {
+ color: $color-light;
+ }
}
}
}
-$theme-charcoal: #3d454d;
-$theme-charcoal-light: #485157;
-$theme-charcoal-dark: #383f45;
-$theme-charcoal-text: #b9bbbe;
+$theme-charcoal-light: #b9bbbe;
+$theme-charcoal: #485157;
+$theme-charcoal-dark: #3d454d;
+$theme-charcoal-darker: #383f45;
$theme-blue-light: #becde9;
$theme-blue: #2980b9;
$theme-blue-dark: #1970a9;
$theme-blue-darker: #096099;
-$theme-graphite-lighter: #ccc;
-$theme-graphite-light: #777;
-$theme-graphite: #666;
-$theme-graphite-dark: #555;
+$theme-graphite-light: #ccc;
+$theme-graphite: #777;
+$theme-graphite-dark: #666;
+$theme-graphite-darker: #555;
-$theme-gray-light: #979797;
-$theme-gray: #373737;
-$theme-gray-dark: #272727;
-$theme-gray-darker: #222;
+$theme-black-light: #979797;
+$theme-black: #373737;
+$theme-black-dark: #272727;
+$theme-black-darker: #222;
$theme-green-light: #adc;
$theme-green: #019875;
@@ -120,15 +123,15 @@ body {
}
&.ui_charcoal {
- @include gitlab-theme($theme-charcoal-text, $theme-charcoal-light, $theme-charcoal, $theme-charcoal-dark);
+ @include gitlab-theme($theme-charcoal-light, $theme-charcoal, $theme-charcoal-dark, $theme-charcoal-darker);
}
&.ui_graphite {
- @include gitlab-theme($theme-graphite-lighter, $theme-graphite-light, $theme-graphite, $theme-graphite-dark);
+ @include gitlab-theme($theme-graphite-light, $theme-graphite, $theme-graphite-dark, $theme-graphite-darker);
}
- &.ui_gray {
- @include gitlab-theme($theme-gray-light, $theme-gray, $theme-gray-dark, $theme-gray-darker);
+ &.ui_black {
+ @include gitlab-theme($theme-black-light, $theme-black, $theme-black-dark, $theme-black-darker);
}
&.ui_green {
diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss
index e96cd671e34..277d4202950 100644
--- a/app/assets/stylesheets/framework/lists.scss
+++ b/app/assets/stylesheets/framework/lists.scss
@@ -199,6 +199,7 @@ ul.content-list {
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
+ align-items: center;
white-space: nowrap;
}
@@ -208,6 +209,11 @@ ul.content-list {
padding-right: 8px;
}
+ .row-fixed-content {
+ flex: 0 0 auto;
+ margin-left: auto;
+ }
+
.row-title {
font-weight: 600;
}
@@ -239,7 +245,6 @@ ul.content-list {
}
ul.controls {
- padding-top: 1px;
float: right;
list-style: none;
diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss
index bbf9de06630..5abda13a963 100644
--- a/app/assets/stylesheets/framework/nav.scss
+++ b/app/assets/stylesheets/framework/nav.scss
@@ -116,8 +116,8 @@
padding-top: 16px;
padding-bottom: 11px;
display: inline-block;
- width: 50%;
line-height: 28px;
+ white-space: normal;
/* Small devices (phones, tablets, 768px and lower) */
@media (max-width: $screen-xs-max) {
@@ -158,30 +158,24 @@
}
.nav-controls {
- width: 50%;
display: inline-block;
float: right;
text-align: right;
padding: 11px 0;
margin-bottom: 0;
- > .dropdown {
- margin-right: $gl-padding-top;
- display: inline-block;
- vertical-align: top;
-
- &:last-child {
- margin-right: 0;
- }
- }
-
- > .btn {
+ > .btn,
+ > .btn-container,
+ > .dropdown,
+ > input,
+ > form {
margin-right: $gl-padding-top;
display: inline-block;
vertical-align: top;
&:last-child {
margin-right: 0;
+ float: right;
}
}
@@ -189,19 +183,21 @@
float: none;
}
- > form {
- display: inline-block;
- }
-
.icon-label {
display: none;
}
- input {
+ .btn,
+ .dropdown,
+ .dropdown-toggle,
+ input,
+ form {
height: 35px;
+ }
+
+ input {
display: inline-block;
position: relative;
- margin-right: $gl-padding-top;
/* Medium devices (desktops, 992px and up) */
@media (min-width: $screen-md-min) { width: 200px; }
@@ -225,6 +221,7 @@
.btn,
form,
.dropdown,
+ .dropdown-toggle,
.dropdown-menu-toggle,
.form-control {
margin: 0 0 10px;
@@ -263,6 +260,10 @@
.nav-text,
.nav-controls {
width: auto;
+
+ @media (max-width: $screen-xs-max) {
+ width: 100%;
+ }
}
}
@@ -275,6 +276,10 @@
padding: 17px 0;
}
}
+
+ pre {
+ width: 100%;
+ }
}
.layout-nav {
@@ -427,4 +432,41 @@
border-bottom: none;
}
}
-} \ No newline at end of file
+}
+
+@media (max-width: $screen-xs-max) {
+ .top-area {
+ flex-flow: row wrap;
+
+ .nav-controls {
+ $controls-margin: $btn-xs-side-margin - 2px;
+ flex: 0 0 100%;
+
+ &.controls-flex {
+ display: flex;
+ flex-flow: row wrap;
+ align-items: center;
+ justify-content: center;
+ padding: 0 0 $gl-padding-top;
+ }
+
+ .controls-item,
+ .controls-item-full,
+ .controls-item:last-child {
+ flex: 1 1 35%;
+ display: block;
+ width: 100%;
+ margin: $controls-margin;
+
+ .btn,
+ .dropdown {
+ margin: 0;
+ }
+ }
+
+ .controls-item-full {
+ flex: 1 1 100%;
+ }
+ }
+ }
+}
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index 46a06cd7eab..a8641e83154 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -101,6 +101,17 @@
padding: 0 8px;
border-radius: 6px;
}
+
+ .about-gitlab {
+ padding: 7px $gl-sidebar-padding;
+ font-size: $gl-font-size;
+ line-height: 24px;
+ display: block;
+ text-decoration: none;
+ font-weight: normal;
+ position: absolute;
+ bottom: 10px;
+ }
}
.sidebar-action-buttons {
diff --git a/app/assets/stylesheets/framework/tw_bootstrap.scss b/app/assets/stylesheets/framework/tw_bootstrap.scss
index 55bc325b858..28cbae9a449 100644
--- a/app/assets/stylesheets/framework/tw_bootstrap.scss
+++ b/app/assets/stylesheets/framework/tw_bootstrap.scss
@@ -91,7 +91,7 @@
// Labels
.label {
padding: 4px 5px;
- font-size: 13px;
+ font-size: 12px;
font-style: normal;
font-weight: normal;
display: inline-block;
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 3e1fded6b6b..b0c4a6edf57 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -489,9 +489,9 @@ $project-network-controls-color: #888;
*/
$runner-state-shared-bg: #32b186;
$runner-state-specific-bg: #3498db;
-$runner-status-online-color: green;
-$runner-status-offline-color: gray;
-$runner-status-paused-color: red;
+$runner-status-online-color: $green-normal;
+$runner-status-offline-color: $gray-darkest;
+$runner-status-paused-color: $red-normal;
/*
Stat Graph
diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss
index c735f104c20..76a88d96183 100644
--- a/app/assets/stylesheets/pages/boards.scss
+++ b/app/assets/stylesheets/pages/boards.scss
@@ -31,7 +31,7 @@
.dropdown-content {
max-height: 150px;
- }
+ }
}
.issue-board-dropdown-content {
@@ -109,6 +109,12 @@
&.has-border {
border-top: 3px solid;
+ margin-top: -1px;
+ margin-right: -1px;
+ margin-left: -1px;
+ padding-top: 1px;
+ padding-right: 1px;
+ padding-left: 1px;
.board-title {
padding-top: ($gl-padding - 3px);
diff --git a/app/assets/stylesheets/pages/deploy_keys.scss b/app/assets/stylesheets/pages/deploy_keys.scss
new file mode 100644
index 00000000000..2fafe052106
--- /dev/null
+++ b/app/assets/stylesheets/pages/deploy_keys.scss
@@ -0,0 +1,13 @@
+.deploy-keys-list {
+ width: 100%;
+ overflow: auto;
+
+ table {
+ border: 1px solid $table-border-color;
+ }
+}
+
+.deploy-keys-title {
+ padding-bottom: 2px;
+ line-height: 2;
+}
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index eeb5b590625..6b4d1f85564 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -1,50 +1,3 @@
-// Limit MR description for side-by-side diff view
-.limit-container-width {
- .detail-page-header {
- max-width: calc(#{$limited-layout-width} - (#{$gl-padding} * 2));
- margin-left: auto;
- margin-right: auto;
- }
-
- .issuable-details {
- .detail-page-description,
- .mr-source-target,
- .mr-state-widget,
- .merge-manually {
- max-width: calc(#{$limited-layout-width} - (#{$gl-padding} * 2));
- margin-left: auto;
- margin-right: auto;
- }
-
- .merge-request-tabs-holder {
- &.affix {
- border-bottom: 1px solid $border-color;
-
- .nav-links {
- border: 0;
- }
- }
-
- .container-fluid {
- padding-left: 0;
- padding-right: 0;
- max-width: calc(#{$limited-layout-width} - (#{$gl-padding} * 2));
- margin-left: auto;
- margin-right: auto;
- }
- }
- }
-
- .diffs {
- .mr-version-controls,
- .files-changed {
- max-width: calc(#{$limited-layout-width} - (#{$gl-padding} * 2));
- margin-left: auto;
- margin-right: auto;
- }
- }
-}
-
.issuable-details {
section {
.issuable-discussion {
diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss
index 237869aa544..d129eb12a45 100644
--- a/app/assets/stylesheets/pages/labels.scss
+++ b/app/assets/stylesheets/pages/labels.scss
@@ -98,7 +98,7 @@
}
.label {
- padding: 8px 9px 9px $gl-padding;
+ padding: 8px 9px 9px;
font-size: 14px;
}
}
diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss
index dd27a06fcd2..712bd3da22f 100644
--- a/app/assets/stylesheets/pages/login.scss
+++ b/app/assets/stylesheets/pages/login.scss
@@ -105,19 +105,19 @@
li {
flex: 1;
text-align: center;
+ border-left: 1px solid $border-color;
&:first-of-type {
+ border-left: none;
border-top-left-radius: $border-radius-default;
}
&:last-of-type {
- border-left: 1px solid $border-color;
border-top-right-radius: $border-radius-default;
}
&:not(.active) {
background-color: $gray-light;
- border-left: 1px solid $border-color;
}
a {
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index 41b1b47713d..1b1126695a1 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -424,11 +424,19 @@
.merge-request-tabs-holder {
background-color: $white-light;
+ .container-limited {
+ max-width: $limited-layout-width;
+ }
+
&.affix {
top: 100px;
left: 0;
z-index: 10;
transition: right .15s;
+
+ @media (max-width: $screen-xs-max) {
+ right: 0;
+ }
}
&:not(.affix) .container-fluid {
diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index 074abec7692..e54e12be82f 100644
--- a/app/assets/stylesheets/pages/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
@@ -109,7 +109,7 @@
margin: auto;
margin-top: 0;
text-align: center;
- font-size: 13px;
+ font-size: 12px;
@media (max-width: $screen-sm-max) {
// On smaller devices the warning becomes the fourth item in the list,
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index b512da0939f..8f15775ee03 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -557,18 +557,14 @@ ul.notes {
&.is-active {
color: $gl-text-green;
- svg path {
+ svg {
fill: $gl-text-green;
}
}
svg {
position: relative;
- color: $gray-darkest;
-
- path {
- fill: $gray-darkest;
- }
+ fill: $gray-darkest;
}
}
diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss
index b1d9421aa43..dc119843b65 100644
--- a/app/assets/stylesheets/pages/pipelines.scss
+++ b/app/assets/stylesheets/pages/pipelines.scss
@@ -635,8 +635,8 @@
.grouped-pipeline-dropdown {
padding: 0;
- width: 191px;
- min-width: 191px;
+ width: 195px;
+ min-width: 195px;
left: auto;
right: -195px;
top: -4px;
diff --git a/app/assets/stylesheets/pages/profiles/preferences.scss b/app/assets/stylesheets/pages/profiles/preferences.scss
index f8da0983b77..100ace41f2a 100644
--- a/app/assets/stylesheets/pages/profiles/preferences.scss
+++ b/app/assets/stylesheets/pages/profiles/preferences.scss
@@ -22,8 +22,8 @@
background: $theme-graphite;
}
- &.ui_gray {
- background: $theme-gray;
+ &.ui_black {
+ background: $theme-black;
}
&.ui_green {
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index d6aa4c4c032..b99be02ab0c 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -627,6 +627,12 @@ pre.light-well {
}
}
+.commits-search-form {
+ .input-short {
+ min-width: 200px;
+ }
+}
+
.project-last-commit {
@media (min-width: $screen-sm-min) {
margin-top: $gl-padding;
diff --git a/app/controllers/admin/deploy_keys_controller.rb b/app/controllers/admin/deploy_keys_controller.rb
index 285e8495342..4f6a7e9e2cb 100644
--- a/app/controllers/admin/deploy_keys_controller.rb
+++ b/app/controllers/admin/deploy_keys_controller.rb
@@ -10,7 +10,7 @@ class Admin::DeployKeysController < Admin::ApplicationController
end
def create
- @deploy_key = deploy_keys.new(deploy_key_params)
+ @deploy_key = deploy_keys.new(deploy_key_params.merge(user: current_user))
if @deploy_key.save
redirect_to admin_deploy_keys_path
@@ -39,6 +39,6 @@ class Admin::DeployKeysController < Admin::ApplicationController
end
def deploy_key_params
- params.require(:deploy_key).permit(:key, :title)
+ params.require(:deploy_key).permit(:key, :title, :can_push)
end
end
diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb
index 1e3d194e9f9..add1c819adf 100644
--- a/app/controllers/admin/groups_controller.rb
+++ b/app/controllers/admin/groups_controller.rb
@@ -1,17 +1,18 @@
class Admin::GroupsController < Admin::ApplicationController
- before_action :group, only: [:edit, :show, :update, :destroy, :project_update, :members_update]
+ before_action :group, only: [:edit, :update, :destroy, :project_update, :members_update]
def index
- @groups = Group.all
+ @groups = Group.with_statistics
@groups = @groups.sort(@sort = params[:sort])
@groups = @groups.search(params[:name]) if params[:name].present?
@groups = @groups.page(params[:page])
end
def show
+ @group = Group.with_statistics.joins(:route).group('routes.path').find_by_full_path(params[:id])
@members = @group.members.order("access_level DESC").page(params[:members_page])
@requesters = AccessRequestsFinder.new(@group).execute(current_user)
- @projects = @group.projects.page(params[:projects_page])
+ @projects = @group.projects.with_statistics.page(params[:projects_page])
end
def new
diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb
index 1d963bdf7d5..b09ae423096 100644
--- a/app/controllers/admin/projects_controller.rb
+++ b/app/controllers/admin/projects_controller.rb
@@ -3,7 +3,7 @@ class Admin::ProjectsController < Admin::ApplicationController
before_action :group, only: [:show, :transfer]
def index
- @projects = Project.all
+ @projects = Project.with_statistics
@projects = @projects.in_namespace(params[:namespace_id]) if params[:namespace_id].present?
@projects = @projects.where(visibility_level: params[:visibility_level]) if params[:visibility_level].present?
@projects = @projects.with_push if params[:with_push].present?
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index bb912ed10cc..df9039b16b2 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -16,9 +16,6 @@ class Admin::UsersController < Admin::ApplicationController
@joined_projects = user.projects.joined(@user)
end
- def groups
- end
-
def keys
@keys = user.keys
end
diff --git a/app/controllers/concerns/creates_commit.rb b/app/controllers/concerns/creates_commit.rb
index 936d9bab57e..6f43ce5226d 100644
--- a/app/controllers/concerns/creates_commit.rb
+++ b/app/controllers/concerns/creates_commit.rb
@@ -82,7 +82,7 @@ module CreatesCommit
return @merge_request if defined?(@merge_request)
@merge_request = MergeRequestsFinder.new(current_user, project_id: @mr_target_project.id).execute.opened.
- find_by(source_branch: @mr_source_branch, target_branch: @mr_target_branch)
+ find_by(source_branch: @mr_source_branch, target_branch: @mr_target_branch, source_project_id: @mr_source_project)
end
def different_project?
diff --git a/app/controllers/dashboard/todos_controller.rb b/app/controllers/dashboard/todos_controller.rb
index d425d0f9014..e3933e3d7b1 100644
--- a/app/controllers/dashboard/todos_controller.rb
+++ b/app/controllers/dashboard/todos_controller.rb
@@ -4,6 +4,9 @@ class Dashboard::TodosController < Dashboard::ApplicationController
def index
@sort = params[:sort]
@todos = @todos.page(params[:page])
+ if @todos.out_of_range? && @todos.total_pages != 0
+ redirect_to url_for(params.merge(page: @todos.total_pages))
+ end
end
def destroy
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index efe9c001bcf..b61f4e9a2db 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -42,6 +42,8 @@ class GroupsController < Groups::ApplicationController
@notification_setting = current_user.notification_settings_for(group)
end
+ @nested_groups = group.children
+
setup_projects
respond_to do |format|
@@ -75,7 +77,7 @@ class GroupsController < Groups::ApplicationController
end
def projects
- @projects = @group.projects.page(params[:page])
+ @projects = @group.projects.with_statistics.page(params[:page])
end
def update
diff --git a/app/controllers/projects/deploy_keys_controller.rb b/app/controllers/projects/deploy_keys_controller.rb
index 529e0aa2d33..b094491e006 100644
--- a/app/controllers/projects/deploy_keys_controller.rb
+++ b/app/controllers/projects/deploy_keys_controller.rb
@@ -16,7 +16,7 @@ class Projects::DeployKeysController < Projects::ApplicationController
end
def create
- @key = DeployKey.new(deploy_key_params)
+ @key = DeployKey.new(deploy_key_params.merge(user: current_user))
set_index_vars
if @key.valid? && @project.deploy_keys << @key
@@ -53,6 +53,6 @@ class Projects::DeployKeysController < Projects::ApplicationController
end
def deploy_key_params
- params.require(:deploy_key).permit(:key, :title)
+ params.require(:deploy_key).permit(:key, :title, :can_push)
end
end
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index 4f66e01e0f7..2beb0df8a07 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -25,6 +25,9 @@ class Projects::IssuesController < Projects::ApplicationController
def index
@issues = issues_collection
@issues = @issues.page(params[:page])
+ if @issues.out_of_range? && @issues.total_pages != 0
+ return redirect_to url_for(params.merge(page: @issues.total_pages))
+ end
if params[:label_name].present?
@labels = LabelsFinder.new(current_user, project_id: @project.id, title: params[:label_name]).execute
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 3abebdfd032..fc8a289d49d 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -38,6 +38,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def index
@merge_requests = merge_requests_collection
@merge_requests = @merge_requests.page(params[:page])
+ if @merge_requests.out_of_range? && @merge_requests.total_pages != 0
+ return redirect_to url_for(params.merge(page: @merge_requests.total_pages))
+ end
if params[:label_name].present?
labels_params = { project_id: @project.id, title: params[:label_name] }
diff --git a/app/controllers/projects/snippets_controller.rb b/app/controllers/projects/snippets_controller.rb
index 0720be2e55d..02a97c1c574 100644
--- a/app/controllers/projects/snippets_controller.rb
+++ b/app/controllers/projects/snippets_controller.rb
@@ -26,6 +26,9 @@ class Projects::SnippetsController < Projects::ApplicationController
scope: params[:scope]
)
@snippets = @snippets.page(params[:page])
+ if @snippets.out_of_range? && @snippets.total_pages != 0
+ redirect_to namespace_project_snippets_path(page: @snippets.total_pages)
+ end
end
def new
diff --git a/app/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb
index 953091492ae..e2d9d5ed460 100644
--- a/app/controllers/projects/tags_controller.rb
+++ b/app/controllers/projects/tags_controller.rb
@@ -8,7 +8,7 @@ class Projects::TagsController < Projects::ApplicationController
before_action :authorize_admin_project!, only: [:destroy]
def index
- params[:sort] = params[:sort].presence || 'name'
+ params[:sort] = params[:sort].presence || sort_value_recently_updated
@sort = params[:sort]
@tags = TagsFinder.new(@repository, params).execute
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 7445f3c113c..c6c63918fa5 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -61,7 +61,7 @@ module ProjectsHelper
project_link = link_to simple_sanitize(project.name), project_path(project), { class: "project-item-select-holder" }
if current_user
- project_link << button_tag(type: 'button', class: "dropdown-toggle-caret js-projects-dropdown-toggle", aria: { label: "Toggle switch project dropdown" }, data: { target: ".js-dropdown-menu-projects", toggle: "dropdown" }) do
+ project_link << button_tag(type: 'button', class: 'dropdown-toggle-caret js-projects-dropdown-toggle', aria: { label: 'Toggle switch project dropdown' }, data: { target: '.js-dropdown-menu-projects', toggle: 'dropdown', order_by: 'last_activity_at' }) do
icon("chevron-down")
end
end
@@ -90,10 +90,12 @@ module ProjectsHelper
end
def project_for_deploy_key(deploy_key)
- if deploy_key.projects.include?(@project)
+ if deploy_key.has_access_to?(@project)
@project
else
- deploy_key.projects.find { |project| can?(current_user, :read_project, project) }
+ deploy_key.projects.find do |project|
+ can?(current_user, :read_project, project)
+ end
end
end
@@ -171,48 +173,27 @@ module ProjectsHelper
nav_tabs << :merge_requests
end
- 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
- if can?(current_user, :read_environment, project)
- nav_tabs << :environments
- end
-
- if can?(current_user, :admin_project, project)
- nav_tabs << :settings
- end
-
- if can?(current_user, :read_project_member, project)
- nav_tabs << :team
- end
-
- if can?(current_user, :read_issue, project)
- nav_tabs << :issues
- end
-
- if can?(current_user, :read_wiki, project)
- nav_tabs << :wiki
- end
-
- if can?(current_user, :read_project_snippet, project)
- nav_tabs << :snippets
- end
-
- if can?(current_user, :read_label, project)
- nav_tabs << :labels
- end
+ tab_ability_map = {
+ environments: :read_environment,
+ milestones: :read_milestone,
+ pipelines: :read_pipeline,
+ snippets: :read_project_snippet,
+ settings: :admin_project,
+ builds: :read_build,
+ labels: :read_label,
+ issues: :read_issue,
+ team: :read_project_member,
+ wiki: :read_wiki
+ }
- if can?(current_user, :read_milestone, project)
- nav_tabs << :milestones
+ tab_ability_map.each do |tab, ability|
+ if can?(current_user, ability, project)
+ nav_tabs << tab
+ end
end
nav_tabs.flatten
@@ -246,11 +227,6 @@ module ProjectsHelper
end
end
- def repository_size(project = @project)
- size_in_bytes = project.repository_size * 1.megabyte
- number_to_human_size(size_in_bytes, delimiter: ',', precision: 2)
- end
-
def default_url_to_repo(project = @project)
case default_clone_protocol
when 'ssh'
@@ -398,20 +374,6 @@ module ProjectsHelper
[@project.path_with_namespace, sha, "readme"].join('-')
end
- def round_commit_count(project)
- count = project.commit_count
-
- if count > 10000
- '10000+'
- elsif count > 5000
- '5000+'
- elsif count > 1000
- '1000+'
- else
- count
- end
- end
-
def current_ref
@ref || @repository.try(:root_ref)
end
diff --git a/app/helpers/sorting_helper.rb b/app/helpers/sorting_helper.rb
index f03c4627050..ff787fb4131 100644
--- a/app/helpers/sorting_helper.rb
+++ b/app/helpers/sorting_helper.rb
@@ -11,6 +11,7 @@ module SortingHelper
sort_value_due_date_soon => sort_title_due_date_soon,
sort_value_due_date_later => sort_title_due_date_later,
sort_value_largest_repo => sort_title_largest_repo,
+ sort_value_largest_group => sort_title_largest_group,
sort_value_recently_signin => sort_title_recently_signin,
sort_value_oldest_signin => sort_title_oldest_signin,
sort_value_downvotes => sort_title_downvotes,
@@ -92,6 +93,10 @@ module SortingHelper
'Largest repository'
end
+ def sort_title_largest_group
+ 'Largest group'
+ end
+
def sort_title_recently_signin
'Recent sign in'
end
@@ -193,7 +198,11 @@ module SortingHelper
end
def sort_value_largest_repo
- 'repository_size_desc'
+ 'storage_size_desc'
+ end
+
+ def sort_value_largest_group
+ 'storage_size_desc'
end
def sort_value_recently_signin
diff --git a/app/helpers/storage_helper.rb b/app/helpers/storage_helper.rb
new file mode 100644
index 00000000000..e19c67a37ca
--- /dev/null
+++ b/app/helpers/storage_helper.rb
@@ -0,0 +1,7 @@
+module StorageHelper
+ def storage_counter(size_in_bytes)
+ precision = size_in_bytes < 1.megabyte ? 0 : 1
+
+ number_to_human_size(size_in_bytes, delimiter: ',', precision: precision, significant: false)
+ end
+end
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index cb76cdf5981..27042798741 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -43,6 +43,8 @@ module Ci
before_destroy { project }
after_create :execute_hooks
+ after_save :update_project_statistics, if: :artifacts_size_changed?
+ after_destroy :update_project_statistics
class << self
def first_pending
@@ -584,5 +586,9 @@ module Ci
Ci::MaskSecret.mask!(trace, token)
trace
end
+
+ def update_project_statistics
+ ProjectCacheWorker.perform_async(project_id, [], [:build_artifacts_size])
+ end
end
end
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 1814469f6ce..2a97e8bae4a 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -93,11 +93,8 @@ module Ci
.select("max(#{quoted_table_name}.id)")
.group(:ref, :sha)
- if ref
- where(id: max_id, ref: ref)
- else
- where(id: max_id)
- end
+ relation = ref ? where(ref: ref) : self
+ relation.where(id: max_id)
end
def self.latest_status(ref = nil)
@@ -105,7 +102,7 @@ module Ci
end
def self.latest_successful_for(ref)
- success.latest(ref).first
+ success.latest(ref).order(id: :desc).first
end
def self.truncate_sha(sha)
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index 0ea7b1b1098..5e63825bf99 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -92,8 +92,9 @@ module Issuable
after_save :record_metrics
def update_assignee_cache_counts
- # make sure we flush the cache for both the old *and* new assignee
- User.find(assignee_id_was).update_cache_counts if assignee_id_was
+ # make sure we flush the cache for both the old *and* new assignees(if they exist)
+ previous_assignee = User.find_by_id(assignee_id_was) if assignee_id_was
+ previous_assignee.update_cache_counts if previous_assignee
assignee.update_cache_counts if assignee
end
diff --git a/app/models/deploy_key.rb b/app/models/deploy_key.rb
index 2c525d4cd7a..053f2a11aa0 100644
--- a/app/models/deploy_key.rb
+++ b/app/models/deploy_key.rb
@@ -20,4 +20,18 @@ class DeployKey < Key
def destroyed_when_orphaned?
self.private?
end
+
+ def has_access_to?(project)
+ projects.include?(project)
+ end
+
+ def can_push_to?(project)
+ can_push? && has_access_to?(project)
+ end
+
+ private
+
+ # we don't want to notify the user for deploy keys
+ def notify_user
+ end
end
diff --git a/app/models/group.rb b/app/models/group.rb
index ac8a82c8c1e..9888b242e98 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -48,7 +48,13 @@ class Group < Namespace
end
def sort(method)
- order_by(method)
+ if method == 'storage_size_desc'
+ # storage_size is a virtual column so we need to
+ # pass a string to avoid AR adding the table name
+ reorder('storage_size DESC, namespaces.id DESC')
+ else
+ order_by(method)
+ end
end
def reference_prefix
@@ -155,15 +161,17 @@ class Group < Namespace
end
def has_owner?(user)
- owners.include?(user)
+ members_with_parents.owners.where(user_id: user).any?
end
def has_master?(user)
- members.masters.where(user_id: user).any?
+ members_with_parents.masters.where(user_id: user).any?
end
+ # Check if user is a last owner of the group.
+ # Parent owners are ignored for nested groups.
def last_owner?(user)
- has_owner?(user) && owners.size == 1
+ owners.include?(user) && owners.size == 1
end
def avatar_type
@@ -189,6 +197,14 @@ class Group < Namespace
end
def refresh_members_authorized_projects
- UserProjectAccessChangedService.new(users.pluck(:id)).execute
+ UserProjectAccessChangedService.new(users_with_parents.pluck(:id)).execute
+ end
+
+ def members_with_parents
+ GroupMember.where(requested_at: nil, source_id: parents.map(&:id).push(id))
+ end
+
+ def users_with_parents
+ User.where(id: members_with_parents.select(:user_id))
end
end
diff --git a/app/models/key.rb b/app/models/key.rb
index a5d25409730..6f377f0e8ae 100644
--- a/app/models/key.rb
+++ b/app/models/key.rb
@@ -57,10 +57,6 @@ class Key < ActiveRecord::Base
)
end
- def notify_user
- run_after_commit { NotificationService.new.new_key(self) }
- end
-
def post_create_hook
SystemHooksService.new.execute_hooks_for(self, :create)
end
@@ -86,4 +82,8 @@ class Key < ActiveRecord::Base
self.fingerprint = Gitlab::KeyFingerprint.new(self.key).fingerprint
end
+
+ def notify_user
+ run_after_commit { NotificationService.new.new_key(self) }
+ end
end
diff --git a/app/models/lfs_objects_project.rb b/app/models/lfs_objects_project.rb
index 0fd5f089db9..007eed5600a 100644
--- a/app/models/lfs_objects_project.rb
+++ b/app/models/lfs_objects_project.rb
@@ -5,4 +5,13 @@ class LfsObjectsProject < ActiveRecord::Base
validates :lfs_object_id, presence: true
validates :lfs_object_id, uniqueness: { scope: [:project_id], message: "already exists in project" }
validates :project_id, presence: true
+
+ after_create :update_project_statistics
+ after_destroy :update_project_statistics
+
+ private
+
+ def update_project_statistics
+ ProjectCacheWorker.perform_async(project_id, [], [:lfs_objects_size])
+ end
end
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 5ce20cc43d6..61845bf4036 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -198,7 +198,9 @@ class MergeRequest < ActiveRecord::Base
end
def diff_size
- diffs(diff_options).size
+ opts = diff_options || {}
+
+ raw_diffs(opts).size
end
def diff_base_commit
@@ -574,11 +576,7 @@ class MergeRequest < ActiveRecord::Base
ext = Gitlab::ReferenceExtractor.new(project, current_user)
ext.analyze(description)
- issues = ext.issues
- closing_issues = Gitlab::ClosingIssueExtractor.new(project, current_user).
- closed_by_message(description)
-
- issues - closing_issues
+ ext.issues - closes_issues
end
def target_project_path
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index b52f08c7081..d41833de66f 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -9,6 +9,7 @@ class Namespace < ActiveRecord::Base
cache_markdown_field :description, pipeline: :description
has_many :projects, dependent: :destroy
+ has_many :project_statistics
belongs_to :owner, class_name: "User"
belongs_to :parent, class_name: "Namespace"
@@ -38,6 +39,18 @@ class Namespace < ActiveRecord::Base
scope :root, -> { where('type IS NULL') }
+ scope :with_statistics, -> do
+ joins('LEFT JOIN project_statistics ps ON ps.namespace_id = namespaces.id')
+ .group('namespaces.id')
+ .select(
+ 'namespaces.*',
+ 'COALESCE(SUM(ps.storage_size), 0) AS storage_size',
+ 'COALESCE(SUM(ps.repository_size), 0) AS repository_size',
+ 'COALESCE(SUM(ps.lfs_objects_size), 0) AS lfs_objects_size',
+ 'COALESCE(SUM(ps.build_artifacts_size), 0) AS build_artifacts_size',
+ )
+ end
+
class << self
def by_path(path)
find_by('lower(path) = :value', value: path.downcase)
diff --git a/app/models/project.rb b/app/models/project.rb
index 26fa20f856d..e0ffa7e7af7 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -44,6 +44,7 @@ class Project < ActiveRecord::Base
after_create :ensure_dir_exist
after_create :create_project_feature, unless: :project_feature
after_save :ensure_dir_exist, if: :namespace_id_changed?
+ after_save :update_project_statistics, if: :namespace_id_changed?
# set last_activity_at to the same as created_at
after_create :set_last_activity_at
@@ -151,6 +152,7 @@ class Project < ActiveRecord::Base
has_one :import_data, dependent: :destroy, class_name: "ProjectImportData"
has_one :project_feature, dependent: :destroy
+ has_one :statistics, class_name: 'ProjectStatistics', dependent: :delete
has_many :commit_statuses, dependent: :destroy, foreign_key: :gl_project_id
has_many :pipelines, dependent: :destroy, class_name: 'Ci::Pipeline', foreign_key: :gl_project_id
@@ -220,6 +222,7 @@ class Project < ActiveRecord::Base
scope :with_push, -> { joins(:events).where('events.action = ?', Event::PUSHED) }
scope :with_project_feature, -> { joins('LEFT JOIN project_features ON projects.id = project_features.project_id') }
+ scope :with_statistics, -> { includes(:statistics) }
# "enabled" here means "not disabled". It includes private features!
scope :with_feature_enabled, ->(feature) {
@@ -332,8 +335,10 @@ class Project < ActiveRecord::Base
end
def sort(method)
- if method == 'repository_size_desc'
- reorder(repository_size: :desc, id: :desc)
+ if method == 'storage_size_desc'
+ # storage_size is a joined column so we need to
+ # pass a string to avoid AR adding the table name
+ reorder('project_statistics.storage_size DESC, projects.id DESC')
else
order_by(method)
end
@@ -921,7 +926,7 @@ class Project < ActiveRecord::Base
Rails.logger.error "Project #{old_path_with_namespace} cannot be renamed because container registry tags are present"
# we currently doesn't support renaming repository if it contains tags in container registry
- raise Exception.new('Project cannot be renamed, because tags are present in its container registry')
+ raise StandardError.new('Project cannot be renamed, because tags are present in its container registry')
end
if gitlab_shell.mv_repository(repository_storage_path, old_path_with_namespace, new_path_with_namespace)
@@ -948,7 +953,7 @@ class Project < ActiveRecord::Base
# if we cannot move namespace directory we should rollback
# db changes in order to prevent out of sync between db and fs
- raise Exception.new('repository cannot be renamed')
+ raise StandardError.new('repository cannot be renamed')
end
Gitlab::AppLogger.info "Project was renamed: #{old_path_with_namespace} -> #{new_path_with_namespace}"
@@ -1036,14 +1041,6 @@ class Project < ActiveRecord::Base
forked? && project == forked_from_project
end
- def update_repository_size
- update_attribute(:repository_size, repository.size)
- end
-
- def update_commit_count
- update_attribute(:commit_count, repository.commit_count)
- end
-
def forks_count
forks.count
end
@@ -1322,4 +1319,9 @@ class Project < ActiveRecord::Base
def full_path_changed?
path_changed? || namespace_id_changed?
end
+
+ def update_project_statistics
+ stats = statistics || build_statistics
+ stats.update(namespace_id: namespace_id)
+ end
end
diff --git a/app/models/project_statistics.rb b/app/models/project_statistics.rb
new file mode 100644
index 00000000000..2270ac75071
--- /dev/null
+++ b/app/models/project_statistics.rb
@@ -0,0 +1,43 @@
+class ProjectStatistics < ActiveRecord::Base
+ belongs_to :project
+ belongs_to :namespace
+
+ before_save :update_storage_size
+
+ STORAGE_COLUMNS = [:repository_size, :lfs_objects_size, :build_artifacts_size]
+ STATISTICS_COLUMNS = [:commit_count] + STORAGE_COLUMNS
+
+ def total_repository_size
+ repository_size + lfs_objects_size
+ end
+
+ def refresh!(only: nil)
+ STATISTICS_COLUMNS.each do |column, generator|
+ if only.blank? || only.include?(column)
+ public_send("update_#{column}")
+ end
+ end
+
+ save!
+ end
+
+ def update_commit_count
+ self.commit_count = project.repository.commit_count
+ end
+
+ def update_repository_size
+ self.repository_size = project.repository.size
+ end
+
+ def update_lfs_objects_size
+ self.lfs_objects_size = project.lfs_objects.sum(:size)
+ end
+
+ def update_build_artifacts_size
+ self.build_artifacts_size = project.builds.sum(:artifacts_size)
+ end
+
+ def update_storage_size
+ self.storage_size = STORAGE_COLUMNS.sum(&method(:read_attribute))
+ end
+end
diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb
index 6f943feb2a7..0be6e113655 100644
--- a/app/policies/group_policy.rb
+++ b/app/policies/group_policy.rb
@@ -4,7 +4,7 @@ class GroupPolicy < BasePolicy
return unless @user
globally_viewable = @subject.public? || (@subject.internal? && !@user.external?)
- member = @subject.users.include?(@user)
+ member = @subject.users_with_parents.include?(@user)
owner = @user.admin? || @subject.has_owner?(@user)
master = owner || @subject.has_master?(@user)
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index b5db9c12622..71ef8901932 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -171,9 +171,7 @@ class ProjectPolicy < BasePolicy
def disabled_features!
repository_enabled = project.feature_available?(:repository, user)
- unless project.feature_available?(:issues, user)
- cannot!(*named_abilities(:issue))
- end
+ block_issues_abilities
unless project.feature_available?(:merge_requests, user) && repository_enabled
cannot!(*named_abilities(:merge_request))
@@ -245,11 +243,20 @@ class ProjectPolicy < BasePolicy
def project_group_member?(user)
project.group &&
(
- project.group.members.exists?(user_id: user.id) ||
+ project.group.members_with_parents.exists?(user_id: user.id) ||
project.group.requesters.exists?(user_id: user.id)
)
end
+ def block_issues_abilities
+ unless project.feature_available?(:issues, user)
+ cannot! :read_issue if project.default_issues_tracker?
+ cannot! :create_issue
+ cannot! :update_issue
+ cannot! :admin_issue
+ end
+ end
+
def named_abilities(name)
[
:"read_#{name}",
diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb
index 6bbc3a9d9ff..dbe2fda27b5 100644
--- a/app/services/git_push_service.rb
+++ b/app/services/git_push_service.rb
@@ -77,7 +77,7 @@ class GitPushService < BaseService
types = []
end
- ProjectCacheWorker.perform_async(@project.id, types)
+ ProjectCacheWorker.perform_async(@project.id, types, [:commit_count, :repository_size])
end
# Schedules processing of commit messages.
diff --git a/app/services/git_tag_push_service.rb b/app/services/git_tag_push_service.rb
index 20a4445bddf..96432837481 100644
--- a/app/services/git_tag_push_service.rb
+++ b/app/services/git_tag_push_service.rb
@@ -12,7 +12,7 @@ class GitTagPushService < BaseService
project.execute_hooks(@push_data.dup, :tag_push_hooks)
project.execute_services(@push_data.dup, :tag_push_hooks)
Ci::CreatePipelineService.new(project, current_user, @push_data).execute
- ProjectCacheWorker.perform_async(project.id)
+ ProjectCacheWorker.perform_async(project.id, [], [:commit_count, :repository_size])
true
end
diff --git a/app/services/groups/create_service.rb b/app/services/groups/create_service.rb
index 2bccd584dde..febeb661fb5 100644
--- a/app/services/groups/create_service.rb
+++ b/app/services/groups/create_service.rb
@@ -12,6 +12,13 @@ module Groups
return @group
end
+ if @group.parent && !can?(current_user, :admin_group, @group.parent)
+ @group.parent = nil
+ @group.errors.add(:parent_id, 'manage access required to create subgroup')
+
+ return @group
+ end
+
@group.name ||= @group.path.dup
@group.save
@group.add_owner(current_user)
diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb
index ab3d2a9a0cd..4ce5fd993d9 100644
--- a/app/services/issuable_base_service.rb
+++ b/app/services/issuable_base_service.rb
@@ -36,14 +36,10 @@ class IssuableBaseService < BaseService
end
end
- def filter_params(issuable_ability_name = :issue)
- filter_assignee
- filter_milestone
- filter_labels
+ def filter_params(issuable)
+ ability_name = :"admin_#{issuable.to_ability_name}"
- ability = :"admin_#{issuable_ability_name}"
-
- unless can?(current_user, ability, project)
+ unless can?(current_user, ability_name, project)
params.delete(:milestone_id)
params.delete(:labels)
params.delete(:add_label_ids)
@@ -52,14 +48,35 @@ class IssuableBaseService < BaseService
params.delete(:assignee_id)
params.delete(:due_date)
end
+
+ filter_assignee(issuable)
+ filter_milestone
+ filter_labels
end
- def filter_assignee
- if params[:assignee_id] == IssuableFinder::NONE
- params[:assignee_id] = ''
+ def filter_assignee(issuable)
+ return unless params[:assignee_id].present?
+
+ assignee_id = params[:assignee_id]
+
+ if assignee_id.to_s == IssuableFinder::NONE
+ params[:assignee_id] = ""
+ else
+ params.delete(:assignee_id) unless assignee_can_read?(issuable, assignee_id)
end
end
+ def assignee_can_read?(issuable, assignee_id)
+ new_assignee = User.find_by_id(assignee_id)
+
+ return false unless new_assignee.present?
+
+ ability_name = :"read_#{issuable.to_ability_name}"
+ resource = issuable.persisted? ? issuable : project
+
+ can?(new_assignee, ability_name, resource)
+ end
+
def filter_milestone
milestone_id = params[:milestone_id]
return unless milestone_id
@@ -138,7 +155,7 @@ class IssuableBaseService < BaseService
def create(issuable)
merge_slash_commands_into_params!(issuable)
- filter_params
+ filter_params(issuable)
params.delete(:state_event)
params[:author] ||= current_user
@@ -180,7 +197,7 @@ class IssuableBaseService < BaseService
change_state(issuable)
change_subscription(issuable)
change_todo(issuable)
- filter_params
+ filter_params(issuable)
old_labels = issuable.labels.to_a
old_mentioned_users = issuable.mentioned_users.to_a
diff --git a/app/services/issues/base_service.rb b/app/services/issues/base_service.rb
index 742e834df97..35af867a098 100644
--- a/app/services/issues/base_service.rb
+++ b/app/services/issues/base_service.rb
@@ -17,10 +17,6 @@ module Issues
private
- def filter_params
- super(:issue)
- end
-
def execute_hooks(issue, action = 'open')
issue_data = hook_data(issue, action)
hooks_scope = issue.confidential? ? :confidential_issue_hooks : :issue_hooks
diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb
index 800fd39c424..70e25956dc7 100644
--- a/app/services/merge_requests/base_service.rb
+++ b/app/services/merge_requests/base_service.rb
@@ -38,10 +38,6 @@ module MergeRequests
private
- def filter_params
- super(:merge_request)
- end
-
def merge_requests_for(branch)
origin_merge_requests = @project.origin_merge_requests
.opened.where(source_branch: branch).to_a
diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb
index d75592e31f3..1beca9f4109 100644
--- a/app/services/notes/create_service.rb
+++ b/app/services/notes/create_service.rb
@@ -41,7 +41,7 @@ module Notes
# We must add the error after we call #save because errors are reset
# when #save is called
if only_commands
- note.errors.add(:commands_only, 'Your commands have been executed!')
+ note.errors.add(:commands_only, 'Commands applied')
end
note.commands_changes = command_params.keys
diff --git a/app/services/users/refresh_authorized_projects_service.rb b/app/services/users/refresh_authorized_projects_service.rb
index 7d38ac3a374..8559908e0c3 100644
--- a/app/services/users/refresh_authorized_projects_service.rb
+++ b/app/services/users/refresh_authorized_projects_service.rb
@@ -74,7 +74,7 @@ module Users
# remove - The IDs of the authorization rows to remove.
# add - Rows to insert in the form `[user id, project id, access level]`
def update_authorizations(remove = [], add = [])
- return if remove.empty? && add.empty?
+ return if remove.empty? && add.empty? && user.authorized_projects_populated
User.transaction do
user.remove_project_authorizations(remove) unless remove.empty?
diff --git a/app/views/abuse_report_mailer/notify.html.haml b/app/views/abuse_report_mailer/notify.html.haml
index 2741eb44357..d50b4071745 100644
--- a/app/views/abuse_report_mailer/notify.html.haml
+++ b/app/views/abuse_report_mailer/notify.html.haml
@@ -1,7 +1,7 @@
%p
- #{link_to @abuse_report.user.name, user_url(@abuse_report.user)}
- (@#{@abuse_report.user.username}) was reported for abuse by
- #{link_to @abuse_report.reporter.name, user_url(@abuse_report.reporter)}
+ #{link_to @abuse_report.user.name, user_url(@abuse_report.user)}
+ (@#{@abuse_report.user.username}) was reported for abuse by
+ #{link_to @abuse_report.reporter.name, user_url(@abuse_report.reporter)}
(@#{@abuse_report.reporter.username}).
%blockquote
diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml
index 7accd2529af..4612a7a058a 100644
--- a/app/views/admin/application_settings/_form.html.haml
+++ b/app/views/admin/application_settings/_form.html.haml
@@ -321,7 +321,7 @@
= f.text_field :recaptcha_site_key, class: 'form-control'
.help-block
Generate site and private keys at
- %a{ href: 'http://www.google.com/recaptcha', target: 'blank'} http://www.google.com/recaptcha
+ %a{ href: 'http://www.google.com/recaptcha', target: 'blank' } http://www.google.com/recaptcha
.form-group
= f.label :recaptcha_private_key, 'reCAPTCHA Private Key', class: 'control-label col-sm-2'
@@ -342,7 +342,7 @@
= f.text_field :akismet_api_key, class: 'form-control'
.help-block
Generate API key at
- %a{ href: 'http://www.akismet.com', target: 'blank'} http://www.akismet.com
+ %a{ href: 'http://www.akismet.com', target: 'blank' } http://www.akismet.com
%fieldset
%legend Abuse reports
diff --git a/app/views/admin/applications/_delete_form.html.haml b/app/views/admin/applications/_delete_form.html.haml
index 042971e1eed..82781f6716d 100644
--- a/app/views/admin/applications/_delete_form.html.haml
+++ b/app/views/admin/applications/_delete_form.html.haml
@@ -1,4 +1,4 @@
- submit_btn_css ||= 'btn btn-link btn-remove btn-sm'
= form_tag admin_application_path(application) do
- %input{:name => "_method", :type => "hidden", :value => "delete"}/
+ %input{ :name => "_method", :type => "hidden", :value => "delete" }/
= submit_tag 'Destroy', onclick: "return confirm('Are you sure?')", class: submit_btn_css
diff --git a/app/views/admin/applications/index.html.haml b/app/views/admin/applications/index.html.haml
index f8cd98f0ec4..b3a3b4c1d45 100644
--- a/app/views/admin/applications/index.html.haml
+++ b/app/views/admin/applications/index.html.haml
@@ -15,7 +15,7 @@
%th
%tbody.oauth-applications
- @applications.each do |application|
- %tr{:id => "application_#{application.id}"}
+ %tr{ :id => "application_#{application.id}" }
%td= link_to application.name, admin_application_path(application)
%td= application.redirect_uri
%td= application.access_tokens.map(&:resource_owner_id).uniq.count
diff --git a/app/views/admin/background_jobs/show.html.haml b/app/views/admin/background_jobs/show.html.haml
index 05855db963a..4f982a6e369 100644
--- a/app/views/admin/background_jobs/show.html.haml
+++ b/app/views/admin/background_jobs/show.html.haml
@@ -43,4 +43,4 @@
.panel.panel-default
- %iframe{src: sidekiq_path, width: '100%', height: 970, style: "border: none"}
+ %iframe{ src: sidekiq_path, width: '100%', height: 970, style: "border: none" }
diff --git a/app/views/admin/broadcast_messages/index.html.haml b/app/views/admin/broadcast_messages/index.html.haml
index c05538a393c..4f2ae081d7a 100644
--- a/app/views/admin/broadcast_messages/index.html.haml
+++ b/app/views/admin/broadcast_messages/index.html.haml
@@ -10,7 +10,7 @@
%br.clearfix
--if @broadcast_messages.any?
+- if @broadcast_messages.any?
%table.table
%thead
%tr
diff --git a/app/views/admin/deploy_keys/index.html.haml b/app/views/admin/deploy_keys/index.html.haml
index 149593e7f46..7b71bb5b287 100644
--- a/app/views/admin/deploy_keys/index.html.haml
+++ b/app/views/admin/deploy_keys/index.html.haml
@@ -1,27 +1,34 @@
- page_title "Deploy Keys"
-.panel.panel-default.prepend-top-default
- .panel-heading
- Public deploy keys (#{@deploy_keys.count})
- .controls
- = link_to 'New Deploy Key', new_admin_deploy_key_path, class: "btn btn-new btn-sm"
- - if @deploy_keys.any?
- .table-holder
- %table.table
- %thead.panel-heading
+
+%h3.page-title.deploy-keys-title
+ Public deploy keys (#{@deploy_keys.count})
+ .pull-right
+ = link_to 'New Deploy Key', new_admin_deploy_key_path, class: 'btn btn-new btn-sm btn-inverted'
+
+- if @deploy_keys.any?
+ .table-holder.deploy-keys-list
+ %table.table
+ %thead
+ %tr
+ %th.col-sm-2 Title
+ %th.col-sm-4 Fingerprint
+ %th.col-sm-2 Write access allowed
+ %th.col-sm-2 Added at
+ %th.col-sm-2
+ %tbody
+ - @deploy_keys.each do |deploy_key|
%tr
- %th Title
- %th Fingerprint
- %th Added at
- %th
- %tbody
- - @deploy_keys.each do |deploy_key|
- %tr
- %td
- %strong= deploy_key.title
- %td
- %code.key-fingerprint= deploy_key.fingerprint
- %td
- %span.cgray
- added #{time_ago_with_tooltip(deploy_key.created_at)}
- %td
- = link_to 'Remove', admin_deploy_key_path(deploy_key), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-sm btn-remove delete-key pull-right"
+ %td
+ %strong= deploy_key.title
+ %td
+ %code.key-fingerprint= deploy_key.fingerprint
+ %td
+ - if deploy_key.can_push?
+ Yes
+ - else
+ No
+ %td
+ %span.cgray
+ added #{time_ago_with_tooltip(deploy_key.created_at)}
+ %td
+ = link_to 'Remove', admin_deploy_key_path(deploy_key), data: { confirm: 'Are you sure?'}, method: :delete, class: 'btn btn-sm btn-remove delete-key pull-right'
diff --git a/app/views/admin/deploy_keys/new.html.haml b/app/views/admin/deploy_keys/new.html.haml
index 5c410a695bf..a064efc231f 100644
--- a/app/views/admin/deploy_keys/new.html.haml
+++ b/app/views/admin/deploy_keys/new.html.haml
@@ -16,6 +16,14 @@
Paste a machine public key here. Read more about how to generate it
= link_to "here", help_page_path("ssh/README")
= f.text_area :key, class: "form-control thin_area", rows: 5
+ .form-group
+ .control-label
+ .col-sm-10
+ = f.label :can_push do
+ = f.check_box :can_push
+ %strong Write access allowed
+ %p.light.append-bottom-0
+ Allow this key to push to repository as well? (Default only allows pull access.)
.form-actions
= f.submit 'Create', class: "btn-create btn"
diff --git a/app/views/admin/groups/_group.html.haml b/app/views/admin/groups/_group.html.haml
index cf28f92853e..e3a77dfdf10 100644
--- a/app/views/admin/groups/_group.html.haml
+++ b/app/views/admin/groups/_group.html.haml
@@ -5,6 +5,9 @@
= link_to 'Edit', admin_group_edit_path(group), id: "edit_#{dom_id(group)}", class: 'btn'
= link_to 'Delete', [:admin, group], data: { confirm: "Are you sure you want to remove #{group.name}?" }, method: :delete, class: 'btn btn-remove'
.stats
+ %span.badge
+ = storage_counter(group.storage_size)
+
%span
= icon('bookmark')
= number_with_delimiter(group.projects.count)
@@ -13,7 +16,7 @@
= icon('users')
= number_with_delimiter(group.users.count)
- %span.visibility-icon.has-tooltip{data: { container: 'body', placement: 'left' }, title: visibility_icon_description(group)}
+ %span.visibility-icon.has-tooltip{ data: { container: 'body', placement: 'left' }, title: visibility_icon_description(group) }
= visibility_level_icon(group.visibility_level, fw: false)
.avatar-container.s40
diff --git a/app/views/admin/groups/index.html.haml b/app/views/admin/groups/index.html.haml
index 794f910a61f..07775247cfd 100644
--- a/app/views/admin/groups/index.html.haml
+++ b/app/views/admin/groups/index.html.haml
@@ -27,6 +27,8 @@
= sort_title_recently_updated
= link_to admin_groups_path(sort: sort_value_oldest_updated, name: project_name) do
= sort_title_oldest_updated
+ = link_to admin_groups_path(sort: sort_value_largest_group, name: project_name) do
+ = sort_title_largest_group
= link_to new_admin_group_path, class: "btn btn-new" do
New Group
%ul.content-list
diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml
index 7b0175af214..30b3fabdd7e 100644
--- a/app/views/admin/groups/show.html.haml
+++ b/app/views/admin/groups/show.html.haml
@@ -39,6 +39,18 @@
= @group.created_at.to_s(:medium)
%li
+ %span.light Storage:
+ %strong= storage_counter(@group.storage_size)
+ (
+ = storage_counter(@group.repository_size)
+ repositories,
+ = storage_counter(@group.build_artifacts_size)
+ build artifacts,
+ = storage_counter(@group.lfs_objects_size)
+ LFS
+ )
+
+ %li
%span.light Group Git LFS status:
%strong
= group_lfs_status(@group)
@@ -55,8 +67,8 @@
%li
%strong
= link_to project.name_with_namespace, [:admin, project.namespace.becomes(Namespace), project]
- %span.label.label-gray
- = repository_size(project)
+ %span.badge
+ = storage_counter(project.statistics.storage_size)
%span.pull-right.light
%span.monospace= project.path_with_namespace + ".git"
.panel-footer
@@ -73,8 +85,8 @@
%li
%strong
= link_to project.name_with_namespace, [:admin, project.namespace.becomes(Namespace), project]
- %span.label.label-gray
- = repository_size(project)
+ %span.badge
+ = storage_counter(project.statistics.storage_size)
%span.pull-right.light
%span.monospace= project.path_with_namespace + ".git"
@@ -91,7 +103,7 @@
= form_tag admin_group_members_update_path(@group), id: "new_project_member", class: "bulk_import", method: :put do
%div
= users_select_tag(:user_ids, multiple: true, email_user: true, scope: :all)
- %div.prepend-top-10
+ .prepend-top-10
= select_tag :access_level, options_for_select(GroupMember.access_level_roles), class: "project-access-select select2"
%hr
= button_tag 'Add users to group', class: "btn btn-create"
diff --git a/app/views/admin/hooks/index.html.haml b/app/views/admin/hooks/index.html.haml
index c217490963f..551edf14361 100644
--- a/app/views/admin/hooks/index.html.haml
+++ b/app/views/admin/hooks/index.html.haml
@@ -29,7 +29,7 @@
System hook will be triggered on set of events like creating project
or adding ssh key. But you can also enable extra triggers like Push events.
- %div.prepend-top-default
+ .prepend-top-default
= f.check_box :push_events, class: 'pull-left'
.prepend-left-20
= f.label :push_events, class: 'list-label' do
@@ -54,7 +54,7 @@
= f.submit "Add System Hook", class: "btn btn-create"
%hr
--if @hooks.any?
+- if @hooks.any?
.panel.panel-default
.panel-heading
System hooks (#{@hooks.count})
@@ -70,4 +70,3 @@
- if hook.send(trigger)
%span.label.label-gray= trigger.titleize
%span.label.label-gray SSL Verification: #{hook.enable_ssl_verification ? "enabled" : "disabled"}
-
diff --git a/app/views/admin/labels/_label.html.haml b/app/views/admin/labels/_label.html.haml
index be224d66855..77b174fbb27 100644
--- a/app/views/admin/labels/_label.html.haml
+++ b/app/views/admin/labels/_label.html.haml
@@ -1,4 +1,4 @@
-%li{id: dom_id(label)}
+%li{ id: dom_id(label) }
.label-row
= render_colored_label(label, tooltip: false)
= markdown_field(label, :description)
diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml
index 8bc7dc7dd51..2e6f03fcde0 100644
--- a/app/views/admin/projects/index.html.haml
+++ b/app/views/admin/projects/index.html.haml
@@ -69,8 +69,8 @@
.controls
- if project.archived
%span.label.label-warning archived
- %span.label.label-gray
- = repository_size(project)
+ %span.badge
+ = storage_counter(project.statistics.storage_size)
= link_to 'Edit', edit_namespace_project_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn"
= link_to 'Delete', [project.namespace.becomes(Namespace), project], data: { confirm: remove_project_message(project) }, method: :delete, class: "btn btn-remove"
.title
diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml
index 6c7c3c48604..2967da6e692 100644
--- a/app/views/admin/projects/show.html.haml
+++ b/app/views/admin/projects/show.html.haml
@@ -65,9 +65,16 @@
= @project.repository.path_to_repo
%li
- %span.light Size
- %strong
- = repository_size(@project)
+ %span.light Storage:
+ %strong= storage_counter(@project.statistics.storage_size)
+ (
+ = storage_counter(@project.statistics.repository_size)
+ repository,
+ = storage_counter(@project.statistics.build_artifacts_size)
+ build artifacts,
+ = storage_counter(@project.statistics.lfs_objects_size)
+ LFS
+ )
%li
%span.light last commit:
diff --git a/app/views/admin/runners/_runner.html.haml b/app/views/admin/runners/_runner.html.haml
index 64893b38c58..975bd950ae1 100644
--- a/app/views/admin/runners/_runner.html.haml
+++ b/app/views/admin/runners/_runner.html.haml
@@ -1,4 +1,4 @@
-%tr{id: dom_id(runner)}
+%tr{ id: dom_id(runner) }
%td
- if runner.shared?
%span.label.label-success shared
diff --git a/app/views/admin/users/_head.html.haml b/app/views/admin/users/_head.html.haml
index ce5e21e54cc..9984e733956 100644
--- a/app/views/admin/users/_head.html.haml
+++ b/app/views/admin/users/_head.html.haml
@@ -15,10 +15,8 @@
%ul.nav-links
= nav_link(path: 'users#show') do
= link_to "Account", admin_user_path(@user)
- = nav_link(path: 'users#groups') do
- = link_to "Groups", groups_admin_user_path(@user)
= nav_link(path: 'users#projects') do
- = link_to "Projects", projects_admin_user_path(@user)
+ = link_to "Groups and projects", projects_admin_user_path(@user)
= nav_link(path: 'users#keys') do
= link_to "SSH keys", keys_admin_user_path(@user)
= nav_link(controller: :identities) do
diff --git a/app/views/admin/users/_user.html.haml b/app/views/admin/users/_user.html.haml
index 2d9588f9d27..3b5c713ac2d 100644
--- a/app/views/admin/users/_user.html.haml
+++ b/app/views/admin/users/_user.html.haml
@@ -18,7 +18,7 @@
= link_to 'Edit', edit_admin_user_path(user), id: "edit_#{dom_id(user)}", class: 'btn'
- unless user == current_user
.dropdown.inline
- %a.dropdown-new.btn.btn-default#project-settings-button{href: '#', data: { toggle: 'dropdown' } }
+ %a.dropdown-new.btn.btn-default#project-settings-button{ href: '#', data: { toggle: 'dropdown' } }
= icon('cog')
= icon('caret-down')
%ul.dropdown-menu.dropdown-menu-align-right
diff --git a/app/views/admin/users/groups.html.haml b/app/views/admin/users/groups.html.haml
deleted file mode 100644
index 8f6d13b881a..00000000000
--- a/app/views/admin/users/groups.html.haml
+++ /dev/null
@@ -1,20 +0,0 @@
-- page_title "Groups", @user.name, "Users"
-= render 'admin/users/head'
-
-- group_members = @user.group_members.includes(:source)
-- if group_members.any?
- .panel.panel-default
- .panel-heading Groups:
- %ul.well-list
- - group_members.each do |group_member|
- - group = group_member.group
- %li.group_member
- %span{class: ("list-item-name" unless group_member.owner?)}
- %strong= link_to group.name, admin_group_path(group)
- .pull-right
- %span.light= group_member.human_access
- - unless group_member.owner?
- = link_to group_group_member_path(group, group_member), data: { confirm: remove_member_message(group_member) }, method: :delete, remote: true, class: "btn-xs btn btn-remove", title: 'Remove user from group' do
- %i.fa.fa-times.fa-inverse
-- else
- .nothing-here-block This user has no groups.
diff --git a/app/views/admin/users/projects.html.haml b/app/views/admin/users/projects.html.haml
index dd6b7303493..15eaf1c0e67 100644
--- a/app/views/admin/users/projects.html.haml
+++ b/app/views/admin/users/projects.html.haml
@@ -1,15 +1,21 @@
-- page_title "Projects", @user.name, "Users"
+- page_title "Groups and projects", @user.name, "Users"
= render 'admin/users/head'
- if @user.groups.any?
.panel.panel-default
.panel-heading Group projects
%ul.well-list
- - @user.groups.each do |group|
- %li
+ - @user.group_members.includes(:source).each do |group_member|
+ - group = group_member.group
+ %li.group_member
%strong= link_to group.name, admin_group_path(group)
&ndash; access to
#{pluralize(group.projects.count, 'project')}
+ .pull-right
+ %span.light.vertical-align-middle= group_member.human_access
+ - unless group_member.owner?
+ = link_to group_group_member_path(group, group_member), data: { confirm: remove_member_message(group_member) }, method: :delete, remote: true, class: "btn-xs btn btn-remove prepend-left-10", title: 'Remove user from group' do
+ %i.fa.fa-times.fa-inverse
.row
.col-md-6
@@ -35,8 +41,8 @@
- if member.owner?
%span.light Owner
- else
- %span.light= member.human_access
+ %span.light.vertical-align-middle= member.human_access
- if member.respond_to? :project
- = link_to namespace_project_project_member_path(project.namespace, project, member), data: { confirm: remove_member_message(member) }, remote: true, method: :delete, class: "btn-xs btn btn-remove", title: 'Remove user from project' do
+ = link_to namespace_project_project_member_path(project.namespace, project, member), data: { confirm: remove_member_message(member) }, remote: true, method: :delete, class: "btn-xs btn btn-remove prepend-left-10", title: 'Remove user from project' do
%i.fa.fa-times
diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml
index 76c9ed0ee8b..a71240986c9 100644
--- a/app/views/admin/users/show.html.haml
+++ b/app/views/admin/users/show.html.haml
@@ -40,7 +40,7 @@
%li.two-factor-status
%span.light Two-factor Authentication:
- %strong{class: @user.two_factor_enabled? ? 'cgreen' : 'cred'}
+ %strong{ class: @user.two_factor_enabled? ? 'cgreen' : 'cred' }
- if @user.two_factor_enabled?
Enabled
= link_to 'Disable', disable_two_factor_admin_user_path(@user), data: {confirm: 'Are you sure?'}, method: :patch, class: 'btn btn-xs btn-remove pull-right', title: 'Disable Two-factor Authentication'
diff --git a/app/views/ci/lints/_create.html.haml b/app/views/ci/lints/_create.html.haml
index 61c7cce20b2..c91602fcff7 100644
--- a/app/views/ci/lints/_create.html.haml
+++ b/app/views/ci/lints/_create.html.haml
@@ -36,7 +36,7 @@
- if build[:allow_failure]
%b Allowed to fail
--else
+- else
%p
%b Status:
syntax is incorrect
diff --git a/app/views/dashboard/projects/_zero_authorized_projects.html.haml b/app/views/dashboard/projects/_zero_authorized_projects.html.haml
index 4a55aac0df6..1bbd4602ecf 100644
--- a/app/views/dashboard/projects/_zero_authorized_projects.html.haml
+++ b/app/views/dashboard/projects/_zero_authorized_projects.html.haml
@@ -33,7 +33,7 @@
= link_to new_project_path, class: "btn btn-new" do
New project
--if publicish_project_count > 0
+- if publicish_project_count > 0
.blank-state
.blank-state-icon
= icon("globe")
diff --git a/app/views/dashboard/todos/_todo.html.haml b/app/views/dashboard/todos/_todo.html.haml
index cc077fad32a..9849b31d7e2 100644
--- a/app/views/dashboard/todos/_todo.html.haml
+++ b/app/views/dashboard/todos/_todo.html.haml
@@ -1,4 +1,4 @@
-%li{class: "todo todo-#{todo.done? ? 'done' : 'pending'}", id: dom_id(todo), data:{url: todo_target_path(todo)} }
+%li{ class: "todo todo-#{todo.done? ? 'done' : 'pending'}", id: dom_id(todo), data: { url: todo_target_path(todo) } }
= author_avatar(todo, size: 40)
.todo-item.todo-block
diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml
index e13f404fee2..f4efcfb27b2 100644
--- a/app/views/dashboard/todos/index.html.haml
+++ b/app/views/dashboard/todos/index.html.haml
@@ -5,14 +5,14 @@
.top-area
%ul.nav-links
- todo_pending_active = ('active' if params[:state].blank? || params[:state] == 'pending')
- %li{class: "todos-pending #{todo_pending_active}"}>
+ %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}"}>
+ %li{ class: "todos-done #{todo_done_active}" }>
= link_to todos_filter_path(state: 'done') do
%span
Done
@@ -50,7 +50,7 @@
data: { data: todo_actions_options, default_label: 'Action' } })
.pull-right
.dropdown.inline.prepend-left-10
- %button.dropdown-toggle{type: 'button', 'data-toggle' => 'dropdown'}
+ %button.dropdown-toggle{ type: 'button', 'data-toggle' => 'dropdown' }
%span.light
- if @sort.present?
= sort_options_hash[@sort]
diff --git a/app/views/devise/sessions/_new_base.html.haml b/app/views/devise/sessions/_new_base.html.haml
index 84e13693dfd..5d359538efe 100644
--- a/app/views/devise/sessions/_new_base.html.haml
+++ b/app/views/devise/sessions/_new_base.html.haml
@@ -1,16 +1,16 @@
= form_for(resource, as: resource_name, url: session_path(resource_name), html: { class: 'new_user gl-show-field-errors', 'aria-live' => 'assertive'}) do |f|
- %div.form-group
+ .form-group
= f.label "Username or email", for: :login
= f.text_field :login, class: "form-control top", autofocus: "autofocus", autocapitalize: "off", autocorrect: "off", required: true, title: "This field is required."
- %div.form-group
+ .form-group
= f.label :password
= f.password_field :password, class: "form-control bottom", required: true, title: "This field is required."
- if devise_mapping.rememberable?
.remember-me.checkbox
- %label{for: "user_remember_me"}
+ %label{ for: "user_remember_me" }
= f.check_box :remember_me
%span Remember me
.pull-right.forgot-password
= link_to "Forgot your password?", new_password_path(resource_name)
- %div.submit-container.move-submit-down
+ .submit-container.move-submit-down
= f.submit "Sign in", class: "btn btn-save"
diff --git a/app/views/devise/sessions/_new_crowd.html.haml b/app/views/devise/sessions/_new_crowd.html.haml
index a6cadbcbdff..2556cb6f59b 100644
--- a/app/views/devise/sessions/_new_crowd.html.haml
+++ b/app/views/devise/sessions/_new_crowd.html.haml
@@ -1,13 +1,13 @@
= form_tag(omniauth_authorize_path(:user, :crowd), id: 'new_crowd_user', class: 'gl-show-field-errors') do
.form-group
= label_tag :username, 'Username or email'
- = text_field_tag :username, nil, {class: "form-control top", title: "This field is required", autofocus: "autofocus", required: true }
+ = text_field_tag :username, nil, { class: "form-control top", title: "This field is required", autofocus: "autofocus", required: true }
.form-group
= label_tag :password
= password_field_tag :password, nil, { class: "form-control bottom", title: "This field is required.", required: true }
- if devise_mapping.rememberable?
.remember-me.checkbox
- %label{for: "remember_me"}
+ %label{ for: "remember_me" }
= check_box_tag :remember_me, '1', false, id: 'remember_me'
%span Remember me
= submit_tag "Sign in", class: "btn-save btn"
diff --git a/app/views/devise/sessions/_new_ldap.html.haml b/app/views/devise/sessions/_new_ldap.html.haml
index 3ab5461f929..3159d21598a 100644
--- a/app/views/devise/sessions/_new_ldap.html.haml
+++ b/app/views/devise/sessions/_new_ldap.html.haml
@@ -1,13 +1,13 @@
= form_tag(omniauth_callback_path(:user, server['provider_name']), id: 'new_ldap_user', class: "gl-show-field-errors") do
.form-group
= label_tag :username, "#{server['label']} Username"
- = text_field_tag :username, nil, {class: "form-control top", title: "This field is required.", autofocus: "autofocus", required: true }
+ = text_field_tag :username, nil, { class: "form-control top", title: "This field is required.", autofocus: "autofocus", required: true }
.form-group
= label_tag :password
= password_field_tag :password, nil, { class: "form-control bottom", title: "This field is required.", required: true }
- if devise_mapping.rememberable?
.remember-me.checkbox
- %label{for: "remember_me"}
+ %label{ for: "remember_me" }
= check_box_tag :remember_me, '1', false, id: 'remember_me'
%span Remember me
= submit_tag "Sign in", class: "btn-save btn"
diff --git a/app/views/devise/sessions/two_factor.html.haml b/app/views/devise/sessions/two_factor.html.haml
index 2cadc424668..951f03083bf 100644
--- a/app/views/devise/sessions/two_factor.html.haml
+++ b/app/views/devise/sessions/two_factor.html.haml
@@ -7,7 +7,7 @@
.login-box
.login-body
- if @user.two_factor_otp_enabled?
- = form_for(resource, as: resource_name, url: session_path(resource_name), method: :post, html: { class: 'edit_user gl-show-field-errors' }) do |f|
+ = form_for(resource, as: resource_name, url: session_path(resource_name), method: :post, html: { class: "edit_user gl-show-field-errors js-2fa-form #{'hidden' if @user.two_factor_u2f_enabled?}" }) do |f|
- resource_params = params[resource_name].presence || params
= f.hidden_field :remember_me, value: resource_params.fetch(:remember_me, 0)
%div
diff --git a/app/views/devise/shared/_omniauth_box.html.haml b/app/views/devise/shared/_omniauth_box.html.haml
index 8908b64cdac..e87a16a5157 100644
--- a/app/views/devise/shared/_omniauth_box.html.haml
+++ b/app/views/devise/shared/_omniauth_box.html.haml
@@ -1,4 +1,4 @@
-%div.omniauth-container
+.omniauth-container
%p
%span.light
Sign in with &nbsp;
diff --git a/app/views/devise/shared/_signin_box.html.haml b/app/views/devise/shared/_signin_box.html.haml
index 86edaf14e43..eddfce363a7 100644
--- a/app/views/devise/shared/_signin_box.html.haml
+++ b/app/views/devise/shared/_signin_box.html.haml
@@ -1,18 +1,18 @@
- if form_based_providers.any?
- if crowd_enabled?
- .login-box.tab-pane.active{id: "crowd", role: 'tabpanel', class: 'tab-pane'}
+ .login-box.tab-pane.active{ id: "crowd", role: 'tabpanel' }
.login-body
= render 'devise/sessions/new_crowd'
- @ldap_servers.each_with_index do |server, i|
- .login-box.tab-pane{id: "#{server['provider_name']}", role: 'tabpanel', class: (:active if i.zero? && !crowd_enabled?)}
+ .login-box.tab-pane{ id: "#{server['provider_name']}", role: 'tabpanel', class: (:active if i.zero? && !crowd_enabled?) }
.login-body
= render 'devise/sessions/new_ldap', server: server
- if signin_enabled?
- .login-box.tab-pane{id: 'ldap-standard', role: 'tabpanel'}
+ .login-box.tab-pane{ id: 'ldap-standard', role: 'tabpanel' }
.login-body
= render 'devise/sessions/new_base'
- elsif signin_enabled?
- .login-box.tab-pane.active{id: 'login-pane', role: 'tabpanel'}
+ .login-box.tab-pane.active{ id: 'login-pane', role: 'tabpanel' }
.login-body
= render 'devise/sessions/new_base'
diff --git a/app/views/devise/shared/_signup_box.html.haml b/app/views/devise/shared/_signup_box.html.haml
index 3133f6de2e8..545a938f4be 100644
--- a/app/views/devise/shared/_signup_box.html.haml
+++ b/app/views/devise/shared/_signup_box.html.haml
@@ -1,18 +1,18 @@
-#register-pane.login-box{ role: 'tabpanel', class: 'tab-pane' }
+#register-pane.tab-pane.login-box{ role: 'tabpanel' }
.login-body
= form_for(resource, as: "new_#{resource_name}", url: registration_path(resource_name), html: { class: "new_new_user gl-show-field-errors", "aria-live" => "assertive" }) do |f|
.devise-errors
= devise_error_messages!
- %div.form-group
+ .form-group
= f.label :name
= f.text_field :name, class: "form-control top", required: true, title: "This field is required."
- %div.username.form-group
+ .username.form-group
= f.label :username
= f.text_field :username, class: "form-control middle", pattern: Gitlab::Regex::NAMESPACE_REGEX_STR_SIMPLE, required: true, title: 'Please create a username with only alphanumeric characters.'
%p.validation-error.hide Username is already taken.
%p.validation-success.hide Username is available.
%p.validation-pending.hide Checking username availability...
- %div.form-group
+ .form-group
= f.label :email
= f.email_field :email, class: "form-control middle", required: true, title: "Please provide a valid email address."
.form-group.append-bottom-20#password-strength
diff --git a/app/views/devise/shared/_tabs_ldap.html.haml b/app/views/devise/shared/_tabs_ldap.html.haml
index aec1b31ce62..8c4ad30c832 100644
--- a/app/views/devise/shared/_tabs_ldap.html.haml
+++ b/app/views/devise/shared/_tabs_ldap.html.haml
@@ -3,7 +3,7 @@
%li.active
= link_to "Crowd", "#crowd", 'data-toggle' => 'tab'
- @ldap_servers.each_with_index do |server, i|
- %li{class: (:active if i.zero? && !crowd_enabled?)}
+ %li{ class: (:active if i.zero? && !crowd_enabled?) }
= link_to server['label'], "##{server['provider_name']}", 'data-toggle' => 'tab'
- if signin_enabled?
%li
diff --git a/app/views/devise/shared/_tabs_normal.html.haml b/app/views/devise/shared/_tabs_normal.html.haml
index 05246303fb6..c225d800a98 100644
--- a/app/views/devise/shared/_tabs_normal.html.haml
+++ b/app/views/devise/shared/_tabs_normal.html.haml
@@ -1,6 +1,6 @@
-%ul.nav-links.new-session-tabs.nav-tabs{ role: 'tablist'}
+%ul.nav-links.new-session-tabs.nav-tabs{ role: 'tablist' }
%li.active{ role: 'presentation' }
- %a{ href: '#login-pane', data: { toggle: 'tab' }, role: 'tab'} Sign in
+ %a{ href: '#login-pane', data: { toggle: 'tab' }, role: 'tab' } Sign in
- if signin_enabled? && signup_enabled?
- %li{ role: 'presentation'}
- %a{ href: '#register-pane', data: { toggle: 'tab' }, role: 'tab'} Register
+ %li{ role: 'presentation' }
+ %a{ href: '#register-pane', data: { toggle: 'tab' }, role: 'tab' } Register
diff --git a/app/views/discussions/_diff_discussion.html.haml b/app/views/discussions/_diff_discussion.html.haml
index 1411daeb4a6..2deadbeeceb 100644
--- a/app/views/discussions/_diff_discussion.html.haml
+++ b/app/views/discussions/_diff_discussion.html.haml
@@ -1,5 +1,5 @@
- expanded = local_assigns.fetch(:expanded, true)
-%tr.notes_holder{class: ('hide' unless expanded)}
+%tr.notes_holder{ class: ('hide' unless expanded) }
%td.notes_line{ colspan: 2 }
%td.notes_content
.content
diff --git a/app/views/discussions/_jump_to_next.html.haml b/app/views/discussions/_jump_to_next.html.haml
index 7ed09dd1a98..69bd416c4de 100644
--- a/app/views/discussions/_jump_to_next.html.haml
+++ b/app/views/discussions/_jump_to_next.html.haml
@@ -5,5 +5,5 @@
%button.btn.btn-default.discussion-next-btn.has-tooltip{ "@click" => "jumpToNextUnresolvedDiscussion",
title: "Jump to next unresolved discussion",
"aria-label" => "Jump to next unresolved discussion",
- data: { container: "body" }}
+ data: { container: "body" } }
= custom_icon("next_discussion")
diff --git a/app/views/discussions/_parallel_diff_discussion.html.haml b/app/views/discussions/_parallel_diff_discussion.html.haml
index f1072ce0feb..ef16b516e2c 100644
--- a/app/views/discussions/_parallel_diff_discussion.html.haml
+++ b/app/views/discussions/_parallel_diff_discussion.html.haml
@@ -1,9 +1,9 @@
- expanded = discussion_left.try(:expanded?) || discussion_right.try(:expanded?)
-%tr.notes_holder{class: ('hide' unless expanded)}
+%tr.notes_holder{ class: ('hide' unless expanded) }
- if discussion_left
%td.notes_line.old
%td.notes_content.parallel.old
- .content{class: ('hide' unless discussion_left.expanded?)}
+ .content{ class: ('hide' unless discussion_left.expanded?) }
= render "discussions/notes", discussion: discussion_left, line_type: 'old'
- else
%td.notes_line.old= ""
@@ -13,7 +13,7 @@
- if discussion_right
%td.notes_line.new
%td.notes_content.parallel.new
- .content{class: ('hide' unless discussion_right.expanded?)}
+ .content{ class: ('hide' unless discussion_right.expanded?) }
= render "discussions/notes", discussion: discussion_right, line_type: 'new'
- else
%td.notes_line.new= ""
diff --git a/app/views/doorkeeper/applications/_delete_form.html.haml b/app/views/doorkeeper/applications/_delete_form.html.haml
index 001a711b1dd..84b4ce5b606 100644
--- a/app/views/doorkeeper/applications/_delete_form.html.haml
+++ b/app/views/doorkeeper/applications/_delete_form.html.haml
@@ -1,6 +1,6 @@
- submit_btn_css ||= 'btn btn-link btn-remove btn-sm'
= form_tag oauth_application_path(application) do
- %input{:name => "_method", :type => "hidden", :value => "delete"}/
+ %input{ :name => "_method", :type => "hidden", :value => "delete" }/
- if defined? small
= button_tag type: "submit", class: "btn btn-transparent", data: { confirm: "Are you sure?" } do
%span.sr-only
diff --git a/app/views/doorkeeper/applications/index.html.haml b/app/views/doorkeeper/applications/index.html.haml
index 3998e66f40d..aa271150b07 100644
--- a/app/views/doorkeeper/applications/index.html.haml
+++ b/app/views/doorkeeper/applications/index.html.haml
@@ -31,7 +31,7 @@
%th.last-heading
%tbody
- @applications.each do |application|
- %tr{id: "application_#{application.id}"}
+ %tr{ id: "application_#{application.id}" }
%td= link_to application.name, oauth_application_path(application)
%td
- application.redirect_uri.split.each do |uri|
@@ -63,7 +63,7 @@
%tbody
- @authorized_apps.each do |app|
- token = app.authorized_tokens.order('created_at desc').first
- %tr{id: "application_#{app.id}"}
+ %tr{ id: "application_#{app.id}" }
%td= app.name
%td= token.created_at
%td= token.scopes
@@ -72,7 +72,7 @@
%tr
%td
Anonymous
- %div.help-block
+ .help-block
%em Authorization was granted by entering your username and password in the application.
%td= token.created_at
%td= token.scopes
diff --git a/app/views/doorkeeper/authorizations/error.html.haml b/app/views/doorkeeper/authorizations/error.html.haml
index a4c607cea60..6117b00149f 100644
--- a/app/views/doorkeeper/authorizations/error.html.haml
+++ b/app/views/doorkeeper/authorizations/error.html.haml
@@ -1,3 +1,3 @@
%h3.page-title An error has occurred
-%main{:role => "main"}
+%main{ :role => "main" }
%pre= @pre_auth.error_response.body[:error_description]
diff --git a/app/views/doorkeeper/authorizations/new.html.haml b/app/views/doorkeeper/authorizations/new.html.haml
index ce050007204..2a0e301c8dd 100644
--- a/app/views/doorkeeper/authorizations/new.html.haml
+++ b/app/views/doorkeeper/authorizations/new.html.haml
@@ -1,5 +1,5 @@
%h3.page-title Authorization required
-%main{:role => "main"}
+%main{ :role => "main" }
%p.h4
Authorize
%strong.text-info= @pre_auth.client.name
diff --git a/app/views/doorkeeper/authorizations/show.html.haml b/app/views/doorkeeper/authorizations/show.html.haml
index 01f9e46f142..44e868e6782 100644
--- a/app/views/doorkeeper/authorizations/show.html.haml
+++ b/app/views/doorkeeper/authorizations/show.html.haml
@@ -1,3 +1,3 @@
%h3.page-title Authorization code:
-%main{:role => "main"}
+%main{ :role => "main" }
%code#authorization_code= params[:code]
diff --git a/app/views/doorkeeper/authorized_applications/_delete_form.html.haml b/app/views/doorkeeper/authorized_applications/_delete_form.html.haml
index 9f02a8d2ed9..11c1e67878e 100644
--- a/app/views/doorkeeper/authorized_applications/_delete_form.html.haml
+++ b/app/views/doorkeeper/authorized_applications/_delete_form.html.haml
@@ -3,7 +3,7 @@
- path = oauth_authorized_application_path(0, token_id: token)
- else
- path = oauth_authorized_application_path(application)
-
+
= form_tag path do
- %input{:name => "_method", :type => "hidden", :value => "delete"}/
+ %input{ :name => "_method", :type => "hidden", :value => "delete" }/
= submit_tag 'Revoke', onclick: "return confirm('Are you sure?')", class: 'btn btn-remove btn-sm'
diff --git a/app/views/doorkeeper/authorized_applications/index.html.haml b/app/views/doorkeeper/authorized_applications/index.html.haml
index b184b9c01d4..c8a585560a2 100644
--- a/app/views/doorkeeper/authorized_applications/index.html.haml
+++ b/app/views/doorkeeper/authorized_applications/index.html.haml
@@ -1,6 +1,6 @@
%header.page-header
%h1 Your authorized applications
-%main{:role => "main"}
+%main{ :role => "main" }
.table-holder
%table.table.table-striped
%thead
diff --git a/app/views/emojis/index.html.haml b/app/views/emojis/index.html.haml
index 790d90ad3ee..49bd9acd2db 100644
--- a/app/views/emojis/index.html.haml
+++ b/app/views/emojis/index.html.haml
@@ -7,5 +7,5 @@
%ul.clearfix.emoji-menu-list
- emojis.each do |emoji|
%li.pull-left.text-center.emoji-menu-list-item
- %button.emoji-menu-btn.text-center.js-emoji-btn{type: "button"}
+ %button.emoji-menu-btn.text-center.js-emoji-btn{ type: "button" }
= emoji_icon(emoji["name"], emoji["unicode"], emoji["aliases"])
diff --git a/app/views/errors/access_denied.html.haml b/app/views/errors/access_denied.html.haml
index 8bddbef3562..a97cbd4d4b3 100644
--- a/app/views/errors/access_denied.html.haml
+++ b/app/views/errors/access_denied.html.haml
@@ -1,6 +1,5 @@
- content_for(:title, 'Access Denied')
-%img{:alt => "GitLab Logo",
- :src => image_path('logo.svg')}
+%img{ :alt => "GitLab Logo", :src => image_path('logo.svg') }
%h1
403
.container
diff --git a/app/views/errors/encoding.html.haml b/app/views/errors/encoding.html.haml
index 064ff14ad2c..64f7f8e0836 100644
--- a/app/views/errors/encoding.html.haml
+++ b/app/views/errors/encoding.html.haml
@@ -1,6 +1,5 @@
- content_for(:title, 'Encoding Error')
-%img{:alt => "GitLab Logo",
- :src => image_path('logo.svg')}
+%img{ :alt => "GitLab Logo", :src => image_path('logo.svg') }
%h1
500
.container
diff --git a/app/views/errors/git_not_found.html.haml b/app/views/errors/git_not_found.html.haml
index c5c12a410ac..d860957665b 100644
--- a/app/views/errors/git_not_found.html.haml
+++ b/app/views/errors/git_not_found.html.haml
@@ -1,6 +1,5 @@
- content_for(:title, 'Git Resource Not Found')
-%img{:alt => "GitLab Logo",
- :src => image_path('logo.svg')}
+%img{ :alt => "GitLab Logo", :src => image_path('logo.svg') }
%h1
404
.container
diff --git a/app/views/errors/not_found.html.haml b/app/views/errors/not_found.html.haml
index 50a54a93cb5..a0b9a632e22 100644
--- a/app/views/errors/not_found.html.haml
+++ b/app/views/errors/not_found.html.haml
@@ -1,6 +1,5 @@
- content_for(:title, 'Not Found')
-%img{:alt => "GitLab Logo",
- :src => image_path('logo.svg')}
+%img{ :alt => "GitLab Logo", :src => image_path('logo.svg') }
%h1
404
.container
diff --git a/app/views/errors/omniauth_error.html.haml b/app/views/errors/omniauth_error.html.haml
index d91f1878cb6..72508b91134 100644
--- a/app/views/errors/omniauth_error.html.haml
+++ b/app/views/errors/omniauth_error.html.haml
@@ -1,6 +1,5 @@
- content_for(:title, 'Auth Error')
-%img{:alt => "GitLab Logo",
- :src => image_path('logo.svg')}
+%img{ :alt => "GitLab Logo", :src => image_path('logo.svg') }
%h1
422
.container
diff --git a/app/views/events/_event_issue.atom.haml b/app/views/events/_event_issue.atom.haml
index 083c3936212..51585314a62 100644
--- a/app/views/events/_event_issue.atom.haml
+++ b/app/views/events/_event_issue.atom.haml
@@ -1,2 +1,2 @@
-%div{xmlns: "http://www.w3.org/1999/xhtml"}
+%div{ xmlns: "http://www.w3.org/1999/xhtml" }
= markdown(issue.description, pipeline: :atom, project: issue.project, author: issue.author)
diff --git a/app/views/events/_event_merge_request.atom.haml b/app/views/events/_event_merge_request.atom.haml
index d7e05600627..56fc8b86217 100644
--- a/app/views/events/_event_merge_request.atom.haml
+++ b/app/views/events/_event_merge_request.atom.haml
@@ -1,2 +1,2 @@
-%div{xmlns: "http://www.w3.org/1999/xhtml"}
+%div{ xmlns: "http://www.w3.org/1999/xhtml" }
= markdown(merge_request.description, pipeline: :atom, project: merge_request.project, author: merge_request.author)
diff --git a/app/views/events/_event_note.atom.haml b/app/views/events/_event_note.atom.haml
index 1154f982821..6fa2f9bd4db 100644
--- a/app/views/events/_event_note.atom.haml
+++ b/app/views/events/_event_note.atom.haml
@@ -1,2 +1,2 @@
-%div{xmlns: "http://www.w3.org/1999/xhtml"}
+%div{ xmlns: "http://www.w3.org/1999/xhtml" }
= markdown(note.note, pipeline: :atom, project: note.project, author: note.author)
diff --git a/app/views/events/_event_push.atom.haml b/app/views/events/_event_push.atom.haml
index 28bee1d0a33..f8f0bcb7608 100644
--- a/app/views/events/_event_push.atom.haml
+++ b/app/views/events/_event_push.atom.haml
@@ -1,4 +1,4 @@
-%div{xmlns: "http://www.w3.org/1999/xhtml"}
+%div{ xmlns: "http://www.w3.org/1999/xhtml" }
- event.commits.first(15).each do |commit|
%p
%strong= commit[:author][:name]
diff --git a/app/views/events/event/_common.html.haml b/app/views/events/event/_common.html.haml
index bba6e0d2c20..2fb6b5647da 100644
--- a/app/views/events/event/_common.html.haml
+++ b/app/views/events/event/_common.html.haml
@@ -1,6 +1,6 @@
.event-title
%span.author_name= link_to_author event
- %span{class: event.action_name}
+ %span{ class: event.action_name }
- if event.target
= event.action_name
%strong
diff --git a/app/views/events/event/_created_project.html.haml b/app/views/events/event/_created_project.html.haml
index aba64dd17d0..80cf2344fe1 100644
--- a/app/views/events/event/_created_project.html.haml
+++ b/app/views/events/event/_created_project.html.haml
@@ -1,6 +1,6 @@
.event-title
%span.author_name= link_to_author event
- %span{class: event.action_name}
+ %span{ class: event.action_name }
= event_action_name(event)
- if event.project
diff --git a/app/views/explore/groups/index.html.haml b/app/views/explore/groups/index.html.haml
index 4e5d965ccbe..73cf6e87eb4 100644
--- a/app/views/explore/groups/index.html.haml
+++ b/app/views/explore/groups/index.html.haml
@@ -17,7 +17,7 @@
.pull-right
.dropdown.inline
- %button.dropdown-toggle{type: 'button', 'data-toggle' => 'dropdown'}
+ %button.dropdown-toggle{ type: 'button', 'data-toggle' => 'dropdown' }
%span.light
- if @sort.present?
= sort_options_hash[@sort]
diff --git a/app/views/explore/projects/_filter.html.haml b/app/views/explore/projects/_filter.html.haml
index 5ea154c36b4..e3088848492 100644
--- a/app/views/explore/projects/_filter.html.haml
+++ b/app/views/explore/projects/_filter.html.haml
@@ -1,6 +1,6 @@
- if current_user
.dropdown
- %button.dropdown-toggle{href: '#', "data-toggle" => "dropdown"}
+ %button.dropdown-toggle{ href: '#', "data-toggle" => "dropdown" }
= icon('globe')
%span.light Visibility:
- if params[:visibility_level].present?
@@ -20,7 +20,7 @@
- if @tags.present?
.dropdown
- %button.dropdown-toggle{href: '#', "data-toggle" => "dropdown"}
+ %button.dropdown-toggle{ href: '#', "data-toggle" => "dropdown" }
= icon('tags')
%span.light Tags:
- if params[:tag].present?
diff --git a/app/views/groups/_group_lfs_settings.html.haml b/app/views/groups/_group_lfs_settings.html.haml
index af57065f0fc..3c622ca5c3c 100644
--- a/app/views/groups/_group_lfs_settings.html.haml
+++ b/app/views/groups/_group_lfs_settings.html.haml
@@ -8,4 +8,4 @@
Allow projects within this group to use Git LFS
= link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs')
%br/
- %span.descr This setting can be overridden in each project. \ No newline at end of file
+ %span.descr This setting can be overridden in each project.
diff --git a/app/views/groups/projects.html.haml b/app/views/groups/projects.html.haml
index 33fee334d93..2e7e5e5c309 100644
--- a/app/views/groups/projects.html.haml
+++ b/app/views/groups/projects.html.haml
@@ -18,8 +18,8 @@
.pull-right
- if project.archived
%span.label.label-warning archived
- %span.label.label-gray
- = repository_size(project)
+ %span.badge
+ = storage_counter(project.statistics.storage_size)
= link_to 'Members', namespace_project_project_members_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn btn-sm"
= link_to 'Edit', edit_namespace_project_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn btn-sm"
= link_to 'Remove', project, data: { confirm: remove_project_message(project)}, method: :delete, class: "btn btn-sm btn-remove"
diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml
index 52ce26a20b1..d256d14609e 100644
--- a/app/views/groups/show.html.haml
+++ b/app/views/groups/show.html.haml
@@ -22,7 +22,7 @@
= render 'shared/members/access_request_buttons', source: @group
= render 'shared/notifications/button', notification_setting: @notification_setting
-%div.groups-header{ class: container_class }
+.groups-header{ class: container_class }
.top-area
%ul.nav-links
%li.active
@@ -32,6 +32,10 @@
%li
= link_to "#shared", 'data-toggle' => 'tab' do
Shared Projects
+ - if @nested_groups.present?
+ %li
+ = link_to "#groups", 'data-toggle' => 'tab' do
+ Subgroups
.nav-controls
= form_tag request.path, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f|
= search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control', spellcheck: false
@@ -47,3 +51,8 @@
- if @shared_projects.present?
.tab-pane#shared
= render "shared_projects", projects: @shared_projects
+
+ - if @nested_groups.present?
+ .tab-pane#groups
+ %ul.content-list
+ = render partial: 'shared/groups/group', collection: @nested_groups
diff --git a/app/views/help/_shortcuts.html.haml b/app/views/help/_shortcuts.html.haml
index 65842a0479b..b74cc822295 100644
--- a/app/views/help/_shortcuts.html.haml
+++ b/app/views/help/_shortcuts.html.haml
@@ -1,8 +1,8 @@
-#modal-shortcuts.modal{tabindex: -1}
+#modal-shortcuts.modal{ tabindex: -1 }
.modal-dialog
.modal-content
.modal-header
- %a.close{href: "#", "data-dismiss" => "modal"} ×
+ %a.close{ href: "#", "data-dismiss" => "modal" } ×
%h4
Keyboard Shortcuts
%small
@@ -82,7 +82,7 @@
.col-lg-4
%table.shortcut-mappings
- %tbody{ class: 'hidden-shortcut project', style: 'display:none' }
+ %tbody.hidden-shortcut.project{ style: 'display:none' }
%tr
%th
%th Global Dashboard
@@ -190,7 +190,7 @@
%td New issue
.col-lg-4
%table.shortcut-mappings
- %tbody{ class: 'hidden-shortcut network', style: 'display:none' }
+ %tbody.hidden-shortcut.network{ style: 'display:none' }
%tr
%th
%th Network Graph
@@ -240,7 +240,7 @@
.key
shift j
%td Scroll to bottom
- %tbody{ class: 'hidden-shortcut issues', style: 'display:none' }
+ %tbody.hidden-shortcut.issues{ style: 'display:none' }
%tr
%th
%th Issues
@@ -264,7 +264,7 @@
%td.shortcut
.key l
%td Change Label
- %tbody{ class: 'hidden-shortcut merge_requests', style: 'display:none' }
+ %tbody.hidden-shortcut.merge_requests{ style: 'display:none' }
%tr
%th
%th Merge Requests
diff --git a/app/views/help/ui.html.haml b/app/views/help/ui.html.haml
index 070ed90da6d..dd1df46792b 100644
--- a/app/views/help/ui.html.haml
+++ b/app/views/help/ui.html.haml
@@ -182,7 +182,7 @@
.nav-controls
= text_field_tag 'sample', nil, class: 'form-control'
.dropdown
- %button.dropdown-menu-toggle{type: 'button', 'data-toggle' => 'dropdown'}
+ %button.dropdown-menu-toggle{ type: 'button', 'data-toggle' => 'dropdown' }
%span Sort by name
= icon('chevron-down')
%ul.dropdown-menu
@@ -205,121 +205,121 @@
%h2#buttons Buttons
.example
- %button.btn.btn-default{:type => "button"} Default
- %button.btn.btn-gray{:type => "button"} Gray
- %button.btn.btn-primary{:type => "button"} Primary
- %button.btn.btn-success{:type => "button"} Success
- %button.btn.btn-info{:type => "button"} Info
- %button.btn.btn-warning{:type => "button"} Warning
- %button.btn.btn-danger{:type => "button"} Danger
- %button.btn.btn-link{:type => "button"} Link
+ %button.btn.btn-default{ :type => "button" } Default
+ %button.btn.btn-gray{ :type => "button" } Gray
+ %button.btn.btn-primary{ :type => "button" } Primary
+ %button.btn.btn-success{ :type => "button" } Success
+ %button.btn.btn-info{ :type => "button" } Info
+ %button.btn.btn-warning{ :type => "button" } Warning
+ %button.btn.btn-danger{ :type => "button" } Danger
+ %button.btn.btn-link{ :type => "button" } Link
%h2#dropdowns Dropdowns
.example
.clearfix
.dropdown.inline.pull-left
- %button.dropdown-menu-toggle{type: 'button', data: {toggle: 'dropdown'}}
+ %button.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown' } }
Dropdown
= icon('chevron-down')
%ul.dropdown-menu
%li
- %a{href: "#"}
+ %a{ href: "#" }
Dropdown Option
.dropdown.inline.pull-right
- %button.dropdown-menu-toggle{type: 'button', data: {toggle: 'dropdown'}}
+ %button.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown' } }
Dropdown
= icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-align-right
%li
- %a{href: "#"}
+ %a{ href: "#" }
Dropdown Option
.example
%div
.dropdown.inline
- %button.dropdown-menu-toggle{type: 'button', data: {toggle: 'dropdown'}}
+ %button.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown' } }
Dropdown
= icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-selectable
%li
- %a.is-active{href: "#"}
+ %a.is-active{ href: "#" }
Dropdown Option
.example
%div
.dropdown.inline
- %button.dropdown-menu-toggle{type: 'button', data: {toggle: 'dropdown'}}
+ %button.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown' } }
Dropdown
= icon('chevron-down')
.dropdown-menu.dropdown-select.dropdown-menu-selectable
.dropdown-title
%span Dropdown Title
- %button.dropdown-title-button.dropdown-menu-close{aria: {label: "Close"}}
+ %button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } }
= icon('times')
.dropdown-input
- %input.dropdown-input-field{type: "search", placeholder: "Filter results"}
+ %input.dropdown-input-field{ type: "search", placeholder: "Filter results" }
= icon('search')
.dropdown-content
%ul
%li
- %a.is-active{href: "#"}
+ %a.is-active{ href: "#" }
Dropdown Option
%li
- %a{href: "#"}
+ %a{ href: "#" }
Dropdown Option
%li.divider
%li
- %a{href: "#"}
+ %a{ href: "#" }
Dropdown Option
%li
- %a{href: "#"}
+ %a{ href: "#" }
Dropdown Option
%li
- %a{href: "#"}
+ %a{ href: "#" }
Dropdown Option
%li
- %a{href: "#"}
+ %a{ href: "#" }
Dropdown Option
%li
- %a{href: "#"}
+ %a{ href: "#" }
Dropdown Option
.dropdown-footer
%strong Tip:
If an author is not a member of this project, you can still filter by his name while using the search field.
.dropdown.inline
- %button.dropdown-menu-toggle{type: 'button', data: {toggle: 'dropdown'}}
+ %button.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown' } }
Dropdown loading
= icon('chevron-down')
.dropdown-menu.dropdown-select.dropdown-menu-selectable.is-loading
.dropdown-title
%span Dropdown Title
- %button.dropdown-title-button.dropdown-menu-close{aria: {label: "Close"}}
+ %button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } }
= icon('times')
.dropdown-input
- %input.dropdown-input-field{type: "search", placeholder: "Filter results"}
+ %input.dropdown-input-field{ type: "search", placeholder: "Filter results" }
= icon('search')
.dropdown-content
%ul
%li
- %a.is-active{href: "#"}
+ %a.is-active{ href: "#" }
Dropdown Option
%li
- %a{href: "#"}
+ %a{ href: "#" }
Dropdown Option
%li.divider
%li
- %a{href: "#"}
+ %a{ href: "#" }
Dropdown Option
%li
- %a{href: "#"}
+ %a{ href: "#" }
Dropdown Option
%li
- %a{href: "#"}
+ %a{ href: "#" }
Dropdown Option
%li
- %a{href: "#"}
+ %a{ href: "#" }
Dropdown Option
%li
- %a{href: "#"}
+ %a{ href: "#" }
Dropdown Option
.dropdown-footer
%strong Tip:
@@ -330,21 +330,21 @@
.example
%div
.dropdown.inline
- %button.dropdown-menu-toggle{type: 'button', data: {toggle: 'dropdown'}}
+ %button.dropdown-menu-toggle{ type: 'button', data: {toggle: 'dropdown' } }
Dropdown user
= icon('chevron-down')
.dropdown-menu.dropdown-select.dropdown-menu-selectable.dropdown-menu-user
.dropdown-title
%span Dropdown Title
- %button.dropdown-title-button.dropdown-menu-close{aria: {label: "Close"}}
+ %button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } }
= icon('times')
.dropdown-input
- %input.dropdown-input-field{type: "search", placeholder: "Filter results"}
+ %input.dropdown-input-field{ type: "search", placeholder: "Filter results" }
= icon('search')
.dropdown-content
%ul
%li
- %a.dropdown-menu-user-link.is-active{href: "#"}
+ %a.dropdown-menu-user-link.is-active{ href: "#" }
= link_to_member_avatar(@user, size: 30)
%strong.dropdown-menu-user-full-name
= @user.name
@@ -354,24 +354,24 @@
.example
%div
.dropdown.inline
- %button.dropdown-menu-toggle{type: 'button', data: {toggle: 'dropdown'}}
+ %button.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown' } }
Dropdown page 2
= icon('chevron-down')
.dropdown-menu.dropdown-select.dropdown-menu-selectable.dropdown-menu-user.dropdown-menu-paging.is-page-two
.dropdown-page-one
.dropdown-title
- %button.dropdown-title-button.dropdown-menu-back{aria: {label: "Go back"}}
+ %button.dropdown-title-button.dropdown-menu-back{ aria: { label: "Go back" } }
= icon('arrow-left')
%span Dropdown Title
- %button.dropdown-title-button.dropdown-menu-close{aria: {label: "Close"}}
+ %button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } }
= icon('times')
.dropdown-input
- %input.dropdown-input-field{type: "search", placeholder: "Filter results"}
+ %input.dropdown-input-field{ type: "search", placeholder: "Filter results" }
= icon('search')
.dropdown-content
%ul
%li
- %a.dropdown-menu-user-link.is-active{href: "#"}
+ %a.dropdown-menu-user-link.is-active{ href: "#" }
= link_to_member_avatar(@user, size: 30)
%strong.dropdown-menu-user-full-name
= @user.name
@@ -379,13 +379,13 @@
= @user.to_reference
.dropdown-page-two
.dropdown-title
- %button.dropdown-title-button.dropdown-menu-back{aria: {label: "Go back"}}
+ %button.dropdown-title-button.dropdown-menu-back{ aria: { label: "Go back" } }
= icon('arrow-left')
%span Create label
- %button.dropdown-title-button.dropdown-menu-close{aria: {label: "Close"}}
+ %button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } }
= icon('times')
.dropdown-input
- %input.dropdown-input-field{type: "search", placeholder: "Name new label"}
+ %input.dropdown-input-field{ type: "search", placeholder: "Name new label" }
.dropdown-content
%button.btn.btn-primary
Create
@@ -393,16 +393,16 @@
.example
%div
.dropdown.inline
- %button#js-project-dropdown.dropdown-menu-toggle{type: 'button', data: {toggle: 'dropdown'}}
+ %button#js-project-dropdown.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown' } }
Projects
= icon('chevron-down')
.dropdown-menu.dropdown-select.dropdown-menu-selectable
.dropdown-title
%span Go to project
- %button.dropdown-title-button.dropdown-menu-close{aria: {label: "Close"}}
+ %button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } }
= icon('times')
.dropdown-input
- %input.dropdown-input-field{type: "search", placeholder: "Filter results"}
+ %input.dropdown-input-field{ type: "search", placeholder: "Filter results" }
= icon('search')
.dropdown-content
.dropdown-loading
@@ -486,22 +486,22 @@
.example
%form.form-horizontal
.form-group
- %label.col-sm-2.control-label{:for => "inputEmail3"} Email
+ %label.col-sm-2.control-label{ :for => "inputEmail3" } Email
.col-sm-10
- %input#inputEmail3.form-control{:placeholder => "Email", :type => "email"}/
+ %input#inputEmail3.form-control{ :placeholder => "Email", :type => "email" }/
.form-group
- %label.col-sm-2.control-label{:for => "inputPassword3"} Password
+ %label.col-sm-2.control-label{ :for => "inputPassword3" } Password
.col-sm-10
- %input#inputPassword3.form-control{:placeholder => "Password", :type => "password"}/
+ %input#inputPassword3.form-control{ :placeholder => "Password", :type => "password" }/
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
%label
- %input{:type => "checkbox"}/
+ %input{ :type => "checkbox" }/
Remember me
.form-group
.col-sm-offset-2.col-sm-10
- %button.btn.btn-default{:type => "submit"} Sign in
+ %button.btn.btn-default{ :type => "submit" } Sign in
.lead
Form when label rendered above input
@@ -510,16 +510,16 @@
.example
%form
.form-group
- %label{:for => "exampleInputEmail1"} Email address
- %input#exampleInputEmail1.form-control{:placeholder => "Enter email", :type => "email"}/
+ %label{ :for => "exampleInputEmail1" } Email address
+ %input#exampleInputEmail1.form-control{ :placeholder => "Enter email", :type => "email" }/
.form-group
- %label{:for => "exampleInputPassword1"} Password
- %input#exampleInputPassword1.form-control{:placeholder => "Password", :type => "password"}/
+ %label{ :for => "exampleInputPassword1" } Password
+ %input#exampleInputPassword1.form-control{ :placeholder => "Password", :type => "password" }/
.checkbox
%label
- %input{:type => "checkbox"}/
+ %input{ :type => "checkbox" }/
Remember me
- %button.btn.btn-default{:type => "submit"} Sign in
+ %button.btn.btn-default{ :type => "submit" } Sign in
%h2#file File
%h4
diff --git a/app/views/import/_githubish_status.html.haml b/app/views/import/_githubish_status.html.haml
index f12f9482a51..864c5c0ff95 100644
--- a/app/views/import/_githubish_status.html.haml
+++ b/app/views/import/_githubish_status.html.haml
@@ -21,7 +21,7 @@
%th Status
%tbody
- @already_added_projects.each do |project|
- %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"}
+ %tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" }
%td
= provider_project_link(provider, project.import_source)
%td
@@ -38,7 +38,7 @@
= project.human_import_status_name
- @repos.each do |repo|
- %tr{id: "repo_#{repo.id}"}
+ %tr{ id: "repo_#{repo.id}" }
%td
= provider_project_link(provider, repo.full_name)
%td.import-target
diff --git a/app/views/import/bitbucket/status.html.haml b/app/views/import/bitbucket/status.html.haml
index ac09b71ae89..7f1b9ee7141 100644
--- a/app/views/import/bitbucket/status.html.haml
+++ b/app/views/import/bitbucket/status.html.haml
@@ -31,7 +31,7 @@
%th Status
%tbody
- @already_added_projects.each do |project|
- %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"}
+ %tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" }
%td
= link_to project.import_source, "https://bitbucket.org/#{project.import_source}", target: '_blank'
%td
@@ -48,7 +48,7 @@
= project.human_import_status_name
- @repos.each do |repo|
- %tr{id: "repo_#{repo.owner}___#{repo.slug}"}
+ %tr{ id: "repo_#{repo.owner}___#{repo.slug}" }
%td
= link_to repo.full_name, "https://bitbucket.org/#{repo.full_name}", target: "_blank"
%td.import-target
@@ -68,7 +68,7 @@
Import
= icon('spinner spin', class: 'loading-icon')
- @incompatible_repos.each do |repo|
- %tr{id: "repo_#{repo.owner}___#{repo.slug}"}
+ %tr{ id: "repo_#{repo.owner}___#{repo.slug}" }
%td
= link_to repo.full_name, "https://bitbucket.org/#{repo.full_name}", target: '_blank'
%td.import-target
diff --git a/app/views/import/fogbugz/status.html.haml b/app/views/import/fogbugz/status.html.haml
index c8a6fa1aa9e..97e5e51abe0 100644
--- a/app/views/import/fogbugz/status.html.haml
+++ b/app/views/import/fogbugz/status.html.haml
@@ -29,7 +29,7 @@
%th Status
%tbody
- @already_added_projects.each do |project|
- %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"}
+ %tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" }
%td
= project.import_source
%td
@@ -46,7 +46,7 @@
= project.human_import_status_name
- @repos.each do |repo|
- %tr{id: "repo_#{repo.id}"}
+ %tr{ id: "repo_#{repo.id}" }
%td
= repo.name
%td.import-target
diff --git a/app/views/import/gitlab/status.html.haml b/app/views/import/gitlab/status.html.haml
index d31fc2e6adb..d5b88709a34 100644
--- a/app/views/import/gitlab/status.html.haml
+++ b/app/views/import/gitlab/status.html.haml
@@ -24,7 +24,7 @@
%th Status
%tbody
- @already_added_projects.each do |project|
- %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"}
+ %tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" }
%td
= link_to project.import_source, "https://gitlab.com/#{project.import_source}", target: "_blank"
%td
@@ -41,7 +41,7 @@
= project.human_import_status_name
- @repos.each do |repo|
- %tr{id: "repo_#{repo["id"]}"}
+ %tr{ id: "repo_#{repo["id"]}" }
%td
= link_to repo["path_with_namespace"], "https://gitlab.com/#{repo["path_with_namespace"]}", target: "_blank"
%td.import-target
diff --git a/app/views/import/google_code/new.html.haml b/app/views/import/google_code/new.html.haml
index 5d2f149cd5f..336becd229e 100644
--- a/app/views/import/google_code/new.html.haml
+++ b/app/views/import/google_code/new.html.haml
@@ -45,7 +45,7 @@
%p
Upload <code>GoogleCodeProjectHosting.json</code> here:
%p
- %input{type: "file", name: "dump_file", id: "dump_file"}
+ %input{ type: "file", name: "dump_file", id: "dump_file" }
%li
%p
Do you want to customize how Google Code email addresses and usernames are imported into GitLab?
diff --git a/app/views/import/google_code/status.html.haml b/app/views/import/google_code/status.html.haml
index e79f122940a..9f1507cade6 100644
--- a/app/views/import/google_code/status.html.haml
+++ b/app/views/import/google_code/status.html.haml
@@ -34,7 +34,7 @@
%th Status
%tbody
- @already_added_projects.each do |project|
- %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"}
+ %tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" }
%td
= link_to project.import_source, "https://code.google.com/p/#{project.import_source}", target: "_blank"
%td
@@ -51,7 +51,7 @@
= project.human_import_status_name
- @repos.each do |repo|
- %tr{id: "repo_#{repo.id}"}
+ %tr{ id: "repo_#{repo.id}" }
%td
= link_to repo.name, "https://code.google.com/p/#{repo.name}", target: "_blank"
%td.import-target
@@ -61,7 +61,7 @@
Import
= icon("spinner spin", class: "loading-icon")
- @incompatible_repos.each do |repo|
- %tr{id: "repo_#{repo.id}"}
+ %tr{ id: "repo_#{repo.id}" }
%td
= link_to repo.name, "https://code.google.com/p/#{repo.name}", target: "_blank"
%td.import-target
diff --git a/app/views/kaminari/gitlab/_next_page.html.haml b/app/views/kaminari/gitlab/_next_page.html.haml
index 125f09777ba..c93dc7a50e8 100644
--- a/app/views/kaminari/gitlab/_next_page.html.haml
+++ b/app/views/kaminari/gitlab/_next_page.html.haml
@@ -6,8 +6,8 @@
-# per_page: number of items to fetch per page
-# remote: data-remote
- if current_page.last?
- %li{ class: "next disabled" }
+ %li.next.disabled
%span= raw(t 'views.pagination.next')
- else
- %li{ class: "next" }
+ %li.next
= link_to raw(t 'views.pagination.next'), url, rel: 'next', remote: remote
diff --git a/app/views/kaminari/gitlab/_page.html.haml b/app/views/kaminari/gitlab/_page.html.haml
index 750aed8f329..cefe0066a8f 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?}#{' sibling' if page.next? || page.prev?}"}
- = link_to page, url, {remote: remote, rel: page.next? ? 'next' : page.prev? ? 'prev' : nil}
+%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/kaminari/gitlab/_paginator.html.haml b/app/views/kaminari/gitlab/_paginator.html.haml
index f5e0d2ed3f3..8fe6bd653ae 100644
--- a/app/views/kaminari/gitlab/_paginator.html.haml
+++ b/app/views/kaminari/gitlab/_paginator.html.haml
@@ -6,7 +6,7 @@
-# remote: data-remote
-# paginator: the paginator that renders the pagination tags inside
= paginator.render do
- %div.gl-pagination
+ .gl-pagination
%ul.pagination.clearfix
- unless current_page.first?
= first_page_tag unless total_pages < 5 # As kaminari will always show the first 5 pages
@@ -19,4 +19,3 @@
= next_page_tag
- unless current_page.last?
= last_page_tag unless total_pages < 5
-
diff --git a/app/views/kaminari/gitlab/_prev_page.html.haml b/app/views/kaminari/gitlab/_prev_page.html.haml
index 7edf10498a8..b7c6caf7ff4 100644
--- a/app/views/kaminari/gitlab/_prev_page.html.haml
+++ b/app/views/kaminari/gitlab/_prev_page.html.haml
@@ -6,8 +6,8 @@
-# per_page: number of items to fetch per page
-# remote: data-remote
- if current_page.first?
- %li{ class: "prev disabled" }
+ %li.prev.disabled
%span= raw(t 'views.pagination.previous')
- else
- %li{ class: "prev" }
+ %li.prev
= link_to raw(t 'views.pagination.previous'), url, rel: 'prev', remote: remote
diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml
index 3e488cf73b9..3096f0ee19e 100644
--- a/app/views/layouts/_head.html.haml
+++ b/app/views/layouts/_head.html.haml
@@ -1,27 +1,27 @@
- page_description brand_title unless page_description
- site_name = "GitLab"
-%head{prefix: "og: http://ogp.me/ns#"}
- %meta{charset: "utf-8"}
- %meta{'http-equiv' => 'X-UA-Compatible', content: 'IE=edge'}
+%head{ prefix: "og: http://ogp.me/ns#" }
+ %meta{ charset: "utf-8" }
+ %meta{ 'http-equiv' => 'X-UA-Compatible', content: 'IE=edge' }
-# Open Graph - http://ogp.me/
- %meta{property: 'og:type', content: "object"}
- %meta{property: 'og:site_name', content: site_name}
- %meta{property: 'og:title', content: page_title}
- %meta{property: 'og:description', content: page_description}
- %meta{property: 'og:image', content: page_image}
- %meta{property: 'og:url', content: request.base_url + request.fullpath}
+ %meta{ property: 'og:type', content: "object" }
+ %meta{ property: 'og:site_name', content: site_name }
+ %meta{ property: 'og:title', content: page_title }
+ %meta{ property: 'og:description', content: page_description }
+ %meta{ property: 'og:image', content: page_image }
+ %meta{ property: 'og:url', content: request.base_url + request.fullpath }
-# Twitter Card - https://dev.twitter.com/cards/types/summary
- %meta{property: 'twitter:card', content: "summary"}
- %meta{property: 'twitter:title', content: page_title}
- %meta{property: 'twitter:description', content: page_description}
- %meta{property: 'twitter:image', content: page_image}
+ %meta{ property: 'twitter:card', content: "summary" }
+ %meta{ property: 'twitter:title', content: page_title }
+ %meta{ property: 'twitter:description', content: page_description }
+ %meta{ property: 'twitter:image', content: page_image }
= page_card_meta_tags
%title= page_title(site_name)
- %meta{name: "description", content: page_description}
+ %meta{ name: "description", content: page_description }
= favicon_link_tag 'favicon.ico'
@@ -36,20 +36,20 @@
= csrf_meta_tags
- unless browser.safari?
- %meta{name: 'referrer', content: 'origin-when-cross-origin'}
- %meta{name: 'viewport', content: 'width=device-width, initial-scale=1, maximum-scale=1'}
- %meta{name: 'theme-color', content: '#474D57'}
+ %meta{ name: 'referrer', content: 'origin-when-cross-origin' }
+ %meta{ name: 'viewport', content: 'width=device-width, initial-scale=1, maximum-scale=1' }
+ %meta{ name: 'theme-color', content: '#474D57' }
-# Apple Safari/iOS home screen icons
= favicon_link_tag 'touch-icon-iphone.png', rel: 'apple-touch-icon'
= favicon_link_tag 'touch-icon-ipad.png', rel: 'apple-touch-icon', sizes: '76x76'
= favicon_link_tag 'touch-icon-iphone-retina.png', rel: 'apple-touch-icon', sizes: '120x120'
= favicon_link_tag 'touch-icon-ipad-retina.png', rel: 'apple-touch-icon', sizes: '152x152'
- %link{rel: 'mask-icon', href: image_path('logo.svg'), color: 'rgb(226, 67, 41)'}
+ %link{ rel: 'mask-icon', href: image_path('logo.svg'), color: 'rgb(226, 67, 41)' }
-# Windows 8 pinned site tile
- %meta{name: 'msapplication-TileImage', content: image_path('msapplication-tile.png')}
- %meta{name: 'msapplication-TileColor', content: '#30353E'}
+ %meta{ name: 'msapplication-TileImage', content: image_path('msapplication-tile.png') }
+ %meta{ name: 'msapplication-TileColor', content: '#30353E' }
= yield :meta_tags
diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml
index 8e65bd12c56..0e64ebd71b8 100644
--- a/app/views/layouts/_search.html.haml
+++ b/app/views/layouts/_search.html.haml
@@ -6,7 +6,7 @@
- group_data_attrs = { group_path: j(@group.path), name: @group.name, issues_path: issues_group_path(j(@group.path)), mr_path: merge_requests_group_path(j(@group.path)) }
- if @project && @project.persisted?
- project_data_attrs = { project_path: j(@project.path), name: j(@project.name), issues_path: namespace_project_issues_path(@project.namespace, @project), mr_path: namespace_project_merge_requests_path(@project.namespace, @project) }
-.search.search-form{class: "#{'has-location-badge' if label.present?}"}
+.search.search-form{ class: "#{'has-location-badge' if label.present?}" }
= form_tag search_path, method: :get, class: 'navbar-form' do |f|
.search-input-container
- if label.present?
@@ -44,4 +44,4 @@
= hidden_field_tag :snippets, true
= hidden_field_tag :repository_ref, @ref
= button_tag 'Go' if ENV['RAILS_ENV'] == 'test'
- .search-autocomplete-opts.hide{:'data-autocomplete-path' => search_autocomplete_path, :'data-autocomplete-project-id' => @project.try(:id), :'data-autocomplete-project-ref' => @ref }
+ .search-autocomplete-opts.hide{ :'data-autocomplete-path' => search_autocomplete_path, :'data-autocomplete-project-id' => @project.try(:id), :'data-autocomplete-project-ref' => @ref }
diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml
index 6c2285fa2b6..935517d4913 100644
--- a/app/views/layouts/application.html.haml
+++ b/app/views/layouts/application.html.haml
@@ -1,7 +1,7 @@
!!! 5
%html{ lang: "en", class: "#{page_class}" }
= render "layouts/head"
- %body{class: "#{user_application_theme}", data: {page: body_data_page, project: "#{@project.path if @project}", group: "#{@group.path if @group}"}}
+ %body{ class: "#{user_application_theme}", data: { page: body_data_page, project: "#{@project.path if @project}", group: "#{@group.path if @group}" } }
= Gon::Base.render_data
-# Ideally this would be inside the head, but turbolinks only evaluates page-specific JS in the body.
diff --git a/app/views/layouts/devise.html.haml b/app/views/layouts/devise.html.haml
index afd9958f073..3368a9beb29 100644
--- a/app/views/layouts/devise.html.haml
+++ b/app/views/layouts/devise.html.haml
@@ -1,7 +1,7 @@
!!! 5
-%html{ lang: "en", class: "devise-layout-html"}
+%html.devise-layout-html
= render "layouts/head"
- %body.ui_charcoal.login-page.application.navless{ data: { page: body_data_page }}
+ %body.ui_charcoal.login-page.application.navless{ data: { page: body_data_page } }
.page-wrap
= Gon::Base.render_data
= render "layouts/header/empty"
diff --git a/app/views/layouts/devise_empty.html.haml b/app/views/layouts/devise_empty.html.haml
index 6bd427b02ac..7466423a934 100644
--- a/app/views/layouts/devise_empty.html.haml
+++ b/app/views/layouts/devise_empty.html.haml
@@ -1,5 +1,5 @@
!!! 5
-%html{ lang: "en"}
+%html{ lang: "en" }
= render "layouts/head"
%body.ui_charcoal.login-page.application.navless
= Gon::Base.render_data
diff --git a/app/views/layouts/devise_mailer.html.haml b/app/views/layouts/devise_mailer.html.haml
index c258eafdd51..e1e1f9ae516 100644
--- a/app/views/layouts/devise_mailer.html.haml
+++ b/app/views/layouts/devise_mailer.html.haml
@@ -1,7 +1,7 @@
!!! 5
%html
%head
- %meta(content='text/html; charset=UTF-8' http-equiv='Content-Type')
+ %meta{ content: 'text/html; charset=UTF-8', 'http-equiv'=> 'Content-Type' }
= stylesheet_link_tag 'mailers/devise'
%body
@@ -9,7 +9,7 @@
%tr
%td
%table#header
- %td{valign: "top"}
+ %td{ valign: "top" }
= image_tag('mailers/gitlab_header_logo.png', id: 'logo', alt: 'GitLab Wordmark')
%table#body
diff --git a/app/views/layouts/errors.html.haml b/app/views/layouts/errors.html.haml
index a3b925f6afd..6d9ec043590 100644
--- a/app/views/layouts/errors.html.haml
+++ b/app/views/layouts/errors.html.haml
@@ -1,7 +1,7 @@
!!! 5
-%html{ lang: "en"}
+%html{ lang: "en" }
%head
- %meta{:content => "width=device-width, initial-scale=1, maximum-scale=1", :name => "viewport"}
+ %meta{ :content => "width=device-width, initial-scale=1, maximum-scale=1", :name => "viewport" }
%title= yield(:title)
:css
body {
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
index 5456be77aab..f4e0244596c 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -1,11 +1,11 @@
%header.navbar.navbar-fixed-top.navbar-gitlab{ class: nav_header_class }
- %a{ href: "#content-body", tabindex: "1", class: "sr-only gl-accessibility" } Skip to content
- %div{ class: "container-fluid" }
+ %a.sr-only.gl-accessibility{ href: "#content-body", tabindex: "1" } Skip to content
+ .container-fluid
.header-content
%button.side-nav-toggle{ type: 'button', "aria-label" => "Toggle global navigation" }
%span.sr-only Toggle navigation
= icon('bars')
- %button.navbar-toggle{type: 'button'}
+ %button.navbar-toggle{ type: 'button' }
%span.sr-only Toggle navigation
= icon('ellipsis-v')
diff --git a/app/views/layouts/nav/_admin_settings.html.haml b/app/views/layouts/nav/_admin_settings.html.haml
index 38e9b80d129..9de0e12a826 100644
--- a/app/views/layouts/nav/_admin_settings.html.haml
+++ b/app/views/layouts/nav/_admin_settings.html.haml
@@ -1,6 +1,6 @@
.controls
.dropdown.admin-settings-dropdown
- %a.dropdown-new.btn.btn-default{href: '#', 'data-toggle' => 'dropdown'}
+ %a.dropdown-new.btn.btn-default{ href: '#', 'data-toggle' => 'dropdown' }
= icon('cog')
= icon('caret-down')
%ul.dropdown-menu.dropdown-menu-align-right
diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml
index 817e4bebb05..205d23178d2 100644
--- a/app/views/layouts/nav/_dashboard.html.haml
+++ b/app/views/layouts/nav/_dashboard.html.haml
@@ -35,3 +35,7 @@
= link_to dashboard_snippets_path, title: 'Snippets' do
%span
Snippets
+
+ = link_to help_path, title: 'About GitLab CE', class: 'about-gitlab' do
+ %span
+ About GitLab CE
diff --git a/app/views/layouts/nav/_group_settings.html.haml b/app/views/layouts/nav/_group_settings.html.haml
index 1579d8f1662..30feb6813b4 100644
--- a/app/views/layouts/nav/_group_settings.html.haml
+++ b/app/views/layouts/nav/_group_settings.html.haml
@@ -5,7 +5,7 @@
- if can_admin_group || can_edit
.controls
.dropdown.group-settings-dropdown
- %a.dropdown-new.btn.btn-default#group-settings-button{href: '#', 'data-toggle' => 'dropdown'}
+ %a.dropdown-new.btn.btn-default#group-settings-button{ href: '#', 'data-toggle' => 'dropdown' }
= icon('cog')
= icon('caret-down')
%ul.dropdown-menu.dropdown-menu-align-right
diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml
index cc1571cbb4f..3c8c7b8f25e 100644
--- a/app/views/layouts/nav/_project.html.haml
+++ b/app/views/layouts/nav/_project.html.haml
@@ -1,7 +1,7 @@
- if current_user
.controls
.dropdown.project-settings-dropdown
- %a.dropdown-new.btn.btn-default#project-settings-button{href: '#', 'data-toggle' => 'dropdown'}
+ %a.dropdown-new.btn.btn-default#project-settings-button{ href: '#', 'data-toggle' => 'dropdown' }
= icon('cog')
= icon('caret-down')
%ul.dropdown-menu.dropdown-menu-align-right
diff --git a/app/views/layouts/notify.html.haml b/app/views/layouts/notify.html.haml
index 1ec4c3f0c67..76268c1b705 100644
--- a/app/views/layouts/notify.html.haml
+++ b/app/views/layouts/notify.html.haml
@@ -1,14 +1,14 @@
-%html{lang: "en"}
+%html{ lang: "en" }
%head
- %meta{content: "text/html; charset=utf-8", "http-equiv" => "Content-Type"}
+ %meta{ content: "text/html; charset=utf-8", "http-equiv" => "Content-Type" }
%title
GitLab
= stylesheet_link_tag 'notify'
= yield :head
%body
- %div.content
+ .content
= yield
- %div.footer{style: "margin-top: 10px;"}
+ .footer{ style: "margin-top: 10px;" }
%p
&mdash;
%br
diff --git a/app/views/notify/build_fail_email.html.haml b/app/views/notify/build_fail_email.html.haml
index 4bf7c1f4d64..a744c4be9d6 100644
--- a/app/views/notify/build_fail_email.html.haml
+++ b/app/views/notify/build_fail_email.html.haml
@@ -1,5 +1,5 @@
- content_for :header do
- %h1{style: "background: #c40834; color: #FFF; font: normal 20px Helvetica, Arial, sans-serif; margin: 0; padding: 5px 10px; line-height: 32px; font-size: 16px;"}
+ %h1{ style: "background: #c40834; color: #FFF; font: normal 20px Helvetica, Arial, sans-serif; margin: 0; padding: 5px 10px; line-height: 32px; font-size: 16px;" }
GitLab (build failed)
%h3
diff --git a/app/views/notify/build_success_email.html.haml b/app/views/notify/build_success_email.html.haml
index 252a5b7152c..8c2e6db1426 100644
--- a/app/views/notify/build_success_email.html.haml
+++ b/app/views/notify/build_success_email.html.haml
@@ -1,5 +1,5 @@
- content_for :header do
- %h1{style: "background: #38CF5B; color: #FFF; font: normal 20px Helvetica, Arial, sans-serif; margin: 0; padding: 5px 10px; line-height: 32px; font-size: 16px;"}
+ %h1{ style: "background: #38CF5B; color: #FFF; font: normal 20px Helvetica, Arial, sans-serif; margin: 0; padding: 5px 10px; line-height: 32px; font-size: 16px;" }
GitLab (build successful)
%h3
diff --git a/app/views/notify/links/ci/builds/_build.html.haml b/app/views/notify/links/ci/builds/_build.html.haml
index 38cd4e5e145..d35b3839171 100644
--- a/app/views/notify/links/ci/builds/_build.html.haml
+++ b/app/views/notify/links/ci/builds/_build.html.haml
@@ -1,2 +1,2 @@
-%a{href: pipeline_build_url(pipeline, build), style: "color:#3777b0;text-decoration:none;"}
+%a{ href: pipeline_build_url(pipeline, build), style: "color:#3777b0;text-decoration:none;" }
= build.name
diff --git a/app/views/notify/new_issue_email.html.haml b/app/views/notify/new_issue_email.html.haml
index f42b150c0d6..d1855568215 100644
--- a/app/views/notify/new_issue_email.html.haml
+++ b/app/views/notify/new_issue_email.html.haml
@@ -1,7 +1,7 @@
- if current_application_settings.email_author_in_body
%div
#{link_to @issue.author_name, user_url(@issue.author)} wrote:
--if @issue.description
+- if @issue.description
= markdown(@issue.description, pipeline: :email, author: @issue.author)
- if @issue.assignee_id.present?
diff --git a/app/views/notify/new_mention_in_issue_email.html.haml b/app/views/notify/new_mention_in_issue_email.html.haml
index 4f3d36bd9ca..02f21baa368 100644
--- a/app/views/notify/new_mention_in_issue_email.html.haml
+++ b/app/views/notify/new_mention_in_issue_email.html.haml
@@ -4,7 +4,7 @@
- if current_application_settings.email_author_in_body
%div
#{link_to @issue.author_name, user_url(@issue.author)} wrote:
--if @issue.description
+- if @issue.description
= markdown(@issue.description, pipeline: :email, author: @issue.author)
- if @issue.assignee_id.present?
diff --git a/app/views/notify/new_mention_in_merge_request_email.html.haml b/app/views/notify/new_mention_in_merge_request_email.html.haml
index 32aedb9e6b9..cbd434be02a 100644
--- a/app/views/notify/new_mention_in_merge_request_email.html.haml
+++ b/app/views/notify/new_mention_in_merge_request_email.html.haml
@@ -11,5 +11,5 @@
%p
Assignee: #{@merge_request.author_name} &rarr; #{@merge_request.assignee_name}
--if @merge_request.description
+- if @merge_request.description
= markdown(@merge_request.description, pipeline: :email, author: @merge_request.author)
diff --git a/app/views/notify/new_merge_request_email.html.haml b/app/views/notify/new_merge_request_email.html.haml
index 158404de396..8890b300f7d 100644
--- a/app/views/notify/new_merge_request_email.html.haml
+++ b/app/views/notify/new_merge_request_email.html.haml
@@ -8,5 +8,5 @@
%p
Assignee: #{@merge_request.author_name} &rarr; #{@merge_request.assignee_name}
--if @merge_request.description
+- if @merge_request.description
= markdown(@merge_request.description, pipeline: :email, author: @merge_request.author)
diff --git a/app/views/notify/pipeline_failed_email.html.haml b/app/views/notify/pipeline_failed_email.html.haml
index 001d9c48555..82c7fe229b8 100644
--- a/app/views/notify/pipeline_failed_email.html.haml
+++ b/app/views/notify/pipeline_failed_email.html.haml
@@ -1,9 +1,9 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-%html{lang: "en"}
+%html{ lang: "en" }
%head
- %meta{content: "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/
- %meta{content: "width=device-width, initial-scale=1", name: "viewport"}/
- %meta{content: "IE=edge", "http-equiv" => "X-UA-Compatible"}/
+ %meta{ content: "text/html; charset=UTF-8", "http-equiv" => "Content-Type" }/
+ %meta{ content: "width=device-width, initial-scale=1", name: "viewport" }/
+ %meta{ content: "IE=edge", "http-equiv" => "X-UA-Compatible" }/
%title= message.subject
:css
/* CLIENT-SPECIFIC STYLES */
@@ -41,139 +41,139 @@
padding-right: 10px !important;
}
}
- %body{style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;height:100%;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;"}
- %table#body{border: "0", cellpadding: "0", cellspacing: "0", style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;"}
+ %body{ style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;height:100%;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;" }
+ %table#body{ border: "0", cellpadding: "0", cellspacing: "0", style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;" }
%tbody
%tr.line
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#6b4fbb;height:4px;font-size:4px;line-height:4px;"}  
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#6b4fbb;height:4px;font-size:4px;line-height:4px;" }  
%tr.header
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;"}
- %img{alt: "GitLab", height: "50", src: image_url('mailers/ci_pipeline_notif_v1/gitlab-logo.gif'), width: "55"}/
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" }
+ %img{ alt: "GitLab", height: "50", src: image_url('mailers/ci_pipeline_notif_v1/gitlab-logo.gif'), width: "55" }/
%tr
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;"}
- %table.wrapper{border: "0", cellpadding: "0", cellspacing: "0", style: "width:640px;margin:0 auto;border-collapse:separate;border-spacing:0;"}
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;" }
+ %table.wrapper{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:640px;margin:0 auto;border-collapse:separate;border-spacing:0;" }
%tbody
%tr
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#ffffff;text-align:left;padding:18px 25px;border:1px solid #ededed;border-radius:3px;overflow:hidden;"}
- %table.content{border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;border-collapse:separate;border-spacing:0;"}
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#ffffff;text-align:left;padding:18px 25px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" }
+ %table.content{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;border-collapse:separate;border-spacing:0;" }
%tbody
%tr.alert
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:10px;border-radius:3px;font-size:14px;line-height:1.3;text-align:center;overflow:hidden;background-color:#d22f57;color:#ffffff;"}
- %table.img{border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;margin:0 auto;"}
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:10px;border-radius:3px;font-size:14px;line-height:1.3;text-align:center;overflow:hidden;background-color:#d22f57;color:#ffffff;" }
+ %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;margin:0 auto;" }
%tbody
%tr
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;padding-right:5px;"}
- %img{alt: "x", height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-x-red-inverted.gif'), style: "display:block;", width: "13"}/
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;"}
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;padding-right:5px;" }
+ %img{ alt: "x", height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-x-red-inverted.gif'), style: "display:block;", width: "13" }/
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;" }
Your pipeline has failed.
%tr.spacer
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;"}
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }
&nbsp;
%tr.section
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 15px;border:1px solid #ededed;border-radius:3px;overflow:hidden;"}
- %table.info{border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;"}
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 15px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" }
+ %table.info{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;" }
%tbody
%tr
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;"} Project
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;"}
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;" } Project
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;" }
- namespace_name = @project.group ? @project.group.name : @project.namespace.owner.name
- namespace_url = @project.group ? group_url(@project.group) : user_url(@project.namespace.owner)
- %a.muted{href: namespace_url, style: "color:#333333;text-decoration:none;"}
+ %a.muted{ href: namespace_url, style: "color:#333333;text-decoration:none;" }
= namespace_name
\/
- %a.muted{href: project_url(@project), style: "color:#333333;text-decoration:none;"}
+ %a.muted{ href: project_url(@project), style: "color:#333333;text-decoration:none;" }
= @project.name
%tr
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;"} Branch
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;"}
- %table.img{border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;"}
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Branch
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
+ %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
%tbody
%tr
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;"}
- %img{height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-branch-gray.gif'), style: "display:block;", width: "13"}/
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;"}
- %a.muted{href: commits_url(@pipeline), style: "color:#333333;text-decoration:none;"}
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
+ %img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-branch-gray.gif'), style: "display:block;", width: "13", alt: "Branch icon" }/
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
+ %a.muted{ href: commits_url(@pipeline), style: "color:#333333;text-decoration:none;" }
= @pipeline.ref
%tr
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;"} Commit
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;"}
- %table.img{border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;"}
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Commit
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
+ %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
%tbody
%tr
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;"}
- %img{height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-commit-gray.gif'), style: "display:block;", width: "13"}/
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;"}
- %a{href: commit_url(@pipeline), style: "color:#3777b0;text-decoration:none;"}
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
+ %img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-commit-gray.gif'), style: "display:block;", width: "13", alt: "Commit icon" }/
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
+ %a{ href: commit_url(@pipeline), style: "color:#3777b0;text-decoration:none;" }
= @pipeline.short_sha
- if @merge_request
in
- %a{href: merge_request_url(@merge_request), style: "color:#3777b0;text-decoration:none;"}
+ %a{ href: merge_request_url(@merge_request), style: "color:#3777b0;text-decoration:none;" }
= @merge_request.to_reference
- .commit{style: "color:#5c5c5c;font-weight:300;"}
+ .commit{ style: "color:#5c5c5c;font-weight:300;" }
= @pipeline.git_commit_message.truncate(50)
%tr
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;"} Author
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;"}
- %table.img{border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;"}
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Author
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
+ %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
%tbody
%tr
- commit = @pipeline.commit
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;"}
- %img.avatar{height: "24", src: avatar_icon(commit.author || commit.author_email, 24), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24"}/
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;"}
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
+ %img.avatar{ height: "24", src: avatar_icon(commit.author || commit.author_email, 24), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar" }/
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
- if commit.author
- %a.muted{href: user_url(commit.author), style: "color:#333333;text-decoration:none;"}
+ %a.muted{ href: user_url(commit.author), style: "color:#333333;text-decoration:none;" }
= commit.author.name
- else
%span
= commit.author_name
%tr.spacer
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;"}
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }
&nbsp;
- failed = @pipeline.statuses.latest.failed
%tr.pre-section
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#333333;font-size:15px;font-weight:400;line-height:1.4;padding:15px 0;"}
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#333333;font-size:15px;font-weight:400;line-height:1.4;padding:15px 0;" }
Pipeline
- %a{href: pipeline_url(@pipeline), style: "color:#3777b0;text-decoration:none;"}
+ %a{ href: pipeline_url(@pipeline), style: "color:#3777b0;text-decoration:none;" }
= "\##{@pipeline.id}"
had
= failed.size
failed
= "#{'build'.pluralize(failed.size)}."
%tr.warning
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;border:1px solid #ededed;border-bottom:0;border-radius:3px 3px 0 0;overflow:hidden;background-color:#fdf4f6;color:#d22852;font-size:14px;line-height:1.4;text-align:center;padding:8px 15px;"}
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;border:1px solid #ededed;border-bottom:0;border-radius:3px 3px 0 0;overflow:hidden;background-color:#fdf4f6;color:#d22852;font-size:14px;line-height:1.4;text-align:center;padding:8px 15px;" }
Logs may contain sensitive data. Please consider before forwarding this email.
%tr.section
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 15px;border:1px solid #ededed;border-radius:3px;overflow:hidden;border-top:0;border-radius:0 0 3px 3px;"}
- %table.builds{border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;border-collapse:collapse;"}
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 15px;border:1px solid #ededed;border-radius:3px;overflow:hidden;border-top:0;border-radius:0 0 3px 3px;" }
+ %table.builds{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;border-collapse:collapse;" }
%tbody
- failed.each do |build|
%tr.build-state
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:20px 0;color:#8c8c8c;font-weight:500;font-size:15px;"}
- %table.img{border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;"}
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:20px 0;color:#8c8c8c;font-weight:500;font-size:15px;" }
+ %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
%tbody
%tr
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#8c8c8c;font-weight:500;font-size:15px;vertical-align:middle;padding-right:5px;"}
- %img{alt: "x", height: "10", src: image_url('mailers/ci_pipeline_notif_v1/icon-x-red.gif'), style: "display:block;", width: "10"}/
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#8c8c8c;font-weight:500;font-size:15px;vertical-align:middle;"}
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#8c8c8c;font-weight:500;font-size:15px;vertical-align:middle;padding-right:5px;" }
+ %img{ alt: "x", height: "10", src: image_url('mailers/ci_pipeline_notif_v1/icon-x-red.gif'), style: "display:block;", width: "10" }/
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#8c8c8c;font-weight:500;font-size:15px;vertical-align:middle;" }
= build.stage
- %td{align: "right", style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:20px 0;color:#8c8c8c;font-weight:500;font-size:15px;"}
+ %td{ align: "right", style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:20px 0;color:#8c8c8c;font-weight:500;font-size:15px;" }
= render "notify/links/#{build.to_partial_path}", pipeline: @pipeline, build: build
%tr.build-log
- if build.has_trace?
- %td{colspan: "2", style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 0 15px;"}
- %pre{style: "font-family:Monaco,'Lucida Console','Courier New',Courier,monospace;background-color:#fafafa;border-radius:3px;overflow:hidden;white-space:pre-wrap;word-break:break-all;font-size:13px;line-height:1.4;padding:12px;color:#333333;margin:0;"}
+ %td{ colspan: "2", style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 0 15px;" }
+ %pre{ style: "font-family:Monaco,'Lucida Console','Courier New',Courier,monospace;background-color:#fafafa;border-radius:3px;overflow:hidden;white-space:pre-wrap;word-break:break-all;font-size:13px;line-height:1.4;padding:12px;color:#333333;margin:0;" }
= build.trace_html(last_lines: 10).html_safe
- else
- %td{colspan: "2"}
+ %td{ colspan: "2" }
%tr.footer
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;"}
- %img{alt: "GitLab", height: "33", src: image_url('mailers/ci_pipeline_notif_v1/gitlab-logo-full-horizontal.gif'), style: "display:block;margin:0 auto 1em;", width: "90"}/
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" }
+ %img{ alt: "GitLab", height: "33", src: image_url('mailers/ci_pipeline_notif_v1/gitlab-logo-full-horizontal.gif'), style: "display:block;margin:0 auto 1em;", width: "90" }/
%div
- %a{href: profile_notifications_url, style: "color:#3777b0;text-decoration:none;"} Manage all notifications
+ %a{ href: profile_notifications_url, style: "color:#3777b0;text-decoration:none;" } Manage all notifications
&middot;
- %a{href: help_url, style: "color:#3777b0;text-decoration:none;"} Help
+ %a{ href: help_url, style: "color:#3777b0;text-decoration:none;" } Help
%div
You're receiving this email because of your account on
= succeed "." do
- %a{href: root_url, style: "color:#3777b0;text-decoration:none;"}= Gitlab.config.gitlab.host
+ %a{ href: root_url, style: "color:#3777b0;text-decoration:none;" }= Gitlab.config.gitlab.host
diff --git a/app/views/notify/pipeline_success_email.html.haml b/app/views/notify/pipeline_success_email.html.haml
index 56c1949ab2b..6dddb3b6373 100644
--- a/app/views/notify/pipeline_success_email.html.haml
+++ b/app/views/notify/pipeline_success_email.html.haml
@@ -1,9 +1,9 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-%html{lang: "en"}
+%html{ lang: "en" }
%head
- %meta{content: "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/
- %meta{content: "width=device-width, initial-scale=1", name: "viewport"}/
- %meta{content: "IE=edge", "http-equiv" => "X-UA-Compatible"}/
+ %meta{ content: "text/html; charset=UTF-8", "http-equiv" => "Content-Type" }/
+ %meta{ content: "width=device-width, initial-scale=1", name: "viewport" }/
+ %meta{ content: "IE=edge", "http-equiv" => "X-UA-Compatible" }/
%title= message.subject
:css
/* CLIENT-SPECIFIC STYLES */
@@ -41,114 +41,114 @@
padding-right: 10px !important;
}
}
- %body{style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;height:100%;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;"}
- %table#body{border: "0", cellpadding: "0", cellspacing: "0", style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;"}
+ %body{ style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;height:100%;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;" }
+ %table#body{ border: "0", cellpadding: "0", cellspacing: "0", style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;" }
%tbody
%tr.line
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#6b4fbb;height:4px;font-size:4px;line-height:4px;"}  
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#6b4fbb;height:4px;font-size:4px;line-height:4px;" }  
%tr.header
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;"}
- %img{alt: "GitLab", height: "50", src: image_url('mailers/ci_pipeline_notif_v1/gitlab-logo.gif'), width: "55"}/
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" }
+ %img{ alt: "GitLab", height: "50", src: image_url('mailers/ci_pipeline_notif_v1/gitlab-logo.gif'), width: "55" }/
%tr
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;"}
- %table.wrapper{border: "0", cellpadding: "0", cellspacing: "0", style: "width:640px;margin:0 auto;border-collapse:separate;border-spacing:0;"}
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;" }
+ %table.wrapper{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:640px;margin:0 auto;border-collapse:separate;border-spacing:0;" }
%tbody
%tr
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#ffffff;text-align:left;padding:18px 25px;border:1px solid #ededed;border-radius:3px;overflow:hidden;"}
- %table.content{border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;border-collapse:separate;border-spacing:0;"}
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#ffffff;text-align:left;padding:18px 25px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" }
+ %table.content{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;border-collapse:separate;border-spacing:0;" }
%tbody
%tr.success
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:10px;border-radius:3px;font-size:14px;line-height:1.3;text-align:center;overflow:hidden;color:#ffffff;background-color:#31af64;"}
- %table.img{border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;margin:0 auto;"}
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:10px;border-radius:3px;font-size:14px;line-height:1.3;text-align:center;overflow:hidden;color:#ffffff;background-color:#31af64;" }
+ %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;margin:0 auto;" }
%tbody
%tr
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;padding-right:5px;"}
- %img{alt: "✓", height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-check-green-inverted.gif'), style: "display:block;", width: "13"}/
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;"}
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;padding-right:5px;" }
+ %img{ alt: "✓", height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-check-green-inverted.gif'), style: "display:block;", width: "13" }/
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;" }
Your pipeline has passed.
%tr.spacer
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;"}
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }
&nbsp;
%tr.section
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 15px;border:1px solid #ededed;border-radius:3px;overflow:hidden;"}
- %table.info{border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;"}
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 15px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" }
+ %table.info{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;" }
%tbody
%tr
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;"} Project
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;"}
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;" } Project
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;" }
- namespace_name = @project.group ? @project.group.name : @project.namespace.owner.name
- namespace_url = @project.group ? group_url(@project.group) : user_url(@project.namespace.owner)
- %a.muted{href: namespace_url, style: "color:#333333;text-decoration:none;"}
+ %a.muted{ href: namespace_url, style: "color:#333333;text-decoration:none;" }
= namespace_name
\/
- %a.muted{href: project_url(@project), style: "color:#333333;text-decoration:none;"}
+ %a.muted{ href: project_url(@project), style: "color:#333333;text-decoration:none;" }
= @project.name
%tr
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;"} Branch
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;"}
- %table.img{border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;"}
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Branch
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
+ %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
%tbody
%tr
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;"}
- %img{height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-branch-gray.gif'), style: "display:block;", width: "13"}/
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;"}
- %a.muted{href: commits_url(@pipeline), style: "color:#333333;text-decoration:none;"}
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
+ %img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-branch-gray.gif'), style: "display:block;", width: "13", alt: "Branch icon" }/
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
+ %a.muted{ href: commits_url(@pipeline), style: "color:#333333;text-decoration:none;" }
= @pipeline.ref
%tr
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;"} Commit
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;"}
- %table.img{border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;"}
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Commit
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
+ %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
%tbody
%tr
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;"}
- %img{height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-commit-gray.gif'), style: "display:block;", width: "13"}/
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;"}
- %a{href: commit_url(@pipeline), style: "color:#3777b0;text-decoration:none;"}
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
+ %img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-commit-gray.gif'), style: "display:block;", width: "13", alt: "Commit icon" }/
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
+ %a{ href: commit_url(@pipeline), style: "color:#3777b0;text-decoration:none;" }
= @pipeline.short_sha
- if @merge_request
in
- %a{href: merge_request_url(@merge_request), style: "color:#3777b0;text-decoration:none;"}
+ %a{ href: merge_request_url(@merge_request), style: "color:#3777b0;text-decoration:none;" }
= @merge_request.to_reference
- .commit{style: "color:#5c5c5c;font-weight:300;"}
+ .commit{ style: "color:#5c5c5c;font-weight:300;" }
= @pipeline.git_commit_message.truncate(50)
%tr
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;"} Author
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;"}
- %table.img{border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;"}
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Author
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
+ %table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
%tbody
%tr
- commit = @pipeline.commit
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;"}
- %img.avatar{height: "24", src: avatar_icon(commit.author || commit.author_email, 24), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24"}/
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;"}
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
+ %img.avatar{ height: "24", src: avatar_icon(commit.author || commit.author_email, 24), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar" }/
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
- if commit.author
- %a.muted{href: user_url(commit.author), style: "color:#333333;text-decoration:none;"}
+ %a.muted{ href: user_url(commit.author), style: "color:#333333;text-decoration:none;" }
= commit.author.name
- else
%span
= commit.author_name
%tr.spacer
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;"}
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }
&nbsp;
%tr.success-message
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#333333;font-size:15px;font-weight:400;line-height:1.4;padding:15px 5px;text-align:center;"}
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#333333;font-size:15px;font-weight:400;line-height:1.4;padding:15px 5px;text-align:center;" }
- build_count = @pipeline.statuses.latest.size
- stage_count = @pipeline.stages_count
Pipeline
- %a{href: pipeline_url(@pipeline), style: "color:#3777b0;text-decoration:none;"}
+ %a{ href: pipeline_url(@pipeline), style: "color:#3777b0;text-decoration:none;" }
= "\##{@pipeline.id}"
successfully completed
= "#{build_count} #{'build'.pluralize(build_count)}"
in
= "#{stage_count} #{'stage'.pluralize(stage_count)}."
%tr.footer
- %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;"}
- %img{alt: "GitLab", height: "33", src: image_url('mailers/ci_pipeline_notif_v1/gitlab-logo-full-horizontal.gif'), style: "display:block;margin:0 auto 1em;", width: "90"}/
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" }
+ %img{ alt: "GitLab", height: "33", src: image_url('mailers/ci_pipeline_notif_v1/gitlab-logo-full-horizontal.gif'), style: "display:block;margin:0 auto 1em;", width: "90" }/
%div
- %a{href: profile_notifications_url, style: "color:#3777b0;text-decoration:none;"} Manage all notifications
+ %a{ href: profile_notifications_url, style: "color:#3777b0;text-decoration:none;" } Manage all notifications
&middot;
- %a{href: help_url, style: "color:#3777b0;text-decoration:none;"} Help
+ %a{ href: help_url, style: "color:#3777b0;text-decoration:none;" } Help
%div
You're receiving this email because of your account on
= succeed "." do
- %a{href: root_url, style: "color:#3777b0;text-decoration:none;"}= Gitlab.config.gitlab.host
+ %a{ href: root_url, style: "color:#3777b0;text-decoration:none;" }= Gitlab.config.gitlab.host
diff --git a/app/views/notify/project_was_not_exported_email.text.haml b/app/views/notify/project_was_not_exported_email.text.haml
index b27cb620b9e..27785165c2d 100644
--- a/app/views/notify/project_was_not_exported_email.text.haml
+++ b/app/views/notify/project_was_not_exported_email.text.haml
@@ -3,4 +3,4 @@
= "The errors we encountered were:"
- @errors.each do |error|
- #{error} \ No newline at end of file
+ #{error}
diff --git a/app/views/notify/repository_push_email.html.haml b/app/views/notify/repository_push_email.html.haml
index 25883de257c..36858fa6f34 100644
--- a/app/views/notify/repository_push_email.html.haml
+++ b/app/views/notify/repository_push_email.html.haml
@@ -29,7 +29,7 @@
%ul
- @message.diffs.each do |diff|
%li.file-stats
- %a{href: "#{@message.target_url if @message.disable_diffs?}##{hexdigest(diff.file_path)}" }
+ %a{ href: "#{@message.target_url if @message.disable_diffs?}##{hexdigest(diff.file_path)}" }
- if diff.deleted_file
%span.deleted-file
&minus;
@@ -54,8 +54,8 @@
%h4 Changes:
- diff_files.each do |diff_file|
- file_hash = hexdigest(diff_file.file_path)
- %li{id: file_hash}
- %a{href: @message.target_url + "##{file_hash}"}<
+ %li{ id: file_hash }
+ %a{ href: @message.target_url + "##{file_hash}" }<
- if diff_file.deleted_file
%strong<
= diff_file.old_path
diff --git a/app/views/profiles/chat_names/new.html.haml b/app/views/profiles/chat_names/new.html.haml
index f635acf96e2..5bf22aa94f1 100644
--- a/app/views/profiles/chat_names/new.html.haml
+++ b/app/views/profiles/chat_names/new.html.haml
@@ -1,5 +1,5 @@
%h3.page-title Authorization required
-%main{:role => "main"}
+%main{ :role => "main" }
%p.h4
Authorize
%strong.text-info= @chat_name_params[:chat_name]
diff --git a/app/views/profiles/keys/index.html.haml b/app/views/profiles/keys/index.html.haml
index 93187873501..71b224a413b 100644
--- a/app/views/profiles/keys/index.html.haml
+++ b/app/views/profiles/keys/index.html.haml
@@ -17,5 +17,5 @@
%hr
%h5
Your SSH keys (#{@keys.count})
- %div.append-bottom-default
+ .append-bottom-default
= render 'key_table'
diff --git a/app/views/profiles/notifications/show.html.haml b/app/views/profiles/notifications/show.html.haml
index d79a1a9f368..5c5e5940365 100644
--- a/app/views/profiles/notifications/show.html.haml
+++ b/app/views/profiles/notifications/show.html.haml
@@ -3,7 +3,7 @@
%div
- if @user.errors.any?
- %div.alert.alert-danger
+ .alert.alert-danger
%ul
- @user.errors.full_messages.each do |msg|
%li= msg
diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml
index 2afa026847a..feadd863b00 100644
--- a/app/views/profiles/preferences/show.html.haml
+++ b/app/views/profiles/preferences/show.html.haml
@@ -1,7 +1,7 @@
- page_title 'Preferences'
= render 'profiles/head'
-= form_for @user, url: profile_preferences_path, remote: true, method: :put, html: {class: 'row prepend-top-default js-preferences-form'} do |f|
+= form_for @user, url: profile_preferences_path, remote: true, method: :put, html: { class: 'row prepend-top-default js-preferences-form' } do |f|
.col-lg-3.profile-settings-sidebar
%h4.prepend-top-0
Application theme
@@ -10,7 +10,7 @@
.col-lg-9.application-theme
- Gitlab::Themes.each do |theme|
= label_tag do
- .preview{class: theme.css_class}
+ .preview{ class: theme.css_class }
= f.radio_button :theme_id, theme.id
= theme.name
.col-sm-12
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index 578af9fe98d..2385a90401e 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -30,7 +30,7 @@
The maximum file size allowed is 200KB.
- if @user.avatar?
%hr
- = link_to 'Remove avatar', profile_avatar_path, data: { confirm: "Avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-gray"
+ = link_to 'Remove avatar', profile_avatar_path, data: { confirm: "Avatar will be removed. Are you sure?" }, method: :delete, class: "btn btn-gray"
%hr
.row
.col-lg-3.profile-settings-sidebar
@@ -69,7 +69,7 @@
%span.help-block We also use email for avatar detection if no avatar is uploaded.
.form-group
= f.label :public_email, class: "label-light"
- = f.select :public_email, options_for_select(@user.all_emails, selected: @user.public_email), {include_blank: 'Do not show on profile'}, class: "select2"
+ = f.select :public_email, options_for_select(@user.all_emails, selected: @user.public_email), { include_blank: 'Do not show on profile' }, class: "select2"
%span.help-block This email will be displayed on your public profile.
.form-group
= f.label :skype, class: "label-light"
@@ -101,14 +101,14 @@
.modal-dialog
.modal-content
.modal-header
- %button.close{:type => "button", :'data-dismiss' => "modal"}
+ %button.close{ :type => "button", :'data-dismiss' => "modal" }
%span
&times;
%h4.modal-title
Position and size your new avatar
.modal-body
.profile-crop-image-container
- %img.modal-profile-crop-image
+ %img.modal-profile-crop-image{ alt: "Avatar cropper" }
.crop-controls
.btn-group
%button.btn.btn-primary{ data: { method: "zoom", option: "0.1" } }
@@ -116,5 +116,5 @@
%button.btn.btn-primary{ data: { method: "zoom", option: "-0.1" } }
%span.fa.fa-search-minus
.modal-footer
- %button.btn.btn-primary.js-upload-user-avatar{:type => "button"}
+ %button.btn.btn-primary.js-upload-user-avatar{ :type => "button" }
Set new profile picture
diff --git a/app/views/projects/_activity.html.haml b/app/views/projects/_activity.html.haml
index 4f15f2997fb..0ea733cb978 100644
--- a/app/views/projects/_activity.html.haml
+++ b/app/views/projects/_activity.html.haml
@@ -9,7 +9,7 @@
= render 'shared/event_filter'
- .content_list.project-activity{:"data-href" => activity_project_path(@project)}
+ .content_list.project-activity{ :"data-href" => activity_project_path(@project) }
= spinner
:javascript
diff --git a/app/views/projects/_bitbucket_import_modal.html.haml b/app/views/projects/_bitbucket_import_modal.html.haml
index e74fd5b93ea..c24a496486c 100644
--- a/app/views/projects/_bitbucket_import_modal.html.haml
+++ b/app/views/projects/_bitbucket_import_modal.html.haml
@@ -1,8 +1,8 @@
-%div#bitbucket_import_modal.modal
+#bitbucket_import_modal.modal
.modal-dialog
.modal-content
.modal-header
- %a.close{href: "#", "data-dismiss" => "modal"} ×
+ %a.close{ href: "#", "data-dismiss" => "modal" } ×
%h3 Import projects from Bitbucket
.modal-body
To enable importing projects from Bitbucket,
@@ -10,4 +10,4 @@
as administrator you need to configure
- else
ask your GitLab administrator to configure
- == #{link_to 'OAuth integration', help_page_path("integration/bitbucket")}.
+ = link_to 'OAuth integration', help_page_path("integration/bitbucket")
diff --git a/app/views/projects/_customize_workflow.html.haml b/app/views/projects/_customize_workflow.html.haml
index d2c1e943db1..e2b73cee5a9 100644
--- a/app/views/projects/_customize_workflow.html.haml
+++ b/app/views/projects/_customize_workflow.html.haml
@@ -1,5 +1,5 @@
.row-content-block.project-home-empty
- %div.text-center{ class: container_class }
+ .text-center{ class: container_class }
%h4
Customize your workflow!
%p
diff --git a/app/views/projects/_find_file_link.html.haml b/app/views/projects/_find_file_link.html.haml
index 08e2fc48be7..dbb33090670 100644
--- a/app/views/projects/_find_file_link.html.haml
+++ b/app/views/projects/_find_file_link.html.haml
@@ -1,3 +1,3 @@
-= link_to namespace_project_find_file_path(@project.namespace, @project, @ref), class: 'btn btn-grouped shortcuts-find-file', rel: 'nofollow' do
- = icon('search')
- %span Find File
+= link_to namespace_project_find_file_path(@project.namespace, @project, @ref), class: 'btn btn-grouped shortcuts-find-file', rel: 'nofollow' do
+ = icon('search')
+ %span Find File
diff --git a/app/views/projects/_gitlab_import_modal.html.haml b/app/views/projects/_gitlab_import_modal.html.haml
index e9f39b16aa7..00aef66e1f8 100644
--- a/app/views/projects/_gitlab_import_modal.html.haml
+++ b/app/views/projects/_gitlab_import_modal.html.haml
@@ -1,8 +1,8 @@
-%div#gitlab_import_modal.modal
+#gitlab_import_modal.modal
.modal-dialog
.modal-content
.modal-header
- %a.close{href: "#", "data-dismiss" => "modal"} ×
+ %a.close{ href: "#", "data-dismiss" => "modal" } ×
%h3 Import projects from GitLab.com
.modal-body
To enable importing projects from GitLab.com,
@@ -10,4 +10,4 @@
as administrator you need to configure
- else
ask your GitLab administrator to configure
- == #{link_to 'OAuth integration', help_page_path("integration/gitlab")}.
+ = link_to 'OAuth integration', help_page_path("integration/gitlab")
diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml
index 0d1f2b70018..1b9d87e9969 100644
--- a/app/views/projects/_home_panel.html.haml
+++ b/app/views/projects/_home_panel.html.haml
@@ -5,7 +5,7 @@
= 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)}
+ %span.visibility-icon.has-tooltip{ data: { container: 'body' }, title: visibility_icon_description(@project) }
= visibility_level_icon(@project.visibility_level, fw: false)
.project-home-desc
diff --git a/app/views/projects/_md_preview.html.haml b/app/views/projects/_md_preview.html.haml
index 58d961d93ca..085f79de785 100644
--- a/app/views/projects/_md_preview.html.haml
+++ b/app/views/projects/_md_preview.html.haml
@@ -15,23 +15,23 @@
%li.pull-right
.toolbar-group
- = markdown_toolbar_button({icon: "bold fw", data: { "md-tag" => "**" }, title: "Add bold text" })
- = markdown_toolbar_button({icon: "italic fw", data: { "md-tag" => "*" }, title: "Add italic text" })
- = markdown_toolbar_button({icon: "quote-right fw", data: { "md-tag" => "> ", "md-prepend" => true }, title: "Insert a quote" })
- = markdown_toolbar_button({icon: "code fw", data: { "md-tag" => "`", "md-block" => "```" }, title: "Insert code" })
- = markdown_toolbar_button({icon: "list-ul fw", data: { "md-tag" => "* ", "md-prepend" => true }, title: "Add a bullet list" })
- = markdown_toolbar_button({icon: "list-ol fw", data: { "md-tag" => "1. ", "md-prepend" => true }, title: "Add a numbered list" })
- = markdown_toolbar_button({icon: "check-square-o fw", data: { "md-tag" => "* [ ] ", "md-prepend" => true }, title: "Add a task list" })
+ = markdown_toolbar_button({ icon: "bold fw", data: { "md-tag" => "**" }, title: "Add bold text" })
+ = markdown_toolbar_button({ icon: "italic fw", data: { "md-tag" => "*" }, title: "Add italic text" })
+ = markdown_toolbar_button({ icon: "quote-right fw", data: { "md-tag" => "> ", "md-prepend" => true }, title: "Insert a quote" })
+ = markdown_toolbar_button({ icon: "code fw", data: { "md-tag" => "`", "md-block" => "```" }, title: "Insert code" })
+ = markdown_toolbar_button({ icon: "list-ul fw", data: { "md-tag" => "* ", "md-prepend" => true }, title: "Add a bullet list" })
+ = markdown_toolbar_button({ icon: "list-ol fw", data: { "md-tag" => "1. ", "md-prepend" => true }, title: "Add a numbered list" })
+ = markdown_toolbar_button({ icon: "check-square-o fw", data: { "md-tag" => "* [ ] ", "md-prepend" => true }, title: "Add a task list" })
.toolbar-group
%button.toolbar-btn.js-zen-enter.has-tooltip.hidden-xs{ type: "button", tabindex: -1, aria: { label: "Go full screen" }, title: "Go full screen", data: { container: "body" } }
- =icon("arrows-alt fw")
+ = icon("arrows-alt fw")
.md-write-holder
= yield
- .md.md-preview-holder.js-md-preview.hide{class: (preview_class if defined?(preview_class))}
+ .md.md-preview-holder.js-md-preview.hide{ class: (preview_class if defined?(preview_class)) }
- if defined?(referenced_users) && referenced_users
- %div.referenced-users.hide
+ .referenced-users.hide
%span
= icon("exclamation-triangle")
You are about to add
diff --git a/app/views/projects/_wiki.html.haml b/app/views/projects/_wiki.html.haml
index f00422dd7c0..41d42740f61 100644
--- a/app/views/projects/_wiki.html.haml
+++ b/app/views/projects/_wiki.html.haml
@@ -7,7 +7,7 @@
- else
- can_create_wiki = can?(current_user, :create_wiki, @project)
.project-home-empty{ class: [('row-content-block' if can_create_wiki), ('content-block' unless can_create_wiki)] }
- %div.text-center{ class: container_class }
+ .text-center{ class: container_class }
%h4
This project does not have a wiki homepage yet
- if can_create_wiki
diff --git a/app/views/projects/artifacts/_tree_directory.html.haml b/app/views/projects/artifacts/_tree_directory.html.haml
index def493c56f5..9e49c93388a 100644
--- a/app/views/projects/artifacts/_tree_directory.html.haml
+++ b/app/views/projects/artifacts/_tree_directory.html.haml
@@ -1,6 +1,6 @@
- path_to_directory = browse_namespace_project_build_artifacts_path(@project.namespace, @project, @build, path: directory.path)
-%tr.tree-item{ 'data-link' => path_to_directory}
+%tr.tree-item{ 'data-link' => path_to_directory }
%td.tree-item-file-name
= tree_icon('folder', '755', directory.name)
%span.str-truncated
diff --git a/app/views/projects/artifacts/browse.html.haml b/app/views/projects/artifacts/browse.html.haml
index ede01dcc1aa..d0ff14e45e6 100644
--- a/app/views/projects/artifacts/browse.html.haml
+++ b/app/views/projects/artifacts/browse.html.haml
@@ -8,7 +8,7 @@
Download artifacts archive
.tree-holder
- %div.tree-content-holder
+ .tree-content-holder
%table.table.tree-table
%thead
%tr
diff --git a/app/views/projects/blame/show.html.haml b/app/views/projects/blame/show.html.haml
index f63802ac88b..23f54553014 100644
--- a/app/views/projects/blame/show.html.haml
+++ b/app/views/projects/blame/show.html.haml
@@ -36,7 +36,7 @@
%td.line-numbers
- line_count = blame_group[:lines].count
- (current_line...(current_line + line_count)).each do |i|
- %a.diff-line-num{href: "#L#{i}", id: "L#{i}", 'data-line-number' => i}
+ %a.diff-line-num{ href: "#L#{i}", id: "L#{i}", 'data-line-number' => i }
= icon("link")
= i
\
diff --git a/app/views/projects/blob/_blob.html.haml b/app/views/projects/blob/_blob.html.haml
index 149ee7c59d6..350bdf5f836 100644
--- a/app/views/projects/blob/_blob.html.haml
+++ b/app/views/projects/blob/_blob.html.haml
@@ -22,7 +22,7 @@
- blob_commit = @repository.last_commit_for_path(@commit.id, blob.path)
= render blob_commit, project: @project, ref: @ref
-%div#blob-content-holder.blob-content-holder
+#blob-content-holder.blob-content-holder
%article.file-holder
.file-title
= blob_icon blob.mode, blob.name
diff --git a/app/views/projects/blob/_image.html.haml b/app/views/projects/blob/_image.html.haml
index 4c356d1f07f..a486b2fe491 100644
--- a/app/views/projects/blob/_image.html.haml
+++ b/app/views/projects/blob/_image.html.haml
@@ -5,11 +5,11 @@
- # be wrong/strange if RawController modified the data.
- blob.load_all_data!(@repository)
- blob = sanitize_svg(blob)
- %img{src: "data:#{blob.mime_type};base64,#{Base64.encode64(blob.data)}"}
+ %img{ src: "data:#{blob.mime_type};base64,#{Base64.encode64(blob.data)}", alt: "#{blob.name}" }
- else
.nothing-here-block
The SVG could not be displayed as it is too large, you can
#{link_to('view the raw file', namespace_project_raw_path(@project.namespace, @project, @id), target: '_blank')}
instead.
- else
- %img{src: namespace_project_raw_path(@project.namespace, @project, tree_join(@commit.id, blob.path))}
+ %img{ src: namespace_project_raw_path(@project.namespace, @project, tree_join(@commit.id, blob.path)), alt: "#{blob.name}" }
diff --git a/app/views/projects/blob/_new_dir.html.haml b/app/views/projects/blob/_new_dir.html.haml
index 84694203d7d..7f470b890ba 100644
--- a/app/views/projects/blob/_new_dir.html.haml
+++ b/app/views/projects/blob/_new_dir.html.haml
@@ -2,7 +2,7 @@
.modal-dialog
.modal-content
.modal-header
- %a.close{href: "#", "data-dismiss" => "modal"} ×
+ %a.close{ href: "#", "data-dismiss" => "modal" } ×
%h3.page-title Create New Directory
.modal-body
= form_tag namespace_project_create_dir_path(@project.namespace, @project, @id), method: :post, remote: false, class: 'form-horizontal js-create-dir-form js-quick-submit js-requires-input' do
diff --git a/app/views/projects/blob/_remove.html.haml b/app/views/projects/blob/_remove.html.haml
index 2e1f32fd15e..db6662a95ac 100644
--- a/app/views/projects/blob/_remove.html.haml
+++ b/app/views/projects/blob/_remove.html.haml
@@ -2,7 +2,7 @@
.modal-dialog
.modal-content
.modal-header
- %a.close{href: "#", "data-dismiss" => "modal"} ×
+ %a.close{ href: "#", "data-dismiss" => "modal" } ×
%h3.page-title Delete #{@blob.name}
.modal-body
diff --git a/app/views/projects/blob/_upload.html.haml b/app/views/projects/blob/_upload.html.haml
index 57a27ec904e..61a7ffdd0ab 100644
--- a/app/views/projects/blob/_upload.html.haml
+++ b/app/views/projects/blob/_upload.html.haml
@@ -2,7 +2,7 @@
.modal-dialog
.modal-content
.modal-header
- %a.close{href: "#", "data-dismiss" => "modal"} ×
+ %a.close{ href: "#", "data-dismiss" => "modal" } ×
%h3.page-title #{title}
.modal-body
= form_tag form_path, method: method, class: 'js-quick-submit js-upload-blob-form form-horizontal' do
@@ -12,7 +12,7 @@
Attach a file by drag &amp; drop or
= link_to 'click to upload', '#', class: "markdown-selector"
%br
- .dropzone-alerts{class: "alert alert-danger data", style: "display:none"}
+ .dropzone-alerts.alert.alert-danger.data{ style: "display:none" }
= render 'shared/new_commit_form', placeholder: placeholder
diff --git a/app/views/projects/blob/diff.html.haml b/app/views/projects/blob/diff.html.haml
index a79ae53c780..538f8591f13 100644
--- a/app/views/projects/blob/diff.html.haml
+++ b/app/views/projects/blob/diff.html.haml
@@ -13,15 +13,15 @@
- case diff_view
- when :inline
%td.old_line.diff-line-num{ data: { linenumber: line_old } }
- %a{href: "##{line_old}", data: { linenumber: line_old }}
+ %a{ href: "##{line_old}", data: { linenumber: line_old } }
%td.new_line.diff-line-num{ data: { linenumber: line_new } }
- %a{href: "##{line_new}", data: { linenumber: line_new }}
+ %a{ href: "##{line_new}", data: { linenumber: line_new } }
= line_content
- when :parallel
- %td.old_line.diff-line-num{data: { linenumber: line_old }}
+ %td.old_line.diff-line-num{ data: { linenumber: line_old } }
= link_to raw(line_old), "##{line_old}"
= line_content
- %td.new_line.diff-line-num{data: { linenumber: line_new }}
+ %td.new_line.diff-line-num{ data: { linenumber: line_new } }
= link_to raw(line_new), "##{line_new}"
= line_content
diff --git a/app/views/projects/blob/preview.html.haml b/app/views/projects/blob/preview.html.haml
index 541dc96c45f..5cafb644b40 100644
--- a/app/views/projects/blob/preview.html.haml
+++ b/app/views/projects/blob/preview.html.haml
@@ -20,6 +20,6 @@
- else
%td.old_line.diff-line-num
%td.new_line.diff-line-num
- %td.line_content{class: "#{line.type}"}= diff_line_content(line.text)
+ %td.line_content{ class: "#{line.type}" }= diff_line_content(line.text)
- else
.nothing-here-block No changes.
diff --git a/app/views/projects/blob/show.html.haml b/app/views/projects/blob/show.html.haml
index 0ab78a39cf9..b6738c3380f 100644
--- a/app/views/projects/blob/show.html.haml
+++ b/app/views/projects/blob/show.html.haml
@@ -5,7 +5,7 @@
%div{ class: container_class }
= render 'projects/last_push'
- %div#tree-holder.tree-holder
+ #tree-holder.tree-holder
= render 'blob', blob: @blob
- if can_edit_blob?(@blob)
diff --git a/app/views/projects/boards/components/_card.html.haml b/app/views/projects/boards/components/_card.html.haml
index 1f31496e73f..e4c2aff46ec 100644
--- a/app/views/projects/boards/components/_card.html.haml
+++ b/app/views/projects/boards/components/_card.html.haml
@@ -17,7 +17,7 @@
":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 }
+ %img.avatar.avatar-inline.s20{ ":src" => "issue.assignee.avatar", width: 20, height: 20, alt: "Avatar" }
%button.label.color-label.has-tooltip{ "v-for" => "label in issue.labels",
type: "button",
"v-if" => "(!list.label || label.id !== list.label.id)",
diff --git a/app/views/projects/boards/components/sidebar/_assignee.html.haml b/app/views/projects/boards/components/sidebar/_assignee.html.haml
index 8fe1b832071..e75ce305440 100644
--- a/app/views/projects/boards/components/sidebar/_assignee.html.haml
+++ b/app/views/projects/boards/components/sidebar/_assignee.html.haml
@@ -14,7 +14,7 @@
%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" }
+ width: "32", alt: "Avatar" }
%span.author
{{ issue.assignee.name }}
%span.username
diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml
index 9135cee8364..2eb49685f08 100644
--- a/app/views/projects/branches/_branch.html.haml
+++ b/app/views/projects/branches/_branch.html.haml
@@ -4,7 +4,7 @@
- number_commits_behind = diverging_commit_counts[:behind]
- number_commits_ahead = diverging_commit_counts[:ahead]
- merge_project = can?(current_user, :create_merge_request, @project) ? @project : (current_user && current_user.fork_of(@project))
-%li(class="js-branch-#{branch.name}")
+%li{ class: "js-branch-#{branch.name}" }
%div
= link_to namespace_project_tree_path(@project.namespace, @project, branch.name), class: 'item-title str-truncated' do
= branch.name
@@ -12,7 +12,7 @@
- if branch.name == @repository.root_ref
%span.label.label-primary default
- elsif @repository.merged_to_root_ref? branch.name
- %span.label.label-info.has-tooltip(title="Merged into #{@repository.root_ref}")
+ %span.label.label-info.has-tooltip{ title: "Merged into #{@repository.root_ref}" }
merged
- if @project.protected_branch? branch.name
diff --git a/app/views/projects/branches/index.html.haml b/app/views/projects/branches/index.html.haml
index 5fd664c7a93..ecd812312c0 100644
--- a/app/views/projects/branches/index.html.haml
+++ b/app/views/projects/branches/index.html.haml
@@ -3,7 +3,7 @@
= render "projects/commits/head"
%div{ class: container_class }
- .top-area
+ .top-area.adjust
.nav-text
Protected branches can be managed in project settings
@@ -12,7 +12,7 @@
= search_field_tag :search, params[:search], { placeholder: 'Filter by branch name', id: 'branch-search', class: 'form-control search-text-input input-short', spellcheck: false }
.dropdown.inline
- %button.dropdown-toggle{type: 'button', 'data-toggle' => 'dropdown'}
+ %button.dropdown-menu-toggle{ type: 'button', 'data-toggle' => 'dropdown' }
%span.light
= projects_sort_options_hash[@sort]
= icon('chevron-down')
diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml
index 5a6c8c243fa..e63bdb38bd8 100644
--- a/app/views/projects/branches/new.html.haml
+++ b/app/views/projects/branches/new.html.haml
@@ -2,7 +2,7 @@
- if @error
.alert.alert-danger
- %button{ type: "button", class: "close", "data-dismiss" => "alert"} &times;
+ %button.close{ type: "button", "data-dismiss" => "alert" } &times;
= @error
%h3.page-title
New Branch
diff --git a/app/views/projects/builds/_sidebar.html.haml b/app/views/projects/builds/_sidebar.html.haml
index ce8b66b1945..0b3adcbe121 100644
--- a/app/views/projects/builds/_sidebar.html.haml
+++ b/app/views/projects/builds/_sidebar.html.haml
@@ -114,7 +114,7 @@
- if @build.pipeline.stages_count > 1
.dropdown.build-dropdown
.title Stage
- %button.dropdown-menu-toggle{type: 'button', 'data-toggle' => 'dropdown'}
+ %button.dropdown-menu-toggle{ type: 'button', 'data-toggle' => 'dropdown' }
%span.stage-selection More
= icon('chevron-down')
%ul.dropdown-menu
@@ -125,10 +125,10 @@
.builds-container
- HasStatus::ORDERED_STATUSES.each do |build_status|
- builds.select{|build| build.status == build_status}.each do |build|
- .build-job{class: sidebar_build_class(build, @build), data: {stage: build.stage}}
+ .build-job{ class: sidebar_build_class(build, @build), data: { stage: build.stage } }
= link_to namespace_project_build_path(@project.namespace, @project, build) do
= icon('arrow-right')
- %span{class: "ci-status-icon-#{build.status}"}
+ %span{ class: "ci-status-icon-#{build.status}" }
= ci_icon_for_status(build.status)
%span
- if build.name
@@ -136,4 +136,4 @@
- else
= build.id
- if build.retried?
- %i.fa.fa-refresh.has-tooltip{data: { container: 'body', placement: 'bottom' }, title: 'Build was retried'}
+ %i.fa.fa-refresh.has-tooltip{ data: { container: 'body', placement: 'bottom' }, title: 'Build was retried' }
diff --git a/app/views/projects/builds/index.html.haml b/app/views/projects/builds/index.html.haml
index 06070f12bbd..c623e39b21f 100644
--- a/app/views/projects/builds/index.html.haml
+++ b/app/views/projects/builds/index.html.haml
@@ -19,5 +19,5 @@
= link_to ci_lint_path, class: 'btn btn-default' do
%span CI Lint
- %div.content-list.builds-content-list
+ .content-list.builds-content-list
= render "table", builds: @builds, project: @project
diff --git a/app/views/projects/builds/show.html.haml b/app/views/projects/builds/show.html.haml
index c69c53b656f..54724ef5cab 100644
--- a/app/views/projects/builds/show.html.haml
+++ b/app/views/projects/builds/show.html.haml
@@ -56,10 +56,10 @@
- else
#js-build-scroll.scroll-controls
.scroll-step
- %a{ href: '#up-build-trace', id: 'scroll-top', class: 'scroll-link scroll-top', title: 'Scroll to top' }
+ %a.scroll-link.scroll-top{ href: '#up-build-trace', id: 'scroll-top', title: 'Scroll to top' }
= custom_icon('scroll_up')
= custom_icon('scroll_up_hover_active')
- %a{ href: '#down-build-trace', id: 'scroll-bottom', class: 'scroll-link scroll-bottom', title: 'Scroll to bottom' }
+ %a.scroll-link.scroll-bottom{ href: '#down-build-trace', id: 'scroll-bottom', title: 'Scroll to bottom' }
= custom_icon('scroll_down')
= custom_icon('scroll_down_hover_active')
- if @build.active?
diff --git a/app/views/projects/buttons/_dropdown.html.haml b/app/views/projects/buttons/_dropdown.html.haml
index f5659be25f0..67de8699b2e 100644
--- a/app/views/projects/buttons/_dropdown.html.haml
+++ b/app/views/projects/buttons/_dropdown.html.haml
@@ -1,6 +1,6 @@
- if current_user
.project-action-button.dropdown.inline
- %a.btn.dropdown-toggle{href: '#', "data-toggle" => "dropdown"}
+ %a.btn.dropdown-toggle{ href: '#', "data-toggle" => "dropdown" }
= icon('plus')
= icon("caret-down")
%ul.dropdown-menu.dropdown-menu-align-right.project-home-dropdown
diff --git a/app/views/projects/buttons/_fork.html.haml b/app/views/projects/buttons/_fork.html.haml
index 27da86b9efe..851fe44a86d 100644
--- a/app/views/projects/buttons/_fork.html.haml
+++ b/app/views/projects/buttons/_fork.html.haml
@@ -8,7 +8,7 @@
= 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
+ .count-with-arrow
%span.arrow
= 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 12d35101770..d57eb2cbfbc 100644
--- a/app/views/projects/buttons/_star.html.haml
+++ b/app/views/projects/buttons/_star.html.haml
@@ -6,7 +6,7 @@
- else
= icon('star-o')
%span Star
- %div.count-with-arrow
+ .count-with-arrow
%span.arrow
%span.count.star-count
= @project.star_count
@@ -15,7 +15,7 @@
= link_to new_user_session_path, class: 'btn has-tooltip star-btn', title: 'You must sign in to star a project' do
= icon('star')
Star
- %div.count-with-arrow
+ .count-with-arrow
%span.arrow
%span.count
= @project.star_count
diff --git a/app/views/projects/ci/builds/_build.html.haml b/app/views/projects/ci/builds/_build.html.haml
index f1cb0201032..520113639b7 100644
--- a/app/views/projects/ci/builds/_build.html.haml
+++ b/app/views/projects/ci/builds/_build.html.haml
@@ -7,7 +7,7 @@
- coverage = local_assigns.fetch(:coverage, false)
- allow_retry = local_assigns.fetch(:allow_retry, false)
-%tr.build.commit{class: ('retried' if retried)}
+%tr.build.commit{ class: ('retried' if retried) }
%td.status
= render "ci/status/badge", status: build.detailed_status(current_user)
diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml
index 2f8f153f9a9..e67492a36d1 100644
--- a/app/views/projects/ci/pipelines/_pipeline.html.haml
+++ b/app/views/projects/ci/pipelines/_pipeline.html.haml
@@ -49,7 +49,7 @@
.stage-container.mini-pipeline-graph
.dropdown.inline.build-content
- %button.has-tooltip.builds-dropdown.js-builds-dropdown-button{ type: 'button', data: { toggle: 'dropdown', title: "#{stage.name}: #{detailed_status.label}", placement: 'top', "stage-endpoint" => stage_namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline, stage: stage.name)}}
+ %button.has-tooltip.builds-dropdown.js-builds-dropdown-button{ type: 'button', data: { toggle: 'dropdown', title: "#{stage.name}: #{detailed_status.label}", placement: 'top', "stage-endpoint" => stage_namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline, stage: stage.name) } }
%span.has-tooltip{ class: status_klass }
%span.mini-pipeline-graph-icon-container
%span{ class: status_klass }= custom_icon(icon_status)
@@ -81,7 +81,7 @@
.btn-group.inline
- if actions.any?
.btn-group
- %a.dropdown-toggle.btn.btn-default.js-pipeline-dropdown-manual-actions{type: 'button', 'data-toggle' => 'dropdown'}
+ %a.dropdown-toggle.btn.btn-default.js-pipeline-dropdown-manual-actions{ type: 'button', 'data-toggle' => 'dropdown' }
= custom_icon('icon_play')
= icon('caret-down')
%ul.dropdown-menu.dropdown-menu-align-right
@@ -92,7 +92,7 @@
%span= build.name.humanize
- if artifacts.present?
.btn-group
- %a.dropdown-toggle.btn.btn-default.build-artifacts.js-pipeline-dropdown-download{type: 'button', 'data-toggle' => 'dropdown'}
+ %a.dropdown-toggle.btn.btn-default.build-artifacts.js-pipeline-dropdown-download{ type: 'button', 'data-toggle' => 'dropdown' }
= icon("download")
= icon('caret-down')
%ul.dropdown-menu.dropdown-menu-align-right
diff --git a/app/views/projects/commit/_change.html.haml b/app/views/projects/commit/_change.html.haml
index 782f558e8b0..12e4280d344 100644
--- a/app/views/projects/commit/_change.html.haml
+++ b/app/views/projects/commit/_change.html.haml
@@ -6,11 +6,11 @@
- label = 'Cherry-pick'
- target_label = 'Pick into branch'
-.modal{id: "modal-#{type}-commit"}
+.modal{ id: "modal-#{type}-commit" }
.modal-dialog
.modal-content
.modal-header
- %a.close{href: "#", "data-dismiss" => "modal"} ×
+ %a.close{ href: "#", "data-dismiss" => "modal" } ×
%h3.page-title== #{label} this #{commit.change_type_title(current_user)}
.modal-body
= form_tag send("#{type.underscore}_namespace_project_commit_path", @project.namespace, @project, commit.id), method: :post, remote: false, class: "form-horizontal js-#{type}-form js-requires-input" do
@@ -18,7 +18,7 @@
= label_tag 'target_branch', target_label, class: 'control-label'
.col-sm-10
= hidden_field_tag :target_branch, @project.default_branch, id: 'target_branch'
- = dropdown_tag(@project.default_branch, options: { title: "Switch branch", filter: true, placeholder: "Search branches", toggle_class: 'js-project-refs-dropdown js-target-branch dynamic', dropdown_class: 'dropdown-menu-selectable', data: { field_name: "target_branch", selected: @project.default_branch, target_branch: @project.default_branch, refs_url: namespace_project_branches_path(@project.namespace, @project), submit_form_on_click: false }})
+ = dropdown_tag(@project.default_branch, options: { title: "Switch branch", filter: true, placeholder: "Search branches", toggle_class: 'js-project-refs-dropdown js-target-branch dynamic', dropdown_class: 'dropdown-menu-selectable', data: { field_name: "target_branch", selected: @project.default_branch, target_branch: @project.default_branch, refs_url: namespace_project_branches_path(@project.namespace, @project), submit_form_on_click: false } })
- if can?(current_user, :push_code, @project)
.js-create-merge-request-container
diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml
index c08ed8f6c16..a9ee9230076 100644
--- a/app/views/projects/commit/_commit_box.html.haml
+++ b/app/views/projects/commit/_commit_box.html.haml
@@ -63,7 +63,7 @@
- if @commit.status
.well-segment.pipeline-info
- %div{class: "icon-container ci-status-icon-#{@commit.status}"}
+ %div{ class: "icon-container ci-status-icon-#{@commit.status}" }
= 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"
diff --git a/app/views/projects/commit/show.html.haml b/app/views/projects/commit/show.html.haml
index b8c64d1f13e..7afd3d80ef5 100644
--- a/app/views/projects/commit/show.html.haml
+++ b/app/views/projects/commit/show.html.haml
@@ -8,7 +8,7 @@
- if @commit.status
= render "ci_menu"
- else
- %div.block-connector
+ .block-connector
= render "projects/diffs/diffs", diffs: @diffs
= render "projects/notes/notes_with_form"
- if can_collaborate_with_project?
diff --git a/app/views/projects/commits/_commit_list.html.haml b/app/views/projects/commits/_commit_list.html.haml
index ce416caa494..6f5835cb9be 100644
--- a/app/views/projects/commits/_commit_list.html.haml
+++ b/app/views/projects/commits/_commit_list.html.haml
@@ -1,7 +1,7 @@
- commits, hidden = limited_commits(@commits)
- commits = Commit.decorate(commits, @project)
-%div.panel.panel-default
+.panel.panel-default
.panel-heading
Commits (#{@commits.count})
- if hidden > 0
diff --git a/app/views/projects/commits/show.html.haml b/app/views/projects/commits/show.html.haml
index 9628cbd1634..e77f23c7fd8 100644
--- a/app/views/projects/commits/show.html.haml
+++ b/app/views/projects/commits/show.html.haml
@@ -28,12 +28,12 @@
= search_field_tag :search, params[:search], { placeholder: 'Filter by commit message', id: 'commits-search', class: 'form-control search-text-input input-short', spellcheck: false }
- if current_user && current_user.private_token
.control
- = link_to namespace_project_commits_path(@project.namespace, @project, @ref, {format: :atom, private_token: current_user.private_token}), title: "Commits Feed", class: 'btn' do
+ = link_to namespace_project_commits_path(@project.namespace, @project, @ref, { format: :atom, private_token: current_user.private_token }), title: "Commits Feed", class: 'btn' do
= icon("rss")
%ul.breadcrumb.repo-breadcrumb
= commits_breadcrumbs
- %div{id: dom_id(@project)}
+ %div{ id: dom_id(@project) }
%ol#commits-list.list-unstyled.content_list
= render 'commits', project: @project, ref: @ref
= spinner
diff --git a/app/views/projects/cycle_analytics/show.html.haml b/app/views/projects/cycle_analytics/show.html.haml
index ef1b38d5e21..89e2e162b5b 100644
--- a/app/views/projects/cycle_analytics/show.html.haml
+++ b/app/views/projects/cycle_analytics/show.html.haml
@@ -7,7 +7,7 @@
#cycle-analytics{ class: container_class, "v-cloak" => "true", data: { request_path: project_cycle_analytics_path(@project) } }
- if @cycle_analytics_no_data
- .bordered-box.landing.content-block{"v-if" => "!isOverviewDialogDismissed"}
+ .bordered-box.landing.content-block{ "v-if" => "!isOverviewDialogDismissed" }
= icon("times", class: "dismiss-icon", "@click" => "dismissOverviewDialog()")
.row
.col-sm-3.col-xs-12.svg-container
@@ -20,19 +20,19 @@
= link_to "Read more", help_page_path('user/project/cycle_analytics'), target: '_blank', class: 'btn'
= icon("spinner spin", "v-show" => "isLoading")
- .wrapper{"v-show" => "!isLoading && !hasError"}
+ .wrapper{ "v-show" => "!isLoading && !hasError" }
.panel.panel-default
.panel-heading
Pipeline Health
.content-block
.container-fluid
.row
- .col-sm-3.col-xs-12.column{"v-for" => "item in state.summary"}
+ .col-sm-3.col-xs-12.column{ "v-for" => "item in state.summary" }
%h3.header {{item.value}}
%p.text {{item.title}}
.col-sm-3.col-xs-12.column
.dropdown.inline.js-ca-dropdown
- %button.dropdown-menu-toggle{"data-toggle" => "dropdown", :type => "button"}
+ %button.dropdown-menu-toggle{ "data-toggle" => "dropdown", :type => "button" }
%span.dropdown-label Last 30 days
%i.fa.fa-chevron-down
%ul.dropdown-menu.dropdown-menu-align-right
diff --git a/app/views/projects/deploy_keys/_deploy_key.html.haml b/app/views/projects/deploy_keys/_deploy_key.html.haml
index 450aaeb367c..d1e3cb14022 100644
--- a/app/views/projects/deploy_keys/_deploy_key.html.haml
+++ b/app/views/projects/deploy_keys/_deploy_key.html.haml
@@ -6,6 +6,9 @@
= deploy_key.title
.description
= deploy_key.fingerprint
+ - if deploy_key.can_push?
+ .write-access-allowed
+ Write access allowed
.deploy-key-content.prepend-left-default.deploy-key-projects
- deploy_key.projects.each do |project|
- if can?(current_user, :read_project, project)
diff --git a/app/views/projects/deploy_keys/_form.html.haml b/app/views/projects/deploy_keys/_form.html.haml
index 901605f7ca3..c91bb9c255a 100644
--- a/app/views/projects/deploy_keys/_form.html.haml
+++ b/app/views/projects/deploy_keys/_form.html.haml
@@ -10,4 +10,13 @@
%p.light.append-bottom-0
Paste a machine public key here. Read more about how to generate it
= link_to "here", help_page_path("ssh/README")
+ .form-group
+ .checkbox
+ = f.label :can_push do
+ = f.check_box :can_push
+ %strong Write access allowed
+ .form-group
+ %p.light.append-bottom-0
+ Allow this key to push to repository as well? (Default only allows pull access.)
+
= f.submit "Add key", class: "btn-create btn"
diff --git a/app/views/projects/deployments/_actions.haml b/app/views/projects/deployments/_actions.haml
index 58a214bdbd1..a680b1ca017 100644
--- a/app/views/projects/deployments/_actions.haml
+++ b/app/views/projects/deployments/_actions.haml
@@ -3,7 +3,7 @@
- if actions.present?
.inline
.dropdown
- %a.dropdown-new.btn.btn-default{type: 'button', 'data-toggle' => 'dropdown'}
+ %a.dropdown-new.btn.btn-default{ type: 'button', 'data-toggle' => 'dropdown' }
= custom_icon('icon_play')
= icon('caret-down')
%ul.dropdown-menu.dropdown-menu-align-right
diff --git a/app/views/projects/deployments/_commit.html.haml b/app/views/projects/deployments/_commit.html.haml
index ff250eeca50..170d786ecbf 100644
--- a/app/views/projects/deployments/_commit.html.haml
+++ b/app/views/projects/deployments/_commit.html.haml
@@ -1,4 +1,4 @@
-%div.branch-commit
+.branch-commit
- if deployment.ref
.icon-container
= deployment.tag? ? icon('tag') : icon('code-fork')
diff --git a/app/views/projects/diffs/_content.html.haml b/app/views/projects/diffs/_content.html.haml
index 6120b2191dd..52a1ece7d60 100644
--- a/app/views/projects/diffs/_content.html.haml
+++ b/app/views/projects/diffs/_content.html.haml
@@ -10,7 +10,7 @@
.nothing-here-block This diff was suppressed by a .gitattributes entry.
- elsif diff_file.collapsed?
- url = url_for(params.merge(action: :diff_for_path, old_path: diff_file.old_path, new_path: diff_file.new_path, file_identifier: diff_file.file_identifier))
- .nothing-here-block.diff-collapsed{data: { diff_for_path: url } }
+ .nothing-here-block.diff-collapsed{ data: { diff_for_path: url } }
This diff is collapsed.
%a.click-to-expand
Click to expand it.
diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml
index 6c33d80becd..15df2edefc7 100644
--- a/app/views/projects/diffs/_file.html.haml
+++ b/app/views/projects/diffs/_file.html.haml
@@ -1,5 +1,5 @@
-.diff-file.file-holder{id: file_hash, data: diff_file_html_data(project, diff_file.file_path, diff_commit.id)}
- .file-title{id: "file-path-#{hexdigest(diff_file.file_path)}"}
+.diff-file.file-holder{ id: file_hash, data: diff_file_html_data(project, diff_file.file_path, diff_commit.id) }
+ .file-title{ id: "file-path-#{hexdigest(diff_file.file_path)}" }
= render "projects/diffs/file_header", diff_file: diff_file, blob: blob, diff_commit: diff_commit, project: project, url: "##{file_hash}"
- unless diff_file.submodule?
diff --git a/app/views/projects/diffs/_file_header.html.haml b/app/views/projects/diffs/_file_header.html.haml
index d3ed8e1bf38..90c9a0c6c2b 100644
--- a/app/views/projects/diffs/_file_header.html.haml
+++ b/app/views/projects/diffs/_file_header.html.haml
@@ -21,7 +21,7 @@
- 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')
+ = clipboard_button(clipboard_text: diff_file.new_path, class: 'btn-clipboard btn-transparent prepend-left-5', title: 'Copy file path to clipboard')
- if diff_file.mode_changed?
%small
diff --git a/app/views/projects/diffs/_image.html.haml b/app/views/projects/diffs/_image.html.haml
index 9ec6a7aa5cd..1bccaaf5273 100644
--- a/app/views/projects/diffs/_image.html.haml
+++ b/app/views/projects/diffs/_image.html.haml
@@ -7,16 +7,16 @@
- if diff.renamed_file || diff.new_file || diff.deleted_file
.image
%span.wrap
- .frame{class: image_diff_class(diff)}
- %img{src: diff.deleted_file ? old_file_raw_path : file_raw_path}
+ .frame{ class: image_diff_class(diff) }
+ %img{ src: diff.deleted_file ? old_file_raw_path : file_raw_path, alt: diff.new_path }
%p.image-info= "#{number_to_human_size file.size}"
- else
.image
- %div.two-up.view
+ .two-up.view
%span.wrap
.frame.deleted
- %a{href: namespace_project_blob_path(@project.namespace, @project, tree_join(diff_file.old_ref, diff.old_path))}
- %img{src: old_file_raw_path}
+ %a{ href: namespace_project_blob_path(@project.namespace, @project, tree_join(diff_file.old_ref, diff.old_path)) }
+ %img{ src: old_file_raw_path, alt: diff.old_path }
%p.image-info.hide
%span.meta-filesize= "#{number_to_human_size old_file.size}"
|
@@ -27,8 +27,8 @@
%span.meta-height
%span.wrap
.frame.added
- %a{href: namespace_project_blob_path(@project.namespace, @project, tree_join(diff_file.new_ref, diff.new_path))}
- %img{src: file_raw_path}
+ %a{ href: namespace_project_blob_path(@project.namespace, @project, tree_join(diff_file.new_ref, diff.new_path)) }
+ %img{ src: file_raw_path, alt: diff.new_path }
%p.image-info.hide
%span.meta-filesize= "#{number_to_human_size file.size}"
|
@@ -38,32 +38,32 @@
%b H:
%span.meta-height
- %div.swipe.view.hide
+ .swipe.view.hide
.swipe-frame
.frame.deleted
- %img{src: old_file_raw_path}
+ %img{ src: old_file_raw_path, alt: diff.old_path }
.swipe-wrap
.frame.added
- %img{src: file_raw_path}
+ %img{ src: file_raw_path, alt: diff.new_path }
%span.swipe-bar
%span.top-handle
%span.bottom-handle
- %div.onion-skin.view.hide
+ .onion-skin.view.hide
.onion-skin-frame
.frame.deleted
- %img{src: old_file_raw_path}
+ %img{ src: old_file_raw_path, alt: diff.old_path }
.frame.added
- %img{src: file_raw_path}
+ %img{ src: file_raw_path, alt: diff.new_path }
.controls
.transparent
.drag-track
- .dragger{:style => "left: 0px;"}
+ .dragger{ :style => "left: 0px;" }
.opaque
.view-modes.hide
%ul.view-modes-menu
- %li.two-up{data: {mode: 'two-up'}} 2-up
- %li.swipe{data: {mode: 'swipe'}} Swipe
- %li.onion-skin{data: {mode: 'onion-skin'}} Onion skin
+ %li.two-up{ data: { mode: 'two-up' } } 2-up
+ %li.swipe{ data: { mode: 'swipe' } } Swipe
+ %li.onion-skin{ data: { mode: 'onion-skin' } } Onion skin
diff --git a/app/views/projects/diffs/_line.html.haml b/app/views/projects/diffs/_line.html.haml
index 16c96b66714..cd18ba2ed00 100644
--- a/app/views/projects/diffs/_line.html.haml
+++ b/app/views/projects/diffs/_line.html.haml
@@ -16,13 +16,13 @@
- if plain
= link_text
- else
- %a{href: "##{line_code}", data: { linenumber: link_text }}
+ %a{ href: "##{line_code}", data: { linenumber: link_text } }
%td.new_line.diff-line-num{ class: type, data: { linenumber: line.new_pos } }
- link_text = type == "old" ? " " : line.new_pos
- if plain
= link_text
- else
- %a{href: "##{line_code}", data: { linenumber: link_text }}
+ %a{ href: "##{line_code}", data: { linenumber: link_text } }
%td.line_content.noteable_line{ class: type, data: (diff_view_line_data(line_code, diff_file.position(line), type) unless plain) }<
- if email
%pre= line.text
diff --git a/app/views/projects/diffs/_parallel_view.html.haml b/app/views/projects/diffs/_parallel_view.html.haml
index 78aa9fb7391..b087485aa17 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.js-syntax-highlight{ data: diff_view_data }
+.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|
@@ -13,9 +13,9 @@
- else
- left_line_code = diff_file.line_code(left)
- left_position = diff_file.position(left)
- %td.old_line.diff-line-num{id: left_line_code, class: left.type, data: { linenumber: left.old_pos }}
- %a{href: "##{left_line_code}" }= raw(left.old_pos)
- %td.line_content.parallel.noteable_line{class: left.type, data: diff_view_line_data(left_line_code, left_position, 'old')}= diff_line_content(left.text)
+ %td.old_line.diff-line-num{ id: left_line_code, class: left.type, data: { linenumber: left.old_pos } }
+ %a{ href: "##{left_line_code}" }= raw(left.old_pos)
+ %td.line_content.parallel.noteable_line{ class: left.type, data: diff_view_line_data(left_line_code, left_position, 'old') }= diff_line_content(left.text)
- else
%td.old_line.diff-line-num.empty-cell
%td.line_content.parallel
@@ -26,9 +26,9 @@
- else
- right_line_code = diff_file.line_code(right)
- right_position = diff_file.position(right)
- %td.new_line.diff-line-num{id: right_line_code, class: right.type, data: { linenumber: right.new_pos }}
- %a{href: "##{right_line_code}" }= raw(right.new_pos)
- %td.line_content.parallel.noteable_line{class: right.type, data: diff_view_line_data(right_line_code, right_position, 'new')}= diff_line_content(right.text)
+ %td.new_line.diff-line-num{ id: right_line_code, class: right.type, data: { linenumber: right.new_pos } }
+ %a{ href: "##{right_line_code}" }= raw(right.new_pos)
+ %td.line_content.parallel.noteable_line{ class: right.type, data: diff_view_line_data(right_line_code, right_position, 'new') }= diff_line_content(right.text)
- else
%td.old_line.diff-line-num.empty-cell
%td.line_content.parallel
diff --git a/app/views/projects/diffs/_stats.html.haml b/app/views/projects/diffs/_stats.html.haml
index 66d6254aa1e..290f696d582 100644
--- a/app/views/projects/diffs/_stats.html.haml
+++ b/app/views/projects/diffs/_stats.html.haml
@@ -14,24 +14,23 @@
%li
- if diff_file.deleted_file
%span.deleted-file
- %a{href: "##{file_hash}"}
+ %a{ href: "##{file_hash}" }
%i.fa.fa-minus
= diff_file.old_path
- elsif diff_file.renamed_file
%span.renamed-file
- %a{href: "##{file_hash}"}
+ %a{ href: "##{file_hash}" }
%i.fa.fa-minus
= diff_file.old_path
&rarr;
= diff_file.new_path
- elsif diff_file.new_file
%span.new-file
- %a{href: "##{file_hash}"}
+ %a{ href: "##{file_hash}" }
%i.fa.fa-plus
= diff_file.new_path
- else
%span.edit-file
- %a{href: "##{file_hash}"}
+ %a{ href: "##{file_hash}" }
%i.fa.fa-adjust
= diff_file.new_path
-
diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml
index c0a83091c8c..3525a07a687 100644
--- a/app/views/projects/empty.html.haml
+++ b/app/views/projects/empty.html.haml
@@ -32,7 +32,7 @@
.empty_wrapper
%h3.page-title-empty
Command line instructions
- %div.git-empty
+ .git-empty
%fieldset
%h5 Git global setup
%pre.light-well
diff --git a/app/views/projects/environments/index.html.haml b/app/views/projects/environments/index.html.haml
index 0c6f696f5b9..8c728eb0f6a 100644
--- a/app/views/projects/environments/index.html.haml
+++ b/app/views/projects/environments/index.html.haml
@@ -16,4 +16,4 @@
"css-class" => container_class,
"commit-icon-svg" => custom_icon("icon_commit"),
"terminal-icon-svg" => custom_icon("icon_terminal"),
- "play-icon-svg" => custom_icon("icon_play")}}
+ "play-icon-svg" => custom_icon("icon_play") } }
diff --git a/app/views/projects/environments/terminal.html.haml b/app/views/projects/environments/terminal.html.haml
index a6726e509e0..431253c1299 100644
--- a/app/views/projects/environments/terminal.html.haml
+++ b/app/views/projects/environments/terminal.html.haml
@@ -6,7 +6,7 @@
= stylesheet_link_tag "xterm/xterm"
= page_specific_javascript_tag("terminal/terminal_bundle.js")
-%div{class: container_class}
+%div{ class: container_class }
.top-area
.row
.col-sm-6
@@ -18,5 +18,5 @@
.nav-controls
= render 'projects/deployments/actions', deployment: @environment.last_deployment
-.terminal-container{class: container_class}
- #terminal{data:{project_path: "#{terminal_namespace_project_environment_path(@project.namespace, @project, @environment)}.ws"}}
+.terminal-container{ class: container_class }
+ #terminal{ data: { project_path: "#{terminal_namespace_project_environment_path(@project.namespace, @project, @environment)}.ws" } }
diff --git a/app/views/projects/find_file/show.html.haml b/app/views/projects/find_file/show.html.haml
index 9322c82904f..4cdb44325b3 100644
--- a/app/views/projects/find_file/show.html.haml
+++ b/app/views/projects/find_file/show.html.haml
@@ -9,11 +9,11 @@
= link_to namespace_project_tree_path(@project.namespace, @project, @ref) do
= @project.path
%li.file-finder
- %input#file_find.form-control.file-finder-input{type: "text", placeholder: 'Find by path', autocomplete: 'off'}
+ %input#file_find.form-control.file-finder-input{ type: "text", placeholder: 'Find by path', autocomplete: 'off' }
- %div.tree-content-holder
+ .tree-content-holder
.table-holder
- %table.table.files-slider{class: "table_#{@hex_path} tree-table table-striped" }
+ %table.table.files-slider{ class: "table_#{@hex_path} tree-table table-striped" }
%tbody
= spinner nil, true
diff --git a/app/views/projects/forks/index.html.haml b/app/views/projects/forks/index.html.haml
index 5ee3979c7e7..6c8a6f051a9 100644
--- a/app/views/projects/forks/index.html.haml
+++ b/app/views/projects/forks/index.html.haml
@@ -1,7 +1,7 @@
.top-area
.nav-text
- full_count_title = "#{@public_forks_count} public and #{@private_forks_count} private"
- == #{pluralize(@total_forks_count, 'fork')}: #{full_count_title}
+ = "#{pluralize(@total_forks_count, 'fork')}: #{full_count_title}"
.nav-controls
= form_tag request.original_url, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f|
@@ -9,7 +9,7 @@
spellcheck: false, data: { 'filter-selector' => 'span.namespace-name' }
.dropdown
- %button.dropdown-toggle{type: 'button', 'data-toggle' => 'dropdown'}
+ %button.dropdown-toggle{ type: 'button', 'data-toggle' => 'dropdown' }
%span.light sort:
- if @sort.present?
= sort_options_hash[@sort]
diff --git a/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml b/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml
index 9f444f076c0..6d7af1685fd 100644
--- a/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml
+++ b/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml
@@ -6,7 +6,7 @@
- stage = local_assigns.fetch(:stage, false)
- coverage = local_assigns.fetch(:coverage, false)
-%tr.generic_commit_status{class: ('retried' if retried)}
+%tr.generic_commit_status{ class: ('retried' if retried) }
%td.status
= render 'ci/status/badge', status: generic_commit_status.detailed_status(current_user)
diff --git a/app/views/projects/graphs/ci/_build_times.haml b/app/views/projects/graphs/ci/_build_times.haml
index 195f18afc76..bb0975a9535 100644
--- a/app/views/projects/graphs/ci/_build_times.haml
+++ b/app/views/projects/graphs/ci/_build_times.haml
@@ -2,7 +2,7 @@
%p.light
Commit duration in minutes for last 30 commits
- %canvas#build_timesChart{height: 200}
+ %canvas#build_timesChart{ height: 200 }
:javascript
var data = {
diff --git a/app/views/projects/graphs/ci/_builds.haml b/app/views/projects/graphs/ci/_builds.haml
index 1fbf6ca2c1c..431657c4dcb 100644
--- a/app/views/projects/graphs/ci/_builds.haml
+++ b/app/views/projects/graphs/ci/_builds.haml
@@ -13,18 +13,18 @@
%p.light
Builds for last week
(#{date_from_to(Date.today - 7.days, Date.today)})
- %canvas#weekChart{height: 200}
+ %canvas#weekChart{ height: 200 }
.prepend-top-default
%p.light
Builds for last month
(#{date_from_to(Date.today - 30.days, Date.today)})
- %canvas#monthChart{height: 200}
+ %canvas#monthChart{ height: 200 }
.prepend-top-default
%p.light
Builds for last year
- %canvas#yearChart.padded{height: 250}
+ %canvas#yearChart.padded{ height: 250 }
- [:week, :month, :year].each do |scope|
:javascript
diff --git a/app/views/projects/graphs/show.html.haml b/app/views/projects/graphs/show.html.haml
index ac5f792d140..5ebb939a109 100644
--- a/app/views/projects/graphs/show.html.haml
+++ b/app/views/projects/graphs/show.html.haml
@@ -21,7 +21,7 @@
%h3#date_header.page-title
%p.light
Commits to #{@ref}, excluding merge commits. Limited to 6,000 commits.
- %input#brush_change{:type => "hidden"}
+ %input#brush_change{ :type => "hidden" }
.graphs.row
#contributors-master
#contributors.clearfix
diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml
index c80210d6ff4..bd46af339cf 100644
--- a/app/views/projects/issues/_issue.html.haml
+++ b/app/views/projects/issues/_issue.html.haml
@@ -43,7 +43,7 @@
= icon('clock-o')
= issue.milestone.title
- if issue.due_date
- %span{class: "#{'cred' if issue.overdue?}"}
+ %span{ class: "#{'cred' if issue.overdue?}" }
&nbsp;
= icon('calendar')
= issue.due_date.to_s(:medium)
diff --git a/app/views/projects/issues/_new_branch.html.haml b/app/views/projects/issues/_new_branch.html.haml
index c56b6cc11f5..13e2150f997 100644
--- a/app/views/projects/issues/_new_branch.html.haml
+++ b/app/views/projects/issues/_new_branch.html.haml
@@ -1,9 +1,6 @@
- if can?(current_user, :push_code, @project)
.pull-right
- #new-branch.new-branch{'data-path' => can_create_branch_namespace_project_issue_path(@project.namespace, @project, @issue)}
- = link_to '#', class: 'checking btn btn-grouped', disabled: 'disabled' do
- = icon('spinner spin')
- Checking branches
+ #new-branch.new-branch{ 'data-path' => can_create_branch_namespace_project_issue_path(@project.namespace, @project, @issue) }
= link_to namespace_project_branches_path(@project.namespace, @project, branch_name: @issue.to_branch_name, issue_iid: @issue.iid),
method: :post, class: 'btn btn-new btn-inverted btn-grouped has-tooltip available hide', title: @issue.to_branch_name do
New branch
diff --git a/app/views/projects/mattermosts/_team_selection.html.haml b/app/views/projects/mattermosts/_team_selection.html.haml
index 7980f7c9a72..24e86b8497f 100644
--- a/app/views/projects/mattermosts/_team_selection.html.haml
+++ b/app/views/projects/mattermosts/_team_selection.html.haml
@@ -8,7 +8,9 @@
= @teams.one? ? 'The team' : 'Select the team'
where the slash commands will be used in
- selected_id = @teams.keys.first if @teams.one?
- = f.select(:team_id, mattermost_teams_options(@teams), {}, { class: 'form-control', selected: "#{selected_id}", disabled: @teams.one? })
+ - options = mattermost_teams_options(@teams)
+ - options = options_for_select(options, selected_id)
+ = f.select(:team_id, options, {}, { class: 'form-control', selected: "#{selected_id}" })
.help-block
- if @teams.one?
This is the only team where you are an administrator.
diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml
index b3c43286a50..e3b0aa7e644 100644
--- a/app/views/projects/merge_requests/_merge_request.html.haml
+++ b/app/views/projects/merge_requests/_merge_request.html.haml
@@ -65,7 +65,7 @@
&nbsp;
- merge_request.labels.each do |label|
= link_to_label(label, subject: merge_request.project, type: :merge_request)
-
+
- if merge_request.tasks?
&nbsp;
%span.task-status
diff --git a/app/views/projects/merge_requests/_new_submit.html.haml b/app/views/projects/merge_requests/_new_submit.html.haml
index 349181be784..34ead6427e0 100644
--- a/app/views/projects/merge_requests/_new_submit.html.haml
+++ b/app/views/projects/merge_requests/_new_submit.html.haml
@@ -10,7 +10,7 @@
%span.pull-right
= link_to 'Change branches', mr_change_branches_path(@merge_request)
%hr
-= form_for [@project.namespace.becomes(Namespace), @project, @merge_request], html: { class: 'merge-request-form form-horizontal common-note-form js-requires-input' } do |f|
+= form_for [@project.namespace.becomes(Namespace), @project, @merge_request], html: { class: 'merge-request-form form-horizontal common-note-form js-requires-input js-quick-submit' } do |f|
= render 'shared/issuable/form', f: f, issuable: @merge_request
= f.hidden_field :source_project_id
= f.hidden_field :source_branch
diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml
index d1fa51ae7ee..633701c6f40 100644
--- a/app/views/projects/merge_requests/_show.html.haml
+++ b/app/views/projects/merge_requests/_show.html.haml
@@ -5,10 +5,10 @@
- content_for :page_specific_javascripts do
= page_specific_javascript_tag('diff_notes/diff_notes_bundle.js')
-.merge-request{'data-url' => merge_request_path(@merge_request)}
+.merge-request{ 'data-url' => merge_request_path(@merge_request) }
= render "projects/merge_requests/show/mr_title"
- .merge-request-details.issuable-details{data: {id: @merge_request.project.id}}
+ .merge-request-details.issuable-details{ data: { id: @merge_request.project.id } }
= render "projects/merge_requests/show/mr_box"
.append-bottom-default.mr-source-target.prepend-top-default
- if @merge_request.open?
@@ -47,6 +47,9 @@
= succeed '.' do
= link_to "command line", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal"
+ .content-block.content-block-small
+ = render 'award_emoji/awards_block', awardable: @merge_request, inline: true
+
- if @commits_count.nonzero?
.merge-request-tabs-holder{ class: ("js-tabs-affix" unless ENV['RAILS_ENV'] == 'test') }
%div{ class: container_class }
@@ -70,7 +73,7 @@
Changes
%span.badge= @merge_request.diff_size
%li#resolve-count-app.line-resolve-all-container.pull-right.prepend-top-10.hidden-xs{ "v-cloak" => true }
- %resolve-count{ "inline-template" => true, ":logged-out" => "#{current_user.nil?}" }
+ %resolve-count{ "inline-template" => true, ":logged-out" => "#{current_user.nil?}" }
%div
.line-resolve-all{ "v-show" => "discussionCount > 0",
":class" => "{ 'has-next-btn': !loggedOut && resolvedDiscussionCount !== discussionCount }" }
@@ -83,9 +86,6 @@
.tab-content#diff-notes-app
#notes.notes.tab-pane.voting_notes
- .content-block.content-block-small
- = render 'award_emoji/awards_block', awardable: @merge_request, inline: true
-
.row
%section.col-md-12
.issuable-discussion
diff --git a/app/views/projects/merge_requests/conflicts.html.haml b/app/views/projects/merge_requests/conflicts.html.haml
index 16789f68f70..b8b87dcdcaf 100644
--- a/app/views/projects/merge_requests/conflicts.html.haml
+++ b/app/views/projects/merge_requests/conflicts.html.haml
@@ -9,29 +9,29 @@
= render 'shared/issuable/sidebar', issuable: @merge_request
-#conflicts{"v-cloak" => "true", data: { conflicts_path: conflicts_namespace_project_merge_request_path(@merge_request.project.namespace, @merge_request.project, @merge_request, format: :json),
+#conflicts{ "v-cloak" => "true", data: { conflicts_path: conflicts_namespace_project_merge_request_path(@merge_request.project.namespace, @merge_request.project, @merge_request, format: :json),
resolve_conflicts_path: resolve_conflicts_namespace_project_merge_request_path(@merge_request.project.namespace, @merge_request.project, @merge_request) } }
- .loading{"v-if" => "isLoading"}
+ .loading{ "v-if" => "isLoading" }
%i.fa.fa-spinner.fa-spin
- .nothing-here-block{"v-if" => "hasError"}
+ .nothing-here-block{ "v-if" => "hasError" }
{{conflictsData.errorMessage}}
= render partial: "projects/merge_requests/conflicts/commit_stats"
- .files-wrapper{"v-if" => "!isLoading && !hasError"}
+ .files-wrapper{ "v-if" => "!isLoading && !hasError" }
.files
- .diff-file.file-holder.conflict{"v-for" => "file in conflictsData.files"}
+ .diff-file.file-holder.conflict{ "v-for" => "file in conflictsData.files" }
.file-title
- %i.fa.fa-fw{":class" => "file.iconClass"}
+ %i.fa.fa-fw{ ":class" => "file.iconClass" }
%strong {{file.filePath}}
= render partial: 'projects/merge_requests/conflicts/file_actions'
.diff-content.diff-wrap-lines
- .diff-wrap-lines.code.file-content.js-syntax-highlight{"v-show" => "!isParallel && file.resolveMode === 'interactive' && file.type === 'text'" }
+ .diff-wrap-lines.code.file-content.js-syntax-highlight{ "v-show" => "!isParallel && file.resolveMode === 'interactive' && file.type === 'text'" }
= render partial: "projects/merge_requests/conflicts/components/inline_conflict_lines"
- .diff-wrap-lines.code.file-content.js-syntax-highlight{"v-show" => "isParallel && file.resolveMode === 'interactive' && file.type === 'text'" }
+ .diff-wrap-lines.code.file-content.js-syntax-highlight{ "v-show" => "isParallel && file.resolveMode === 'interactive' && file.type === 'text'" }
%parallel-conflict-lines{ ":file" => "file" }
- %div{"v-show" => "file.resolveMode === 'edit' || file.type === 'text-editor'"}
+ %div{ "v-show" => "file.resolveMode === 'edit' || file.type === 'text-editor'" }
= render partial: "projects/merge_requests/conflicts/components/diff_file_editor"
= render partial: "projects/merge_requests/conflicts/submit_form"
diff --git a/app/views/projects/merge_requests/conflicts/_commit_stats.html.haml b/app/views/projects/merge_requests/conflicts/_commit_stats.html.haml
index 5ab3cd96163..964dc40a213 100644
--- a/app/views/projects/merge_requests/conflicts/_commit_stats.html.haml
+++ b/app/views/projects/merge_requests/conflicts/_commit_stats.html.haml
@@ -1,9 +1,9 @@
-.content-block.oneline-block.files-changed{"v-if" => "!isLoading && !hasError"}
- .inline-parallel-buttons{"v-if" => "showDiffViewTypeSwitcher"}
+.content-block.oneline-block.files-changed{ "v-if" => "!isLoading && !hasError" }
+ .inline-parallel-buttons{ "v-if" => "showDiffViewTypeSwitcher" }
.btn-group
- %button.btn{":class" => "{'active': !isParallel}", "@click" => "handleViewTypeChange('inline')"}
+ %button.btn{ ":class" => "{'active': !isParallel}", "@click" => "handleViewTypeChange('inline')" }
Inline
- %button.btn{":class" => "{'active': isParallel}", "@click" => "handleViewTypeChange('parallel')"}
+ %button.btn{ ":class" => "{'active': isParallel}", "@click" => "handleViewTypeChange('parallel')" }
Side-by-side
.js-toggle-container
diff --git a/app/views/projects/merge_requests/conflicts/_file_actions.html.haml b/app/views/projects/merge_requests/conflicts/_file_actions.html.haml
index 05af57acf03..2595ce74ac0 100644
--- a/app/views/projects/merge_requests/conflicts/_file_actions.html.haml
+++ b/app/views/projects/merge_requests/conflicts/_file_actions.html.haml
@@ -1,5 +1,5 @@
.file-actions
- .btn-group{"v-if" => "file.type === 'text'"}
+ .btn-group{ "v-if" => "file.type === 'text'" }
%button.btn{ ":class" => "{ 'active': file.resolveMode == 'interactive' }",
'@click' => "onClickResolveModeButton(file, 'interactive')",
type: 'button' }
@@ -8,5 +8,5 @@
'@click' => "onClickResolveModeButton(file, 'edit')",
type: 'button' }
Edit inline
- %a.btn.view-file.btn-file-option{":href" => "file.blobPath"}
+ %a.btn.view-file.btn-file-option{ ":href" => "file.blobPath" }
View file @{{conflictsData.shortCommitSha}}
diff --git a/app/views/projects/merge_requests/conflicts/_submit_form.html.haml b/app/views/projects/merge_requests/conflicts/_submit_form.html.haml
index 6ffaa9ad4d2..62c9748c510 100644
--- a/app/views/projects/merge_requests/conflicts/_submit_form.html.haml
+++ b/app/views/projects/merge_requests/conflicts/_submit_form.html.haml
@@ -10,7 +10,7 @@
.col-sm-offset-2.col-sm-10
.row
.col-xs-6
- %button{ type: "button", class: "btn btn-success js-submit-button", "@click" => "commit()", ":disabled" => "!readyToCommit" }
+ %button.btn.btn-success.js-submit-button{ type: "button", "@click" => "commit()", ":disabled" => "!readyToCommit" }
%span {{commitButtonText}}
.col-xs-6.text-right
= link_to "Cancel", namespace_project_merge_request_path(@merge_request.project.namespace, @merge_request.project, @merge_request), class: "btn btn-cancel"
diff --git a/app/views/projects/merge_requests/conflicts/components/_diff_file_editor.html.haml b/app/views/projects/merge_requests/conflicts/components/_diff_file_editor.html.haml
index 3c927d362c2..aff3fb82fa6 100644
--- a/app/views/projects/merge_requests/conflicts/components/_diff_file_editor.html.haml
+++ b/app/views/projects/merge_requests/conflicts/components/_diff_file_editor.html.haml
@@ -1,4 +1,4 @@
-%diff-file-editor{"inline-template" => "true", ":file" => "file", ":on-cancel-discard-confirmation" => "cancelDiscardConfirmation", ":on-accept-discard-confirmation" => "acceptDiscardConfirmation"}
+%diff-file-editor{ "inline-template" => "true", ":file" => "file", ":on-cancel-discard-confirmation" => "cancelDiscardConfirmation", ":on-accept-discard-confirmation" => "acceptDiscardConfirmation" }
.diff-editor-wrap{ "v-show" => "file.showEditor" }
.discard-changes-alert-wrap{ "v-if" => "file.promptDiscardConfirmation" }
.discard-changes-alert
diff --git a/app/views/projects/merge_requests/conflicts/components/_inline_conflict_lines.html.haml b/app/views/projects/merge_requests/conflicts/components/_inline_conflict_lines.html.haml
index d35c7bee163..d828cb6cf9e 100644
--- a/app/views/projects/merge_requests/conflicts/components/_inline_conflict_lines.html.haml
+++ b/app/views/projects/merge_requests/conflicts/components/_inline_conflict_lines.html.haml
@@ -1,14 +1,14 @@
-%inline-conflict-lines{ "inline-template" => "true", ":file" => "file"}
+%inline-conflict-lines{ "inline-template" => "true", ":file" => "file" }
%table
- %tr.line_holder.diff-inline{"v-for" => "line in file.inlineLines"}
- %td.diff-line-num.new_line{":class" => "lineCssClass(line)", "v-if" => "!line.isHeader"}
+ %tr.line_holder.diff-inline{ "v-for" => "line in file.inlineLines" }
+ %td.diff-line-num.new_line{ ":class" => "lineCssClass(line)", "v-if" => "!line.isHeader" }
%a {{line.new_line}}
- %td.diff-line-num.old_line{":class" => "lineCssClass(line)", "v-if" => "!line.isHeader"}
+ %td.diff-line-num.old_line{ ":class" => "lineCssClass(line)", "v-if" => "!line.isHeader" }
%a {{line.old_line}}
- %td.line_content{":class" => "lineCssClass(line)", "v-if" => "!line.isHeader", "v-html" => "line.richText"}
- %td.diff-line-num.header{":class" => "lineCssClass(line)", "v-if" => "line.isHeader"}
- %td.diff-line-num.header{":class" => "lineCssClass(line)", "v-if" => "line.isHeader"}
- %td.line_content.header{":class" => "lineCssClass(line)", "v-if" => "line.isHeader"}
- %strong{"v-html" => "line.richText"}
+ %td.line_content{ ":class" => "lineCssClass(line)", "v-if" => "!line.isHeader", "v-html" => "line.richText" }
+ %td.diff-line-num.header{ ":class" => "lineCssClass(line)", "v-if" => "line.isHeader" }
+ %td.diff-line-num.header{ ":class" => "lineCssClass(line)", "v-if" => "line.isHeader" }
+ %td.line_content.header{ ":class" => "lineCssClass(line)", "v-if" => "line.isHeader" }
+ %strong{ "v-html" => "line.richText" }
%button.btn{ "@click" => "handleSelected(file, line.id, line.section)" }
{{line.buttonTitle}}
diff --git a/app/views/projects/merge_requests/show/_how_to_merge.html.haml b/app/views/projects/merge_requests/show/_how_to_merge.html.haml
index f1d5441f9dd..ec76c6a5417 100644
--- a/app/views/projects/merge_requests/show/_how_to_merge.html.haml
+++ b/app/views/projects/merge_requests/show/_how_to_merge.html.haml
@@ -1,8 +1,8 @@
-%div#modal_merge_info.modal
+#modal_merge_info.modal
.modal-dialog
.modal-content
.modal-header
- %a.close{href: "#", "data-dismiss" => "modal"} ×
+ %a.close{ href: "#", "data-dismiss" => "modal" } ×
%h3 Check out, review, and merge locally
.modal-body
%p
diff --git a/app/views/projects/merge_requests/show/_mr_box.html.haml b/app/views/projects/merge_requests/show/_mr_box.html.haml
index ed23d06ee5e..683cb8a5a27 100644
--- a/app/views/projects/merge_requests/show/_mr_box.html.haml
+++ b/app/views/projects/merge_requests/show/_mr_box.html.haml
@@ -4,7 +4,7 @@
%div
- if @merge_request.description.present?
- .description{class: can?(current_user, :update_merge_request, @merge_request) ? 'js-task-list-container' : ''}
+ .description{ class: can?(current_user, :update_merge_request, @merge_request) ? 'js-task-list-container' : '' }
.wiki
= preserve do
= markdown_field(@merge_request, :description)
diff --git a/app/views/projects/merge_requests/show/_versions.html.haml b/app/views/projects/merge_requests/show/_versions.html.haml
index 5cc92595fe0..ac4a03220b9 100644
--- a/app/views/projects/merge_requests/show/_versions.html.haml
+++ b/app/views/projects/merge_requests/show/_versions.html.haml
@@ -1,6 +1,6 @@
- if @merge_request_diffs.size > 1
.mr-version-controls
- %div.mr-version-menus-container.content-block
+ .mr-version-menus-container.content-block
Changes between
%span.dropdown.inline.mr-version-dropdown
%a.dropdown-toggle.btn.btn-default{ data: {toggle: :dropdown} }
@@ -13,7 +13,7 @@
.dropdown-menu.dropdown-select.dropdown-menu-selectable
.dropdown-title
%span Version:
- %button.dropdown-title-button.dropdown-menu-close{aria: {label: "Close"}}
+ %button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } }
= icon('times', class: 'dropdown-menu-close-icon')
.dropdown-content
%ul
@@ -43,7 +43,7 @@
.dropdown-menu.dropdown-select.dropdown-menu-selectable
.dropdown-title
%span Compared with:
- %button.dropdown-title-button.dropdown-menu-close{aria: {label: "Close"}}
+ %button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } }
= icon('times', class: 'dropdown-menu-close-icon')
.dropdown-content
%ul
diff --git a/app/views/projects/merge_requests/widget/_closed.html.haml b/app/views/projects/merge_requests/widget/_closed.html.haml
index f3cc0e7e8a1..15f47ecf210 100644
--- a/app/views/projects/merge_requests/widget/_closed.html.haml
+++ b/app/views/projects/merge_requests/widget/_closed.html.haml
@@ -6,7 +6,7 @@
- if @merge_request.closed_event
by #{link_to_member(@project, @merge_request.closed_event.author, avatar: true)}
#{time_ago_with_tooltip(@merge_request.closed_event.created_at)}
- %p
+ %p
= succeed '.' do
The changes were not merged into
%span.label-branch= @merge_request.target_branch
diff --git a/app/views/projects/merge_requests/widget/_heading.html.haml b/app/views/projects/merge_requests/widget/_heading.html.haml
index 5bc417d1760..c80dc33058d 100644
--- a/app/views/projects/merge_requests/widget/_heading.html.haml
+++ b/app/views/projects/merge_requests/widget/_heading.html.haml
@@ -17,7 +17,7 @@
- # TODO, remove in later versions when services like Jenkins will set CI status via Commit status API
.mr-widget-heading
- %w[success skipped canceled failed running pending].each do |status|
- .ci_widget{class: "ci-#{status} ci-status-icon-#{status}", style: "display:none"}
+ .ci_widget{ class: "ci-#{status} ci-status-icon-#{status}", style: "display:none" }
= ci_icon_for_status(status)
%span
CI build
@@ -32,11 +32,11 @@
= icon("spinner spin")
Checking CI status for #{@merge_request.diff_head_commit.short_id}&hellip;
- .ci_widget.ci-not_found{style: "display:none"}
+ .ci_widget.ci-not_found{ style: "display:none" }
= icon("times-circle")
Could not find CI status for #{@merge_request.diff_head_commit.short_id}.
- .ci_widget.ci-error{style: "display:none"}
+ .ci_widget.ci-error{ style: "display:none" }
= icon("times-circle")
Could not connect to the CI server. Please check your settings and try again.
diff --git a/app/views/projects/merge_requests/widget/_open.html.haml b/app/views/projects/merge_requests/widget/_open.html.haml
index f4aa1609a1b..c0d6ab669b8 100644
--- a/app/views/projects/merge_requests/widget/_open.html.haml
+++ b/app/views/projects/merge_requests/widget/_open.html.haml
@@ -42,6 +42,4 @@
- if mr_issues_mentioned_but_not_closing.present?
#{"Issue".pluralize(mr_issues_mentioned_but_not_closing.size)}
!= markdown issues_sentence(mr_issues_mentioned_but_not_closing), pipeline: :gfm, author: @merge_request.author
- #{mr_issues_mentioned_but_not_closing.size > 1 ? 'are' : 'is'} mentioned but will not closed.
-
-
+ #{mr_issues_mentioned_but_not_closing.size > 1 ? 'are' : 'is'} mentioned but will not be closed.
diff --git a/app/views/projects/merge_requests/widget/open/_archived.html.haml b/app/views/projects/merge_requests/widget/open/_archived.html.haml
index ab30fa6b243..0d61e56d8fb 100644
--- a/app/views/projects/merge_requests/widget/open/_archived.html.haml
+++ b/app/views/projects/merge_requests/widget/open/_archived.html.haml
@@ -1,4 +1,4 @@
-%h4
+%h4
Project is archived
%p
This merge request cannot be merged because archived projects cannot be written to.
diff --git a/app/views/projects/merge_requests/widget/open/_conflicts.html.haml b/app/views/projects/merge_requests/widget/open/_conflicts.html.haml
index af3096f04d9..c98b2c42597 100644
--- a/app/views/projects/merge_requests/widget/open/_conflicts.html.haml
+++ b/app/views/projects/merge_requests/widget/open/_conflicts.html.haml
@@ -1,21 +1,23 @@
+- can_resolve = @merge_request.conflicts_can_be_resolved_by?(current_user)
+- can_resolve_in_ui = @merge_request.conflicts_can_be_resolved_in_ui?
+- can_merge = @merge_request.can_be_merged_via_command_line_by?(current_user)
+
%h4.has-conflicts
= icon("exclamation-triangle")
This merge request contains merge conflicts
%p
- Please
- - if @merge_request.conflicts_can_be_resolved_by?(current_user)
- - if @merge_request.conflicts_can_be_resolved_in_ui?
- = link_to "resolve these conflicts", conflicts_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)
- - else
- %span.has-tooltip{title: "These conflicts cannot be resolved through GitLab"}
- resolve these conflicts locally
- - else
- resolve these conflicts
-
+ To merge this request, resolve these conflicts
+ - if can_resolve && !can_resolve_in_ui
+ locally
or
+ - unless can_merge
+ ask someone with write access to this repository to
+ merge it locally.
- - if @merge_request.can_be_merged_via_command_line_by?(current_user)
- #{link_to "merge this request manually", "#modal_merge_info", class: "how_to_merge_link vlink", "data-toggle" => "modal"}.
- - else
- ask someone with write access to this repository to merge this request manually.
+- if (can_resolve && can_resolve_in_ui) || can_merge
+ .btn-group
+ - if can_resolve && can_resolve_in_ui
+ = link_to "Resolve conflicts", conflicts_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: "btn"
+ - if can_merge
+ = link_to "Merge locally", "#modal_merge_info", class: "btn how_to_merge_link vlink", "data-toggle" => "modal"
diff --git a/app/views/projects/merge_requests/widget/open/_nothing.html.haml b/app/views/projects/merge_requests/widget/open/_nothing.html.haml
index 35626b624b7..7af8c01c134 100644
--- a/app/views/projects/merge_requests/widget/open/_nothing.html.haml
+++ b/app/views/projects/merge_requests/widget/open/_nothing.html.haml
@@ -1,4 +1,4 @@
-%h4
+%h4
= icon("exclamation-triangle")
Nothing to merge from
%span.label-branch= source_branch_with_namespace(@merge_request)
diff --git a/app/views/projects/milestones/_form.html.haml b/app/views/projects/milestones/_form.html.haml
index 513710e8e66..0f4a8508751 100644
--- a/app/views/projects/milestones/_form.html.haml
+++ b/app/views/projects/milestones/_form.html.haml
@@ -20,6 +20,6 @@
- if @milestone.new_record?
= f.submit 'Create milestone', class: "btn-create btn"
= link_to "Cancel", namespace_project_milestones_path(@project.namespace, @project), class: "btn btn-cancel"
- -else
+ - else
= f.submit 'Save changes', class: "btn-save btn"
= link_to "Cancel", namespace_project_milestone_path(@project.namespace, @project, @milestone), class: "btn btn-cancel"
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index 866b278ce57..064e92b15eb 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -77,7 +77,7 @@
- if git_import_enabled?
= link_to "#", class: 'btn js-toggle-button import_git' do
= icon('git', text: 'Repo by URL')
- %div{ class: 'import_gitlab_project' }
+ .import_gitlab_project
- if gitlab_project_import_enabled?
= link_to new_import_gitlab_project_path, class: 'btn btn_import_gitlab_project project-submit' do
= icon('gitlab', text: 'GitLab export')
diff --git a/app/views/projects/notes/_form.html.haml b/app/views/projects/notes/_form.html.haml
index 46b402545cd..39731668a61 100644
--- a/app/views/projects/notes/_form.html.haml
+++ b/app/views/projects/notes/_form.html.haml
@@ -23,5 +23,5 @@
.note-form-actions.clearfix
= f.submit 'Comment', class: "btn btn-nr btn-create append-right-10 comment-btn js-comment-button"
= yield(:note_actions)
- %a.btn.btn-cancel.js-note-discard{role: "button", data: {cancel_text: "Cancel"}}
+ %a.btn.btn-cancel.js-note-discard{ role: "button", data: {cancel_text: "Cancel" } }
Discard draft
diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml
index 399cf85cd0f..eb869ea85bf 100644
--- a/app/views/projects/notes/_note.html.haml
+++ b/app/views/projects/notes/_note.html.haml
@@ -5,7 +5,7 @@
%li.timeline-entry{ id: dom_id(note), class: ["note", "note-row-#{note.id}", ('system-note' if note.system)], data: {author_id: note.author.id, editable: note_editable} }
.timeline-entry-inner
.timeline-icon
- %a{href: user_path(note.author)}
+ %a{ href: user_path(note.author) }
= image_tag avatar_icon(note.author), alt: '', class: 'avatar s40'
.timeline-content
.note-header
@@ -15,7 +15,7 @@
- unless note.system
commented
- if note.system
- %span{class: 'system-note-message'}
+ %span.system-note-message
= note.redacted_note_html
%a{ href: "##{dom_id(note)}" }
= time_ago_with_tooltip(note.created_at, placement: 'bottom', html_class: 'note-created-ago')
@@ -61,7 +61,7 @@
= icon('pencil', class: 'link-highlight')
= link_to namespace_project_note_path(note.project.namespace, note.project, note), title: 'Remove comment', method: :delete, data: { confirm: 'Are you sure you want to remove this comment?' }, remote: true, class: 'note-action-button hidden-xs js-note-delete danger' do
= icon('trash-o', class: 'danger-highlight')
- .note-body{class: note_editable ? 'js-task-list-container' : ''}
+ .note-body{ class: note_editable ? 'js-task-list-container' : '' }
.note-text.md
= preserve do
= note.redacted_note_html
diff --git a/app/views/projects/pipelines/_info.html.haml b/app/views/projects/pipelines/_info.html.haml
index b00ba2d5307..ca76f13ef5e 100644
--- a/app/views/projects/pipelines/_info.html.haml
+++ b/app/views/projects/pipelines/_info.html.haml
@@ -23,7 +23,7 @@
.info-well
- if @commit.status
.well-segment.pipeline-info
- %div{class: "icon-container ci-status-icon-#{@commit.status}"}
+ %div{ class: "icon-container ci-status-icon-#{@commit.status}" }
= ci_icon_for_status(@commit.status)
= pluralize @pipeline.statuses.count(:id), "build"
- if @pipeline.ref
diff --git a/app/views/projects/pipelines/index.html.haml b/app/views/projects/pipelines/index.html.haml
index e423fc8c471..b0ba7824514 100644
--- a/app/views/projects/pipelines/index.html.haml
+++ b/app/views/projects/pipelines/index.html.haml
@@ -5,23 +5,23 @@
%div{ class: container_class }
.top-area
%ul.nav-links
- %li{class: ('active' if @scope.nil?)}>
+ %li{ class: ('active' if @scope.nil?) }>
= link_to project_pipelines_path(@project) do
All
%span.badge.js-totalbuilds-count
= number_with_delimiter(@pipelines_count)
- %li{class: ('active' if @scope == 'running')}>
+ %li{ class: ('active' if @scope == 'running') }>
= link_to project_pipelines_path(@project, scope: :running) do
Running
%span.badge.js-running-count
= number_with_delimiter(@running_or_pending_count)
- %li{class: ('active' if @scope == 'branches')}>
+ %li{ class: ('active' if @scope == 'branches') }>
= link_to project_pipelines_path(@project, scope: :branches) do
Branches
- %li{class: ('active' if @scope == 'tags')}>
+ %li{ class: ('active' if @scope == 'tags') }>
= link_to project_pipelines_path(@project, scope: :tags) do
Tags
diff --git a/app/views/projects/pipelines/show.html.haml b/app/views/projects/pipelines/show.html.haml
index 29a41bc664b..49c1d886423 100644
--- a/app/views/projects/pipelines/show.html.haml
+++ b/app/views/projects/pipelines/show.html.haml
@@ -2,7 +2,7 @@
- page_title "Pipeline"
= render "projects/pipelines/head"
-%div.js-pipeline-container{ class: container_class, data: { controller_action: "#{controller.action_name}" } }
+.js-pipeline-container{ class: container_class, data: { controller_action: "#{controller.action_name}" } }
- if @commit
= render "projects/pipelines/info"
diff --git a/app/views/projects/runners/_runner.html.haml b/app/views/projects/runners/_runner.html.haml
index 6e58e5a0c78..7036b8a5ccc 100644
--- a/app/views/projects/runners/_runner.html.haml
+++ b/app/views/projects/runners/_runner.html.haml
@@ -1,4 +1,4 @@
-%li.runner{id: dom_id(runner)}
+%li.runner{ id: dom_id(runner) }
%h4
= runner_status_icon(runner)
%span.monospace
diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml
index a915c159cb4..80d4081dd7b 100644
--- a/app/views/projects/show.html.haml
+++ b/app/views/projects/show.html.haml
@@ -17,10 +17,10 @@
%ul.nav
%li
= link_to project_files_path(@project) do
- Files (#{repository_size})
+ Files (#{storage_counter(@project.statistics.total_repository_size)})
%li
= link_to namespace_project_commits_path(@project.namespace, @project, current_ref) do
- #{'Commit'.pluralize(@project.commit_count)} (#{number_with_delimiter(@project.commit_count)})
+ #{'Commit'.pluralize(@project.statistics.commit_count)} (#{number_with_delimiter(@project.statistics.commit_count)})
%li
= link_to namespace_project_branches_path(@project.namespace, @project) do
#{'Branch'.pluralize(@repository.branch_count)} (#{number_with_delimiter(@repository.branch_count)})
@@ -70,8 +70,8 @@
= link_to 'Set up Koding', add_koding_stack_path(@project)
- if @repository.gitlab_ci_yml.blank? && @project.deployment_service.present?
%li.missing
- = link_to add_special_file_path(@project, file_name: '.gitlab-ci.yml', commit_message: 'Set up autodeploy', target_branch: 'autodeploy', context: 'autodeploy') do
- Set up autodeploy
+ = link_to add_special_file_path(@project, file_name: '.gitlab-ci.yml', commit_message: 'Set up auto deploy', target_branch: 'auto-deploy', context: 'autodeploy') do
+ Set up auto deploy
- if @repository.commit
.project-last-commit{ class: container_class }
diff --git a/app/views/projects/snippets/show.html.haml b/app/views/projects/snippets/show.html.haml
index 79d87b7db12..485b23815bc 100644
--- a/app/views/projects/snippets/show.html.haml
+++ b/app/views/projects/snippets/show.html.haml
@@ -15,4 +15,4 @@
.row-content-block.top-block.content-component-block
= render 'award_emoji/awards_block', awardable: @snippet, inline: true
- %div#notes= render "projects/notes/notes_with_form"
+ #notes= render "projects/notes/notes_with_form"
diff --git a/app/views/projects/stage/_graph.html.haml b/app/views/projects/stage/_graph.html.haml
index 6919b210a00..d9d392fa02f 100644
--- a/app/views/projects/stage/_graph.html.haml
+++ b/app/views/projects/stage/_graph.html.haml
@@ -10,11 +10,10 @@
- status_groups.each do |group_name, grouped_statuses|
- if grouped_statuses.one?
- status = grouped_statuses.first
- %li.build{ 'id' => "ci-badge-#{group_name}" }
+ %li.build{ 'id' => "ci-badge-#{group_name}" }
.curve
= render 'ci/status/graph_badge', subject: status
- else
- %li.build{ 'id' => "ci-badge-#{group_name}" }
+ %li.build{ 'id' => "ci-badge-#{group_name}" }
.curve
= render 'projects/stage/in_stage_group', name: group_name, subject: grouped_statuses
-
diff --git a/app/views/projects/stage/_in_stage_group.html.haml b/app/views/projects/stage/_in_stage_group.html.haml
index 65e5f31e86c..c4cb9ab50d0 100644
--- a/app/views/projects/stage/_in_stage_group.html.haml
+++ b/app/views/projects/stage/_in_stage_group.html.haml
@@ -1,6 +1,6 @@
- group_status = CommitStatus.where(id: subject).status
%button.dropdown-menu-toggle.build-content.has-tooltip{ type: 'button', data: { toggle: 'dropdown', title: "#{name} - #{group_status}" } }
- %span{class: "ci-status-icon ci-status-icon-#{group_status}"}
+ %span{ class: "ci-status-icon ci-status-icon-#{group_status}" }
= ci_icon_for_status(group_status)
%span.ci-status-text
= name
diff --git a/app/views/projects/stage/_stage.html.haml b/app/views/projects/stage/_stage.html.haml
index 1684e02fbad..28e1c060875 100644
--- a/app/views/projects/stage/_stage.html.haml
+++ b/app/views/projects/stage/_stage.html.haml
@@ -1,13 +1,13 @@
%tr
- %th{colspan: 10}
+ %th{ colspan: 10 }
%strong
%a{ name: stage.name }
- %span{class: "ci-status-link ci-status-icon-#{stage.status}"}
+ %span{ class: "ci-status-link ci-status-icon-#{stage.status}" }
= ci_icon_for_status(stage.status)
&nbsp;
= stage.name.titleize
= render stage.statuses.latest_ordered, coverage: @project.build_coverage_enabled?, stage: false, ref: false, pipeline_link: false, allow_retry: true
= render stage.statuses.retried_ordered, coverage: @project.build_coverage_enabled?, stage: false, ref: false, pipeline_link: false, retried: true
%tr
- %td{colspan: 10}
+ %td{ colspan: 10 }
&nbsp;
diff --git a/app/views/projects/tags/_tag.html.haml b/app/views/projects/tags/_tag.html.haml
index c42641afea0..8ef069b9e05 100644
--- a/app/views/projects/tags/_tag.html.haml
+++ b/app/views/projects/tags/_tag.html.haml
@@ -1,7 +1,7 @@
- commit = @repository.commit(tag.dereferenced_target)
- release = @releases.find { |release| release.tag == tag.name }
-%li
- %div
+%li.flex-row
+ .row-main-content.str-truncated
= link_to namespace_project_tag_path(@project.namespace, @project, tag.name) do
%span.item-title
= icon('tag')
@@ -10,24 +10,25 @@
&nbsp;
= strip_gpg_signature(tag.message)
- .controls
- = render 'projects/buttons/download', project: @project, ref: tag.name
+ - if commit
+ .block-truncated
+ = render 'projects/branches/commit', commit: commit, project: @project
+ - else
+ %p
+ Cant find HEAD commit for this tag
+ - if release && release.description.present?
+ .description.prepend-top-default
+ .wiki
+ = preserve do
+ = markdown_field(release, :description)
- - if can?(current_user, :push_code, @project)
- = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, tag.name), class: 'btn has-tooltip', title: "Edit release notes", data: { container: "body" } do
- = icon("pencil")
+ .row-fixed-content.controls
+ = render 'projects/buttons/download', project: @project, ref: tag.name
- - if can?(current_user, :admin_project, @project)
- = link_to namespace_project_tag_path(@project.namespace, @project, tag.name), class: 'btn btn-remove remove-row has-tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{tag.name}' tag cannot be undone. Are you sure?", container: 'body' }, remote: true do
- = icon("trash-o")
+ - if can?(current_user, :push_code, @project)
+ = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, tag.name), class: 'btn has-tooltip', title: "Edit release notes", data: { container: "body" } do
+ = icon("pencil")
- - if commit
- = render 'projects/branches/commit', commit: commit, project: @project
- - else
- %p
- Cant find HEAD commit for this tag
- - if release && release.description.present?
- .description.prepend-top-default
- .wiki
- = preserve do
- = markdown_field(release, :description)
+ - if can?(current_user, :admin_project, @project)
+ = link_to namespace_project_tag_path(@project.namespace, @project, tag.name), class: 'btn btn-remove remove-row has-tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{tag.name}' tag cannot be undone. Are you sure?", container: 'body' }, remote: true do
+ = icon("trash-o")
diff --git a/app/views/projects/tags/index.html.haml b/app/views/projects/tags/index.html.haml
index 1d39f3a7534..e2f132f7742 100644
--- a/app/views/projects/tags/index.html.haml
+++ b/app/views/projects/tags/index.html.haml
@@ -2,16 +2,16 @@
- page_title "Tags"
= render "projects/commits/head"
-%div{ class: container_class }
- .top-area
- .nav-text
+.flex-list{ class: container_class }
+ .top-area.flex-row
+ .nav-text.row-main-content
Tags give the ability to mark specific points in history as being important
- .nav-controls
+ .nav-controls.row-fixed-content
= form_tag(filter_tags_path, method: :get) do
= search_field_tag :search, params[:search], { placeholder: 'Filter by tag name', id: 'tag-search', class: 'form-control search-text-input input-short', spellcheck: false }
- .dropdown.inline
+ .dropdown
%button.dropdown-toggle{ type: 'button', data: { toggle: 'dropdown'} }
%span.light
= projects_sort_options_hash[@sort]
@@ -30,7 +30,7 @@
.tags
- if @tags.any?
- %ul.content-list
+ %ul.flex-list.content-list
= render partial: 'tag', collection: @tags
= paginate @tags, theme: 'gitlab'
diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml
index c06a413eb2f..160d4c7a223 100644
--- a/app/views/projects/tags/new.html.haml
+++ b/app/views/projects/tags/new.html.haml
@@ -2,7 +2,7 @@
- if @error
.alert.alert-danger
- %button{ type: "button", class: "close", "data-dismiss" => "alert"} &times;
+ %button.close{ type: "button", "data-dismiss" => "alert" } &times;
= @error
%h3.page-title
diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml
index 12facb6eb73..fad3c5c2173 100644
--- a/app/views/projects/tags/show.html.haml
+++ b/app/views/projects/tags/show.html.haml
@@ -12,21 +12,23 @@
- else
Cant find HEAD commit for this tag
- .nav-controls
+ .nav-controls.controls-flex
- if can?(current_user, :push_code, @project)
- = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, @tag.name), class: 'btn has-tooltip', title: 'Edit release notes' do
+ = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, @tag.name), class: 'btn controls-item has-tooltip', title: 'Edit release notes' do
= icon("pencil")
- = link_to namespace_project_tree_path(@project.namespace, @project, @tag.name), class: 'btn has-tooltip', title: 'Browse files' do
+ = link_to namespace_project_tree_path(@project.namespace, @project, @tag.name), class: 'btn controls-item has-tooltip', title: 'Browse files' do
= icon('files-o')
- = link_to namespace_project_commits_path(@project.namespace, @project, @tag.name), class: 'btn has-tooltip', title: 'Browse commits' do
+ = link_to namespace_project_commits_path(@project.namespace, @project, @tag.name), class: 'btn controls-item has-tooltip', title: 'Browse commits' do
= icon('history')
- = render 'projects/buttons/download', project: @project, ref: @tag.name
+ .btn-container.controls-item
+ = render 'projects/buttons/download', project: @project, ref: @tag.name
- if can?(current_user, :admin_project, @project)
- .pull-right
+ .btn-container.controls-item-full
= link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: 'btn btn-remove remove-row has-tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{@tag.name}' tag cannot be undone. Are you sure?" } do
%i.fa.fa-trash-o
+
- if @tag.message.present?
- %pre.body
+ %pre.wrap
= strip_gpg_signature(@tag.message)
.append-bottom-default.prepend-top-default
diff --git a/app/views/projects/tree/_blob_item.html.haml b/app/views/projects/tree/_blob_item.html.haml
index ee417b58cbf..425b460eb09 100644
--- a/app/views/projects/tree/_blob_item.html.haml
+++ b/app/views/projects/tree/_blob_item.html.haml
@@ -6,4 +6,4 @@
%span.str-truncated= file_name
%td.hidden-xs.tree-commit
%td.tree-time-ago.cgray.text-right
- = render 'projects/tree/spinner' \ No newline at end of file
+ = render 'projects/tree/spinner'
diff --git a/app/views/projects/tree/_submodule_item.html.haml b/app/views/projects/tree/_submodule_item.html.haml
index 2b5f671c09e..04d52361db0 100644
--- a/app/views/projects/tree/_submodule_item.html.haml
+++ b/app/views/projects/tree/_submodule_item.html.haml
@@ -1,4 +1,4 @@
-%tr{ class: "tree-item" }
+%tr.tree-item
%td.tree-item-file-name
%i.fa.fa-archive.fa-fw
= submodule_link(submodule_item, @ref)
diff --git a/app/views/projects/tree/_tree_content.html.haml b/app/views/projects/tree/_tree_content.html.haml
index d37c376c36b..038a960bd0c 100644
--- a/app/views/projects/tree/_tree_content.html.haml
+++ b/app/views/projects/tree/_tree_content.html.haml
@@ -1,13 +1,13 @@
-%div.tree-content-holder
+.tree-content-holder
.table-holder
- %table.table#tree-slider{class: "table_#{@hex_path} tree-table" }
+ %table.table#tree-slider{ class: "table_#{@hex_path} tree-table" }
%thead
%tr
%th Name
%th.hidden-xs
.pull-left Last commit
.last-commit.hidden-sm.pull-left
- %small.light
+ %small.light
= clipboard_button(clipboard_text: @commit.id)
= link_to @commit.short_id, namespace_project_commit_path(@project.namespace, @project, @commit), class: "monospace"
= time_ago_with_tooltip(@commit.committed_date)
diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml
index 1c5f8b3928b..259207a6dfd 100644
--- a/app/views/projects/tree/_tree_header.html.haml
+++ b/app/views/projects/tree/_tree_header.html.haml
@@ -15,11 +15,11 @@
- if current_user
%li
- if !on_top_of_branch?
- %span.btn.add-to-tree.disabled.has-tooltip{title: "You can only add files when you are on a branch", data: { container: 'body' }}
+ %span.btn.add-to-tree.disabled.has-tooltip{ title: "You can only add files when you are on a branch", data: { container: 'body' } }
= icon('plus')
- else
%span.dropdown
- %a.dropdown-toggle.btn.add-to-tree{href: '#', "data-toggle" => "dropdown"}
+ %a.dropdown-toggle.btn.add-to-tree{ href: '#', "data-toggle" => "dropdown" }
= icon('plus')
%ul.dropdown-menu
- if can_edit_tree?
@@ -28,11 +28,11 @@
= icon('pencil fw')
New file
%li
- = link_to '#modal-upload-blob', { 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal'} do
+ = link_to '#modal-upload-blob', { 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal' } do
= icon('file fw')
Upload file
%li
- = link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal'} do
+ = link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal' } do
= icon('folder fw')
New directory
- elsif can?(current_user, :fork_project, @project)
diff --git a/app/views/projects/tree/_tree_item.html.haml b/app/views/projects/tree/_tree_item.html.haml
index 1ccef6d52ab..15c9536133c 100644
--- a/app/views/projects/tree/_tree_item.html.haml
+++ b/app/views/projects/tree/_tree_item.html.haml
@@ -6,4 +6,4 @@
%span.str-truncated= path
%td.hidden-xs.tree-commit
%td.tree-time-ago.text-right
- = render 'projects/tree/spinner' \ No newline at end of file
+ = render 'projects/tree/spinner'
diff --git a/app/views/projects/variables/index.html.haml b/app/views/projects/variables/index.html.haml
index 39303700131..cf7ae0b489f 100644
--- a/app/views/projects/variables/index.html.haml
+++ b/app/views/projects/variables/index.html.haml
@@ -15,4 +15,4 @@
No variables found, add one with the form above.
- else
= render "table"
- %button.btn.btn-info.js-btn-toggle-reveal-values{"data-status" => 'hidden'} Reveal Values
+ %button.btn.btn-info.js-btn-toggle-reveal-values{ "data-status" => 'hidden' } Reveal Values
diff --git a/app/views/projects/wikis/_new.html.haml b/app/views/projects/wikis/_new.html.haml
index c32cb122c26..c74f53b4c39 100644
--- a/app/views/projects/wikis/_new.html.haml
+++ b/app/views/projects/wikis/_new.html.haml
@@ -1,11 +1,11 @@
- @no_container = true
%div{ class: container_class }
- %div#modal-new-wiki.modal
+ #modal-new-wiki.modal
.modal-dialog
.modal-content
.modal-header
- %a.close{href: "#", "data-dismiss" => "modal"} ×
+ %a.close{ href: "#", "data-dismiss" => "modal" } ×
%h3.page-title New Wiki Page
.modal-body
%form.new-wiki-page
diff --git a/app/views/search/_category.html.haml b/app/views/search/_category.html.haml
index 2c378231237..8cbecb725b5 100644
--- a/app/views/search/_category.html.haml
+++ b/app/views/search/_category.html.haml
@@ -1,70 +1,70 @@
%ul.nav-links.search-filter
- if @project
- %li{class: ("active" if @scope == 'blobs')}
+ %li{ class: ("active" if @scope == 'blobs') }
= link_to search_filter_path(scope: 'blobs') do
Code
%span.badge
= @search_results.blobs_count
- %li{class: ("active" if @scope == 'issues')}
+ %li{ class: ("active" if @scope == 'issues') }
= link_to search_filter_path(scope: 'issues') do
Issues
%span.badge
= @search_results.issues_count
- %li{class: ("active" if @scope == 'merge_requests')}
+ %li{ class: ("active" if @scope == 'merge_requests') }
= link_to search_filter_path(scope: 'merge_requests') do
Merge requests
%span.badge
= @search_results.merge_requests_count
- %li{class: ("active" if @scope == 'milestones')}
+ %li{ class: ("active" if @scope == 'milestones') }
= link_to search_filter_path(scope: 'milestones') do
Milestones
%span.badge
= @search_results.milestones_count
- %li{class: ("active" if @scope == 'notes')}
+ %li{ class: ("active" if @scope == 'notes') }
= link_to search_filter_path(scope: 'notes') do
Comments
%span.badge
= @search_results.notes_count
- %li{class: ("active" if @scope == 'wiki_blobs')}
+ %li{ class: ("active" if @scope == 'wiki_blobs') }
= link_to search_filter_path(scope: 'wiki_blobs') do
Wiki
%span.badge
= @search_results.wiki_blobs_count
- %li{class: ("active" if @scope == 'commits')}
+ %li{ class: ("active" if @scope == 'commits') }
= link_to search_filter_path(scope: 'commits') do
Commits
%span.badge
= @search_results.commits_count
- elsif @show_snippets
- %li{class: ("active" if @scope == 'snippet_blobs')}
+ %li{ class: ("active" if @scope == 'snippet_blobs') }
= link_to search_filter_path(scope: 'snippet_blobs', snippets: true, group_id: nil, project_id: nil) do
Snippet Contents
%span.badge
= @search_results.snippet_blobs_count
- %li{class: ("active" if @scope == 'snippet_titles')}
+ %li{ class: ("active" if @scope == 'snippet_titles') }
= link_to search_filter_path(scope: 'snippet_titles', snippets: true, group_id: nil, project_id: nil) do
Titles and Filenames
%span.badge
= @search_results.snippet_titles_count
- else
- %li{class: ("active" if @scope == 'projects')}
+ %li{ class: ("active" if @scope == 'projects') }
= link_to search_filter_path(scope: 'projects') do
Projects
%span.badge
= @search_results.projects_count
- %li{class: ("active" if @scope == 'issues')}
+ %li{ class: ("active" if @scope == 'issues') }
= link_to search_filter_path(scope: 'issues') do
Issues
%span.badge
= @search_results.issues_count
- %li{class: ("active" if @scope == 'merge_requests')}
+ %li{ class: ("active" if @scope == 'merge_requests') }
= link_to search_filter_path(scope: 'merge_requests') do
Merge requests
%span.badge
= @search_results.merge_requests_count
- %li{class: ("active" if @scope == 'milestones')}
+ %li{ class: ("active" if @scope == 'milestones') }
= link_to search_filter_path(scope: 'milestones') do
Milestones
%span.badge
diff --git a/app/views/shared/_clone_panel.html.haml b/app/views/shared/_clone_panel.html.haml
index 3b82d8e686f..96b75440309 100644
--- a/app/views/shared/_clone_panel.html.haml
+++ b/app/views/shared/_clone_panel.html.haml
@@ -2,12 +2,12 @@
.git-clone-holder.input-group
.input-group-btn
- -if allowed_protocols_present?
+ - if allowed_protocols_present?
.clone-dropdown-btn.btn.btn-static
%span
= enabled_project_button(project, enabled_protocol)
- else
- %a#clone-dropdown.clone-dropdown-btn.btn{href: '#', data: { toggle: 'dropdown' }}
+ %a#clone-dropdown.clone-dropdown-btn.btn{ href: '#', data: { toggle: 'dropdown' } }
%span
= default_clone_protocol.upcase
= icon('caret-down')
diff --git a/app/views/shared/_confirm_modal.html.haml b/app/views/shared/_confirm_modal.html.haml
index b0fc60573f7..e7cb93b17a7 100644
--- a/app/views/shared/_confirm_modal.html.haml
+++ b/app/views/shared/_confirm_modal.html.haml
@@ -1,8 +1,8 @@
-#modal-confirm-danger.modal{tabindex: -1}
+#modal-confirm-danger.modal{ tabindex: -1 }
.modal-dialog
.modal-content
.modal-header
- %a.close{href: "#", "data-dismiss" => "modal"} ×
+ %a.close{ href: "#", "data-dismiss" => "modal" } ×
%h3.page-title
Confirmation required
diff --git a/app/views/shared/_file_highlight.html.haml b/app/views/shared/_file_highlight.html.haml
index e26693bf5b9..8d64cb5d698 100644
--- a/app/views/shared/_file_highlight.html.haml
+++ b/app/views/shared/_file_highlight.html.haml
@@ -9,8 +9,8 @@
- offset = defined?(first_line_number) ? first_line_number : 1
- i = index + offset
-# We're not using `link_to` because it is too slow once we get to thousands of lines.
- %a.diff-line-num{href: "#{link}#L#{i}", id: "L#{i}", 'data-line-number' => i}
+ %a.diff-line-num{ href: "#{link}#L#{i}", id: "L#{i}", 'data-line-number' => i }
= link_icon
= i
- .blob-content{data: {blob_id: blob.id}}
+ .blob-content{ data: { blob_id: blob.id } }
= highlight(blob.path, blob.data, repository: repository, plain: blob.no_highlighting?)
diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml
index db324d8868e..f11f4471a9d 100644
--- a/app/views/shared/_label.html.haml
+++ b/app/views/shared/_label.html.haml
@@ -4,7 +4,7 @@
- status = label_subscription_status(label, @project).inquiry if current_user
- subject = local_assigns[:subject]
-%li{id: label_css_id, data: { id: label.id } }
+%li{ id: label_css_id, data: { id: label.id } }
= render "shared/label_row", label: label
.visible-xs.visible-sm-inline-block.visible-md-inline-block.dropdown
@@ -56,7 +56,7 @@
= icon('spinner spin', class: 'label-subscribe-button-loading')
.dropdown.dropdown-group-label{ class: ('hidden' unless status.unsubscribed?) }
- %button.dropdown-menu-toggle{type: 'button', 'data-toggle' => 'dropdown'}
+ %button.dropdown-menu-toggle{ type: 'button', 'data-toggle' => 'dropdown' }
%span Subscribe
= icon('chevron-down')
%ul.dropdown-menu
diff --git a/app/views/shared/_milestones_filter.html.haml b/app/views/shared/_milestones_filter.html.haml
index 186ed4a7c8b..39294fe1a09 100644
--- a/app/views/shared/_milestones_filter.html.haml
+++ b/app/views/shared/_milestones_filter.html.haml
@@ -2,17 +2,17 @@
- counts = milestone_counts(@project.milestones)
%ul.nav-links
- %li{class: milestone_class_for_state(params[:state], 'opened', true)}>
+ %li{ class: milestone_class_for_state(params[:state], 'opened', true) }>
= link_to milestones_filter_path(state: 'opened') do
Open
- if @project
%span.badge #{counts[:opened]}
- %li{class: milestone_class_for_state(params[:state], 'closed')}>
+ %li{ class: milestone_class_for_state(params[:state], 'closed') }>
= link_to milestones_filter_path(state: 'closed') do
Closed
- if @project
%span.badge #{counts[:closed]}
- %li{class: milestone_class_for_state(params[:state], 'all')}>
+ %li{ class: milestone_class_for_state(params[:state], 'all') }>
= link_to milestones_filter_path(state: 'all') do
All
- if @project
diff --git a/app/views/shared/_nav_scroll.html.haml b/app/views/shared/_nav_scroll.html.haml
index 4e3b1b3a571..61646f150c1 100644
--- a/app/views/shared/_nav_scroll.html.haml
+++ b/app/views/shared/_nav_scroll.html.haml
@@ -1,4 +1,4 @@
.fade-left
= icon('angle-left')
.fade-right
- = icon('angle-right') \ No newline at end of file
+ = icon('angle-right')
diff --git a/app/views/shared/_sort_dropdown.html.haml b/app/views/shared/_sort_dropdown.html.haml
index ede3c7090d7..0ce0d759e86 100644
--- a/app/views/shared/_sort_dropdown.html.haml
+++ b/app/views/shared/_sort_dropdown.html.haml
@@ -1,5 +1,5 @@
.dropdown.inline.prepend-left-10
- %button.dropdown-toggle{type: 'button', data: {toggle: 'dropdown'}}
+ %button.dropdown-toggle{ type: 'button', data: {toggle: 'dropdown' } }
%span.light
- if @sort.present?
= sort_options_hash[@sort]
diff --git a/app/views/shared/groups/_group.html.haml b/app/views/shared/groups/_group.html.haml
index 8164f61797c..f9a7aa4e29b 100644
--- a/app/views/shared/groups/_group.html.haml
+++ b/app/views/shared/groups/_group.html.haml
@@ -21,7 +21,7 @@
= icon('users')
= number_with_delimiter(group.users.count)
- %span.visibility-icon.has-tooltip{data: { container: 'body', placement: 'left' }, title: visibility_icon_description(group)}
+ %span.visibility-icon.has-tooltip{ data: { container: 'body', placement: 'left' }, title: visibility_icon_description(group) }
= visibility_level_icon(group.visibility_level, fw: false)
.avatar-container.s40
diff --git a/app/views/shared/issuable/_filter.html.haml b/app/views/shared/issuable/_filter.html.haml
index e3503981afe..b42eaabb111 100644
--- a/app/views/shared/issuable/_filter.html.haml
+++ b/app/views/shared/issuable/_filter.html.haml
@@ -31,11 +31,11 @@
- if issuable_filter_present?
.filter-item.inline.reset-filters
- %a{href: page_filter_path(without: issuable_filter_params)} Reset filters
+ %a{ href: page_filter_path(without: issuable_filter_params) } Reset filters
.pull-right
- if boards_page
- #js-boards-seach.issue-boards-search
+ #js-boards-search.issue-boards-search
%input.pull-left.form-control{ type: "search", placeholder: "Filter by name...", "v-model" => "filters.search", "debounce" => "250" }
- if can?(current_user, :admin_list, @project)
.dropdown.pull-right
@@ -56,9 +56,9 @@
= dropdown_tag("Status", options: { toggle_class: "js-issue-status", title: "Change status", dropdown_class: "dropdown-menu-status dropdown-menu-selectable", data: { field_name: "update[state_event]" } } ) do
%ul
%li
- %a{href: "#", data: {id: "reopen"}} Open
+ %a{ href: "#", data: { id: "reopen" } } Open
%li
- %a{href: "#", data: {id: "close"}} Closed
+ %a{ href: "#", data: {id: "close" } } Closed
.filter-item.inline
= dropdown_tag("Assignee", options: { toggle_class: "js-user-search js-update-assignee js-filter-submit js-filter-bulk-update", title: "Assign to", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable",
placeholder: "Search authors", data: { first_user: (current_user.username if current_user), null_user: true, current_user: true, project_id: @project.id, field_name: "update[assignee_id]" } })
@@ -70,9 +70,9 @@
= dropdown_tag("Subscription", options: { toggle_class: "js-subscription-event", title: "Change subscription", dropdown_class: "dropdown-menu-selectable", data: { field_name: "update[subscription_event]" } } ) do
%ul
%li
- %a{href: "#", data: {id: "subscribe"}} Subscribe
+ %a{ href: "#", data: { id: "subscribe" } } Subscribe
%li
- %a{href: "#", data: {id: "unsubscribe"}} Unsubscribe
+ %a{ href: "#", data: { id: "unsubscribe" } } Unsubscribe
= hidden_field_tag 'update[issuable_ids]', []
= hidden_field_tag :state_event, params[:state_event]
diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml
index bdb00bfa33c..dcc1f3ba676 100644
--- a/app/views/shared/issuable/_form.html.haml
+++ b/app/views/shared/issuable/_form.html.haml
@@ -58,7 +58,7 @@
as resolved. Ask someone with sufficient rights to resolve the them.
- is_footer = !(issuable.is_a?(MergeRequest) && issuable.new_record?)
-.row-content-block{class: (is_footer ? "footer-block" : "middle-block")}
+.row-content-block{ class: (is_footer ? "footer-block" : "middle-block") }
- if issuable.new_record?
= form.submit "Submit #{issuable.class.model_name.human.downcase}", class: 'btn btn-create'
- else
diff --git a/app/views/shared/issuable/_label_dropdown.html.haml b/app/views/shared/issuable/_label_dropdown.html.haml
index 22b5a6aa11b..93c7fa0c7d6 100644
--- a/app/views/shared/issuable/_label_dropdown.html.haml
+++ b/app/views/shared/issuable/_label_dropdown.html.haml
@@ -19,7 +19,7 @@
= hidden_field_tag data_options[:field_name], use_id ? label.try(:id) : label.try(:title), id: nil
.dropdown
- %button.dropdown-menu-toggle.js-label-select.js-multiselect{class: classes.join(' '), type: "button", data: dropdown_data}
+ %button.dropdown-menu-toggle.js-label-select.js-multiselect{ class: classes.join(' '), type: "button", data: dropdown_data }
%span.dropdown-toggle-text{ class: ("is-default" if selected.nil? || selected.empty?) }
= multi_label_name(selected, "Labels")
= icon('chevron-down')
diff --git a/app/views/shared/issuable/_label_page_default.html.haml b/app/views/shared/issuable/_label_page_default.html.haml
index a8f01026ca5..9a8529c6cbb 100644
--- a/app/views/shared/issuable/_label_page_default.html.haml
+++ b/app/views/shared/issuable/_label_page_default.html.haml
@@ -17,7 +17,7 @@
%ul.dropdown-footer-list
- if can?(current_user, :admin_label, @project)
%li
- %a.dropdown-toggle-page{href: "#"}
+ %a.dropdown-toggle-page{ href: "#" }
Create new label
%li
= link_to namespace_project_labels_path(@project.namespace, @project), :"data-is-link" => true do
diff --git a/app/views/shared/issuable/_nav.html.haml b/app/views/shared/issuable/_nav.html.haml
index d938edf4dbd..1154316c03f 100644
--- a/app/views/shared/issuable/_nav.html.haml
+++ b/app/views/shared/issuable/_nav.html.haml
@@ -3,23 +3,23 @@
- issuables = @issues || @merge_requests
%ul.nav-links.issues-state-filters
- %li{class: ("active" if params[:state] == 'opened')}>
+ %li{ class: ("active" if params[:state] == 'opened') }>
= link_to page_filter_path(state: 'opened', label: true), id: 'state-opened', title: "Filter by #{page_context_word} that are currently opened." do
#{issuables_state_counter_text(type, :opened)}
- if type == :merge_requests
- %li{class: ("active" if params[:state] == 'merged')}>
+ %li{ class: ("active" if params[:state] == 'merged') }>
= link_to page_filter_path(state: 'merged', label: true), id: 'state-merged', title: 'Filter by merge requests that are currently merged.' do
#{issuables_state_counter_text(type, :merged)}
- %li{class: ("active" if params[:state] == 'closed')}>
+ %li{ class: ("active" if params[:state] == 'closed') }>
= link_to page_filter_path(state: 'closed', label: true), id: 'state-closed', title: 'Filter by merge requests that are currently closed and unmerged.' do
#{issuables_state_counter_text(type, :closed)}
- else
- %li{class: ("active" if params[:state] == 'closed')}>
+ %li{ class: ("active" if params[:state] == 'closed') }>
= link_to page_filter_path(state: 'closed', label: true), id: 'state-all', title: 'Filter by issues that are currently closed.' do
#{issuables_state_counter_text(type, :closed)}
- %li{class: ("active" if params[:state] == 'all')}>
+ %li{ class: ("active" if params[:state] == 'all') }>
= link_to page_filter_path(state: 'all', label: true), id: 'state-all', title: "Show all #{page_context_word}." do
#{issuables_state_counter_text(type, :all)}
diff --git a/app/views/shared/issuable/_participants.html.haml b/app/views/shared/issuable/_participants.html.haml
index 33a9a494857..171da899937 100644
--- a/app/views/shared/issuable/_participants.html.haml
+++ b/app/views/shared/issuable/_participants.html.haml
@@ -13,8 +13,8 @@
.participants-author.js-participants-author
= link_to_member(@project, participant, name: false, size: 24)
- if participants_extra > 0
- %div.participants-more
- %a.js-participants-more{href: "#", data: {original_text: "+ #{participants_size - 7} more", less_text: "- show less"}}
+ .participants-more
+ %a.js-participants-more{ href: "#", data: { original_text: "+ #{participants_size - 7} more", less_text: "- show less" } }
+ #{participants_extra} more
:javascript
IssuableContext.prototype.PARTICIPANTS_ROW_COUNT = #{participants_row};
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index 9fe1be5a597..bc57d48ae7c 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -17,9 +17,9 @@
Add todo
= icon('spin spinner', class: 'hidden js-issuable-todo-loading')
- = form_for [@project.namespace.becomes(Namespace), @project, issuable], remote: true, format: :json, html: {class: 'issuable-context-form inline-update js-issuable-update'} do |f|
+ = form_for [@project.namespace.becomes(Namespace), @project, issuable], remote: true, format: :json, html: { class: 'issuable-context-form inline-update js-issuable-update' } do |f|
.block.assignee
- .sidebar-collapsed-icon.sidebar-collapsed-user{data: {toggle: "tooltip", placement: "left", container: "body"}, title: (issuable.assignee.name if issuable.assignee)}
+ .sidebar-collapsed-icon.sidebar-collapsed-user{ data: { toggle: "tooltip", placement: "left", container: "body" }, title: (issuable.assignee.name if issuable.assignee) }
- if issuable.assignee
= link_to_member(@project, issuable.assignee, size: 24)
- else
@@ -54,7 +54,7 @@
= icon('clock-o')
%span
- if issuable.milestone
- %span.has-tooltip{title: milestone_remaining_days(issuable.milestone), data: {container: 'body', html: 1, placement: 'left'}}
+ %span.has-tooltip{ title: milestone_remaining_days(issuable.milestone), data: { container: 'body', html: 1, placement: 'left' } }
= issuable.milestone.title
- else
None
@@ -129,8 +129,8 @@
- selected_labels.each do |label|
= hidden_field_tag "#{issuable.to_ability_name}[label_names][]", label.id, id: nil
.dropdown
- %button.dropdown-menu-toggle.js-label-select.js-multiselect.js-label-sidebar-dropdown{type: "button", data: {toggle: "dropdown", default_label: "Labels", field_name: "#{issuable.to_ability_name}[label_names][]", ability_name: issuable.to_ability_name, show_no: "true", show_any: "true", namespace_path: @project.try(:namespace).try(:path), project_path: @project.try(:path), issue_update: issuable_json_path(issuable), labels: (namespace_project_labels_path(@project.namespace, @project, :json) if @project)}}
- %span.dropdown-toggle-text{ class: ("is-default" if selected_labels.empty?)}
+ %button.dropdown-menu-toggle.js-label-select.js-multiselect.js-label-sidebar-dropdown{ type: "button", data: {toggle: "dropdown", default_label: "Labels", field_name: "#{issuable.to_ability_name}[label_names][]", ability_name: issuable.to_ability_name, show_no: "true", show_any: "true", namespace_path: @project.try(:namespace).try(:path), project_path: @project.try(:path), issue_update: issuable_json_path(issuable), labels: (namespace_project_labels_path(@project.namespace, @project, :json) if @project) } }
+ %span.dropdown-toggle-text{ class: ("is-default" if selected_labels.empty?) }
= multi_label_name(selected_labels, "Labels")
= icon('chevron-down')
.dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable
@@ -141,7 +141,7 @@
= render "shared/issuable/participants", participants: issuable.participants(current_user)
- if current_user
- subscribed = issuable.subscribed?(current_user, @project)
- .block.light.subscription{data: {url: toggle_subscription_path(issuable)}}
+ .block.light.subscription{ data: { url: toggle_subscription_path(issuable) } }
.sidebar-collapsed-icon
= icon('rss')
%span.issuable-header-text.hide-collapsed.pull-left
@@ -157,7 +157,7 @@
.cross-project-reference.hide-collapsed
%span
Reference:
- %cite{title: project_ref}
+ %cite{ title: project_ref }
= project_ref
= clipboard_button(clipboard_text: project_ref)
diff --git a/app/views/shared/members/_group.html.haml b/app/views/shared/members/_group.html.haml
index 8928de9097b..a46ba3b0605 100644
--- a/app/views/shared/members/_group.html.haml
+++ b/app/views/shared/members/_group.html.haml
@@ -3,7 +3,7 @@
- can_admin_member = can?(current_user, :admin_project_member, @project)
- dom_id = "group_member_#{group_link.id}"
%li.member.group_member{ id: dom_id }
- %span{ class: "list-item-name" }
+ %span.list-item-name
= image_tag group_icon(group), class: "avatar s40", alt: ''
%strong
= link_to group.name, group_path(group)
diff --git a/app/views/shared/milestones/_issuable.html.haml b/app/views/shared/milestones/_issuable.html.haml
index 9e1b0379428..51c195ffbcd 100644
--- a/app/views/shared/milestones/_issuable.html.haml
+++ b/app/views/shared/milestones/_issuable.html.haml
@@ -14,15 +14,15 @@
- if issuable.is_a?(Issue)
= confidential_icon(issuable)
= link_to_gfm issuable.title, [project.namespace.becomes(Namespace), project, issuable], title: issuable.title
- %div{class: 'issuable-detail'}
+ .issuable-detail
= link_to [project.namespace.becomes(Namespace), project, issuable] do
- %span{ class: 'issuable-number' }>= issuable.to_reference
+ %span.issuable-number >= issuable.to_reference
- issuable.labels.each do |label|
= link_to polymorphic_path(base_url_args, { milestone_title: @milestone.title, label_name: label.title, state: 'all' }) do
- render_colored_label(label)
- %span{ class: "assignee-icon" }
+ %span.assignee-icon
- if assignee
= link_to polymorphic_path(base_url_args, { milestone_title: @milestone.title, assignee_id: issuable.assignee_id, state: 'all' }),
class: 'has-tooltip', title: "Assigned to #{assignee.name}", data: { container: 'body' } do
diff --git a/app/views/shared/milestones/_milestone.html.haml b/app/views/shared/milestones/_milestone.html.haml
index 3dccfb147bf..3200aacf542 100644
--- a/app/views/shared/milestones/_milestone.html.haml
+++ b/app/views/shared/milestones/_milestone.html.haml
@@ -1,7 +1,7 @@
- dashboard = local_assigns[:dashboard]
- custom_dom_id = dom_id(@project ? milestone : milestone.milestones.first)
-%li{class: "milestone milestone-#{milestone.closed? ? 'closed' : 'open'}", id: custom_dom_id }
+%li{ class: "milestone milestone-#{milestone.closed? ? 'closed' : 'open'}", id: custom_dom_id }
.row
.col-sm-6
%strong= link_to_gfm truncate(milestone.title, length: 100), milestone_path
diff --git a/app/views/shared/notifications/_custom_notifications.html.haml b/app/views/shared/notifications/_custom_notifications.html.haml
index a82fc95df84..b5c0a7fd6d4 100644
--- a/app/views/shared/notifications/_custom_notifications.html.haml
+++ b/app/views/shared/notifications/_custom_notifications.html.haml
@@ -18,7 +18,7 @@
%p
Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out
= succeed "." do
- %a{ href: "http://docs.gitlab.com/ce/workflow/notifications.html", target: "_blank"} notification emails
+ %a{ href: "http://docs.gitlab.com/ce/workflow/notifications.html", target: "_blank" } notification emails
.col-lg-8
- NotificationSetting::EMAIL_EVENTS.each_with_index do |event, index|
- field_id = "#{notifications_menu_identifier("modal", notification_setting)}_notification_setting[#{event}]"
diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml
index 264391fe84f..4a27965754d 100644
--- a/app/views/shared/projects/_project.html.haml
+++ b/app/views/shared/projects/_project.html.haml
@@ -25,7 +25,7 @@
%span
= icon('star')
= number_with_delimiter(project.star_count)
- %span.visibility-icon.has-tooltip{data: { container: 'body', placement: 'left' }, title: visibility_icon_description(project)}
+ %span.visibility-icon.has-tooltip{ data: { container: 'body', placement: 'left' }, title: visibility_icon_description(project) }
= visibility_level_icon(project.visibility_level, fw: true)
.title
diff --git a/app/views/shared/snippets/_blob.html.haml b/app/views/shared/snippets/_blob.html.haml
index dcdba01aee9..ad5c0c2d8c8 100644
--- a/app/views/shared/snippets/_blob.html.haml
+++ b/app/views/shared/snippets/_blob.html.haml
@@ -1,6 +1,6 @@
- unless @snippet.content.empty?
- if markup?(@snippet.file_name)
- %textarea.markdown-snippet-copy.blob-content{data: {blob_id: @snippet.id}}
+ %textarea.markdown-snippet-copy.blob-content{ data: { blob_id: @snippet.id } }
= @snippet.content
.file-content.wiki
- if gitlab_markdown?(@snippet.file_name)
diff --git a/app/views/sherlock/file_samples/show.html.haml b/app/views/sherlock/file_samples/show.html.haml
index 94d4dd4fa7d..92151176fce 100644
--- a/app/views/sherlock/file_samples/show.html.haml
+++ b/app/views/sherlock/file_samples/show.html.haml
@@ -41,7 +41,7 @@
%th= t('sherlock.percent')
%tbody
- @file_sample.line_samples.each_with_index do |sample, index|
- %tr{class: sample.majority_of?(@file_sample.duration) ? 'slow' : ''}
+ %tr{ class: sample.majority_of?(@file_sample.duration) ? 'slow' : '' }
%td= index + 1
%td= sample.events
%td
diff --git a/app/views/sherlock/queries/_general.html.haml b/app/views/sherlock/queries/_general.html.haml
index 7073c0f4d90..5a447f791dc 100644
--- a/app/views/sherlock/queries/_general.html.haml
+++ b/app/views/sherlock/queries/_general.html.haml
@@ -26,7 +26,7 @@
.panel.panel-default
.panel-heading
.pull-right
- %button.js-clipboard-trigger.btn.btn-xs{title: t('sherlock.copy_to_clipboard'), type: :button}
+ %button.js-clipboard-trigger.btn.btn-xs{ title: t('sherlock.copy_to_clipboard'), type: :button }
%i.fa.fa-clipboard
%pre.hidden
= @query.formatted_query
@@ -41,7 +41,7 @@
.panel.panel-default
.panel-heading
.pull-right
- %button.js-clipboard-trigger.btn.btn-xs{title: t('sherlock.copy_to_clipboard'), type: :button}
+ %button.js-clipboard-trigger.btn.btn-xs{ title: t('sherlock.copy_to_clipboard'), type: :button }
%i.fa.fa-clipboard
%pre.hidden
= @query.explain
diff --git a/app/views/sherlock/queries/show.html.haml b/app/views/sherlock/queries/show.html.haml
index fc2863dca8e..c45da6ee9a4 100644
--- a/app/views/sherlock/queries/show.html.haml
+++ b/app/views/sherlock/queries/show.html.haml
@@ -3,10 +3,10 @@
%ul.nav-links
%li.active
- %a(href="#tab-general" data-toggle="tab")
+ %a{ href: "#tab-general", data: { toggle: "tab" } }
= t('sherlock.general')
%li
- %a(href="#tab-backtrace" data-toggle="tab")
+ %a{ href: "#tab-backtrace", data: { toggle: "tab" } }
= t('sherlock.backtrace')
.row-content-block
diff --git a/app/views/sherlock/transactions/index.html.haml b/app/views/sherlock/transactions/index.html.haml
index da969c02765..bc05659dfa8 100644
--- a/app/views/sherlock/transactions/index.html.haml
+++ b/app/views/sherlock/transactions/index.html.haml
@@ -28,7 +28,7 @@
%tr
%td= trans.type
%td
- %span{title: trans.path}
+ %span{ title: trans.path }
= truncate(trans.path, length: 70)
%td
= trans.duration.round(2)
diff --git a/app/views/sherlock/transactions/show.html.haml b/app/views/sherlock/transactions/show.html.haml
index 8aa6b437d95..eab91e8fbe4 100644
--- a/app/views/sherlock/transactions/show.html.haml
+++ b/app/views/sherlock/transactions/show.html.haml
@@ -3,15 +3,15 @@
%ul.nav-links
%li.active
- %a(href="#tab-general" data-toggle="tab")
+ %a{ href: "#tab-general", data: { toggle: "tab" } }
= t('sherlock.general')
%li
- %a(href="#tab-queries" data-toggle="tab")
+ %a{ href: "#tab-queries", data: { toggle: "tab" } }
= t('sherlock.queries')
%span.badge
#{@transaction.queries.length}
%li
- %a(href="#tab-file-samples" data-toggle="tab")
+ %a{ href: "#tab-file-samples", data: { toggle: "tab" } }
= t('sherlock.file_samples')
%span.badge
#{@transaction.file_samples.length}
diff --git a/app/views/u2f/_authenticate.html.haml b/app/views/u2f/_authenticate.html.haml
index fa998c91f72..f878bece2fa 100644
--- a/app/views/u2f/_authenticate.html.haml
+++ b/app/views/u2f/_authenticate.html.haml
@@ -1,30 +1,21 @@
#js-authenticate-u2f
+%a.btn.btn-block.btn-info#js-login-2fa-device{ href: '#' } Sign in via 2FA code
%script#js-authenticate-u2f-not-supported{ type: "text/template" }
%p Your browser doesn't support U2F. Please use Google Chrome desktop (version 41 or newer).
-%script#js-authenticate-u2f-setup{ type: "text/template" }
- %div
- %p Insert your security key (if you haven't already), and press the button below.
- %a.btn.btn-info#js-login-u2f-device{ href: 'javascript:void(0)' } Sign in via U2F device
-
%script#js-authenticate-u2f-in-progress{ type: "text/template" }
%p Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now.
%script#js-authenticate-u2f-error{ type: "text/template" }
%div
%p <%= error_message %> (error code: <%= error_code %>)
- %a.btn.btn-warning#js-u2f-try-again Try again?
+ %a.btn.btn-block.btn-warning#js-u2f-try-again Try again?
%script#js-authenticate-u2f-authenticated{ type: "text/template" }
%div
- %p We heard back from your U2F device. Click this button to authenticate with the GitLab server.
- = form_tag(new_user_session_path, method: :post) do |f|
+ %p We heard back from your U2F device. You have been authenticated.
+ = form_tag(new_user_session_path, method: :post, id: 'js-login-u2f-form') do |f|
- resource_params = params[resource_name].presence || params
= hidden_field_tag 'user[remember_me]', resource_params.fetch(:remember_me, 0)
= hidden_field_tag 'user[device_response]', nil, class: 'form-control', required: true, id: "js-device-response"
- = submit_tag "Authenticate via U2F Device", class: "btn btn-success"
-
-:javascript
- var u2fAuthenticate = new U2FAuthenticate($("#js-authenticate-u2f"), gon.u2f);
- u2fAuthenticate.start();
diff --git a/app/views/u2f/_register.html.haml b/app/views/u2f/_register.html.haml
index fcc33f04237..adc07bcba73 100644
--- a/app/views/u2f/_register.html.haml
+++ b/app/views/u2f/_register.html.haml
@@ -27,7 +27,7 @@
%a.btn.btn-warning#js-u2f-try-again Try again?
%script#js-register-u2f-registered{ type: "text/template" }
- %div.row.append-bottom-10
+ .row.append-bottom-10
.col-md-12
%p Your device was successfully set up! Give it a name and register it with the GitLab server.
= form_tag(create_u2f_profile_two_factor_auth_path, method: :post) do
diff --git a/app/views/users/calendar.html.haml b/app/views/users/calendar.html.haml
index 09ff8a76d27..6228245d8d0 100644
--- a/app/views/users/calendar.html.haml
+++ b/app/views/users/calendar.html.haml
@@ -6,4 +6,4 @@
new Calendar(
#{@activity_dates.to_json},
'#{user_calendar_activities_path}'
- ); \ No newline at end of file
+ );
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index 1e0752bd3c3..fb25eed4f37 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -18,11 +18,11 @@
- elsif current_user
- if @user.abuse_report
%button.btn.btn-danger{ title: 'Already reported for abuse',
- data: { toggle: 'tooltip', placement: 'bottom', container: 'body' }}
+ data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } }
= icon('exclamation-circle')
- else
= link_to new_abuse_report_path(user_id: @user.id, ref_url: request.referrer), class: 'btn btn-gray',
- title: 'Report abuse', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
+ title: 'Report abuse', data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do
= icon('exclamation-circle')
- if current_user
= link_to user_path(@user, :atom, { private_token: current_user.private_token }), class: 'btn btn-gray' do
@@ -101,12 +101,12 @@
.tab-content
#activity.tab-pane
.row-content-block.calender-block.white.second-block.hidden-xs
- .user-calendar{data: {href: user_calendar_path}}
+ .user-calendar{ data: { href: user_calendar_path } }
%h4.center.light
%i.fa.fa-spinner.fa-spin
.user-calendar-activities
- .content_list{ data: {href: user_path} }
+ .content_list{ data: { href: user_path } }
= spinner
#groups.tab-pane
diff --git a/app/workers/project_cache_worker.rb b/app/workers/project_cache_worker.rb
index 27d7e652721..8ff9d07860f 100644
--- a/app/workers/project_cache_worker.rb
+++ b/app/workers/project_cache_worker.rb
@@ -6,26 +6,27 @@ class ProjectCacheWorker
LEASE_TIMEOUT = 15.minutes.to_i
# project_id - The ID of the project for which to flush the cache.
- # refresh - An Array containing extra types of data to refresh such as
- # `:readme` to flush the README and `:changelog` to flush the
- # CHANGELOG.
- def perform(project_id, refresh = [])
+ # files - An Array containing extra types of files to refresh such as
+ # `:readme` to flush the README and `:changelog` to flush the
+ # CHANGELOG.
+ # statistics - An Array containing columns from ProjectStatistics to
+ # refresh, if empty all columns will be refreshed
+ def perform(project_id, files = [], statistics = [])
project = Project.find_by(id: project_id)
return unless project && project.repository.exists?
- update_repository_size(project)
- project.update_commit_count
+ update_statistics(project, statistics.map(&:to_sym))
- project.repository.refresh_method_caches(refresh.map(&:to_sym))
+ project.repository.refresh_method_caches(files.map(&:to_sym))
end
- def update_repository_size(project)
- return unless try_obtain_lease_for(project.id, :update_repository_size)
+ def update_statistics(project, statistics = [])
+ return unless try_obtain_lease_for(project.id, :update_statistics)
- Rails.logger.info("Updating repository size for project #{project.id}")
+ Rails.logger.info("Updating statistics for project #{project.id}")
- project.update_repository_size
+ project.statistics.refresh!(only: statistics)
end
private
diff --git a/changelogs/unreleased/18786-go-to-a-project-order.yml b/changelogs/unreleased/18786-go-to-a-project-order.yml
new file mode 100644
index 00000000000..1b9e246d1a7
--- /dev/null
+++ b/changelogs/unreleased/18786-go-to-a-project-order.yml
@@ -0,0 +1,4 @@
+---
+title: Go to a project order
+merge_request: 7737
+author: Jacopo Beschi @jacopo-beschi
diff --git a/changelogs/unreleased/19988-prevent-empty-pagination-when-list-not-empty.yml b/changelogs/unreleased/19988-prevent-empty-pagination-when-list-not-empty.yml
new file mode 100644
index 00000000000..5570ede4a9a
--- /dev/null
+++ b/changelogs/unreleased/19988-prevent-empty-pagination-when-list-not-empty.yml
@@ -0,0 +1,4 @@
+---
+title: Prevent empty pagination when list is not empty
+merge_request: 8172
+author:
diff --git a/changelogs/unreleased/21135-resolve-these-conflicts-link-is-too-subtle.yml b/changelogs/unreleased/21135-resolve-these-conflicts-link-is-too-subtle.yml
new file mode 100644
index 00000000000..574c322803c
--- /dev/null
+++ b/changelogs/unreleased/21135-resolve-these-conflicts-link-is-too-subtle.yml
@@ -0,0 +1,4 @@
+---
+title: Improve visibility of "Resolve conflicts" and "Merge locally" actions
+merge_request: 8229
+author:
diff --git a/changelogs/unreleased/24820-buttons-in-the-branches-page-are-stacking-on-top-of-each-other-depending-on-the-selected-filter.yml b/changelogs/unreleased/24820-buttons-in-the-branches-page-are-stacking-on-top-of-each-other-depending-on-the-selected-filter.yml
new file mode 100644
index 00000000000..fac7697ede1
--- /dev/null
+++ b/changelogs/unreleased/24820-buttons-in-the-branches-page-are-stacking-on-top-of-each-other-depending-on-the-selected-filter.yml
@@ -0,0 +1,4 @@
+---
+title: fix button layout issue on branches page
+merge_request: 8074
+author:
diff --git a/changelogs/unreleased/24876-page-jumps-to-wrong-position-when-clicking-a-comment-anchor.yml b/changelogs/unreleased/24876-page-jumps-to-wrong-position-when-clicking-a-comment-anchor.yml
new file mode 100644
index 00000000000..c31c89dc4bc
--- /dev/null
+++ b/changelogs/unreleased/24876-page-jumps-to-wrong-position-when-clicking-a-comment-anchor.yml
@@ -0,0 +1,4 @@
+---
+title: ensure permalinks scroll to correct position on multiple clicks
+merge_request: 8046
+author:
diff --git a/changelogs/unreleased/24941-login-tabs-border.yml b/changelogs/unreleased/24941-login-tabs-border.yml
new file mode 100644
index 00000000000..b06c21ad71a
--- /dev/null
+++ b/changelogs/unreleased/24941-login-tabs-border.yml
@@ -0,0 +1,4 @@
+---
+title: fix border in login session tabs
+merge_request: 8346
+author:
diff --git a/changelogs/unreleased/25430-make-colors-in-runner-status-more-colorblind-friendly.yml b/changelogs/unreleased/25430-make-colors-in-runner-status-more-colorblind-friendly.yml
new file mode 100644
index 00000000000..e60c42cdfba
--- /dev/null
+++ b/changelogs/unreleased/25430-make-colors-in-runner-status-more-colorblind-friendly.yml
@@ -0,0 +1,4 @@
+---
+title: Change status colors of runners to better defaults
+merge_request:
+author:
diff --git a/changelogs/unreleased/25705-your-commands-have-been-executed-is-overkill.yml b/changelogs/unreleased/25705-your-commands-have-been-executed-is-overkill.yml
new file mode 100644
index 00000000000..850e98518a6
--- /dev/null
+++ b/changelogs/unreleased/25705-your-commands-have-been-executed-is-overkill.yml
@@ -0,0 +1,3 @@
+---
+title: Replace wording for slash command confirmation message
+merge_request: 8123
diff --git a/changelogs/unreleased/25829-update-username-button-remains-disabled-upon-failure.yml b/changelogs/unreleased/25829-update-username-button-remains-disabled-upon-failure.yml
new file mode 100644
index 00000000000..c82bacd8bcd
--- /dev/null
+++ b/changelogs/unreleased/25829-update-username-button-remains-disabled-upon-failure.yml
@@ -0,0 +1,4 @@
+---
+title: re-enable change username button after failure
+merge_request: 8332
+author:
diff --git a/changelogs/unreleased/25930-discussion-actions-overlap-header-text.yml b/changelogs/unreleased/25930-discussion-actions-overlap-header-text.yml
deleted file mode 100644
index 54a461c24ed..00000000000
--- a/changelogs/unreleased/25930-discussion-actions-overlap-header-text.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix discussion overlap text in regular screens
-merge_request: 8273
-author:
diff --git a/changelogs/unreleased/25996-Move-award-emoji-out-of-the-discussion-tab-for-MR.yml b/changelogs/unreleased/25996-Move-award-emoji-out-of-the-discussion-tab-for-MR.yml
new file mode 100644
index 00000000000..e05e2dd6fed
--- /dev/null
+++ b/changelogs/unreleased/25996-Move-award-emoji-out-of-the-discussion-tab-for-MR.yml
@@ -0,0 +1,4 @@
+---
+title: Move award emoji's out of the discussion tab for merge requests
+merge_request:
+author:
diff --git a/changelogs/unreleased/26018-mini-pipeline-hover-cross-broswer.yml b/changelogs/unreleased/26018-mini-pipeline-hover-cross-broswer.yml
deleted file mode 100644
index 501f0b25a21..00000000000
--- a/changelogs/unreleased/26018-mini-pipeline-hover-cross-broswer.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fixes mini-pipeline-graph dropdown animation and stage position in chrome, firefox and safari
-merge_request: 8282
-author:
diff --git a/changelogs/unreleased/26126-cache-even-when-no-projects.yml b/changelogs/unreleased/26126-cache-even-when-no-projects.yml
new file mode 100644
index 00000000000..53e14ac9edf
--- /dev/null
+++ b/changelogs/unreleased/26126-cache-even-when-no-projects.yml
@@ -0,0 +1,4 @@
+---
+title: Cache project authorizations even when user has access to zero projects
+merge_request: 8327
+author:
diff --git a/changelogs/unreleased/26134-ctrl-enter-does-not-submit-a-new-merge-request.yml b/changelogs/unreleased/26134-ctrl-enter-does-not-submit-a-new-merge-request.yml
new file mode 100644
index 00000000000..40183f8d3fa
--- /dev/null
+++ b/changelogs/unreleased/26134-ctrl-enter-does-not-submit-a-new-merge-request.yml
@@ -0,0 +1,4 @@
+---
+title: Make CTRL+Enter submits a new merge request
+merge_request: 8360
+author: Saad Shahd
diff --git a/changelogs/unreleased/26192-fixes-too-short-input.yml b/changelogs/unreleased/26192-fixes-too-short-input.yml
new file mode 100644
index 00000000000..ff707f4694d
--- /dev/null
+++ b/changelogs/unreleased/26192-fixes-too-short-input.yml
@@ -0,0 +1,4 @@
+---
+title: Fixes too short input for placeholder message in commit listing page
+merge_request: 8367
+author:
diff --git a/changelogs/unreleased/26218-rety-button-pipeline-builds-name-drodown-broken.yml b/changelogs/unreleased/26218-rety-button-pipeline-builds-name-drodown-broken.yml
new file mode 100644
index 00000000000..ef8581b6fb3
--- /dev/null
+++ b/changelogs/unreleased/26218-rety-button-pipeline-builds-name-drodown-broken.yml
@@ -0,0 +1,4 @@
+---
+title: Increases width of mini-pipeline-graph dropdown to prevent wrong position on chrome on ubuntu
+merge_request: 8399
+author:
diff --git a/changelogs/unreleased/7898-fixes-issue-boards-list-colored-top-border-visual-glitch.yml b/changelogs/unreleased/7898-fixes-issue-boards-list-colored-top-border-visual-glitch.yml
new file mode 100644
index 00000000000..74412c32375
--- /dev/null
+++ b/changelogs/unreleased/7898-fixes-issue-boards-list-colored-top-border-visual-glitch.yml
@@ -0,0 +1,4 @@
+---
+title: Fixes issue boards list colored top border visual glitch
+merge_request: 7898
+author: Pier Paolo Ramon
diff --git a/changelogs/unreleased/dz-improve-admin-group-routing.yml b/changelogs/unreleased/dz-improve-admin-group-routing.yml
new file mode 100644
index 00000000000..2360b965c90
--- /dev/null
+++ b/changelogs/unreleased/dz-improve-admin-group-routing.yml
@@ -0,0 +1,4 @@
+---
+title: Fix 500 error when visit group from admin area if group name contains dot
+merge_request:
+author:
diff --git a/changelogs/unreleased/dz-nested-group-misc.yml b/changelogs/unreleased/dz-nested-group-misc.yml
new file mode 100644
index 00000000000..9c9d0b1c644
--- /dev/null
+++ b/changelogs/unreleased/dz-nested-group-misc.yml
@@ -0,0 +1,4 @@
+---
+title: Show nested groups tab on group page
+merge_request: 8308
+author:
diff --git a/changelogs/unreleased/dz-rename-invalid-users.yml b/changelogs/unreleased/dz-rename-invalid-users.yml
new file mode 100644
index 00000000000..f420b069531
--- /dev/null
+++ b/changelogs/unreleased/dz-rename-invalid-users.yml
@@ -0,0 +1,4 @@
+---
+title: Rename users with namespace ending with .git
+merge_request: 8309
+author:
diff --git a/changelogs/unreleased/dz-rename-reserved-project-names.yml b/changelogs/unreleased/dz-rename-reserved-project-names.yml
new file mode 100644
index 00000000000..30bcc1a0396
--- /dev/null
+++ b/changelogs/unreleased/dz-rename-reserved-project-names.yml
@@ -0,0 +1,4 @@
+---
+title: Rename projects wth reserved names
+merge_request: 8234
+author:
diff --git a/changelogs/unreleased/feature-1376-allow-write-access-deploy-keys.yml b/changelogs/unreleased/feature-1376-allow-write-access-deploy-keys.yml
new file mode 100644
index 00000000000..0fd590a877b
--- /dev/null
+++ b/changelogs/unreleased/feature-1376-allow-write-access-deploy-keys.yml
@@ -0,0 +1,4 @@
+---
+title: Allow to add deploy keys with write-access
+merge_request: 5807
+author: Ali Ibrahim
diff --git a/changelogs/unreleased/feature-admin-merge-groups-and-projects.yml b/changelogs/unreleased/feature-admin-merge-groups-and-projects.yml
new file mode 100644
index 00000000000..0c0b74b686a
--- /dev/null
+++ b/changelogs/unreleased/feature-admin-merge-groups-and-projects.yml
@@ -0,0 +1,4 @@
+---
+title: Merged the 'Groups' and 'Projects' tabs when viewing user profiles
+merge_request: 8323
+author: James Gregory
diff --git a/changelogs/unreleased/feature-more-storage-statistics.yml b/changelogs/unreleased/feature-more-storage-statistics.yml
new file mode 100644
index 00000000000..824fd36dc34
--- /dev/null
+++ b/changelogs/unreleased/feature-more-storage-statistics.yml
@@ -0,0 +1,4 @@
+---
+title: Add more storage statistics
+merge_request: 7754
+author: Markus Koller
diff --git a/changelogs/unreleased/filename-to-file-path.yml b/changelogs/unreleased/filename-to-file-path.yml
new file mode 100644
index 00000000000..3c6c838595a
--- /dev/null
+++ b/changelogs/unreleased/filename-to-file-path.yml
@@ -0,0 +1,3 @@
+---
+title: Rename filename to file path in tooltip of file header in merge request diff
+merge_request: 8314 \ No newline at end of file
diff --git a/changelogs/unreleased/fix-api-deprecation.yml b/changelogs/unreleased/fix-api-deprecation.yml
new file mode 100644
index 00000000000..90285ddf058
--- /dev/null
+++ b/changelogs/unreleased/fix-api-deprecation.yml
@@ -0,0 +1,4 @@
+---
+title: Fix a Grape deprecation, use `#request_method` instead of `#route_method`
+merge_request:
+author:
diff --git a/changelogs/unreleased/fix-boards-search-typo.yml b/changelogs/unreleased/fix-boards-search-typo.yml
new file mode 100644
index 00000000000..0c083fc0d10
--- /dev/null
+++ b/changelogs/unreleased/fix-boards-search-typo.yml
@@ -0,0 +1,4 @@
+---
+title: 'Fix typo: seach to search'
+merge_request: 8370
+author:
diff --git a/changelogs/unreleased/fix-light-hr-in-descriptions.yml b/changelogs/unreleased/fix-light-hr-in-descriptions.yml
new file mode 100644
index 00000000000..8efd471e416
--- /dev/null
+++ b/changelogs/unreleased/fix-light-hr-in-descriptions.yml
@@ -0,0 +1,4 @@
+---
+title: Darkened hr border color in descriptions because of update of bootstrap
+merge_request: 8333
+author:
diff --git a/changelogs/unreleased/fix-mentioned-issue-text-grammar.yml b/changelogs/unreleased/fix-mentioned-issue-text-grammar.yml
new file mode 100644
index 00000000000..1d001e6b568
--- /dev/null
+++ b/changelogs/unreleased/fix-mentioned-issue-text-grammar.yml
@@ -0,0 +1,4 @@
+---
+title: Fix a minor grammar error in merge request widget
+merge_request: 8337
+author:
diff --git a/changelogs/unreleased/gfm-new-line-fix.yml b/changelogs/unreleased/gfm-new-line-fix.yml
new file mode 100644
index 00000000000..751bb356b5f
--- /dev/null
+++ b/changelogs/unreleased/gfm-new-line-fix.yml
@@ -0,0 +1,4 @@
+---
+title: Fixed GFM dropdown not showing on new lines
+merge_request:
+author:
diff --git a/changelogs/unreleased/issue_22664.yml b/changelogs/unreleased/issue_22664.yml
new file mode 100644
index 00000000000..18a8d9ec6be
--- /dev/null
+++ b/changelogs/unreleased/issue_22664.yml
@@ -0,0 +1,4 @@
+---
+title: Check if user can read project before being assigned to issue
+merge_request:
+author:
diff --git a/changelogs/unreleased/issue_25578.yml b/changelogs/unreleased/issue_25578.yml
new file mode 100644
index 00000000000..e10f1d232af
--- /dev/null
+++ b/changelogs/unreleased/issue_25578.yml
@@ -0,0 +1,4 @@
+---
+title: Fix redirect after update file when user has forked project
+merge_request:
+author:
diff --git a/changelogs/unreleased/issue_25682.yml b/changelogs/unreleased/issue_25682.yml
new file mode 100644
index 00000000000..a50138756ba
--- /dev/null
+++ b/changelogs/unreleased/issue_25682.yml
@@ -0,0 +1,4 @@
+---
+title: Parse JIRA issue references even if Issue Tracker is disabled
+merge_request:
+author:
diff --git a/changelogs/unreleased/issues-8081.yml b/changelogs/unreleased/issues-8081.yml
new file mode 100644
index 00000000000..82f746937bc
--- /dev/null
+++ b/changelogs/unreleased/issues-8081.yml
@@ -0,0 +1,4 @@
+---
+title: change 'gray' color theme name to 'black' to match the actual color
+merge_request: 7908
+author: BM5k
diff --git a/changelogs/unreleased/mentioned-but-not-closed-issues-messages.yml b/changelogs/unreleased/mentioned-but-not-closed-issues-messages.yml
new file mode 100644
index 00000000000..ba3b13bcdb7
--- /dev/null
+++ b/changelogs/unreleased/mentioned-but-not-closed-issues-messages.yml
@@ -0,0 +1,4 @@
+---
+title: Fix unclear closing issue behaviour on Merge Request show page
+merge_request: 8345
+author: Gabriel Gizotti
diff --git a/changelogs/unreleased/re-style-issue-new-branch.yml b/changelogs/unreleased/re-style-issue-new-branch.yml
new file mode 100644
index 00000000000..977a54ff2ae
--- /dev/null
+++ b/changelogs/unreleased/re-style-issue-new-branch.yml
@@ -0,0 +1,3 @@
+---
+title: Remove checking branches state in issue new branch button
+merge_request: 8023
diff --git a/changelogs/unreleased/view-ce-vs-ee.yml b/changelogs/unreleased/view-ce-vs-ee.yml
new file mode 100644
index 00000000000..38bce4ac7c3
--- /dev/null
+++ b/changelogs/unreleased/view-ce-vs-ee.yml
@@ -0,0 +1,4 @@
+---
+title: About GitLab link in sidebar that links to help page
+merge_request: 8316
+author:
diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb
index 3d1a41a4652..d4197da3fa9 100644
--- a/config/initializers/inflections.rb
+++ b/config/initializers/inflections.rb
@@ -10,5 +10,5 @@
# end
#
ActiveSupport::Inflector.inflections do |inflect|
- inflect.uncountable %w(award_emoji)
+ inflect.uncountable %w(award_emoji project_statistics)
end
diff --git a/config/routes/admin.rb b/config/routes/admin.rb
index 0dd2c8f7aef..8e99239f350 100644
--- a/config/routes/admin.rb
+++ b/config/routes/admin.rb
@@ -6,7 +6,6 @@ namespace :admin do
member do
get :projects
get :keys
- get :groups
put :block
put :unblock
put :unlock
@@ -32,7 +31,7 @@ namespace :admin do
scope(path: 'groups/*id',
controller: :groups,
- constraints: { id: Gitlab::Regex.namespace_route_regex }) do
+ constraints: { id: Gitlab::Regex.namespace_route_regex, format: /(html|json|atom)/ }) do
scope(as: :group) do
put :members_update
diff --git a/db/migrate/20160811172945_add_can_push_to_keys.rb b/db/migrate/20160811172945_add_can_push_to_keys.rb
new file mode 100644
index 00000000000..5fd303fe8fb
--- /dev/null
+++ b/db/migrate/20160811172945_add_can_push_to_keys.rb
@@ -0,0 +1,14 @@
+class AddCanPushToKeys < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+ disable_ddl_transaction!
+
+ DOWNTIME = false
+
+ def up
+ add_column_with_default(:keys, :can_push, :boolean, default: false, allow_null: false)
+ end
+
+ def down
+ remove_column(:keys, :can_push)
+ end
+end
diff --git a/db/migrate/20161201155511_create_project_statistics.rb b/db/migrate/20161201155511_create_project_statistics.rb
new file mode 100644
index 00000000000..26e6d3623eb
--- /dev/null
+++ b/db/migrate/20161201155511_create_project_statistics.rb
@@ -0,0 +1,20 @@
+class CreateProjectStatistics < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def change
+ # use bigint columns to support values >2GB
+ counter_column = { limit: 8, null: false, default: 0 }
+
+ create_table :project_statistics do |t|
+ t.references :project, null: false, index: { unique: true }, foreign_key: { on_delete: :cascade }
+ t.references :namespace, null: false, index: true
+ t.integer :commit_count, counter_column
+ t.integer :storage_size, counter_column
+ t.integer :repository_size, counter_column
+ t.integer :lfs_objects_size, counter_column
+ t.integer :build_artifacts_size, counter_column
+ end
+ end
+end
diff --git a/db/migrate/20161201160452_migrate_project_statistics.rb b/db/migrate/20161201160452_migrate_project_statistics.rb
new file mode 100644
index 00000000000..3ae3f2c159b
--- /dev/null
+++ b/db/migrate/20161201160452_migrate_project_statistics.rb
@@ -0,0 +1,23 @@
+class MigrateProjectStatistics < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = true
+ DOWNTIME_REASON = 'Removes two columns from the projects table'
+
+ def up
+ # convert repository_size in float (megabytes) to integer (bytes),
+ # initialize total storage_size with repository_size
+ execute <<-EOF
+ INSERT INTO project_statistics (project_id, namespace_id, commit_count, storage_size, repository_size)
+ SELECT id, namespace_id, commit_count, (repository_size * 1024 * 1024), (repository_size * 1024 * 1024) FROM projects
+ EOF
+
+ remove_column :projects, :repository_size
+ remove_column :projects, :commit_count
+ end
+
+ def down
+ add_column_with_default :projects, :repository_size, :float, default: 0.0
+ add_column_with_default :projects, :commit_count, :integer, default: 0
+ end
+end
diff --git a/db/migrate/20161226122833_remove_dot_git_from_usernames.rb b/db/migrate/20161226122833_remove_dot_git_from_usernames.rb
new file mode 100644
index 00000000000..809b09feb84
--- /dev/null
+++ b/db/migrate/20161226122833_remove_dot_git_from_usernames.rb
@@ -0,0 +1,87 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class RemoveDotGitFromUsernames < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+ include Gitlab::ShellAdapter
+
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = false
+
+ def up
+ invalid_users.each do |user|
+ id = user['id']
+ namespace_id = user['namespace_id']
+ path_was = user['username']
+ path_was_wildcard = quote_string("#{path_was}/%")
+ path = quote_string(rename_path(path_was))
+
+ move_namespace(namespace_id, path_was, path)
+
+ execute "UPDATE routes SET path = '#{path}' WHERE source_type = 'Namespace' AND source_id = #{namespace_id}"
+ execute "UPDATE namespaces SET path = '#{path}' WHERE id = #{namespace_id}"
+ execute "UPDATE users SET username = '#{path}' WHERE id = #{id}"
+
+ select_all("SELECT id, path FROM routes WHERE path LIKE '#{path_was_wildcard}'").each do |route|
+ new_path = "#{path}/#{route['path'].split('/').last}"
+ execute "UPDATE routes SET path = '#{new_path}' WHERE id = #{route['id']}"
+ end
+ end
+ end
+
+ def down
+ # nothing to do here
+ end
+
+ private
+
+ def invalid_users
+ select_all("SELECT u.id, u.username, n.path AS namespace_path, n.id AS namespace_id FROM users u
+ INNER JOIN namespaces n ON n.owner_id = u.id
+ WHERE n.type is NULL AND n.path LIKE '%.git'")
+ end
+
+ def route_exists?(path)
+ select_all("SELECT id, path FROM routes WHERE path = '#{quote_string(path)}'").present?
+ end
+
+ # Accepts invalid path like test.git and returns test_git or
+ # test_git1 if test_git already taken
+ def rename_path(path)
+ # To stay closer with original name and reduce risk of duplicates
+ # we rename suffix instead of removing it
+ path = path.sub(/\.git\z/, '_git')
+
+ counter = 0
+ base = path
+
+ while route_exists?(path)
+ counter += 1
+ path = "#{base}#{counter}"
+ end
+
+ path
+ end
+
+ def move_namespace(namespace_id, path_was, path)
+ repository_storage_paths = select_all("SELECT distinct(repository_storage) FROM projects WHERE namespace_id = #{namespace_id}").map do |row|
+ Gitlab.config.repositories.storages[row['repository_storage']]
+ end.compact
+
+ # Move the namespace directory in all storages paths used by member projects
+ repository_storage_paths.each do |repository_storage_path|
+ # Ensure old directory exists before moving it
+ gitlab_shell.add_namespace(repository_storage_path, path_was)
+
+ unless gitlab_shell.mv_namespace(repository_storage_path, path_was, path)
+ Rails.logger.error "Exception moving path #{repository_storage_path} from #{path_was} to #{path}"
+
+ # if we cannot move namespace directory we should rollback
+ # db changes in order to prevent out of sync between db and fs
+ raise Exception.new('namespace directory cannot be moved')
+ end
+ end
+
+ Gitlab::UploadsTransfer.new.rename_namespace(path_was, path)
+ end
+end
diff --git a/db/migrate/20161227192806_rename_slack_and_mattermost_notification_services.rb b/db/migrate/20161227192806_rename_slack_and_mattermost_notification_services.rb
new file mode 100644
index 00000000000..50ad7437227
--- /dev/null
+++ b/db/migrate/20161227192806_rename_slack_and_mattermost_notification_services.rb
@@ -0,0 +1,25 @@
+class RenameSlackAndMattermostNotificationServices < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def up
+ update_column_in_batches(:services, :type, 'SlackService') do |table, query|
+ query.where(table[:type].eq('SlackNotificationService'))
+ end
+
+ update_column_in_batches(:services, :type, 'MattermostService') do |table, query|
+ query.where(table[:type].eq('MattermostNotificationService'))
+ end
+ end
+
+ def down
+ update_column_in_batches(:services, :type, 'SlackNotificationService') do |table, query|
+ query.where(table[:type].eq('SlackService'))
+ end
+
+ update_column_in_batches(:services, :type, 'MattermostNotificationService') do |table, query|
+ query.where(table[:type].eq('MattermostService'))
+ end
+ end
+end
diff --git a/db/post_migrate/20161221140236_remove_unneeded_services.rb b/db/post_migrate/20161221140236_remove_unneeded_services.rb
index a94ccc43a41..6b7e94c8641 100644
--- a/db/post_migrate/20161221140236_remove_unneeded_services.rb
+++ b/db/post_migrate/20161221140236_remove_unneeded_services.rb
@@ -4,6 +4,8 @@ class RemoveUnneededServices < ActiveRecord::Migration
DOWNTIME = false
def up
+ disable_statement_timeout
+
execute("DELETE FROM services WHERE active = false AND properties = '{}';")
end
diff --git a/db/post_migrate/20161221153951_rename_reserved_project_names.rb b/db/post_migrate/20161221153951_rename_reserved_project_names.rb
new file mode 100644
index 00000000000..7f7c2424a5c
--- /dev/null
+++ b/db/post_migrate/20161221153951_rename_reserved_project_names.rb
@@ -0,0 +1,135 @@
+require 'thread'
+
+class RenameReservedProjectNames < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+ include Gitlab::ShellAdapter
+
+ DOWNTIME = false
+
+ THREAD_COUNT = 8
+
+ KNOWN_PATHS = %w(.well-known
+ all
+ assets
+ blame
+ blob
+ commits
+ create
+ create_dir
+ edit
+ files
+ files
+ find_file
+ groups
+ hooks
+ issues
+ logs_tree
+ merge_requests
+ new
+ new
+ preview
+ profile
+ projects
+ public
+ raw
+ repository
+ robots.txt
+ s
+ snippets
+ teams
+ tree
+ u
+ unsubscribes
+ update
+ users
+ wikis)
+
+ def up
+ queues = Array.new(THREAD_COUNT) { Queue.new }
+ start = false
+
+ threads = Array.new(THREAD_COUNT) do |index|
+ Thread.new do
+ queue = queues[index]
+
+ # Wait until we have input to process.
+ until start; end
+
+ rename_projects(queue.pop) until queue.empty?
+ end
+ end
+
+ enum = queues.each
+
+ reserved_projects.each_slice(100) do |slice|
+ begin
+ queue = enum.next
+ rescue StopIteration
+ enum.rewind
+ retry
+ end
+
+ queue << slice
+ end
+
+ start = true
+
+ threads.each(&:join)
+ end
+
+ def down
+ # nothing to do here
+ end
+
+ private
+
+ def reserved_projects
+ Project.unscoped.
+ includes(:namespace).
+ where('EXISTS (SELECT 1 FROM namespaces WHERE projects.namespace_id = namespaces.id)').
+ where('projects.path' => KNOWN_PATHS)
+ end
+
+ def route_exists?(full_path)
+ quoted_path = ActiveRecord::Base.connection.quote_string(full_path)
+
+ ActiveRecord::Base.connection.
+ select_all("SELECT id, path FROM routes WHERE path = '#{quoted_path}'").present?
+ end
+
+ # Adds number to the end of the path that is not taken by other route
+ def rename_path(namespace_path, path_was)
+ counter = 0
+ path = "#{path_was}#{counter}"
+
+ while route_exists?("#{namespace_path}/#{path}")
+ counter += 1
+ path = "#{path_was}#{counter}"
+ end
+
+ path
+ end
+
+ def rename_projects(projects)
+ projects.each do |project|
+ id = project.id
+ path_was = project.path
+ namespace_path = project.namespace.path
+ path = rename_path(namespace_path, path_was)
+
+ begin
+ # Because project path update is quite complex operation we can't safely
+ # copy-paste all code from GitLab. As exception we use Rails code here
+ project.rename_repo if rename_project_row(project, path)
+ rescue Exception => e # rubocop: disable Lint/RescueException
+ Rails.logger.error "Exception when renaming project #{id}: #{e.message}"
+ end
+ end
+ end
+
+ def rename_project_row(project, path)
+ project.respond_to?(:update_attributes) &&
+ project.update_attributes(path: path) &&
+ project.respond_to?(:rename_repo)
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 05b6c807660..923ece86edb 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20161221140236) do
+ActiveRecord::Schema.define(version: 20161227192806) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -527,6 +527,7 @@ ActiveRecord::Schema.define(version: 20161221140236) do
t.string "type"
t.string "fingerprint"
t.boolean "public", default: false, null: false
+ t.boolean "can_push", default: false, null: false
end
add_index "keys", ["fingerprint"], name: "index_keys_on_fingerprint", unique: true, using: :btree
@@ -901,6 +902,19 @@ ActiveRecord::Schema.define(version: 20161221140236) do
add_index "project_import_data", ["project_id"], name: "index_project_import_data_on_project_id", using: :btree
+ create_table "project_statistics", force: :cascade do |t|
+ t.integer "project_id", null: false
+ t.integer "namespace_id", null: false
+ t.integer "commit_count", limit: 8, default: 0, null: false
+ t.integer "storage_size", limit: 8, default: 0, null: false
+ t.integer "repository_size", limit: 8, default: 0, null: false
+ t.integer "lfs_objects_size", limit: 8, default: 0, null: false
+ t.integer "build_artifacts_size", limit: 8, default: 0, null: false
+ end
+
+ add_index "project_statistics", ["namespace_id"], name: "index_project_statistics_on_namespace_id", using: :btree
+ add_index "project_statistics", ["project_id"], name: "index_project_statistics_on_project_id", unique: true, using: :btree
+
create_table "projects", force: :cascade do |t|
t.string "name"
t.string "path"
@@ -915,11 +929,9 @@ ActiveRecord::Schema.define(version: 20161221140236) do
t.boolean "archived", default: false, null: false
t.string "avatar"
t.string "import_status"
- t.float "repository_size", default: 0.0
t.integer "star_count", default: 0, null: false
t.string "import_type"
t.string "import_source"
- t.integer "commit_count", default: 0
t.text "import_error"
t.integer "ci_id"
t.boolean "shared_runners_enabled", default: true, null: false
@@ -1288,9 +1300,10 @@ ActiveRecord::Schema.define(version: 20161221140236) do
add_foreign_key "personal_access_tokens", "users"
add_foreign_key "project_authorizations", "projects", on_delete: :cascade
add_foreign_key "project_authorizations", "users", on_delete: :cascade
+ add_foreign_key "project_statistics", "projects", on_delete: :cascade
add_foreign_key "protected_branch_merge_access_levels", "protected_branches"
add_foreign_key "protected_branch_push_access_levels", "protected_branches"
add_foreign_key "subscriptions", "projects", on_delete: :cascade
add_foreign_key "trending_projects", "projects", on_delete: :cascade
add_foreign_key "u2f_registrations", "users"
-end
+end \ No newline at end of file
diff --git a/doc/administration/build_artifacts.md b/doc/administration/build_artifacts.md
index 3ba8387c7f0..cca422892ec 100644
--- a/doc/administration/build_artifacts.md
+++ b/doc/administration/build_artifacts.md
@@ -88,3 +88,9 @@ artifacts through the [Admin area settings](../user/admin_area/settings/continuo
[reconfigure gitlab]: restart_gitlab.md "How to restart GitLab"
[restart gitlab]: restart_gitlab.md "How to restart GitLab"
+
+## Storage statistics
+
+You can see the total storage used for build artifacts on groups and projects
+in the administration area, as well as through the [groups](../api/groups.md)
+and [projects APIs](../api/projects.md).
diff --git a/doc/api/deploy_keys.md b/doc/api/deploy_keys.md
index 5f248ab6f91..284d5f88c55 100644
--- a/doc/api/deploy_keys.md
+++ b/doc/api/deploy_keys.md
@@ -20,12 +20,14 @@ Example response:
"id": 1,
"title": "Public key",
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
+ "can_push": false,
"created_at": "2013-10-02T10:12:29Z"
},
{
"id": 3,
"title": "Another Public key",
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
+ "can_push": true,
"created_at": "2013-10-02T11:12:29Z"
}
]
@@ -55,12 +57,14 @@ Example response:
"id": 1,
"title": "Public key",
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
+ "can_push": false,
"created_at": "2013-10-02T10:12:29Z"
},
{
"id": 3,
"title": "Another Public key",
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
+ "can_push": false,
"created_at": "2013-10-02T11:12:29Z"
}
]
@@ -92,6 +96,7 @@ Example response:
"id": 1,
"title": "Public key",
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
+ "can_push": false,
"created_at": "2013-10-02T10:12:29Z"
}
```
@@ -107,14 +112,15 @@ project only if original one was is accessible by the same user.
POST /projects/:id/deploy_keys
```
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of the project |
-| `title` | string | yes | New deploy key's title |
-| `key` | string | yes | New deploy key |
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `id` | integer | yes | The ID of the project |
+| `title` | string | yes | New deploy key's title |
+| `key` | string | yes | New deploy key |
+| `can_push` | boolean | no | Can deploy key push to the project's repository |
```bash
-curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --header "Content-Type: application/json" --data '{"title": "My deploy key", "key": "ssh-rsa AAAA..."}' "https://gitlab.example.com/api/v3/projects/5/deploy_keys/"
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --header "Content-Type: application/json" --data '{"title": "My deploy key", "key": "ssh-rsa AAAA...", "can_push": "true"}' "https://gitlab.example.com/api/v3/projects/5/deploy_keys/"
```
Example response:
@@ -124,6 +130,7 @@ Example response:
"key" : "ssh-rsa AAAA...",
"id" : 12,
"title" : "My deploy key",
+ "can_push": true,
"created_at" : "2015-08-29T12:44:31.550Z"
}
```
diff --git a/doc/api/groups.md b/doc/api/groups.md
index 134d7bda22f..bc737bff8ee 100644
--- a/doc/api/groups.md
+++ b/doc/api/groups.md
@@ -13,6 +13,7 @@ Parameters:
| `search` | string | no | Return list of authorized groups matching the search criteria |
| `order_by` | string | no | Order groups by `name` or `path`. Default is `name` |
| `sort` | string | no | Order groups in `asc` or `desc` order. Default is `asc` |
+| `statistics` | boolean | no | Include group statistics (admins only) |
```
GET /groups
@@ -31,7 +32,6 @@ GET /groups
You can search for groups by name or path, see below.
-=======
## List owned groups
Get a list of groups which are owned by the authenticated user.
@@ -40,6 +40,12 @@ Get a list of groups which are owned by the authenticated user.
GET /groups/owned
```
+Parameters:
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `statistics` | boolean | no | Include group statistics |
+
## List a group's projects
Get a list of projects in this group.
diff --git a/doc/api/projects.md b/doc/api/projects.md
index edffad555a5..122075bbd11 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -307,6 +307,8 @@ Parameters:
| `order_by` | string | no | Return projects ordered by `id`, `name`, `path`, `created_at`, `updated_at`, or `last_activity_at` fields. Default is `created_at` |
| `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` |
| `search` | string | no | Return list of authorized projects matching the search criteria |
+| `simple` | boolean | no | Return only the ID, URL, name, and path of each project |
+| `statistics` | boolean | no | Include project statistics |
### List starred projects
@@ -325,6 +327,7 @@ Parameters:
| `order_by` | string | no | Return projects ordered by `id`, `name`, `path`, `created_at`, `updated_at`, or `last_activity_at` fields. Default is `created_at` |
| `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` |
| `search` | string | no | Return list of authorized projects matching the search criteria |
+| `simple` | boolean | no | Return only the ID, URL, name, and path of each project |
### List ALL projects
@@ -343,6 +346,7 @@ Parameters:
| `order_by` | string | no | Return projects ordered by `id`, `name`, `path`, `created_at`, `updated_at`, or `last_activity_at` fields. Default is `created_at` |
| `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` |
| `search` | string | no | Return list of authorized projects matching the search criteria |
+| `statistics` | boolean | no | Include project statistics |
### Get single project
diff --git a/doc/api/settings.md b/doc/api/settings.md
index 218546aafea..0bd38a6e664 100644
--- a/doc/api/settings.md
+++ b/doc/api/settings.md
@@ -79,6 +79,7 @@ PUT /application/settings
| `enabled_git_access_protocol` | string | no | Enabled protocols for Git access. Allowed values are: `ssh`, `http`, and `nil` to allow both protocols. |
| `koding_enabled` | boolean | no | Enable Koding integration. Default is `false`. |
| `koding_url` | string | yes (if `koding_enabled` is `true`) | The Koding instance URL for integration. |
+| `disabled_oauth_sign_in_sources` | Array of strings | no | Disabled OAuth sign-in sources |
```bash
curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/application/settings?signup_enabled=false&default_project_visibility=1
diff --git a/doc/ci/README.md b/doc/ci/README.md
index 6a9495f8892..dd14698e9cd 100644
--- a/doc/ci/README.md
+++ b/doc/ci/README.md
@@ -23,7 +23,7 @@
- [CI/CD pipelines settings](../user/project/pipelines/settings.md)
- [Review Apps](review_apps/index.md)
- [Git submodules](git_submodules.md) Using Git submodules in your CI jobs
-- [Autodeploy](autodeploy/index.md)
+- [Auto deploy](autodeploy/index.md)
## Breaking changes
diff --git a/doc/ci/autodeploy/img/auto_deploy_button.png b/doc/ci/autodeploy/img/auto_deploy_button.png
new file mode 100644
index 00000000000..423e76a6cda
--- /dev/null
+++ b/doc/ci/autodeploy/img/auto_deploy_button.png
Binary files differ
diff --git a/doc/ci/autodeploy/img/auto_deploy_dropdown.png b/doc/ci/autodeploy/img/auto_deploy_dropdown.png
new file mode 100644
index 00000000000..957870ec8c7
--- /dev/null
+++ b/doc/ci/autodeploy/img/auto_deploy_dropdown.png
Binary files differ
diff --git a/doc/ci/autodeploy/img/autodeploy_button.png b/doc/ci/autodeploy/img/autodeploy_button.png
deleted file mode 100644
index 9e2cd57a0ba..00000000000
--- a/doc/ci/autodeploy/img/autodeploy_button.png
+++ /dev/null
Binary files differ
diff --git a/doc/ci/autodeploy/img/autodeploy_dropdown.png b/doc/ci/autodeploy/img/autodeploy_dropdown.png
deleted file mode 100644
index d2733de83df..00000000000
--- a/doc/ci/autodeploy/img/autodeploy_dropdown.png
+++ /dev/null
Binary files differ
diff --git a/doc/ci/autodeploy/index.md b/doc/ci/autodeploy/index.md
index 9c79d8c457e..630207ffa09 100644
--- a/doc/ci/autodeploy/index.md
+++ b/doc/ci/autodeploy/index.md
@@ -1,8 +1,8 @@
-# Autodeploy
+# Auto deploy
> [Introduced][mr-8135] in GitLab 8.15.
-Autodeploy is an easy way to configure GitLab CI for the deployment of your
+Auto deploy is an easy way to configure GitLab CI for the deployment of your
application. GitLab Community maintains a list of `.gitlab-ci.yml`
templates for various infrastructure providers and deployment scripts
powering them. These scripts are responsible for packaging your application,
@@ -15,7 +15,7 @@ deployment.
## Supported templates
-The list of supported autodeploy templates is available [here][autodeploy-templates].
+The list of supported auto deploy templates is available [here][auto-deploy-templates].
## Configuration
@@ -24,17 +24,17 @@ credentials. For example, if you want to deploy to OpenShift you have to
enable [Kubernetes service][kubernetes-service].
1. Configure GitLab Runner to use Docker or Kubernetes executor with
[privileged mode enabled][docker-in-docker].
-1. Navigate to the "Project" tab and click "Set up autodeploy" button.
- ![Autodeploy button](img/autodeploy_button.png)
+1. Navigate to the "Project" tab and click "Set up auto deploy" button.
+ ![Auto deploy button](img/auto_deploy_button.png)
1. Select a template.
- ![Dropdown with autodeploy templates](img/autodeploy_dropdown.png)
+ ![Dropdown with auto deploy templates](img/auto_deploy_dropdown.png)
1. Commit your changes and create a merge request.
1. Test your deployment configuration using a [Review App][review-app] that was
created automatically for you.
[mr-8135]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8135
[project-services]: ../../project_services/project_services.md
-[autodeploy-templates]: https://gitlab.com/gitlab-org/gitlab-ci-yml/tree/master/autodeploy
+[auto-deploy-templates]: https://gitlab.com/gitlab-org/gitlab-ci-yml/tree/master/autodeploy
[kubernetes-service]: ../../project_services/kubernetes.md
[docker-in-docker]: ../docker/using_docker_build.md#use-docker-in-docker-executor
[review-app]: ../review_apps/index.md
diff --git a/doc/development/sidekiq_debugging.md b/doc/development/sidekiq_debugging.md
index cea11e5f126..d6d770e27c1 100644
--- a/doc/development/sidekiq_debugging.md
+++ b/doc/development/sidekiq_debugging.md
@@ -3,12 +3,15 @@
## Log arguments to Sidekiq jobs
If you want to see what arguments are being passed to Sidekiq jobs you can set
-the SIDEKIQ_LOG_ARGUMENTS environment variable.
+the `SIDEKIQ_LOG_ARGUMENTS` [environment variable]
+(https://docs.gitlab.com/omnibus/settings/environment-variables.html) to `1` (true).
+
+Example:
```
-SIDEKIQ_LOG_ARGUMENTS=1 bundle exec foreman start
+gitlab_rails['env'] = {"SIDEKIQ_LOG_ARGUMENTS" => "1"}
```
-It is not recommend to enable this setting in production because some Sidekiq
-jobs (such as sending a password reset email) take secret arguments (for
-example the password reset token).
+Please note: It is not recommend to enable this setting in production because some
+Sidekiq jobs (such as sending a password reset email) take secret arguments (for
+example the password reset token). \ No newline at end of file
diff --git a/doc/development/ux_guide/animation.md b/doc/development/ux_guide/animation.md
index daeb15460c2..903e54bf9dc 100644
--- a/doc/development/ux_guide/animation.md
+++ b/doc/development/ux_guide/animation.md
@@ -39,4 +39,19 @@ When information is updating in place, a quick, subtle animation is needed. The
![Quick update animation](img/animation-quickupdate.gif)
-> TODO: Add guidance for other kinds of animation \ No newline at end of file
+### Moving transitions
+
+When elements move on screen, there should be a quick animation so it is clear to users what moved where. The timing of this animation differs based on the amount of movement and change. Consider animations between `200ms` and `400ms`.
+
+#### Shifting elements on reorder
+An example of a moving transition is when elements have to move out of the way when you drag an element.
+
+View the [interactive example](http://codepen.io/awhildy/full/ALyKPE/) here.
+
+![Reorder animation](img/animation-reorder.gif)
+
+#### Autoscroll the page
+Another example of a moving transition is when you have to autoscroll the page to keep an active element visible.
+
+View the [interactive example](http://codepen.io/awhildy/full/PbxgVo/) here.
+![Autoscroll animation](img/animation-autoscroll.gif) \ No newline at end of file
diff --git a/doc/development/ux_guide/basics.md b/doc/development/ux_guide/basics.md
index e81729556d8..76ec7fd466b 100644
--- a/doc/development/ux_guide/basics.md
+++ b/doc/development/ux_guide/basics.md
@@ -50,13 +50,13 @@ GitLab uses Font Awesome icons throughout our interface.
## Color
-| | |
-| :------: | :------- |
-| ![Blue](img/color-blue.png) | Blue is used to highlight primary active elements (such as the current tab), as well as other organizational and managing commands.|
-| ![Green](img/color-green.png) | Green is for actions that create new objects. |
-| ![Orange](img/color-orange.png) | Orange is used for warnings |
-| ![Red](img/color-red.png) | Red is reserved for delete and other destructive commands |
-| ![Grey](img/color-grey.png) | Grey is used for neutral secondary elements. Depending on context, white is sometimes used instead. |
+| | State | Action |
+| :------: | :------- | :------- |
+| ![Blue](img/color-blue.png) | Primary and active (such as the current tab) | Organizational, managing, and retry commands|
+| ![Green](img/color-green.png) | Opened | Create new objects |
+| ![Orange](img/color-orange.png) | Warning | Non destructive action |
+| ![Red](img/color-red.png) | Closed | Delete and other destructive commands |
+| ![Grey](img/color-grey.png) | Neutral | Neutral secondary commands |
> TODO: Establish a perspective for color in terms of our personality and rationalize with Marketing usage.
diff --git a/doc/development/ux_guide/copy.md b/doc/development/ux_guide/copy.md
index 8896d450f14..e48897426cb 100644
--- a/doc/development/ux_guide/copy.md
+++ b/doc/development/ux_guide/copy.md
@@ -101,3 +101,11 @@ The form should be titled `Edit issue`. The submit button should be labeled `Sav
| Approve | Approve an open merge request ||
| Remove approval, unapproved | Remove approval of an open merge request | Do not use `unapprove` as that is not an English word|
| Merge | Merge an open merge request ||
+
+### Comments & Discussions
+
+#### Comment
+A **comment** is a written piece of text that users of GitLab can create. Comments have the meta data of author and time stamp. Comments can be added in a variety of contexts, such as issues, merge requests, and discussions.
+
+#### Dicussion
+A **discussion** is a group of 1 or more comments. A discussion can include sub discussions. Some discussions have the special capability of being able to be **resolved**. Both the comments in the discussion and the discussion itself can be resolved. \ No newline at end of file
diff --git a/doc/development/ux_guide/img/animation-autoscroll.gif b/doc/development/ux_guide/img/animation-autoscroll.gif
new file mode 100644
index 00000000000..155b0234c64
--- /dev/null
+++ b/doc/development/ux_guide/img/animation-autoscroll.gif
Binary files differ
diff --git a/doc/development/ux_guide/img/animation-reorder.gif b/doc/development/ux_guide/img/animation-reorder.gif
new file mode 100644
index 00000000000..ccdeb3d396f
--- /dev/null
+++ b/doc/development/ux_guide/img/animation-reorder.gif
Binary files differ
diff --git a/doc/workflow/lfs/lfs_administration.md b/doc/workflow/lfs/lfs_administration.md
index b3c73e947f0..5f6a718135d 100644
--- a/doc/workflow/lfs/lfs_administration.md
+++ b/doc/workflow/lfs/lfs_administration.md
@@ -40,6 +40,12 @@ In `config/gitlab.yml`:
storage_path: /mnt/storage/lfs-objects
```
+## Storage statistics
+
+You can see the total storage used for LFS objects on groups and projects
+in the administration area, as well as through the [groups](../api/groups.md)
+and [projects APIs](../api/projects.md).
+
## Known limitations
* Currently, storing GitLab Git LFS objects on a non-local storage (like S3 buckets)
@@ -47,3 +53,5 @@ In `config/gitlab.yml`:
* Currently, removing LFS objects from GitLab Git LFS storage is not supported
* LFS authentications via SSH was added with GitLab 8.12
* Only compatible with the GitLFS client versions 1.1.0 and up, or 1.0.2.
+* The storage statistics currently count each LFS object multiple times for
+ every project linking to it
diff --git a/features/admin/deploy_keys.feature b/features/admin/deploy_keys.feature
new file mode 100644
index 00000000000..95ac77cddd2
--- /dev/null
+++ b/features/admin/deploy_keys.feature
@@ -0,0 +1,23 @@
+@admin
+Feature: Admin Deploy Keys
+ Background:
+ Given I sign in as an admin
+ And there are public deploy keys in system
+
+ Scenario: Deploy Keys list
+ When I visit admin deploy keys page
+ Then I should see all public deploy keys
+
+ Scenario: Deploy Keys new
+ When I visit admin deploy keys page
+ And I click 'New Deploy Key'
+ And I submit new deploy key
+ Then I should be on admin deploy keys page
+ And I should see newly created deploy key without write access
+
+ Scenario: Deploy Keys new with write access
+ When I visit admin deploy keys page
+ And I click 'New Deploy Key'
+ And I submit new deploy key with write access
+ Then I should be on admin deploy keys page
+ And I should see newly created deploy key with write access
diff --git a/features/steps/admin/deploy_keys.rb b/features/steps/admin/deploy_keys.rb
new file mode 100644
index 00000000000..79312a5d1c5
--- /dev/null
+++ b/features/steps/admin/deploy_keys.rb
@@ -0,0 +1,59 @@
+class Spinach::Features::AdminDeployKeys < Spinach::FeatureSteps
+ include SharedAuthentication
+ include SharedPaths
+ include SharedAdmin
+
+ step 'there are public deploy keys in system' do
+ create(:deploy_key, public: true)
+ create(:another_deploy_key, public: true)
+ end
+
+ step 'I should see all public deploy keys' do
+ DeployKey.are_public.each do |p|
+ expect(page).to have_content p.title
+ end
+ end
+
+ step 'I visit admin deploy key page' do
+ visit admin_deploy_key_path(deploy_key)
+ end
+
+ step 'I visit admin deploy keys page' do
+ visit admin_deploy_keys_path
+ end
+
+ step 'I click \'New Deploy Key\'' do
+ click_link 'New Deploy Key'
+ end
+
+ step 'I submit new deploy key' do
+ fill_in "deploy_key_title", with: "laptop"
+ fill_in "deploy_key_key", with: "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAzrEJUIR6Y03TCE9rIJ+GqTBvgb8t1jI9h5UBzCLuK4VawOmkLornPqLDrGbm6tcwM/wBrrLvVOqi2HwmkKEIecVO0a64A4rIYScVsXIniHRS6w5twyn1MD3sIbN+socBDcaldECQa2u1dI3tnNVcs8wi77fiRe7RSxePsJceGoheRQgC8AZ510UdIlO+9rjIHUdVN7LLyz512auAfYsgx1OfablkQ/XJcdEwDNgi9imI6nAXhmoKUm1IPLT2yKajTIC64AjLOnE0YyCh6+7RFMpiMyu1qiOCpdjYwTgBRiciNRZCH8xIedyCoAmiUgkUT40XYHwLuwiPJICpkAzp7Q== user@laptop"
+ click_button "Create"
+ end
+
+ step 'I submit new deploy key with write access' do
+ fill_in "deploy_key_title", with: "server"
+ fill_in "deploy_key_key", with: "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAzrEJUIR6Y03TCE9rIJ+GqTBvgb8t1jI9h5UBzCLuK4VawOmkLornPqLDrGbm6tcwM/wBrrLvVOqi2HwmkKEIecVO0a64A4rIYScVsXIniHRS6w5twyn1MD3sIbN+socBDcaldECQa2u1dI3tnNVcs8wi77fiRe7RSxePsJceGoheRQgC8AZ510UdIlO+9rjIHUdVN7LLyz512auAfYsgx1OfablkQ/XJcdEwDNgi9imI6nAXhmoKUm1IPLT2yKajTIC64AjLOnE0YyCh6+7RFMpiMyu1qiOCpdjYwTgBRiciNRZCH8xIedyCoAmiUgkUT40XYHwLuwiPJICpkAzp7Q== user@laptop"
+ check "deploy_key_can_push"
+ click_button "Create"
+ end
+
+ step 'I should be on admin deploy keys page' do
+ expect(current_path).to eq admin_deploy_keys_path
+ end
+
+ step 'I should see newly created deploy key without write access' do
+ expect(page).to have_content(deploy_key.title)
+ expect(page).to have_content('No')
+ end
+
+ step 'I should see newly created deploy key with write access' do
+ expect(page).to have_content(deploy_key.title)
+ expect(page).to have_content('Yes')
+ end
+
+ def deploy_key
+ @deploy_key ||= DeployKey.are_public.first
+ end
+end
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index dfbb3ab86dd..d2fadf6a3d0 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -78,21 +78,21 @@ module API
expose :container_registry_enabled
# Expose old field names with the new permissions methods to keep API compatible
- expose(:issues_enabled) { |project, options| project.feature_available?(:issues, options[:user]) }
- expose(:merge_requests_enabled) { |project, options| project.feature_available?(:merge_requests, options[:user]) }
- expose(:wiki_enabled) { |project, options| project.feature_available?(:wiki, options[:user]) }
- expose(:builds_enabled) { |project, options| project.feature_available?(:builds, options[:user]) }
- expose(:snippets_enabled) { |project, options| project.feature_available?(:snippets, options[:user]) }
+ expose(:issues_enabled) { |project, options| project.feature_available?(:issues, options[:current_user]) }
+ expose(:merge_requests_enabled) { |project, options| project.feature_available?(:merge_requests, options[:current_user]) }
+ expose(:wiki_enabled) { |project, options| project.feature_available?(:wiki, options[:current_user]) }
+ expose(:builds_enabled) { |project, options| project.feature_available?(:builds, options[:current_user]) }
+ expose(:snippets_enabled) { |project, options| project.feature_available?(:snippets, options[:current_user]) }
expose :created_at, :last_activity_at
expose :shared_runners_enabled
expose :lfs_enabled?, as: :lfs_enabled
expose :creator_id
- expose :namespace
+ expose :namespace, using: 'API::Entities::Namespace'
expose :forked_from_project, using: Entities::BasicProjectDetails, if: lambda{ |project, options| project.forked? }
expose :avatar_url
expose :star_count, :forks_count
- expose :open_issues_count, if: lambda { |project, options| project.feature_available?(:issues, options[:user]) && project.default_issues_tracker? }
+ expose :open_issues_count, if: lambda { |project, options| project.feature_available?(:issues, options[:current_user]) && project.default_issues_tracker? }
expose :runners_token, if: lambda { |_project, options| options[:user_can_admin_project] }
expose :public_builds
expose :shared_with_groups do |project, options|
@@ -101,6 +101,16 @@ module API
expose :only_allow_merge_if_build_succeeds
expose :request_access_enabled
expose :only_allow_merge_if_all_discussions_are_resolved
+
+ expose :statistics, using: 'API::Entities::ProjectStatistics', if: :statistics
+ end
+
+ class ProjectStatistics < Grape::Entity
+ expose :commit_count
+ expose :storage_size
+ expose :repository_size
+ expose :lfs_objects_size
+ expose :build_artifacts_size
end
class Member < UserBasic
@@ -127,6 +137,15 @@ module API
expose :avatar_url
expose :web_url
expose :request_access_enabled
+
+ expose :statistics, if: :statistics do
+ with_options format_with: -> (value) { value.to_i } do
+ expose :storage_size
+ expose :repository_size
+ expose :lfs_objects_size
+ expose :build_artifacts_size
+ end
+ end
end
class GroupDetail < Group
@@ -298,7 +317,7 @@ module API
end
class SSHKey < Grape::Entity
- expose :id, :title, :key, :created_at
+ expose :id, :title, :key, :created_at, :can_push
end
class SSHKeyWithUser < SSHKey
@@ -391,7 +410,7 @@ module API
end
class Namespace < Grape::Entity
- expose :id, :path, :kind
+ expose :id, :name, :path, :kind
end
class MemberAccess < Grape::Entity
@@ -440,12 +459,12 @@ module API
class ProjectWithAccess < Project
expose :permissions do
expose :project_access, using: Entities::ProjectAccess do |project, options|
- project.project_members.find_by(user_id: options[:user].id)
+ project.project_members.find_by(user_id: options[:current_user].id)
end
expose :group_access, using: Entities::GroupAccess do |project, options|
if project.group
- project.group.group_members.find_by(user_id: options[:user].id)
+ project.group.group_members.find_by(user_id: options[:current_user].id)
end
end
end
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index 9b9d3df7435..e04d2e40fb6 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -11,6 +11,20 @@ module API
optional :lfs_enabled, type: Boolean, desc: 'Enable/disable LFS for the projects in this group'
optional :request_access_enabled, type: Boolean, desc: 'Allow users to request member access'
end
+
+ params :statistics_params do
+ optional :statistics, type: Boolean, default: false, desc: 'Include project statistics'
+ end
+
+ def present_groups(groups, options = {})
+ options = options.reverse_merge(
+ with: Entities::Group,
+ current_user: current_user,
+ )
+
+ groups = groups.with_statistics if options[:statistics]
+ present paginate(groups), options
+ end
end
resource :groups do
@@ -18,6 +32,7 @@ module API
success Entities::Group
end
params do
+ use :statistics_params
optional :skip_groups, type: Array[Integer], desc: 'Array of group ids to exclude from list'
optional :all_available, type: Boolean, desc: 'Show all group that you have access to'
optional :search, type: String, desc: 'Search for a specific group'
@@ -38,7 +53,7 @@ module API
groups = groups.where.not(id: params[:skip_groups]) if params[:skip_groups].present?
groups = groups.reorder(params[:order_by] => params[:sort])
- present paginate(groups), with: Entities::Group
+ present_groups groups, statistics: params[:statistics] && current_user.is_admin?
end
desc 'Get list of owned groups for authenticated user' do
@@ -46,10 +61,10 @@ module API
end
params do
use :pagination
+ use :statistics_params
end
get '/owned' do
- groups = current_user.owned_groups
- present paginate(groups), with: Entities::Group, user: current_user
+ present_groups current_user.owned_groups, statistics: params[:statistics]
end
desc 'Create a group. Available only for users who can create groups.' do
@@ -66,7 +81,7 @@ module API
group = ::Groups::CreateService.new(current_user, declared_params(include_missing: false)).execute
if group.persisted?
- present group, with: Entities::Group
+ present group, with: Entities::Group, current_user: current_user
else
render_api_error!("Failed to save group #{group.errors.messages}", 400)
end
@@ -92,7 +107,7 @@ module API
authorize! :admin_group, group
if ::Groups::UpdateService.new(group, current_user, declared_params(include_missing: false)).execute
- present group, with: Entities::GroupDetail
+ present group, with: Entities::GroupDetail, current_user: current_user
else
render_validation_error!(group)
end
@@ -103,7 +118,7 @@ module API
end
get ":id" do
group = find_group!(params[:id])
- present group, with: Entities::GroupDetail
+ present group, with: Entities::GroupDetail, current_user: current_user
end
desc 'Remove a group.'
@@ -134,7 +149,7 @@ module API
projects = GroupProjectsFinder.new(group).execute(current_user)
projects = filter_projects(projects)
entity = params[:simple] ? Entities::BasicProjectDetails : Entities::Project
- present paginate(projects), with: entity, user: current_user
+ present paginate(projects), with: entity, current_user: current_user
end
desc 'Transfer a project to the group namespace. Available only for admin.' do
@@ -150,7 +165,7 @@ module API
result = ::Projects::TransferService.new(project, current_user).execute(group)
if result
- present group, with: Entities::GroupDetail
+ present group, with: Entities::GroupDetail, current_user: current_user
else
render_api_error!("Failed to transfer project #{project.errors.messages}", 400)
end
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 106694e13df..20b5bc1502a 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -91,7 +91,7 @@ module API
end
def authenticate_non_get!
- authenticate! unless %w[GET HEAD].include?(route.route_method)
+ authenticate! unless %w[GET HEAD].include?(route.request_method)
end
def authenticate_by_gitlab_shell_token!
@@ -243,7 +243,7 @@ module API
rack_response({ 'message' => '500 Internal Server Error' }.to_json, 500)
end
- # Projects helpers
+ # project helpers
def filter_projects(projects)
if params[:search].present?
diff --git a/lib/api/notes.rb b/lib/api/notes.rb
index d0faf17714b..284e4cf549a 100644
--- a/lib/api/notes.rb
+++ b/lib/api/notes.rb
@@ -69,8 +69,6 @@ module API
optional :created_at, type: String, desc: 'The creation date of the note'
end
post ":id/#{noteables_str}/:noteable_id/notes" do
- required_attributes! [:body]
-
opts = {
note: params[:body],
noteable_type: noteables_str.classify,
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 2929d2157dc..3be14e8eb76 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -40,6 +40,15 @@ module API
resource :projects do
helpers do
+ params :collection_params do
+ use :sort_params
+ use :filter_params
+ use :pagination
+
+ optional :simple, type: Boolean, default: false,
+ desc: 'Return only the ID, URL, name, and path of each project'
+ end
+
params :sort_params do
optional :order_by, type: String, values: %w[id name path created_at updated_at last_activity_at],
default: 'created_at', desc: 'Return projects ordered by field'
@@ -52,97 +61,94 @@ module API
optional :visibility, type: String, values: %w[public internal private],
desc: 'Limit by visibility'
optional :search, type: String, desc: 'Return list of authorized projects matching the search criteria'
- use :sort_params
+ end
+
+ params :statistics_params do
+ optional :statistics, type: Boolean, default: false, desc: 'Include project statistics'
end
params :create_params do
optional :namespace_id, type: Integer, desc: 'Namespace ID for the new project. Default to the user namespace.'
optional :import_url, type: String, desc: 'URL from which the project is imported'
end
+
+ def present_projects(projects, options = {})
+ options = options.reverse_merge(
+ with: Entities::Project,
+ current_user: current_user,
+ simple: params[:simple],
+ )
+
+ projects = filter_projects(projects)
+ projects = projects.with_statistics if options[:statistics]
+ options[:with] = Entities::BasicProjectDetails if options[:simple]
+
+ present paginate(projects), options
+ end
end
desc 'Get a list of visible projects for authenticated user' do
success Entities::BasicProjectDetails
end
params do
- optional :simple, type: Boolean, default: false,
- desc: 'Return only the ID, URL, name, and path of each project'
- use :filter_params
- use :pagination
+ use :collection_params
end
get '/visible' do
- projects = ProjectsFinder.new.execute(current_user)
- projects = filter_projects(projects)
- entity = params[:simple] || !current_user ? Entities::BasicProjectDetails : Entities::ProjectWithAccess
-
- present paginate(projects), with: entity, user: current_user
+ entity = current_user ? Entities::ProjectWithAccess : Entities::BasicProjectDetails
+ present_projects ProjectsFinder.new.execute(current_user), with: entity
end
desc 'Get a projects list for authenticated user' do
success Entities::BasicProjectDetails
end
params do
- optional :simple, type: Boolean, default: false,
- desc: 'Return only the ID, URL, name, and path of each project'
- use :filter_params
- use :pagination
+ use :collection_params
end
get do
authenticate!
- projects = current_user.authorized_projects
- projects = filter_projects(projects)
- entity = params[:simple] ? Entities::BasicProjectDetails : Entities::ProjectWithAccess
-
- present paginate(projects), with: entity, user: current_user
+ present_projects current_user.authorized_projects,
+ with: Entities::ProjectWithAccess
end
desc 'Get an owned projects list for authenticated user' do
success Entities::BasicProjectDetails
end
params do
- use :filter_params
- use :pagination
+ use :collection_params
+ use :statistics_params
end
get '/owned' do
authenticate!
- projects = current_user.owned_projects
- projects = filter_projects(projects)
-
- present paginate(projects), with: Entities::ProjectWithAccess, user: current_user
+ present_projects current_user.owned_projects,
+ with: Entities::ProjectWithAccess,
+ statistics: params[:statistics]
end
desc 'Gets starred project for the authenticated user' do
success Entities::BasicProjectDetails
end
params do
- use :filter_params
- use :pagination
+ use :collection_params
end
get '/starred' do
authenticate!
- projects = current_user.viewable_starred_projects
- projects = filter_projects(projects)
-
- present paginate(projects), with: Entities::Project, user: current_user
+ present_projects current_user.viewable_starred_projects
end
desc 'Get all projects for admin user' do
success Entities::BasicProjectDetails
end
params do
- use :filter_params
- use :pagination
+ use :collection_params
+ use :statistics_params
end
get '/all' do
authenticated_as_admin!
- projects = Project.all
- projects = filter_projects(projects)
-
- present paginate(projects), with: Entities::ProjectWithAccess, user: current_user
+ present_projects Project.all, with: Entities::ProjectWithAccess, statistics: params[:statistics]
end
desc 'Search for projects the current user has access to' do
@@ -221,7 +227,7 @@ module API
end
get ":id" do
entity = current_user ? Entities::ProjectWithAccess : Entities::BasicProjectDetails
- present user_project, with: entity, user: current_user,
+ present user_project, with: entity, current_user: current_user,
user_can_admin_project: can?(current_user, :admin_project, user_project)
end
diff --git a/lib/api/settings.rb b/lib/api/settings.rb
index c4cb1c7924a..9eb9a105bde 100644
--- a/lib/api/settings.rb
+++ b/lib/api/settings.rb
@@ -9,23 +9,117 @@ module API
end
end
- # Get current applicaiton settings
- #
- # Example Request:
- # GET /application/settings
+ desc 'Get the current application settings' do
+ success Entities::ApplicationSetting
+ end
get "application/settings" do
present current_settings, with: Entities::ApplicationSetting
end
- # Modify application settings
- #
- # Example Request:
- # PUT /application/settings
+ desc 'Modify application settings' do
+ success Entities::ApplicationSetting
+ end
+ params do
+ optional :default_branch_protection, type: Integer, values: [0, 1, 2], desc: 'Determine if developers can push to master'
+ optional :default_project_visibility, type: Integer, values: Gitlab::VisibilityLevel.values, desc: 'The default project visibility'
+ optional :default_snippet_visibility, type: Integer, values: Gitlab::VisibilityLevel.values, desc: 'The default snippet visibility'
+ optional :default_group_visibility, type: Integer, values: Gitlab::VisibilityLevel.values, desc: 'The default group visibility'
+ optional :restricted_visibility_levels, type: Array[String], desc: 'Selected levels cannot be used by non-admin users for projects or snippets. If the public level is restricted, user profiles are only visible to logged in users.'
+ optional :import_sources, type: Array[String], values: %w[github bitbucket gitlab google_code fogbugz git gitlab_project],
+ desc: 'Enabled sources for code import during project creation. OmniAuth must be configured for GitHub, Bitbucket, and GitLab.com'
+ optional :disabled_oauth_sign_in_sources, type: Array[String], desc: 'Disable certain OAuth sign-in sources'
+ optional :enabled_git_access_protocol, type: String, values: %w[ssh http nil], desc: 'Allow only the selected protocols to be used for Git access.'
+ optional :gravatar_enabled, type: Boolean, desc: 'Flag indicating if the Gravatar service is enabled'
+ optional :default_projects_limit, type: Integer, desc: 'The maximum number of personal projects'
+ optional :max_attachment_size, type: Integer, desc: 'Maximum attachment size in MB'
+ optional :session_expire_delay, type: Integer, desc: 'Session duration in minutes. GitLab restart is required to apply changes.'
+ optional :user_oauth_applications, type: Boolean, desc: 'Allow users to register any application to use GitLab as an OAuth provider'
+ optional :user_default_external, type: Boolean, desc: 'Newly registered users will by default be external'
+ optional :signup_enabled, type: Boolean, desc: 'Flag indicating if sign up is enabled'
+ optional :send_user_confirmation_email, type: Boolean, desc: 'Send confirmation email on sign-up'
+ optional :domain_whitelist, type: String, desc: 'ONLY users with e-mail addresses that match these domain(s) will be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: domain.com, *.domain.com'
+ optional :domain_blacklist_enabled, type: Boolean, desc: 'Enable domain blacklist for sign ups'
+ given domain_blacklist_enabled: ->(val) { val } do
+ requires :domain_blacklist, type: String, desc: 'Users with e-mail addresses that match these domain(s) will NOT be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: domain.com, *.domain.com'
+ end
+ optional :after_sign_up_text, type: String, desc: 'Text shown after sign up'
+ optional :signin_enabled, type: Boolean, desc: 'Flag indicating if sign in is enabled'
+ optional :require_two_factor_authentication, type: Boolean, desc: 'Require all users to setup Two-factor authentication'
+ given require_two_factor_authentication: ->(val) { val } do
+ requires :two_factor_grace_period, type: Integer, desc: 'Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication'
+ end
+ optional :home_page_url, type: String, desc: 'We will redirect non-logged in users to this page'
+ optional :after_sign_out_path, type: String, desc: 'We will redirect users to this page after they sign out'
+ optional :sign_in_text, type: String, desc: 'The sign in text of the GitLab application'
+ optional :help_page_text, type: String, desc: 'Custom text displayed on the help page'
+ optional :shared_runners_enabled, type: Boolean, desc: 'Enable shared runners for new projects'
+ given shared_runners_enabled: ->(val) { val } do
+ requires :shared_runners_text, type: String, desc: 'Shared runners text '
+ end
+ optional :max_artifacts_size, type: Integer, desc: "Set the maximum file size each build's artifacts can have"
+ optional :container_registry_token_expire_delay, type: Integer, desc: 'Authorization token duration (minutes)'
+ optional :metrics_enabled, type: Boolean, desc: 'Enable the InfluxDB metrics'
+ given metrics_enabled: ->(val) { val } do
+ requires :metrics_host, type: String, desc: 'The InfluxDB host'
+ requires :metrics_port, type: Integer, desc: 'The UDP port to use for connecting to InfluxDB'
+ requires :metrics_pool_size, type: Integer, desc: 'The amount of InfluxDB connections to open'
+ requires :metrics_timeout, type: Integer, desc: 'The amount of seconds after which an InfluxDB connection will time out'
+ requires :metrics_method_call_threshold, type: Integer, desc: 'A method call is only tracked when it takes longer to complete than the given amount of milliseconds.'
+ requires :metrics_sample_interval, type: Integer, desc: 'The sampling interval in seconds'
+ requires :metrics_packet_size, type: Integer, desc: 'The amount of points to store in a single UDP packet'
+ end
+ optional :sidekiq_throttling_enabled, type: Boolean, desc: 'Enable Sidekiq Job Throttling'
+ given sidekiq_throttling_enabled: ->(val) { val } do
+ requires :sidekiq_throttling_queus, type: Array[String], desc: 'Choose which queues you wish to throttle'
+ requires :sidekiq_throttling_factor, type: Float, desc: 'The factor by which the queues should be throttled. A value between 0.0 and 1.0, exclusive.'
+ end
+ optional :recaptcha_enabled, type: Boolean, desc: 'Helps prevent bots from creating accounts'
+ given recaptcha_enabled: ->(val) { val } do
+ requires :recaptcha_site_key, type: String, desc: 'Generate site key at http://www.google.com/recaptcha'
+ requires :recaptcha_private_key, type: String, desc: 'Generate private key at http://www.google.com/recaptcha'
+ end
+ optional :akismet_enabled, type: Boolean, desc: 'Helps prevent bots from creating issues'
+ given akismet_enabled: ->(val) { val } do
+ requires :akismet_api_key, type: String, desc: 'Generate API key at http://www.akismet.com'
+ end
+ optional :admin_notification_email, type: String, desc: 'Abuse reports will be sent to this address if it is set. Abuse reports are always available in the admin area.'
+ optional :sentry_enabled, type: Boolean, desc: 'Sentry is an error reporting and logging tool which is currently not shipped with GitLab, get it here: https://getsentry.com'
+ given sentry_enabled: ->(val) { val } do
+ requires :sentry_dsn, type: String, desc: 'Sentry Data Source Name'
+ end
+ optional :repository_storage, type: String, desc: 'Storage paths for new projects'
+ optional :repository_checks_enabled, type: Boolean, desc: "GitLab will periodically run 'git fsck' in all project and wiki repositories to look for silent disk corruption issues."
+ optional :koding_enabled, type: Boolean, desc: 'Enable Koding'
+ given koding_enabled: ->(val) { val } do
+ requires :koding_url, type: String, desc: 'The Koding team URL'
+ end
+ optional :version_check_enabled, type: Boolean, desc: 'Let GitLab inform you when an update is available.'
+ optional :email_author_in_body, type: Boolean, desc: 'Some email servers do not support overriding the email sender name. Enable this option to include the name of the author of the issue, merge request or comment in the email body instead.'
+ optional :html_emails_enabled, type: Boolean, desc: 'By default GitLab sends emails in HTML and plain text formats so mail clients can choose what format to use. Disable this option if you only want to send emails in plain text format.'
+ optional :housekeeping_enabled, type: Boolean, desc: 'Enable automatic repository housekeeping (git repack, git gc)'
+ given housekeeping_enabled: ->(val) { val } do
+ requires :housekeeping_bitmaps_enabled, type: Boolean, desc: "Creating pack file bitmaps makes housekeeping take a little longer but bitmaps should accelerate 'git clone' performance."
+ requires :housekeeping_incremental_repack_period, type: Integer, desc: "Number of Git pushes after which an incremental 'git repack' is run."
+ requires :housekeeping_full_repack_period, type: Integer, desc: "Number of Git pushes after which a full 'git repack' is run."
+ requires :housekeeping_gc_period, type: Integer, desc: "Number of Git pushes after which 'git gc' is run."
+ end
+ at_least_one_of :default_branch_protection, :default_project_visibility, :default_snippet_visibility,
+ :default_group_visibility, :restricted_visibility_levels, :import_sources,
+ :enabled_git_access_protocol, :gravatar_enabled, :default_projects_limit,
+ :max_attachment_size, :session_expire_delay, :disabled_oauth_sign_in_sources,
+ :user_oauth_applications, :user_default_external, :signup_enabled,
+ :send_user_confirmation_email, :domain_whitelist, :domain_blacklist_enabled,
+ :after_sign_up_text, :signin_enabled, :require_two_factor_authentication,
+ :home_page_url, :after_sign_out_path, :sign_in_text, :help_page_text,
+ :shared_runners_enabled, :max_artifacts_size, :container_registry_token_expire_delay,
+ :metrics_enabled, :sidekiq_throttling_enabled, :recaptcha_enabled,
+ :akismet_enabled, :admin_notification_email, :sentry_enabled,
+ :repository_storage, :repository_checks_enabled, :koding_enabled,
+ :version_check_enabled, :email_author_in_body, :html_emails_enabled,
+ :housekeeping_enabled
+ end
put "application/settings" do
- attributes = ["repository_storage"] + current_settings.attributes.keys - ["id"]
- attrs = attributes_for_keys(attributes)
-
- if current_settings.update_attributes(attrs)
+ if current_settings.update_attributes(declared_params(include_missing: false))
present current_settings, with: Entities::ApplicationSetting
else
render_validation_error!(current_settings)
diff --git a/lib/gitlab/auth/result.rb b/lib/gitlab/auth/result.rb
index 6be7f690676..39b86c61a18 100644
--- a/lib/gitlab/auth/result.rb
+++ b/lib/gitlab/auth/result.rb
@@ -9,8 +9,7 @@ module Gitlab
def lfs_deploy_token?(for_project)
type == :lfs_deploy_token &&
- actor &&
- actor.projects.include?(for_project)
+ actor.try(:has_access_to?, for_project)
end
def success?
diff --git a/lib/gitlab/checks/change_access.rb b/lib/gitlab/checks/change_access.rb
index 3d203017d9f..9c391fa92a3 100644
--- a/lib/gitlab/checks/change_access.rb
+++ b/lib/gitlab/checks/change_access.rb
@@ -1,14 +1,16 @@
module Gitlab
module Checks
class ChangeAccess
- attr_reader :user_access, :project
+ attr_reader :user_access, :project, :skip_authorization
- def initialize(change, user_access:, project:, env: {})
+ def initialize(
+ change, user_access:, project:, env: {}, skip_authorization: false)
@oldrev, @newrev, @ref = change.values_at(:oldrev, :newrev, :ref)
@branch_name = Gitlab::Git.branch_name(@ref)
@user_access = user_access
@project = project
@env = env
+ @skip_authorization = skip_authorization
end
def exec
@@ -24,6 +26,7 @@ module Gitlab
protected
def protected_branch_checks
+ return if skip_authorization
return unless @branch_name
return unless project.protected_branch?(@branch_name)
@@ -49,6 +52,8 @@ module Gitlab
end
def tag_checks
+ return if skip_authorization
+
tag_ref = Gitlab::Git.tag_name(@ref)
if tag_ref && protected_tag?(tag_ref) && user_access.cannot_do_action?(:admin_project)
@@ -57,6 +62,8 @@ module Gitlab
end
def push_checks
+ return if skip_authorization
+
if user_access.cannot_do_action?(:push_code)
"You are not allowed to push code to this project."
end
diff --git a/lib/gitlab/diff/file_collection/merge_request_diff.rb b/lib/gitlab/diff/file_collection/merge_request_diff.rb
index 56530448f36..329d12f13d1 100644
--- a/lib/gitlab/diff/file_collection/merge_request_diff.rb
+++ b/lib/gitlab/diff/file_collection/merge_request_diff.rb
@@ -61,7 +61,10 @@ module Gitlab
end
def cacheable?(diff_file)
- @merge_request_diff.present? && diff_file.blob && diff_file.blob.text?
+ @merge_request_diff.present? &&
+ diff_file.blob &&
+ diff_file.blob.text? &&
+ @project.repository.diffable?(diff_file.blob)
end
def cache_key
diff --git a/lib/gitlab/email/reply_parser.rb b/lib/gitlab/email/reply_parser.rb
index f586c5ab062..8c8dd1b9cef 100644
--- a/lib/gitlab/email/reply_parser.rb
+++ b/lib/gitlab/email/reply_parser.rb
@@ -13,9 +13,17 @@ module Gitlab
encoding = body.encoding
- body = discourse_email_trimmer(body)
+ body = EmailReplyTrimmer.trim(body)
- body = EmailReplyParser.parse_reply(body)
+ return '' unless body
+
+ # not using /\s+$/ here because that deletes empty lines
+ body = body.gsub(/[ \t]$/, '')
+
+ # NOTE: We currently don't support empty quotes.
+ # EmailReplyTrimmer allows this as a special case,
+ # so we detect it manually here.
+ return "" if body.lines.all? { |l| l.strip.empty? || l.start_with?('>') }
body.force_encoding(encoding).encode("UTF-8")
end
@@ -57,30 +65,6 @@ module Gitlab
rescue
nil
end
-
- REPLYING_HEADER_LABELS = %w(From Sent To Subject Reply To Cc Bcc Date)
- REPLYING_HEADER_REGEX = Regexp.union(REPLYING_HEADER_LABELS.map { |label| "#{label}:" })
-
- def discourse_email_trimmer(body)
- lines = body.scrub.lines.to_a
- range_end = 0
-
- lines.each_with_index do |l, idx|
- # This one might be controversial but so many reply lines have years, times and end with a colon.
- # Let's try it and see how well it works.
- break if (l =~ /\d{4}/ && l =~ /\d:\d\d/ && l =~ /\:$/) ||
- (l =~ /On \w+ \d+,? \d+,?.*wrote:/)
-
- # Headers on subsequent lines
- break if (0..2).all? { |off| lines[idx + off] =~ REPLYING_HEADER_REGEX }
- # Headers on the same line
- break if REPLYING_HEADER_LABELS.count { |label| l.include?(label) } >= 3
-
- range_end = idx
- end
-
- lines[0..range_end].join.strip
- end
end
end
end
diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb
index c6b6efda360..7e1484613f2 100644
--- a/lib/gitlab/git_access.rb
+++ b/lib/gitlab/git_access.rb
@@ -7,7 +7,8 @@ module Gitlab
ERROR_MESSAGES = {
upload: 'You are not allowed to upload code for this project.',
download: 'You are not allowed to download code from this project.',
- deploy_key: 'Deploy keys are not allowed to push code.',
+ deploy_key_upload:
+ 'This deploy key does not have write access to this project.',
no_repo: 'A repository for this project does not exist yet.'
}
@@ -31,12 +32,13 @@ module Gitlab
check_active_user!
check_project_accessibility!
check_command_existence!(cmd)
+ check_repository_existence!
case cmd
when *DOWNLOAD_COMMANDS
- download_access_check
+ check_download_access!
when *PUSH_COMMANDS
- push_access_check(changes)
+ check_push_access!(changes)
end
build_status_object(true)
@@ -44,32 +46,10 @@ module Gitlab
build_status_object(false, ex.message)
end
- def download_access_check
- if user
- user_download_access_check
- elsif deploy_key.nil? && !guest_can_downlod_code?
- raise UnauthorizedError, ERROR_MESSAGES[:download]
- end
- end
-
- def push_access_check(changes)
- if user
- user_push_access_check(changes)
- else
- raise UnauthorizedError, ERROR_MESSAGES[deploy_key ? :deploy_key : :upload]
- end
- end
-
- def guest_can_downlod_code?
+ def guest_can_download_code?
Guest.can?(:download_code, project)
end
- def user_download_access_check
- unless user_can_download_code? || build_can_download_code?
- raise UnauthorizedError, ERROR_MESSAGES[:download]
- end
- end
-
def user_can_download_code?
authentication_abilities.include?(:download_code) && user_access.can_do_action?(:download_code)
end
@@ -78,35 +58,6 @@ module Gitlab
authentication_abilities.include?(:build_download_code) && user_access.can_do_action?(:build_download_code)
end
- def user_push_access_check(changes)
- unless authentication_abilities.include?(:push_code)
- raise UnauthorizedError, ERROR_MESSAGES[:upload]
- end
-
- if changes.blank?
- return # Allow access.
- end
-
- unless project.repository.exists?
- raise UnauthorizedError, ERROR_MESSAGES[:no_repo]
- end
-
- changes_list = Gitlab::ChangesList.new(changes)
-
- # Iterate over all changes to find if user allowed all of them to be applied
- changes_list.each do |change|
- status = change_access_check(change)
- unless status.allowed?
- # If user does not have access to make at least one change - cancel all push
- raise UnauthorizedError, status.message
- end
- end
- end
-
- def change_access_check(change)
- Checks::ChangeAccess.new(change, user_access: user_access, project: project, env: @env).exec
- end
-
def protocol_allowed?
Gitlab::ProtocolAccess.allowed?(protocol)
end
@@ -120,6 +71,8 @@ module Gitlab
end
def check_active_user!
+ return if deploy_key?
+
if user && !user_access.allowed?
raise UnauthorizedError, "Your account has been blocked."
end
@@ -137,33 +90,92 @@ module Gitlab
end
end
- def matching_merge_request?(newrev, branch_name)
- Checks::MatchingMergeRequest.new(newrev, branch_name, project).match?
+ def check_repository_existence!
+ unless project.repository.exists?
+ raise UnauthorizedError, ERROR_MESSAGES[:no_repo]
+ end
end
- def deploy_key
- actor if actor.is_a?(DeployKey)
+ def check_download_access!
+ return if deploy_key?
+
+ passed = user_can_download_code? ||
+ build_can_download_code? ||
+ guest_can_download_code?
+
+ unless passed
+ raise UnauthorizedError, ERROR_MESSAGES[:download]
+ end
end
- def deploy_key_can_read_project?
+ def check_push_access!(changes)
if deploy_key
- return true if project.public?
- deploy_key.projects.include?(project)
+ check_deploy_key_push_access!
+ elsif user
+ check_user_push_access!
else
- false
+ raise UnauthorizedError, ERROR_MESSAGES[:upload]
end
+
+ return if changes.blank? # Allow access.
+
+ check_change_access!(changes)
end
- def can_read_project?
- if user
- user_access.can_read_project?
- elsif deploy_key
- deploy_key_can_read_project?
- else
- Guest.can?(:read_project, project)
+ def check_user_push_access!
+ unless authentication_abilities.include?(:push_code)
+ raise UnauthorizedError, ERROR_MESSAGES[:upload]
end
end
+ def check_deploy_key_push_access!
+ unless deploy_key.can_push_to?(project)
+ raise UnauthorizedError, ERROR_MESSAGES[:deploy_key_upload]
+ end
+ end
+
+ def check_change_access!(changes)
+ changes_list = Gitlab::ChangesList.new(changes)
+
+ # Iterate over all changes to find if user allowed all of them to be applied
+ changes_list.each do |change|
+ status = check_single_change_access(change)
+ unless status.allowed?
+ # If user does not have access to make at least one change - cancel all push
+ raise UnauthorizedError, status.message
+ end
+ end
+ end
+
+ def check_single_change_access(change)
+ Checks::ChangeAccess.new(
+ change,
+ user_access: user_access,
+ project: project,
+ env: @env,
+ skip_authorization: deploy_key?).exec
+ end
+
+ def matching_merge_request?(newrev, branch_name)
+ Checks::MatchingMergeRequest.new(newrev, branch_name, project).match?
+ end
+
+ def deploy_key
+ actor if deploy_key?
+ end
+
+ def deploy_key?
+ actor.is_a?(DeployKey)
+ end
+
+ def can_read_project?
+ if deploy_key
+ deploy_key.has_access_to?(project)
+ elsif user
+ user.can?(:read_project, project)
+ end || Guest.can?(:read_project, project)
+ end
+
protected
def user
diff --git a/lib/gitlab/git_access_wiki.rb b/lib/gitlab/git_access_wiki.rb
index 2c06c4ff1ef..67eaa5e088d 100644
--- a/lib/gitlab/git_access_wiki.rb
+++ b/lib/gitlab/git_access_wiki.rb
@@ -1,6 +1,6 @@
module Gitlab
class GitAccessWiki < GitAccess
- def guest_can_downlod_code?
+ def guest_can_download_code?
Guest.can?(:download_wiki_code, project)
end
@@ -8,7 +8,7 @@ module Gitlab
authentication_abilities.include?(:download_code) && user_access.can_do_action?(:download_wiki_code)
end
- def change_access_check(change)
+ def check_single_change_access(change)
if user_access.can_do_action?(:create_wiki)
build_status_object(true)
else
diff --git a/lib/gitlab/template/gitlab_ci_yml_template.rb b/lib/gitlab/template/gitlab_ci_yml_template.rb
index d19b0a52043..9d2ecee9756 100644
--- a/lib/gitlab/template/gitlab_ci_yml_template.rb
+++ b/lib/gitlab/template/gitlab_ci_yml_template.rb
@@ -15,7 +15,7 @@ module Gitlab
{
'General' => '',
'Pages' => 'Pages',
- 'Autodeploy' => 'autodeploy'
+ 'Auto deploy' => 'autodeploy'
}
end
@@ -28,7 +28,7 @@ module Gitlab
end
def dropdown_names(context)
- categories = context == 'autodeploy' ? ['Autodeploy'] : ['General', 'Pages']
+ categories = context == 'autodeploy' ? ['Auto deploy'] : ['General', 'Pages']
super().slice(*categories)
end
end
diff --git a/lib/gitlab/themes.rb b/lib/gitlab/themes.rb
index d4020af76f9..19ab76ae80f 100644
--- a/lib/gitlab/themes.rb
+++ b/lib/gitlab/themes.rb
@@ -15,7 +15,7 @@ module Gitlab
Theme.new(1, 'Graphite', 'ui_graphite'),
Theme.new(2, 'Charcoal', 'ui_charcoal'),
Theme.new(3, 'Green', 'ui_green'),
- Theme.new(4, 'Gray', 'ui_gray'),
+ Theme.new(4, 'Black', 'ui_black'),
Theme.new(5, 'Violet', 'ui_violet'),
Theme.new(6, 'Blue', 'ui_blue')
].freeze
diff --git a/lib/gitlab/user_access.rb b/lib/gitlab/user_access.rb
index 9858d2e7d83..6c7e673fb9f 100644
--- a/lib/gitlab/user_access.rb
+++ b/lib/gitlab/user_access.rb
@@ -8,6 +8,8 @@ module Gitlab
end
def can_do_action?(action)
+ return false if no_user_or_blocked?
+
@permission_cache ||= {}
@permission_cache[action] ||= user.can?(action, project)
end
@@ -17,7 +19,7 @@ module Gitlab
end
def allowed?
- return false if user.blank? || user.blocked?
+ return false if no_user_or_blocked?
if user.requires_ldap_check? && user.try_obtain_ldap_lease
return false unless Gitlab::LDAP::Access.allowed?(user)
@@ -27,7 +29,7 @@ module Gitlab
end
def can_push_to_branch?(ref)
- return false unless user
+ return false if no_user_or_blocked?
if project.protected_branch?(ref)
return true if project.empty_repo? && project.user_can_push_to_empty_repo?(user)
@@ -40,7 +42,7 @@ module Gitlab
end
def can_merge_to_branch?(ref)
- return false unless user
+ return false if no_user_or_blocked?
if project.protected_branch?(ref)
access_levels = project.protected_branches.matching(ref).map(&:merge_access_levels).flatten
@@ -51,9 +53,15 @@ module Gitlab
end
def can_read_project?
- return false unless user
+ return false if no_user_or_blocked?
user.can?(:read_project, project)
end
+
+ private
+
+ def no_user_or_blocked?
+ user.nil? || user.blocked?
+ end
end
end
diff --git a/lib/tasks/gitlab/import.rake b/lib/tasks/gitlab/import.rake
index dbdd4e977e8..a2eca74a3c8 100644
--- a/lib/tasks/gitlab/import.rake
+++ b/lib/tasks/gitlab/import.rake
@@ -63,8 +63,7 @@ namespace :gitlab do
if project.persisted?
puts " * Created #{project.name} (#{repo_path})".color(:green)
- project.update_repository_size
- project.update_commit_count
+ ProjectCacheWorker.perform(project.id)
else
puts " * Failed trying to create #{project.name} (#{repo_path})".color(:red)
puts " Errors: #{project.errors.messages}".color(:red)
diff --git a/lib/tasks/gitlab/update_commit_count.rake b/lib/tasks/gitlab/update_commit_count.rake
deleted file mode 100644
index 3bd10b0208b..00000000000
--- a/lib/tasks/gitlab/update_commit_count.rake
+++ /dev/null
@@ -1,20 +0,0 @@
-namespace :gitlab do
- desc "GitLab | Update commit count for projects"
- task update_commit_count: :environment do
- projects = Project.where(commit_count: 0)
- puts "#{projects.size} projects need to be updated. This might take a while."
- ask_to_continue unless ENV['force'] == 'yes'
-
- projects.find_each(batch_size: 100) do |project|
- print "#{project.name_with_namespace.color(:yellow)} ... "
-
- unless project.repo_exists?
- puts "skipping, because the repo is empty".color(:magenta)
- next
- end
-
- project.update_commit_count
- puts project.commit_count.to_s.color(:green)
- end
- end
-end
diff --git a/spec/controllers/dashboard/todos_controller_spec.rb b/spec/controllers/dashboard/todos_controller_spec.rb
new file mode 100644
index 00000000000..288984cfba9
--- /dev/null
+++ b/spec/controllers/dashboard/todos_controller_spec.rb
@@ -0,0 +1,37 @@
+require 'spec_helper'
+
+describe Dashboard::TodosController do
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+ let(:todo_service) { TodoService.new }
+
+ describe 'GET #index' do
+ before do
+ sign_in(user)
+ project.team << [user, :developer]
+ end
+
+ context 'when using pagination' do
+ let(:last_page) { user.todos.page().total_pages }
+ let!(:issues) { create_list(:issue, 2, project: project, assignee: user) }
+
+ before do
+ issues.each { |issue| todo_service.new_issue(issue, user) }
+ allow(Kaminari.config).to receive(:default_per_page).and_return(1)
+ end
+
+ it 'redirects to last_page if page number is larger than number of pages' do
+ get :index, page: (last_page + 1).to_param
+
+ expect(response).to redirect_to(dashboard_todos_path(page: last_page))
+ end
+
+ it 'redirects to correspondent page' do
+ get :index, page: last_page
+
+ expect(assigns(:todos).current_page).to eq(last_page)
+ expect(response).to have_http_status(200)
+ end
+ end
+ end
+end
diff --git a/spec/controllers/projects/blob_controller_spec.rb b/spec/controllers/projects/blob_controller_spec.rb
index 3efef757ae2..f35c5d992d9 100644
--- a/spec/controllers/projects/blob_controller_spec.rb
+++ b/spec/controllers/projects/blob_controller_spec.rb
@@ -1,7 +1,7 @@
require 'rails_helper'
describe Projects::BlobController do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :public) }
let(:user) { create(:user) }
before do
@@ -84,5 +84,35 @@ describe Projects::BlobController do
end
end
end
+
+ context 'when user has forked project' do
+ let(:guest) { create(:user) }
+ let!(:forked_project) { Projects::ForkService.new(project, guest).execute }
+ let!(:merge_request) { create(:merge_request, source_project: project, target_project: project, source_branch: "fork-test-1", target_branch: "master") }
+
+ before { sign_in(guest) }
+
+ it "redirects to forked project new merge request" do
+ default_params[:target_branch] = "fork-test-1"
+ default_params[:create_merge_request] = 1
+
+ allow_any_instance_of(Files::UpdateService).to receive(:commit).and_return(:success)
+
+ put :update, default_params
+
+ expect(response).to redirect_to(
+ new_namespace_project_merge_request_path(
+ forked_project.namespace,
+ forked_project,
+ merge_request: {
+ source_project_id: forked_project.id,
+ target_project_id: project.id,
+ source_branch: "fork-test-1",
+ target_branch: "master"
+ }
+ )
+ )
+ end
+ end
end
end
diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb
index dbe5ddccbcf..e2321f2034b 100644
--- a/spec/controllers/projects/issues_controller_spec.rb
+++ b/spec/controllers/projects/issues_controller_spec.rb
@@ -52,6 +52,36 @@ describe Projects::IssuesController do
expect(response).to have_http_status(404)
end
end
+
+ context 'with page param' do
+ let(:last_page) { project.issues.page().total_pages }
+ let!(:issue_list) { create_list(:issue, 2, project: project) }
+
+ before do
+ sign_in(user)
+ project.team << [user, :developer]
+ allow(Kaminari.config).to receive(:default_per_page).and_return(1)
+ end
+
+ it 'redirects to last_page if page number is larger than number of pages' do
+ get :index,
+ namespace_id: project.namespace.path.to_param,
+ project_id: project.path.to_param,
+ page: (last_page + 1).to_param
+
+ expect(response).to redirect_to(namespace_project_issues_path(page: last_page, state: controller.params[:state], scope: controller.params[:scope]))
+ end
+
+ it 'redirects to specified page' do
+ get :index,
+ namespace_id: project.namespace.path.to_param,
+ project_id: project.path.to_param,
+ page: last_page.to_param
+
+ expect(assigns(:issues).current_page).to eq(last_page)
+ expect(response).to have_http_status(200)
+ end
+ end
end
describe 'GET #new' do
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index 440b897ddc6..2a411d78395 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -127,11 +127,29 @@ describe Projects::MergeRequestsController do
end
describe 'GET index' do
- def get_merge_requests
+ def get_merge_requests(page = nil)
get :index,
namespace_id: project.namespace.to_param,
project_id: project.to_param,
- state: 'opened'
+ state: 'opened', page: page.to_param
+ end
+
+ context 'when page param' do
+ let(:last_page) { project.merge_requests.page().total_pages }
+ let!(:merge_request) { create(:merge_request_with_diffs, target_project: project, source_project: project) }
+
+ it 'redirects to last_page if page number is larger than number of pages' do
+ get_merge_requests(last_page + 1)
+
+ expect(response).to redirect_to(namespace_project_merge_requests_path(page: last_page, state: controller.params[:state], scope: controller.params[:scope]))
+ end
+
+ it 'redirects to specified page' do
+ get_merge_requests(last_page)
+
+ expect(assigns(:merge_requests).current_page).to eq(last_page)
+ expect(response).to have_http_status(200)
+ end
end
context 'when filtering by opened state' do
diff --git a/spec/controllers/projects/snippets_controller_spec.rb b/spec/controllers/projects/snippets_controller_spec.rb
index 72a3ebf2ebd..32b0e42c3cd 100644
--- a/spec/controllers/projects/snippets_controller_spec.rb
+++ b/spec/controllers/projects/snippets_controller_spec.rb
@@ -11,6 +11,28 @@ describe Projects::SnippetsController do
end
describe 'GET #index' do
+ context 'when page param' do
+ let(:last_page) { project.snippets.page().total_pages }
+ let!(:project_snippet) { create(:project_snippet, :public, project: project, author: user) }
+
+ it 'redirects to last_page if page number is larger than number of pages' do
+ get :index,
+ namespace_id: project.namespace.path,
+ project_id: project.path, page: (last_page + 1).to_param
+
+ expect(response).to redirect_to(namespace_project_snippets_path(page: last_page))
+ end
+
+ it 'redirects to specified page' do
+ get :index,
+ namespace_id: project.namespace.path,
+ project_id: project.path, page: last_page.to_param
+
+ expect(assigns(:snippets).current_page).to eq(last_page)
+ expect(response).to have_http_status(200)
+ end
+ end
+
context 'when the project snippet is private' do
let!(:project_snippet) { create(:project_snippet, :private, project: project, author: user) }
diff --git a/spec/factories/lfs_objects.rb b/spec/factories/lfs_objects.rb
index a81645acd2b..477fab9e964 100644
--- a/spec/factories/lfs_objects.rb
+++ b/spec/factories/lfs_objects.rb
@@ -2,7 +2,7 @@ include ActionDispatch::TestProcess
FactoryGirl.define do
factory :lfs_object do
- oid "b68143e6463773b1b6c6fd009a76c32aeec041faff32ba2ed42fd7f708a17f80"
+ sequence(:oid) { |n| "b68143e6463773b1b6c6fd009a76c32aeec041faff32ba2ed42fd7f708a%05x" % n }
size 499013
end
diff --git a/spec/factories/project_statistics.rb b/spec/factories/project_statistics.rb
new file mode 100644
index 00000000000..72d43096216
--- /dev/null
+++ b/spec/factories/project_statistics.rb
@@ -0,0 +1,6 @@
+FactoryGirl.define do
+ factory :project_statistics do
+ project { create :project }
+ namespace { project.namespace }
+ end
+end
diff --git a/spec/features/admin/admin_groups_spec.rb b/spec/features/admin/admin_groups_spec.rb
index 0aa01fc499a..9c19db6b420 100644
--- a/spec/features/admin/admin_groups_spec.rb
+++ b/spec/features/admin/admin_groups_spec.rb
@@ -17,6 +17,16 @@ feature 'Admin Groups', feature: true do
end
end
+ describe 'show a group' do
+ scenario 'shows the group' do
+ group = create(:group, :private)
+
+ visit admin_group_path(group)
+
+ expect(page).to have_content("Group: #{group.name}")
+ end
+ end
+
describe 'group edit' do
scenario 'shows the visibility level radio populated with the group visibility_level value' do
group = create(:group, :private)
diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb
index e31325ce47b..55ffc6761f8 100644
--- a/spec/features/admin/admin_users_spec.rb
+++ b/spec/features/admin/admin_users_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
describe "Admin::Users", feature: true do
+ include WaitForAjax
+
before { login_as :admin }
describe "GET /admin/users" do
@@ -252,5 +254,20 @@ describe "Admin::Users", feature: true do
end
expect(page).to have_content @project.name
end
+
+ it 'shows the group access level' do
+ within(:css, '.append-bottom-default + .panel') do
+ expect(page).to have_content 'Developer'
+ end
+ end
+
+ it 'allows group membership to be revoked', js: true do
+ page.within(first('.group_member')) do
+ find('.btn-remove').click
+ end
+ wait_for_ajax
+
+ expect(page).not_to have_selector('.group_member')
+ end
end
end
diff --git a/spec/features/auto_deploy_spec.rb b/spec/features/auto_deploy_spec.rb
index 92f1ab90881..ea7a97d1d4f 100644
--- a/spec/features/auto_deploy_spec.rb
+++ b/spec/features/auto_deploy_spec.rb
@@ -26,7 +26,7 @@ describe 'Auto deploy' do
it 'does not show a button to set up auto deploy' do
visit namespace_project_path(project.namespace, project)
- expect(page).to have_no_content('Set up autodeploy')
+ expect(page).to have_no_content('Set up auto deploy')
end
end
@@ -37,11 +37,11 @@ describe 'Auto deploy' do
end
it 'shows a button to set up auto deploy' do
- expect(page).to have_link('Set up autodeploy')
+ expect(page).to have_link('Set up auto deploy')
end
- it 'includes Kubernetes as an available template', js: true do
- click_link 'Set up autodeploy'
+ it 'includes OpenShift as an available template', js: true do
+ click_link 'Set up auto deploy'
click_button 'Choose a GitLab CI Yaml template'
within '.gitlab-ci-yml-selector' do
@@ -49,8 +49,8 @@ describe 'Auto deploy' do
end
end
- it 'creates a merge request using "autodeploy" branch', js: true do
- click_link 'Set up autodeploy'
+ it 'creates a merge request using "auto-deploy" branch', js: true do
+ click_link 'Set up auto deploy'
click_button 'Choose a GitLab CI Yaml template'
within '.gitlab-ci-yml-selector' do
click_on 'OpenShift'
@@ -58,7 +58,7 @@ describe 'Auto deploy' do
wait_for_ajax
click_button 'Commit Changes'
- expect(page).to have_content('New Merge Request From autodeploy into master')
+ expect(page).to have_content('New Merge Request From auto-deploy into master')
end
end
end
diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb
index 973d5b286e9..bfac5a1b8ab 100644
--- a/spec/features/boards/boards_spec.rb
+++ b/spec/features/boards/boards_spec.rb
@@ -109,7 +109,7 @@ describe 'Issue Boards', feature: true, js: true do
end
it 'search backlog list' do
- page.within('#js-boards-seach') do
+ page.within('#js-boards-search') do
find('.form-control').set(issue1.title)
end
@@ -122,7 +122,7 @@ describe 'Issue Boards', feature: true, js: true do
end
it 'search done list' do
- page.within('#js-boards-seach') do
+ page.within('#js-boards-search') do
find('.form-control').set(issue8.title)
end
@@ -135,7 +135,7 @@ describe 'Issue Boards', feature: true, js: true do
end
it 'search list' do
- page.within('#js-boards-seach') do
+ page.within('#js-boards-search') do
find('.form-control').set(issue5.title)
end
diff --git a/spec/features/dashboard/datetime_on_tooltips_spec.rb b/spec/features/dashboard/datetime_on_tooltips_spec.rb
index 44dfc2dff45..dc9d09fa396 100644
--- a/spec/features/dashboard/datetime_on_tooltips_spec.rb
+++ b/spec/features/dashboard/datetime_on_tooltips_spec.rb
@@ -6,7 +6,7 @@ feature 'Tooltips on .timeago dates', feature: true, js: true do
let(:user) { create(:user) }
let(:project) { create(:project, name: 'test', namespace: user.namespace) }
let(:created_date) { Date.yesterday.to_time }
- let(:expected_format) { created_date.strftime('%b %-d, %Y %l:%M%P UTC') }
+ let(:expected_format) { created_date.strftime('%b %-d, %Y %l:%M%P') }
context 'on the activity tab' do
before do
diff --git a/spec/features/groups_spec.rb b/spec/features/groups_spec.rb
index 4b19886274e..a515c92db37 100644
--- a/spec/features/groups_spec.rb
+++ b/spec/features/groups_spec.rb
@@ -107,4 +107,17 @@ feature 'Group', feature: true do
expect(page).to have_css('.group-home-desc a[rel]')
end
end
+
+ describe 'group page with nested groups', js: true do
+ let!(:group) { create(:group) }
+ let!(:nested_group) { create(:group, parent: group) }
+ let!(:path) { group_path(group) }
+
+ it 'has nested groups tab with nested groups inside' do
+ visit path
+ click_link 'Subgroups'
+
+ expect(page).to have_content(nested_group.full_name)
+ end
+ end
end
diff --git a/spec/features/issuables/default_sort_order_spec.rb b/spec/features/issuables/default_sort_order_spec.rb
index 9a2b879e789..73553f97d6f 100644
--- a/spec/features/issuables/default_sort_order_spec.rb
+++ b/spec/features/issuables/default_sort_order_spec.rb
@@ -180,16 +180,10 @@ describe 'Projects > Issuables > Default sort order', feature: true do
end
def visit_merge_requests_with_state(project, state)
- visit_merge_requests project
- visit_issuables_with_state state
+ visit_merge_requests project, state: state
end
def visit_issues_with_state(project, state)
- visit_issues project
- visit_issuables_with_state state
- end
-
- def visit_issuables_with_state(state)
- within('.issues-state-filters') { find("span", text: state.titleize).click }
+ visit_issues project, state: state
end
end
diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb
index d0294908d2c..82c9bd0e6e6 100644
--- a/spec/features/issues/gfm_autocomplete_spec.rb
+++ b/spec/features/issues/gfm_autocomplete_spec.rb
@@ -47,7 +47,19 @@ feature 'GFM autocomplete', feature: true, js: true do
expect_to_wrap(true, label_item, note, label.title)
end
- it "does not show drpdown when preceded with a special character" do
+ it "shows dropdown after a new line" do
+ note = find('#note_note')
+ page.within '.timeline-content-form' do
+ note.native.send_keys('test')
+ note.native.send_keys(:enter)
+ note.native.send_keys(:enter)
+ note.native.send_keys('@')
+ end
+
+ expect(page).to have_selector('.atwho-container')
+ end
+
+ it "does not show dropdown when preceded with a special character" do
note = find('#note_note')
page.within '.timeline-content-form' do
note.native.send_keys('')
@@ -65,6 +77,17 @@ feature 'GFM autocomplete', feature: true, js: true do
expect(page).to have_selector('.atwho-container', visible: false)
end
+ it "does not throw an error if no labels exist" do
+ note = find('#note_note')
+ page.within '.timeline-content-form' do
+ note.native.send_keys('')
+ note.native.send_keys('~')
+ note.click
+ end
+
+ expect(page).to have_selector('.atwho-container', visible: false)
+ end
+
it 'doesn\'t wrap for assignee values' do
note = find('#note_note')
page.within '.timeline-content-form' do
diff --git a/spec/features/issues/user_uses_slash_commands_spec.rb b/spec/features/issues/user_uses_slash_commands_spec.rb
index 3f2da1c380c..31f75512f4a 100644
--- a/spec/features/issues/user_uses_slash_commands_spec.rb
+++ b/spec/features/issues/user_uses_slash_commands_spec.rb
@@ -30,7 +30,7 @@ feature 'Issues > User uses slash commands', feature: true, js: true do
write_note("/due 2016-08-28")
expect(page).not_to have_content '/due 2016-08-28'
- expect(page).to have_content 'Your commands have been executed!'
+ expect(page).to have_content 'Commands applied'
issue.reload
@@ -51,7 +51,7 @@ feature 'Issues > User uses slash commands', feature: true, js: true do
write_note("/due 2016-08-28")
expect(page).to have_content '/due 2016-08-28'
- expect(page).not_to have_content 'Your commands have been executed!'
+ expect(page).not_to have_content 'Commands applied'
issue.reload
@@ -70,7 +70,7 @@ feature 'Issues > User uses slash commands', feature: true, js: true do
write_note("/remove_due_date")
expect(page).not_to have_content '/remove_due_date'
- expect(page).to have_content 'Your commands have been executed!'
+ expect(page).to have_content 'Commands applied'
issue.reload
@@ -91,7 +91,7 @@ feature 'Issues > User uses slash commands', feature: true, js: true do
write_note("/remove_due_date")
expect(page).to have_content '/remove_due_date'
- expect(page).not_to have_content 'Your commands have been executed!'
+ expect(page).not_to have_content 'Commands applied'
issue.reload
diff --git a/spec/features/merge_requests/closes_issues_spec.rb b/spec/features/merge_requests/closes_issues_spec.rb
index dc32c8f7373..c73065cdce1 100644
--- a/spec/features/merge_requests/closes_issues_spec.rb
+++ b/spec/features/merge_requests/closes_issues_spec.rb
@@ -41,7 +41,7 @@ feature 'Merge Request closing issues message', feature: true do
let(:merge_request_description) { "Description\n\nRefers to #{issue_1.to_reference} and #{issue_2.to_reference}" }
it 'does not display closing issue message' do
- expect(page).to have_content("Issues #{issue_1.to_reference} and #{issue_2.to_reference} are mentioned but will not closed.")
+ expect(page).to have_content("Issues #{issue_1.to_reference} and #{issue_2.to_reference} are mentioned but will not be closed.")
end
end
@@ -49,7 +49,7 @@ feature 'Merge Request closing issues message', feature: true do
let(:merge_request_description) { "Description\n\ncloses #{issue_1.to_reference}\n\n refers to #{issue_2.to_reference}" }
it 'does not display closing issue message' do
- expect(page).to have_content("Accepting this merge request will close issue #{issue_1.to_reference}. Issue #{issue_2.to_reference} is mentioned but will not closed.")
+ expect(page).to have_content("Accepting this merge request will close issue #{issue_1.to_reference}. Issue #{issue_2.to_reference} is mentioned but will not be closed.")
end
end
end
diff --git a/spec/features/merge_requests/merge_request_versions_spec.rb b/spec/features/merge_requests/merge_request_versions_spec.rb
index 09451f41de4..cd2272dd38f 100644
--- a/spec/features/merge_requests/merge_request_versions_spec.rb
+++ b/spec/features/merge_requests/merge_request_versions_spec.rb
@@ -24,7 +24,7 @@ feature 'Merge Request versions', js: true, feature: true do
before do
page.within '.mr-version-dropdown' do
find('.btn-default').click
- click_link 'version 1'
+ find(:link, 'version 1').trigger('click')
end
end
@@ -45,7 +45,7 @@ feature 'Merge Request versions', js: true, feature: true do
before do
page.within '.mr-version-compare-dropdown' do
find('.btn-default').click
- click_link 'version 1'
+ find(:link, 'version 1').trigger('click')
end
end
diff --git a/spec/features/merge_requests/user_uses_slash_commands_spec.rb b/spec/features/merge_requests/user_uses_slash_commands_spec.rb
index 7b8af555f0e..b1b3a47a1ce 100644
--- a/spec/features/merge_requests/user_uses_slash_commands_spec.rb
+++ b/spec/features/merge_requests/user_uses_slash_commands_spec.rb
@@ -31,7 +31,7 @@ feature 'Merge Requests > User uses slash commands', feature: true, js: true do
write_note("/wip")
expect(page).not_to have_content '/wip'
- expect(page).to have_content 'Your commands have been executed!'
+ expect(page).to have_content 'Commands applied'
expect(merge_request.reload.work_in_progress?).to eq true
end
@@ -42,7 +42,7 @@ feature 'Merge Requests > User uses slash commands', feature: true, js: true do
write_note("/wip")
expect(page).not_to have_content '/wip'
- expect(page).to have_content 'Your commands have been executed!'
+ expect(page).to have_content 'Commands applied'
expect(merge_request.reload.work_in_progress?).to eq false
end
@@ -61,7 +61,7 @@ feature 'Merge Requests > User uses slash commands', feature: true, js: true do
write_note("/wip")
expect(page).not_to have_content '/wip'
- expect(page).not_to have_content 'Your commands have been executed!'
+ expect(page).not_to have_content 'Commands applied'
expect(merge_request.reload.work_in_progress?).to eq false
end
diff --git a/spec/features/milestones/milestones_spec.rb b/spec/features/milestones/milestones_spec.rb
index 8b603f51545..aadd72a9f8e 100644
--- a/spec/features/milestones/milestones_spec.rb
+++ b/spec/features/milestones/milestones_spec.rb
@@ -1,6 +1,8 @@
require 'rails_helper'
describe 'Milestone draggable', feature: true, js: true do
+ include WaitForAjax
+
let(:milestone) { create(:milestone, project: project, title: 8.14) }
let(:project) { create(:empty_project, :public) }
let(:user) { create(:user) }
@@ -74,6 +76,8 @@ describe 'Milestone draggable', feature: true, js: true do
visit namespace_project_milestone_path(project.namespace, project, milestone)
issue.drag_to(issue_target)
+
+ wait_for_ajax
end
def create_and_drag_merge_request(params = {})
@@ -82,5 +86,7 @@ describe 'Milestone draggable', feature: true, js: true do
visit namespace_project_milestone_path(project.namespace, project, milestone)
page.find("a[href='#tab-merge-requests']").click
merge_request.drag_to(merge_request_target)
+
+ wait_for_ajax
end
end
diff --git a/spec/features/projects/features_visibility_spec.rb b/spec/features/projects/features_visibility_spec.rb
index 3bb33394be7..9079350186d 100644
--- a/spec/features/projects/features_visibility_spec.rb
+++ b/spec/features/projects/features_visibility_spec.rb
@@ -42,6 +42,17 @@ describe 'Edit Project Settings', feature: true do
end
end
+ context "When external issue tracker is enabled" do
+ it "does not hide issues tab" do
+ project.project_feature.update(issues_access_level: ProjectFeature::DISABLED)
+ allow_any_instance_of(Project).to receive(:external_issue_tracker).and_return(JiraService.new)
+
+ visit namespace_project_path(project.namespace, project)
+
+ expect(page).to have_selector(".shortcuts-issues")
+ end
+ end
+
context "pipelines subtabs" do
it "shows builds when enabled" do
visit namespace_project_pipelines_path(project.namespace, project)
diff --git a/spec/features/tags/master_creates_tag_spec.rb b/spec/features/tags/master_creates_tag_spec.rb
index 08a97085a9c..ca25c696f75 100644
--- a/spec/features/tags/master_creates_tag_spec.rb
+++ b/spec/features/tags/master_creates_tag_spec.rb
@@ -34,7 +34,7 @@ feature 'Master creates tag', feature: true do
expect(current_path).to eq(
namespace_project_tag_path(project.namespace, project, 'v3.0'))
expect(page).to have_content 'v3.0'
- page.within 'pre.body' do
+ page.within 'pre.wrap' do
expect(page).to have_content "Awesome tag message\n\n- hello\n- world"
end
end
diff --git a/spec/features/u2f_spec.rb b/spec/features/u2f_spec.rb
index be21b403084..a8d00bb8e5a 100644
--- a/spec/features/u2f_spec.rb
+++ b/spec/features/u2f_spec.rb
@@ -45,12 +45,12 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
it 'allows registering a new device with a name' do
visit profile_account_path
manage_two_factor_authentication
- expect(page.body).to match("You've already enabled two-factor authentication using mobile")
+ expect(page).to have_content("You've already enabled two-factor authentication using mobile")
u2f_device = register_u2f_device
- expect(page.body).to match(u2f_device.name)
- expect(page.body).to match('Your U2F device was registered')
+ expect(page).to have_content(u2f_device.name)
+ expect(page).to have_content('Your U2F device was registered')
end
it 'allows registering more than one device' do
@@ -59,30 +59,30 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
# First device
manage_two_factor_authentication
first_device = register_u2f_device
- expect(page.body).to match('Your U2F device was registered')
+ expect(page).to have_content('Your U2F device was registered')
# Second device
second_device = register_u2f_device
- expect(page.body).to match('Your U2F device was registered')
+ expect(page).to have_content('Your U2F device was registered')
- expect(page.body).to match(first_device.name)
- expect(page.body).to match(second_device.name)
+ expect(page).to have_content(first_device.name)
+ expect(page).to have_content(second_device.name)
expect(U2fRegistration.count).to eq(2)
end
it 'allows deleting a device' do
visit profile_account_path
manage_two_factor_authentication
- expect(page.body).to match("You've already enabled two-factor authentication using mobile")
+ expect(page).to have_content("You've already enabled two-factor authentication using mobile")
first_u2f_device = register_u2f_device
second_u2f_device = register_u2f_device
click_on "Delete", match: :first
- expect(page.body).to match('Successfully deleted')
+ expect(page).to have_content('Successfully deleted')
expect(page.body).not_to match(first_u2f_device.name)
- expect(page.body).to match(second_u2f_device.name)
+ expect(page).to have_content(second_u2f_device.name)
end
end
@@ -91,7 +91,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
visit profile_account_path
manage_two_factor_authentication
u2f_device = register_u2f_device
- expect(page.body).to match('Your U2F device was registered')
+ expect(page).to have_content('Your U2F device was registered')
logout
# Second user
@@ -100,7 +100,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
visit profile_account_path
manage_two_factor_authentication
register_u2f_device(u2f_device)
- expect(page.body).to match('Your U2F device was registered')
+ expect(page).to have_content('Your U2F device was registered')
expect(U2fRegistration.count).to eq(2)
end
@@ -117,8 +117,8 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
click_on 'Register U2F Device'
expect(U2fRegistration.count).to eq(0)
- expect(page.body).to match("The form contains the following error")
- expect(page.body).to match("did not send a valid JSON response")
+ expect(page).to have_content("The form contains the following error")
+ expect(page).to have_content("did not send a valid JSON response")
end
it "allows retrying registration" do
@@ -130,12 +130,12 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
click_on 'Setup New U2F Device'
expect(page).to have_content('Your device was successfully set up')
click_on 'Register U2F Device'
- expect(page.body).to match("The form contains the following error")
+ expect(page).to have_content("The form contains the following error")
# Successful registration
register_u2f_device
- expect(page.body).to match('Your U2F device was registered')
+ expect(page).to have_content('Your U2F device was registered')
expect(U2fRegistration.count).to eq(1)
end
end
@@ -160,10 +160,9 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
login_with(user)
@u2f_device.respond_to_u2f_authentication
- click_on "Sign in via U2F device"
- expect(page.body).to match('We heard back from your U2F device')
- click_on "Authenticate via U2F Device"
- expect(page.body).to match('href="/users/sign_out"')
+
+ expect(page).to have_content('We heard back from your U2F device')
+ expect(page).to have_css('.sign-out-link', visible: false)
end
end
@@ -173,11 +172,9 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
login_with(user)
@u2f_device.respond_to_u2f_authentication
- click_on "Sign in via U2F device"
- expect(page.body).to match('We heard back from your U2F device')
- click_on "Authenticate via U2F Device"
- expect(page.body).to match('href="/users/sign_out"')
+ expect(page).to have_content('We heard back from your U2F device')
+ expect(page).to have_css('.sign-out-link', visible: false)
end
end
@@ -185,8 +182,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
login_with(user, remember: true)
@u2f_device.respond_to_u2f_authentication
- click_on "Sign in via U2F device"
- expect(page.body).to match('We heard back from your U2F device')
+ expect(page).to have_content('We heard back from your U2F device')
within 'div#js-authenticate-u2f' do
field = first('input#user_remember_me', visible: false)
@@ -208,11 +204,8 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
# Try authenticating user with the old U2F device
login_as(current_user)
@u2f_device.respond_to_u2f_authentication
- click_on "Sign in via U2F device"
- expect(page.body).to match('We heard back from your U2F device')
- click_on "Authenticate via U2F Device"
-
- expect(page.body).to match('Authentication via U2F device failed')
+ expect(page).to have_content('We heard back from your U2F device')
+ expect(page).to have_content('Authentication via U2F device failed')
end
end
@@ -229,11 +222,9 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
# Try authenticating user with the same U2F device
login_as(current_user)
@u2f_device.respond_to_u2f_authentication
- click_on "Sign in via U2F device"
- expect(page.body).to match('We heard back from your U2F device')
- click_on "Authenticate via U2F Device"
+ expect(page).to have_content('We heard back from your U2F device')
- expect(page.body).to match('href="/users/sign_out"')
+ expect(page).to have_css('.sign-out-link', visible: false)
end
end
end
@@ -243,11 +234,9 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
unregistered_device = FakeU2fDevice.new(page, FFaker::Name.first_name)
login_as(user)
unregistered_device.respond_to_u2f_authentication
- click_on "Sign in via U2F device"
- expect(page.body).to match('We heard back from your U2F device')
- click_on "Authenticate via U2F Device"
+ expect(page).to have_content('We heard back from your U2F device')
- expect(page.body).to match('Authentication via U2F device failed')
+ expect(page).to have_content('Authentication via U2F device failed')
end
end
@@ -270,11 +259,9 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
[first_device, second_device].each do |device|
login_as(user)
device.respond_to_u2f_authentication
- click_on "Sign in via U2F device"
- expect(page.body).to match('We heard back from your U2F device')
- click_on "Authenticate via U2F Device"
+ expect(page).to have_content('We heard back from your U2F device')
- expect(page.body).to match('href="/users/sign_out"')
+ expect(page).to have_css('.sign-out-link', visible: false)
logout
end
@@ -299,4 +286,50 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
end
end
end
+
+ describe 'fallback code authentication' do
+ let(:user) { create(:user) }
+
+ def assert_fallback_ui(page)
+ expect(page).to have_button('Verify code')
+ expect(page).to have_css('#user_otp_attempt')
+ expect(page).not_to have_link('Sign in via 2FA code')
+ expect(page).not_to have_css('#js-authenticate-u2f')
+ end
+
+ before do
+ # Register and logout
+ login_as(user)
+ user.update_attribute(:otp_required_for_login, true)
+ visit profile_account_path
+ end
+
+ describe 'when no u2f device is registered' do
+ before do
+ logout
+ login_with(user)
+ end
+
+ it 'shows the fallback otp code UI' do
+ assert_fallback_ui(page)
+ end
+ end
+
+ describe 'when a u2f device is registered' do
+ before do
+ manage_two_factor_authentication
+ @u2f_device = register_u2f_device
+ logout
+ login_with(user)
+ end
+
+ it 'provides a button that shows the fallback otp code UI' do
+ expect(page).to have_link('Sign in via 2FA code')
+
+ click_link('Sign in via 2FA code')
+
+ assert_fallback_ui(page)
+ end
+ end
+ end
end
diff --git a/spec/helpers/storage_helper_spec.rb b/spec/helpers/storage_helper_spec.rb
new file mode 100644
index 00000000000..4627a1e1872
--- /dev/null
+++ b/spec/helpers/storage_helper_spec.rb
@@ -0,0 +1,21 @@
+require 'spec_helper'
+
+describe StorageHelper do
+ describe '#storage_counter' do
+ it 'formats bytes to one decimal place' do
+ expect(helper.storage_counter(1.23.megabytes)).to eq '1.2 MB'
+ end
+
+ it 'does not add decimals for sizes < 1 MB' do
+ expect(helper.storage_counter(23.5.kilobytes)).to eq '24 KB'
+ end
+
+ it 'does not add decimals for zeroes' do
+ expect(helper.storage_counter(2.megabytes)).to eq '2 MB'
+ end
+
+ it 'uses commas as thousands separator' do
+ expect(helper.storage_counter(100_000_000_000_000_000)).to eq '90,949.5 TB'
+ end
+ end
+end
diff --git a/spec/javascripts/fixtures/project_title.html.haml b/spec/javascripts/fixtures/project_title.html.haml
index 4547feeb212..9d1f7877116 100644
--- a/spec/javascripts/fixtures/project_title.html.haml
+++ b/spec/javascripts/fixtures/project_title.html.haml
@@ -4,7 +4,7 @@
GitLab Org
%a.project-item-select-holder{href: "/gitlab-org/gitlab-test"}
GitLab Test
- %i.fa.chevron-down.dropdown-toggle-caret.js-projects-dropdown-toggle{ "data-toggle" => "dropdown", "data-target" => ".header-content" }
+ %i.fa.chevron-down.dropdown-toggle-caret.js-projects-dropdown-toggle{ "data-toggle" => "dropdown", "data-target" => ".header-content", "data-order-by" => "last_activity_at" }
.js-dropdown-menu-projects
.dropdown-menu.dropdown-select.dropdown-menu-projects
.dropdown-title
diff --git a/spec/javascripts/project_title_spec.js b/spec/javascripts/project_title_spec.js
index bb802a4b5e3..216b77f37c0 100644
--- a/spec/javascripts/project_title_spec.js
+++ b/spec/javascripts/project_title_spec.js
@@ -26,6 +26,7 @@
var fakeAjaxResponse = function fakeAjaxResponse(req) {
var d;
expect(req.url).toBe('/api/v3/projects.json?simple=true');
+ expect(req.data).toEqual({ search: '', order_by: 'last_activity_at', per_page: 20 });
d = $.Deferred();
d.resolve(this.projects_data);
return d.promise();
diff --git a/spec/javascripts/u2f/authenticate_spec.js b/spec/javascripts/u2f/authenticate_spec.js
index a8874ab12d3..064d18519ea 100644
--- a/spec/javascripts/u2f/authenticate_spec.js
+++ b/spec/javascripts/u2f/authenticate_spec.js
@@ -14,18 +14,19 @@
beforeEach(function() {
this.u2fDevice = new MockU2FDevice;
this.container = $("#js-authenticate-u2f");
- this.component = new U2FAuthenticate(this.container, {
- sign_requests: []
- }, "token");
+ this.component = new window.gl.U2FAuthenticate(
+ this.container,
+ '#js-login-u2f-form',
+ {
+ sign_requests: []
+ },
+ document.querySelector('#js-login-2fa-device'),
+ document.querySelector('.js-2fa-form')
+ );
return this.component.start();
});
it('allows authenticating via a U2F device', function() {
- var authenticatedMessage, deviceResponse, inProgressMessage, setupButton, setupMessage;
- setupButton = this.container.find("#js-login-u2f-device");
- setupMessage = this.container.find("p");
- expect(setupMessage.text()).toContain('Insert your security key');
- expect(setupButton.text()).toBe('Sign in via U2F device');
- setupButton.trigger('click');
+ var authenticatedMessage, deviceResponse, inProgressMessage;
inProgressMessage = this.container.find("p");
expect(inProgressMessage.text()).toContain("Trying to communicate with your device");
this.u2fDevice.respondToAuthenticateRequest({
@@ -33,7 +34,7 @@
});
authenticatedMessage = this.container.find("p");
deviceResponse = this.container.find('#js-device-response');
- expect(authenticatedMessage.text()).toContain("Click this button to authenticate with the GitLab server");
+ expect(authenticatedMessage.text()).toContain('We heard back from your U2F device. You have been authenticated.');
return expect(deviceResponse.val()).toBe('{"deviceData":"this is data from the device"}');
});
return describe("errors", function() {
@@ -62,7 +63,7 @@
deviceData: "this is data from the device"
});
authenticatedMessage = this.container.find("p");
- return expect(authenticatedMessage.text()).toContain("Click this button to authenticate with the GitLab server");
+ return expect(authenticatedMessage.text()).toContain("We heard back from your U2F device. You have been authenticated.");
});
});
});
diff --git a/spec/lib/banzai/reference_parser/external_issue_parser_spec.rb b/spec/lib/banzai/reference_parser/external_issue_parser_spec.rb
index 50a5d1a19ba..0af36776a54 100644
--- a/spec/lib/banzai/reference_parser/external_issue_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/external_issue_parser_spec.rb
@@ -12,7 +12,17 @@ describe Banzai::ReferenceParser::ExternalIssueParser, lib: true do
context 'when the link has a data-issue attribute' do
before { link['data-external-issue'] = 123 }
- it_behaves_like "referenced feature visibility", "issues"
+ levels = [ProjectFeature::DISABLED, ProjectFeature::PRIVATE, ProjectFeature::ENABLED]
+
+ levels.each do |level|
+ it "creates reference when the feature is #{level}" do
+ project.project_feature.update(issues_access_level: level)
+
+ visible_nodes = subject.nodes_visible_to_user(user, [link])
+
+ expect(visible_nodes).to include(link)
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb b/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb
index 2a680f03476..f2bc15d39d7 100644
--- a/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb
+++ b/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb
@@ -1,21 +1,30 @@
require 'spec_helper'
describe Gitlab::Diff::FileCollection::MergeRequestDiff do
- let(:merge_request) { create :merge_request }
+ let(:merge_request) { create(:merge_request) }
+ let(:diff_files) { described_class.new(merge_request.merge_request_diff, diff_options: nil).diff_files }
- it 'does not hightlight binary files' do
+ it 'does not highlight binary files' do
allow_any_instance_of(Gitlab::Diff::File).to receive(:blob).and_return(double("text?" => false))
expect_any_instance_of(Gitlab::Diff::File).not_to receive(:highlighted_diff_lines)
- described_class.new(merge_request.merge_request_diff, diff_options: nil).diff_files
+ diff_files
end
- it 'does not hightlight file if blob is not accessable' do
+ it 'does not highlight file if blob is not accessable' do
allow_any_instance_of(Gitlab::Diff::File).to receive(:blob).and_return(nil)
expect_any_instance_of(Gitlab::Diff::File).not_to receive(:highlighted_diff_lines)
- described_class.new(merge_request.merge_request_diff, diff_options: nil).diff_files
+ diff_files
+ end
+
+ it 'does not files marked as undiffable in .gitattributes' do
+ allow_any_instance_of(Repository).to receive(:diffable?).and_return(false)
+
+ expect_any_instance_of(Gitlab::Diff::File).not_to receive(:highlighted_diff_lines)
+
+ diff_files
end
end
diff --git a/spec/lib/gitlab/email/reply_parser_spec.rb b/spec/lib/gitlab/email/reply_parser_spec.rb
index c7a0139d32a..28698e89c33 100644
--- a/spec/lib/gitlab/email/reply_parser_spec.rb
+++ b/spec/lib/gitlab/email/reply_parser_spec.rb
@@ -88,8 +88,6 @@ describe Gitlab::Email::ReplyParser, lib: true do
expect(test_parse_body(fixture_file("emails/inline_reply.eml"))).
to eq(
<<-BODY.strip_heredoc.chomp
- On Wed, Oct 8, 2014 at 11:12 AM, techAPJ <info@unconfigured.discourse.org> wrote:
-
> techAPJ <https://meta.discourse.org/users/techapj>
> November 28
>
diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb
index f1d0a190002..44b84afde47 100644
--- a/spec/lib/gitlab/git_access_spec.rb
+++ b/spec/lib/gitlab/git_access_spec.rb
@@ -50,7 +50,7 @@ describe Gitlab::GitAccess, lib: true do
end
end
- describe 'download_access_check' do
+ describe '#check_download_access!' do
subject { access.check('git-upload-pack', '_any') }
describe 'master permissions' do
@@ -82,7 +82,7 @@ describe Gitlab::GitAccess, lib: true do
end
end
- describe 'without acccess to project' do
+ describe 'without access to project' do
context 'pull code' do
it { expect(subject.allowed?).to be_falsey }
end
@@ -112,7 +112,7 @@ describe Gitlab::GitAccess, lib: true do
end
describe 'deploy key permissions' do
- let(:key) { create(:deploy_key) }
+ let(:key) { create(:deploy_key, user: user) }
let(:actor) { key }
context 'pull code' do
@@ -136,7 +136,7 @@ describe Gitlab::GitAccess, lib: true do
end
context 'from private project' do
- let(:project) { create(:project, :internal) }
+ let(:project) { create(:project, :private) }
it { expect(subject).not_to be_allowed }
end
@@ -183,7 +183,7 @@ describe Gitlab::GitAccess, lib: true do
end
end
- describe 'push_access_check' do
+ describe '#check_push_access!' do
before { merge_into_protected_branch }
let(:unprotected_branch) { FFaker::Internet.user_name }
@@ -231,7 +231,7 @@ describe Gitlab::GitAccess, lib: true do
permissions_matrix[role].each do |action, allowed|
context action do
- subject { access.push_access_check(changes[action]) }
+ subject { access.send(:check_push_access!, changes[action]) }
it { expect(subject.allowed?).to allowed ? be_truthy : be_falsey }
end
end
@@ -353,13 +353,13 @@ describe Gitlab::GitAccess, lib: true do
end
end
- shared_examples 'can not push code' do
+ shared_examples 'pushing code' do |can|
subject { access.check('git-receive-pack', '_any') }
context 'when project is authorized' do
before { authorize }
- it { expect(subject).not_to be_allowed }
+ it { expect(subject).public_send(can, be_allowed) }
end
context 'when unauthorized' do
@@ -386,7 +386,7 @@ describe Gitlab::GitAccess, lib: true do
describe 'build authentication abilities' do
let(:authentication_abilities) { build_authentication_abilities }
- it_behaves_like 'can not push code' do
+ it_behaves_like 'pushing code', :not_to do
def authorize
project.team << [user, :reporter]
end
@@ -394,12 +394,26 @@ describe Gitlab::GitAccess, lib: true do
end
describe 'deploy key permissions' do
- let(:key) { create(:deploy_key) }
+ let(:key) { create(:deploy_key, user: user, can_push: can_push) }
let(:actor) { key }
- it_behaves_like 'can not push code' do
- def authorize
- key.projects << project
+ context 'when deploy_key can push' do
+ let(:can_push) { true }
+
+ it_behaves_like 'pushing code', :to do
+ def authorize
+ key.projects << project
+ end
+ end
+ end
+
+ context 'when deploy_key cannot push' do
+ let(:can_push) { false }
+
+ it_behaves_like 'pushing code', :not_to do
+ def authorize
+ key.projects << project
+ end
end
end
end
diff --git a/spec/lib/gitlab/git_access_wiki_spec.rb b/spec/lib/gitlab/git_access_wiki_spec.rb
index 578db51631e..a5d172233cc 100644
--- a/spec/lib/gitlab/git_access_wiki_spec.rb
+++ b/spec/lib/gitlab/git_access_wiki_spec.rb
@@ -27,7 +27,7 @@ describe Gitlab::GitAccessWiki, lib: true do
['6f6d7e7ed 570e7b2ab refs/heads/master']
end
- describe '#download_access_check' do
+ describe '#access_check_download!' do
subject { access.check('git-upload-pack', '_any') }
before do
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index f420d71dee2..ceed9c942c1 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -192,6 +192,7 @@ project:
- authorized_users
- project_authorizations
- route
+- statistics
award_emoji:
- awardable
- user
diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml
index 78d6b2c5032..ac26c831fd0 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -247,6 +247,7 @@ DeployKey:
- type
- fingerprint
- public
+- can_push
Service:
- id
- type
diff --git a/spec/migrations/remove_dot_git_from_usernames.rb b/spec/migrations/remove_dot_git_from_usernames.rb
new file mode 100644
index 00000000000..1b1d2adc463
--- /dev/null
+++ b/spec/migrations/remove_dot_git_from_usernames.rb
@@ -0,0 +1,29 @@
+# encoding: utf-8
+
+require 'spec_helper'
+require Rails.root.join('db', 'migrate', '20161226122833_remove_dot_git_from_usernames.rb')
+
+describe RemoveDotGitFromUsernames do
+ let(:user) { create(:user) }
+
+ describe '#up' do
+ let(:migration) { described_class.new }
+
+ before do
+ namespace = user.namespace
+ namespace.path = 'test.git'
+ namespace.save!(validate: false)
+
+ user.username = 'test.git'
+ user.save!(validate: false)
+ end
+
+ it 'renames user with .git in username' do
+ migration.up
+
+ expect(user.reload.username).to eq('test_git')
+ expect(user.namespace.reload.path).to eq('test_git')
+ expect(user.namespace.route.path).to eq('test_git')
+ end
+ end
+end
diff --git a/spec/migrations/rename_reserved_project_names_spec.rb b/spec/migrations/rename_reserved_project_names_spec.rb
new file mode 100644
index 00000000000..4fb7ed36884
--- /dev/null
+++ b/spec/migrations/rename_reserved_project_names_spec.rb
@@ -0,0 +1,47 @@
+# encoding: utf-8
+
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20161221153951_rename_reserved_project_names.rb')
+
+# This migration uses multiple threads, and thus different transactions. This
+# means data created in this spec may not be visible to some threads. To work
+# around this we use the TRUNCATE cleaning strategy.
+describe RenameReservedProjectNames, truncate: true do
+ let(:migration) { described_class.new }
+ let!(:project) { create(:empty_project) }
+
+ before do
+ project.path = 'projects'
+ project.save!(validate: false)
+ end
+
+ describe '#up' do
+ context 'when project repository exists' do
+ before { project.create_repository }
+
+ context 'when no exception is raised' do
+ it 'renames project with reserved names' do
+ migration.up
+
+ expect(project.reload.path).to eq('projects0')
+ end
+ end
+
+ context 'when exception is raised during rename' do
+ before do
+ allow(project).to receive(:rename_repo).and_raise(StandardError)
+ end
+
+ it 'captures exception from project rename' do
+ expect { migration.up }.not_to raise_error
+ end
+ end
+ end
+
+ context 'when project repository does not exist' do
+ it 'does not raise error' do
+ expect { migration.up }.not_to raise_error
+ end
+ end
+ end
+end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index a7e90c8a381..7e1d1126b97 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -85,4 +85,30 @@ describe Ci::Build, models: true do
it { expect(build.trace_file_path).to eq(build.old_path_to_trace) }
end
end
+
+ describe '#update_project_statistics' do
+ let!(:build) { create(:ci_build, artifacts_size: 23) }
+
+ it 'updates project statistics when the artifact size changes' do
+ expect(ProjectCacheWorker).to receive(:perform_async)
+ .with(build.project_id, [], [:build_artifacts_size])
+
+ build.artifacts_size = 42
+ build.save!
+ end
+
+ it 'does not update project statistics when the artifact size stays the same' do
+ expect(ProjectCacheWorker).not_to receive(:perform_async)
+
+ build.name = 'changed'
+ build.save!
+ end
+
+ it 'updates project statistics when the build is destroyed' do
+ expect(ProjectCacheWorker).to receive(:perform_async)
+ .with(build.project_id, [], [:build_artifacts_size])
+
+ build.destroy
+ end
+ end
end
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index 79341d43c08..d1aee27057a 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -464,6 +464,19 @@ describe Ci::Pipeline, models: true do
end
end
+ describe '.latest_successful_for' do
+ include_context 'with some outdated pipelines'
+
+ let!(:latest_successful_pipeline) do
+ create_pipeline(:success, 'ref', 'D')
+ end
+
+ it 'returns the latest successful pipeline' do
+ expect(described_class.latest_successful_for('ref')).
+ to eq(latest_successful_pipeline)
+ end
+ end
+
describe '#status' do
let!(:build) { create(:ci_build, :created, pipeline: pipeline, name: 'test') }
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index 4fa06a8c60a..1078c959419 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -44,6 +44,45 @@ describe Issue, "Issuable" do
it { expect(described_class).to respond_to(:assigned) }
end
+ describe "before_save" do
+ describe "#update_cache_counts" do
+ context "when previous assignee exists" do
+ before do
+ assignee = create(:user)
+ issue.project.team << [assignee, :developer]
+ issue.update(assignee: assignee)
+ end
+
+ it "updates cache counts for new assignee" do
+ user = create(:user)
+
+ expect(user).to receive(:update_cache_counts)
+
+ issue.update(assignee: user)
+ end
+
+ it "updates cache counts for previous assignee" do
+ old_assignee = issue.assignee
+ allow(User).to receive(:find_by_id).with(old_assignee.id).and_return(old_assignee)
+
+ expect(old_assignee).to receive(:update_cache_counts)
+
+ issue.update(assignee: nil)
+ end
+ end
+
+ context "when previous assignee does not exist" do
+ before{ issue.update(assignee: nil) }
+
+ it "updates cache count for the new assignee" do
+ expect_any_instance_of(User).to receive(:update_cache_counts)
+
+ issue.update(assignee: user)
+ end
+ end
+ end
+ end
+
describe ".search" do
let!(:searchable_issue) { create(:issue, title: "Searchable issue") }
diff --git a/spec/models/deploy_key_spec.rb b/spec/models/deploy_key_spec.rb
index 93623e8e99b..8ef8218cf74 100644
--- a/spec/models/deploy_key_spec.rb
+++ b/spec/models/deploy_key_spec.rb
@@ -1,8 +1,22 @@
require 'spec_helper'
describe DeployKey, models: true do
+ include EmailHelpers
+
describe "Associations" do
it { is_expected.to have_many(:deploy_keys_projects) }
it { is_expected.to have_many(:projects) }
end
+
+ describe 'notification' do
+ let(:user) { create(:user) }
+
+ it 'does not send a notification' do
+ perform_enqueued_jobs do
+ create(:deploy_key, user: user)
+ end
+
+ should_not_email(user)
+ end
+ end
end
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index 7d5ecfbaa64..45fe927202b 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -277,4 +277,15 @@ describe Group, models: true do
it { is_expected.to be_valid }
it { expect(subject.parent).to be_kind_of(Group) }
end
+
+ describe '#members_with_parents' do
+ let!(:group) { create(:group, :nested) }
+ let!(:master) { group.parent.add_user(create(:user), GroupMember::MASTER) }
+ let!(:developer) { group.add_user(create(:user), GroupMember::DEVELOPER) }
+
+ it 'returns parents members' do
+ expect(group.members_with_parents).to include(developer)
+ expect(group.members_with_parents).to include(master)
+ end
+ end
end
diff --git a/spec/models/key_spec.rb b/spec/models/key_spec.rb
index 2a33d819138..7758b7ffa97 100644
--- a/spec/models/key_spec.rb
+++ b/spec/models/key_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
describe Key, models: true do
+ include EmailHelpers
+
describe "Associations" do
it { is_expected.to belong_to(:user) }
end
@@ -96,4 +98,16 @@ describe Key, models: true do
expect(described_class.new(key: " #{valid_key} ").key).to eq(valid_key)
end
end
+
+ describe 'notification' do
+ let(:user) { create(:user) }
+
+ it 'sends a notification' do
+ perform_enqueued_jobs do
+ create(:key, user: user)
+ end
+
+ should_email(user)
+ end
+ end
end
diff --git a/spec/models/lfs_objects_project_spec.rb b/spec/models/lfs_objects_project_spec.rb
new file mode 100644
index 00000000000..7bc278e350f
--- /dev/null
+++ b/spec/models/lfs_objects_project_spec.rb
@@ -0,0 +1,36 @@
+require 'spec_helper'
+
+describe LfsObjectsProject, models: true do
+ subject { create(:lfs_objects_project, project: project) }
+ let(:project) { create(:empty_project) }
+
+ describe 'associations' do
+ it { is_expected.to belong_to(:project) }
+ it { is_expected.to belong_to(:lfs_object) }
+ end
+
+ describe 'validation' do
+ it { is_expected.to validate_presence_of(:lfs_object_id) }
+ it { is_expected.to validate_uniqueness_of(:lfs_object_id).scoped_to(:project_id).with_message("already exists in project") }
+
+ it { is_expected.to validate_presence_of(:project_id) }
+ end
+
+ describe '#update_project_statistics' do
+ it 'updates project statistics when the object is added' do
+ expect(ProjectCacheWorker).to receive(:perform_async)
+ .with(project.id, [], [:lfs_objects_size])
+
+ subject.save!
+ end
+
+ it 'updates project statistics when the object is removed' do
+ subject.save!
+
+ expect(ProjectCacheWorker).to receive(:perform_async)
+ .with(project.id, [], [:lfs_objects_size])
+
+ subject.destroy
+ end
+ end
+end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 5da00a8636a..646e6c6dbb3 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -283,12 +283,16 @@ describe MergeRequest, models: true do
end
describe '#issues_mentioned_but_not_closing' do
- it 'detects issues mentioned in description but not closed' do
- mentioned_issue = create(:issue, project: subject.project)
+ let(:closing_issue) { create :issue, project: subject.project }
+ let(:mentioned_issue) { create :issue, project: subject.project }
+
+ let(:commit) { double('commit', safe_message: "Fixes #{closing_issue.to_reference}") }
+ it 'detects issues mentioned in description but not closed' do
subject.project.team << [subject.author, :developer]
- subject.description = "Is related to #{mentioned_issue.to_reference}"
+ subject.description = "Is related to #{mentioned_issue.to_reference} and #{closing_issue.to_reference}"
+ allow(subject).to receive(:commits).and_return([commit])
allow(subject.project).to receive(:default_branch).
and_return(subject.target_branch)
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index 9fd06bb6b23..600538ff5f4 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -4,6 +4,7 @@ describe Namespace, models: true do
let!(:namespace) { create(:namespace) }
it { is_expected.to have_many :projects }
+ it { is_expected.to have_many :project_statistics }
it { is_expected.to validate_presence_of(:name) }
it { is_expected.to validate_uniqueness_of(:name).scoped_to(:parent_id) }
@@ -57,6 +58,50 @@ describe Namespace, models: true do
end
end
+ describe '.with_statistics' do
+ let(:namespace) { create :namespace }
+
+ let(:project1) do
+ create(:empty_project,
+ namespace: namespace,
+ statistics: build(:project_statistics,
+ storage_size: 606,
+ repository_size: 101,
+ lfs_objects_size: 202,
+ build_artifacts_size: 303))
+ end
+
+ let(:project2) do
+ create(:empty_project,
+ namespace: namespace,
+ statistics: build(:project_statistics,
+ storage_size: 60,
+ repository_size: 10,
+ lfs_objects_size: 20,
+ build_artifacts_size: 30))
+ end
+
+ it "sums all project storage counters in the namespace" do
+ project1
+ project2
+ statistics = Namespace.with_statistics.find(namespace.id)
+
+ expect(statistics.storage_size).to eq 666
+ expect(statistics.repository_size).to eq 111
+ expect(statistics.lfs_objects_size).to eq 222
+ expect(statistics.build_artifacts_size).to eq 333
+ end
+
+ it "correctly handles namespaces without projects" do
+ statistics = Namespace.with_statistics.find(namespace.id)
+
+ expect(statistics.storage_size).to eq 0
+ expect(statistics.repository_size).to eq 0
+ expect(statistics.lfs_objects_size).to eq 0
+ expect(statistics.build_artifacts_size).to eq 0
+ end
+ end
+
describe '#move_dir' do
before do
@namespace = create :namespace
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 88d5d14f855..fb225eb7625 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -49,6 +49,7 @@ describe Project, models: true do
it { is_expected.to have_one(:gitlab_issue_tracker_service).dependent(:destroy) }
it { is_expected.to have_one(:external_wiki_service).dependent(:destroy) }
it { is_expected.to have_one(:project_feature).dependent(:destroy) }
+ it { is_expected.to have_one(:statistics).class_name('ProjectStatistics').dependent(:delete) }
it { is_expected.to have_one(:import_data).class_name('ProjectImportData').dependent(:destroy) }
it { is_expected.to have_one(:last_event).class_name('Event') }
it { is_expected.to have_one(:forked_from_project).through(:forked_project_link) }
@@ -1729,6 +1730,26 @@ describe Project, models: true do
end
end
+ describe '#update_project_statistics' do
+ let(:project) { create(:empty_project) }
+
+ it "is called after creation" do
+ expect(project.statistics).to be_a ProjectStatistics
+ expect(project.statistics).to be_persisted
+ end
+
+ it "copies the namespace_id" do
+ expect(project.statistics.namespace_id).to eq project.namespace_id
+ end
+
+ it "updates the namespace_id when changed" do
+ namespace = create(:namespace)
+ project.update(namespace: namespace)
+
+ expect(project.statistics.namespace_id).to eq namespace.id
+ end
+ end
+
def enable_lfs
allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
end
diff --git a/spec/models/project_statistics_spec.rb b/spec/models/project_statistics_spec.rb
new file mode 100644
index 00000000000..77403cc9eb0
--- /dev/null
+++ b/spec/models/project_statistics_spec.rb
@@ -0,0 +1,160 @@
+require 'rails_helper'
+
+describe ProjectStatistics, models: true do
+ let(:project) { create :empty_project }
+ let(:statistics) { project.statistics }
+
+ describe 'constants' do
+ describe 'STORAGE_COLUMNS' do
+ it 'is an array of symbols' do
+ expect(described_class::STORAGE_COLUMNS).to be_kind_of Array
+ expect(described_class::STORAGE_COLUMNS.map(&:class).uniq).to eq [Symbol]
+ end
+ end
+
+ describe 'STATISTICS_COLUMNS' do
+ it 'is an array of symbols' do
+ expect(described_class::STATISTICS_COLUMNS).to be_kind_of Array
+ expect(described_class::STATISTICS_COLUMNS.map(&:class).uniq).to eq [Symbol]
+ end
+
+ it 'includes all storage columns' do
+ expect(described_class::STATISTICS_COLUMNS & described_class::STORAGE_COLUMNS).to eq described_class::STORAGE_COLUMNS
+ end
+ end
+ end
+
+ describe 'associations' do
+ it { is_expected.to belong_to(:project) }
+ it { is_expected.to belong_to(:namespace) }
+ end
+
+ describe 'statistics columns' do
+ it "support values up to 8 exabytes" do
+ statistics.update!(
+ commit_count: 8.exabytes - 1,
+ repository_size: 2.exabytes,
+ lfs_objects_size: 2.exabytes,
+ build_artifacts_size: 4.exabytes - 1,
+ )
+
+ statistics.reload
+
+ expect(statistics.commit_count).to eq(8.exabytes - 1)
+ expect(statistics.repository_size).to eq(2.exabytes)
+ expect(statistics.lfs_objects_size).to eq(2.exabytes)
+ expect(statistics.build_artifacts_size).to eq(4.exabytes - 1)
+ expect(statistics.storage_size).to eq(8.exabytes - 1)
+ end
+ end
+
+ describe '#total_repository_size' do
+ it "sums repository and LFS object size" do
+ statistics.repository_size = 2
+ statistics.lfs_objects_size = 3
+ statistics.build_artifacts_size = 4
+
+ expect(statistics.total_repository_size).to eq 5
+ end
+ end
+
+ describe '#refresh!' do
+ before do
+ allow(statistics).to receive(:update_commit_count)
+ allow(statistics).to receive(:update_repository_size)
+ allow(statistics).to receive(:update_lfs_objects_size)
+ allow(statistics).to receive(:update_build_artifacts_size)
+ allow(statistics).to receive(:update_storage_size)
+ end
+
+ context "without arguments" do
+ before do
+ statistics.refresh!
+ end
+
+ it "sums all counters" do
+ expect(statistics).to have_received(:update_commit_count)
+ expect(statistics).to have_received(:update_repository_size)
+ expect(statistics).to have_received(:update_lfs_objects_size)
+ expect(statistics).to have_received(:update_build_artifacts_size)
+ end
+ end
+
+ context "when passing an only: argument" do
+ before do
+ statistics.refresh! only: [:lfs_objects_size]
+ end
+
+ it "only updates the given columns" do
+ expect(statistics).to have_received(:update_lfs_objects_size)
+ expect(statistics).not_to have_received(:update_commit_count)
+ expect(statistics).not_to have_received(:update_repository_size)
+ expect(statistics).not_to have_received(:update_build_artifacts_size)
+ end
+ end
+ end
+
+ describe '#update_commit_count' do
+ before do
+ allow(project.repository).to receive(:commit_count).and_return(23)
+ statistics.update_commit_count
+ end
+
+ it "stores the number of commits in the repository" do
+ expect(statistics.commit_count).to eq 23
+ end
+ end
+
+ describe '#update_repository_size' do
+ before do
+ allow(project.repository).to receive(:size).and_return(12.megabytes)
+ statistics.update_repository_size
+ end
+
+ it "stores the size of the repository" do
+ expect(statistics.repository_size).to eq 12.megabytes
+ end
+ end
+
+ describe '#update_lfs_objects_size' do
+ let!(:lfs_object1) { create(:lfs_object, size: 23.megabytes) }
+ let!(:lfs_object2) { create(:lfs_object, size: 34.megabytes) }
+ let!(:lfs_objects_project1) { create(:lfs_objects_project, project: project, lfs_object: lfs_object1) }
+ let!(:lfs_objects_project2) { create(:lfs_objects_project, project: project, lfs_object: lfs_object2) }
+
+ before do
+ statistics.update_lfs_objects_size
+ end
+
+ it "stores the size of related LFS objects" do
+ expect(statistics.lfs_objects_size).to eq 57.megabytes
+ end
+ end
+
+ describe '#update_build_artifacts_size' do
+ let!(:pipeline) { create(:ci_pipeline, project: project) }
+ let!(:build1) { create(:ci_build, pipeline: pipeline, artifacts_size: 45.megabytes) }
+ let!(:build2) { create(:ci_build, pipeline: pipeline, artifacts_size: 56.megabytes) }
+
+ before do
+ statistics.update_build_artifacts_size
+ end
+
+ it "stores the size of related build artifacts" do
+ expect(statistics.build_artifacts_size).to eq 101.megabytes
+ end
+ end
+
+ describe '#update_storage_size' do
+ it "sums all storage counters" do
+ statistics.update!(
+ repository_size: 2,
+ lfs_objects_size: 3,
+ )
+
+ statistics.reload
+
+ expect(statistics.storage_size).to eq 5
+ end
+ end
+end
diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb
index a20ac303a53..5c34ff04152 100644
--- a/spec/policies/group_policy_spec.rb
+++ b/spec/policies/group_policy_spec.rb
@@ -105,4 +105,70 @@ describe GroupPolicy, models: true do
is_expected.to include(*owner_permissions)
end
end
+
+ describe 'private nested group inherit permissions' do
+ let(:nested_group) { create(:group, :private, parent: group) }
+
+ subject { described_class.abilities(current_user, nested_group).to_set }
+
+ context 'with no user' do
+ let(:current_user) { nil }
+
+ it do
+ is_expected.not_to include(:read_group)
+ is_expected.not_to include(*master_permissions)
+ is_expected.not_to include(*owner_permissions)
+ end
+ end
+
+ context 'guests' do
+ let(:current_user) { guest }
+
+ it do
+ is_expected.to include(:read_group)
+ is_expected.not_to include(*master_permissions)
+ is_expected.not_to include(*owner_permissions)
+ end
+ end
+
+ context 'reporter' do
+ let(:current_user) { reporter }
+
+ it do
+ is_expected.to include(:read_group)
+ is_expected.not_to include(*master_permissions)
+ is_expected.not_to include(*owner_permissions)
+ end
+ end
+
+ context 'developer' do
+ let(:current_user) { developer }
+
+ it do
+ is_expected.to include(:read_group)
+ is_expected.not_to include(*master_permissions)
+ is_expected.not_to include(*owner_permissions)
+ end
+ end
+
+ context 'master' do
+ let(:current_user) { master }
+
+ it do
+ is_expected.to include(:read_group)
+ is_expected.to include(*master_permissions)
+ is_expected.not_to include(*owner_permissions)
+ end
+ end
+
+ context 'owner' do
+ let(:current_user) { owner }
+
+ it do
+ is_expected.to include(:read_group)
+ is_expected.to include(*master_permissions)
+ is_expected.to include(*owner_permissions)
+ end
+ end
+ end
end
diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb
index cdeb965b413..0e8d6faea27 100644
--- a/spec/requests/api/groups_spec.rb
+++ b/spec/requests/api/groups_spec.rb
@@ -35,6 +35,14 @@ describe API::Groups, api: true do
expect(json_response.length).to eq(1)
expect(json_response.first['name']).to eq(group1.name)
end
+
+ it "does not include statistics" do
+ get api("/groups", user1), statistics: true
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first).not_to include 'statistics'
+ end
end
context "when authenticated as admin" do
@@ -44,6 +52,31 @@ describe API::Groups, api: true do
expect(json_response).to be_an Array
expect(json_response.length).to eq(2)
end
+
+ it "does not include statistics by default" do
+ get api("/groups", admin)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first).not_to include('statistics')
+ end
+
+ it "includes statistics if requested" do
+ attributes = {
+ storage_size: 702,
+ repository_size: 123,
+ lfs_objects_size: 234,
+ build_artifacts_size: 345,
+ }
+
+ project1.statistics.update!(attributes)
+
+ get api("/groups", admin), statistics: true
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first['statistics']).to eq attributes.stringify_keys
+ end
end
context "when using skip_groups in request" do
diff --git a/spec/requests/api/helpers_spec.rb b/spec/requests/api/helpers_spec.rb
index c3d7ac3eef8..b8ee2293a33 100644
--- a/spec/requests/api/helpers_spec.rb
+++ b/spec/requests/api/helpers_spec.rb
@@ -396,7 +396,7 @@ describe API::Helpers, api: true do
%w[HEAD GET].each do |method_name|
context "method is #{method_name}" do
before do
- expect_any_instance_of(self.class).to receive(:route).and_return(double(route_method: method_name))
+ expect_any_instance_of(self.class).to receive(:route).and_return(double(request_method: method_name))
end
it 'does not raise an error' do
@@ -410,7 +410,7 @@ describe API::Helpers, api: true do
%w[POST PUT PATCH DELETE].each do |method_name|
context "method is #{method_name}" do
before do
- expect_any_instance_of(self.class).to receive(:route).and_return(double(route_method: method_name))
+ expect_any_instance_of(self.class).to receive(:route).and_return(double(request_method: method_name))
end
it 'calls authenticate!' do
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 8304c408064..f5788d15f93 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -49,7 +49,7 @@ describe API::Projects, api: true do
end
end
- context 'when authenticated' do
+ context 'when authenticated as regular user' do
it 'returns an array of projects' do
get api('/projects', user)
expect(response).to have_http_status(200)
@@ -172,6 +172,22 @@ describe API::Projects, api: true do
end
end
end
+
+ it "does not include statistics by default" do
+ get api('/projects/all', admin)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first).not_to include('statistics')
+ end
+
+ it "includes statistics if requested" do
+ get api('/projects/all', admin), statistics: true
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first).to include 'statistics'
+ end
end
end
@@ -196,6 +212,32 @@ describe API::Projects, api: true do
expect(json_response.first['name']).to eq(project4.name)
expect(json_response.first['owner']['username']).to eq(user4.username)
end
+
+ it "does not include statistics by default" do
+ get api('/projects/owned', user4)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first).not_to include('statistics')
+ end
+
+ it "includes statistics if requested" do
+ attributes = {
+ commit_count: 23,
+ storage_size: 702,
+ repository_size: 123,
+ lfs_objects_size: 234,
+ build_artifacts_size: 345,
+ }
+
+ project4.statistics.update!(attributes)
+
+ get api('/projects/owned', user4), statistics: true
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first['statistics']).to eq attributes.stringify_keys
+ end
end
end
@@ -630,6 +672,18 @@ describe API::Projects, api: true do
expect(json_response['name']).to eq(project.name)
end
+ it 'exposes namespace fields' do
+ get api("/projects/#{project.id}", user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response['namespace']).to eq({
+ 'id' => user.namespace.id,
+ 'name' => user.namespace.name,
+ 'path' => user.namespace.path,
+ 'kind' => user.namespace.kind,
+ })
+ end
+
describe 'permissions' do
context 'all projects' do
before { project.team << [user, :master] }
diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb
index 9a8d633d657..ad9d8a25af4 100644
--- a/spec/requests/api/settings_spec.rb
+++ b/spec/requests/api/settings_spec.rb
@@ -44,8 +44,7 @@ describe API::Settings, 'Settings', api: true do
put api("/application/settings", admin), koding_enabled: true
expect(response).to have_http_status(400)
- expect(json_response['message']).to have_key('koding_url')
- expect(json_response['message']['koding_url']).to include "can't be blank"
+ expect(json_response['error']).to eq('koding_url is missing')
end
end
end
diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb
index d71bb08c218..5abda28e26f 100644
--- a/spec/requests/git_http_spec.rb
+++ b/spec/requests/git_http_spec.rb
@@ -371,12 +371,26 @@ describe 'Git HTTP requests', lib: true do
shared_examples 'can download code only' do
it 'downloads get status 200' do
- clone_get "#{project.path_with_namespace}.git", user: 'gitlab-ci-token', password: build.token
+ allow_any_instance_of(Repository).
+ to receive(:exists?).and_return(true)
+
+ clone_get "#{project.path_with_namespace}.git",
+ user: 'gitlab-ci-token', password: build.token
expect(response).to have_http_status(200)
expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
end
+ it 'downloads from non-existing repository and gets 403' do
+ allow_any_instance_of(Repository).
+ to receive(:exists?).and_return(false)
+
+ clone_get "#{project.path_with_namespace}.git",
+ user: 'gitlab-ci-token', password: build.token
+
+ expect(response).to have_http_status(403)
+ end
+
it 'uploads get status 403' do
push_get "#{project.path_with_namespace}.git", user: 'gitlab-ci-token', password: build.token
diff --git a/spec/routing/admin_routing_spec.rb b/spec/routing/admin_routing_spec.rb
index 661b671301e..99c44bde151 100644
--- a/spec/routing/admin_routing_spec.rb
+++ b/spec/routing/admin_routing_spec.rb
@@ -122,12 +122,18 @@ describe Admin::HealthCheckController, "routing" do
end
describe Admin::GroupsController, "routing" do
+ let(:name) { 'complex.group-namegit' }
+
it "to #index" do
expect(get("/admin/groups")).to route_to('admin/groups#index')
end
it "to #show" do
- expect(get("/admin/groups/gitlab")).to route_to('admin/groups#show', id: 'gitlab')
- expect(get("/admin/groups/gitlab/subgroup")).to route_to('admin/groups#show', id: 'gitlab/subgroup')
+ expect(get("/admin/groups/#{name}")).to route_to('admin/groups#show', id: name)
+ expect(get("/admin/groups/#{name}/subgroup")).to route_to('admin/groups#show', id: "#{name}/subgroup")
+ end
+
+ it "to #edit" do
+ expect(get("/admin/groups/#{name}/edit")).to route_to('admin/groups#edit', id: name)
end
end
diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb
index 3303e808a9c..2a0f00ce937 100644
--- a/spec/services/git_push_service_spec.rb
+++ b/spec/services/git_push_service_spec.rb
@@ -583,7 +583,7 @@ describe GitPushService, services: true do
service.push_commits = [commit]
expect(ProjectCacheWorker).to receive(:perform_async).
- with(project.id, %i(readme))
+ with(project.id, %i(readme), %i(commit_count repository_size))
service.update_caches
end
@@ -596,7 +596,7 @@ describe GitPushService, services: true do
it 'does not flush any conditional caches' do
expect(ProjectCacheWorker).to receive(:perform_async).
- with(project.id, []).
+ with(project.id, [], %i(commit_count repository_size)).
and_call_original
service.update_caches
diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb
index 71a0b8e2a12..14717a7455d 100644
--- a/spec/services/groups/create_service_spec.rb
+++ b/spec/services/groups/create_service_spec.rb
@@ -1,11 +1,12 @@
require 'spec_helper'
-describe Groups::CreateService, services: true do
- let!(:user) { create(:user) }
+describe Groups::CreateService, '#execute', services: true do
+ let!(:user) { create(:user) }
let!(:group_params) { { path: "group_path", visibility_level: Gitlab::VisibilityLevel::PUBLIC } }
- describe "execute" do
- let!(:service) { described_class.new(user, group_params ) }
+ describe 'visibility level restrictions' do
+ let!(:service) { described_class.new(user, group_params) }
+
subject { service.execute }
context "create groups without restricted visibility level" do
@@ -14,7 +15,29 @@ describe Groups::CreateService, services: true do
context "cannot create group with restricted visibility level" do
before { allow_any_instance_of(ApplicationSetting).to receive(:restricted_visibility_levels).and_return([Gitlab::VisibilityLevel::PUBLIC]) }
+
it { is_expected.not_to be_persisted }
end
end
+
+ describe 'creating subgroup' do
+ let!(:group) { create(:group) }
+ let!(:service) { described_class.new(user, group_params.merge(parent_id: group.id)) }
+
+ subject { service.execute }
+
+ context 'as group owner' do
+ before { group.add_owner(user) }
+
+ it { is_expected.to be_persisted }
+ end
+
+ context 'as guest' do
+ it 'does not save group and returns an error' do
+ is_expected.not_to be_persisted
+ expect(subject.errors[:parent_id].first).to eq('manage access required to create subgroup')
+ expect(subject.parent_id).to be_nil
+ end
+ end
+ end
end
diff --git a/spec/services/issuable/bulk_update_service_spec.rb b/spec/services/issuable/bulk_update_service_spec.rb
index 5f3020b6525..0475f38fe5e 100644
--- a/spec/services/issuable/bulk_update_service_spec.rb
+++ b/spec/services/issuable/bulk_update_service_spec.rb
@@ -52,7 +52,10 @@ describe Issuable::BulkUpdateService, services: true do
context 'when the new assignee ID is a valid user' do
it 'succeeds' do
- result = bulk_update(issue, assignee_id: create(:user).id)
+ new_assignee = create(:user)
+ project.team << [new_assignee, :developer]
+
+ result = bulk_update(issue, assignee_id: new_assignee.id)
expect(result[:success]).to be_truthy
expect(result[:count]).to eq(1)
@@ -60,15 +63,16 @@ describe Issuable::BulkUpdateService, services: true do
it 'updates the assignee to the use ID passed' do
assignee = create(:user)
+ project.team << [assignee, :developer]
expect { bulk_update(issue, assignee_id: assignee.id) }
.to change { issue.reload.assignee }.from(user).to(assignee)
end
end
- context 'when the new assignee ID is -1' do
- it 'unassigns the issues' do
- expect { bulk_update(issue, assignee_id: -1) }
+ context "when the new assignee ID is #{IssuableFinder::NONE}" do
+ it "unassigns the issues" do
+ expect { bulk_update(issue, assignee_id: IssuableFinder::NONE) }
.to change { issue.reload.assignee }.to(nil)
end
end
diff --git a/spec/services/issues/create_service_spec.rb b/spec/services/issues/create_service_spec.rb
index 8bde61ee336..ac3834c32ff 100644
--- a/spec/services/issues/create_service_spec.rb
+++ b/spec/services/issues/create_service_spec.rb
@@ -135,6 +135,8 @@ describe Issues::CreateService, services: true do
end
end
+ it_behaves_like 'issuable create service'
+
it_behaves_like 'new issuable record that supports slash commands'
context 'for a merge request' do
diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb
index eafbea46905..d83b09fd32c 100644
--- a/spec/services/issues/update_service_spec.rb
+++ b/spec/services/issues/update_service_spec.rb
@@ -142,6 +142,17 @@ describe Issues::UpdateService, services: true do
update_issue(confidential: true)
end
+
+ it 'does not update assignee_id with unauthorized users' do
+ project.update(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
+ update_issue(confidential: true)
+ non_member = create(:user)
+ original_assignee = issue.assignee
+
+ update_issue(assignee_id: non_member.id)
+
+ expect(issue.reload.assignee_id).to eq(original_assignee.id)
+ end
end
context 'todos' do
diff --git a/spec/services/merge_requests/create_service_spec.rb b/spec/services/merge_requests/create_service_spec.rb
index b8142889075..673c0bd6c9c 100644
--- a/spec/services/merge_requests/create_service_spec.rb
+++ b/spec/services/merge_requests/create_service_spec.rb
@@ -84,6 +84,8 @@ describe MergeRequests::CreateService, services: true do
end
end
+ it_behaves_like 'issuable create service'
+
context 'while saving references to issues that the created merge request closes' do
let(:first_issue) { create(:issue, project: project) }
let(:second_issue) { create(:issue, project: project) }
diff --git a/spec/services/merge_requests/merge_request_diff_cache_service_spec.rb b/spec/services/merge_requests/merge_request_diff_cache_service_spec.rb
index 05cdbe5287a..35804d41b46 100644
--- a/spec/services/merge_requests/merge_request_diff_cache_service_spec.rb
+++ b/spec/services/merge_requests/merge_request_diff_cache_service_spec.rb
@@ -11,6 +11,7 @@ describe MergeRequests::MergeRequestDiffCacheService do
expect(Rails.cache).to receive(:read).with(cache_key).and_return({})
expect(Rails.cache).to receive(:write).with(cache_key, anything)
allow_any_instance_of(Gitlab::Diff::File).to receive(:blob).and_return(double("text?" => true))
+ allow_any_instance_of(Repository).to receive(:diffable?).and_return(true)
subject.execute(merge_request)
end
diff --git a/spec/services/notes/slash_commands_service_spec.rb b/spec/services/notes/slash_commands_service_spec.rb
index d1099884a02..960b5cd5e6f 100644
--- a/spec/services/notes/slash_commands_service_spec.rb
+++ b/spec/services/notes/slash_commands_service_spec.rb
@@ -5,6 +5,8 @@ describe Notes::SlashCommandsService, services: true do
let(:project) { create(:empty_project) }
let(:master) { create(:user).tap { |u| project.team << [u, :master] } }
let(:assignee) { create(:user) }
+
+ before { project.team << [assignee, :master] }
end
shared_examples 'note on noteable that does not support slash commands' do
diff --git a/spec/services/users/refresh_authorized_projects_service_spec.rb b/spec/services/users/refresh_authorized_projects_service_spec.rb
index 72c8f7cd8ec..1f6919151de 100644
--- a/spec/services/users/refresh_authorized_projects_service_spec.rb
+++ b/spec/services/users/refresh_authorized_projects_service_spec.rb
@@ -54,12 +54,37 @@ describe Users::RefreshAuthorizedProjectsService do
end
describe '#update_authorizations' do
- it 'does nothing when there are no rows to add and remove' do
- expect(user).not_to receive(:remove_project_authorizations)
- expect(ProjectAuthorization).not_to receive(:insert_authorizations)
- expect(user).not_to receive(:set_authorized_projects_column)
+ context 'when there are no rows to add and remove' do
+ it 'does not change authorizations' do
+ expect(user).not_to receive(:remove_project_authorizations)
+ expect(ProjectAuthorization).not_to receive(:insert_authorizations)
- service.update_authorizations([], [])
+ service.update_authorizations([], [])
+ end
+
+ context 'when the authorized projects column is not set' do
+ before do
+ user.update!(authorized_projects_populated: nil)
+ end
+
+ it 'populates the authorized projects column' do
+ service.update_authorizations([], [])
+
+ expect(user.authorized_projects_populated).to eq true
+ end
+ end
+
+ context 'when the authorized projects column is set' do
+ before do
+ user.update!(authorized_projects_populated: true)
+ end
+
+ it 'does nothing' do
+ expect(user).not_to receive(:set_authorized_projects_column)
+
+ service.update_authorizations([], [])
+ end
+ end
end
it 'removes authorizations that should be removed' do
@@ -84,7 +109,7 @@ describe Users::RefreshAuthorizedProjectsService do
it 'populates the authorized projects column' do
# make sure we start with a nil value no matter what the default in the
# factory may be.
- user.update(authorized_projects_populated: nil)
+ user.update!(authorized_projects_populated: nil)
service.update_authorizations([], [[user.id, project.id, Gitlab::Access::MASTER]])
diff --git a/spec/support/fake_u2f_device.rb b/spec/support/fake_u2f_device.rb
index 8c407b867fe..a7605cd483a 100644
--- a/spec/support/fake_u2f_device.rb
+++ b/spec/support/fake_u2f_device.rb
@@ -5,7 +5,7 @@ class FakeU2fDevice
@page = page
@name = name
end
-
+
def respond_to_u2f_registration
app_id = @page.evaluate_script('gon.u2f.app_id')
challenges = @page.evaluate_script('gon.u2f.challenges')
@@ -28,6 +28,7 @@ class FakeU2fDevice
u2f.sign = function(appId, challenges, signRequests, callback) {
callback(#{json_response});
};
+ window.gl.u2fAuthenticate.start();
")
end
diff --git a/spec/support/features/issuable_slash_commands_shared_examples.rb b/spec/support/features/issuable_slash_commands_shared_examples.rb
index 194620d0a68..a4713e53f63 100644
--- a/spec/support/features/issuable_slash_commands_shared_examples.rb
+++ b/spec/support/features/issuable_slash_commands_shared_examples.rb
@@ -76,7 +76,7 @@ shared_examples 'issuable record that supports slash commands in its description
expect(page).not_to have_content '/assign @bob'
expect(page).not_to have_content '/label ~bug'
expect(page).not_to have_content '/milestone %"ASAP"'
- expect(page).to have_content 'Your commands have been executed!'
+ expect(page).to have_content 'Commands applied'
issuable.reload
@@ -97,7 +97,7 @@ shared_examples 'issuable record that supports slash commands in its description
write_note("/close")
expect(page).not_to have_content '/close'
- expect(page).to have_content 'Your commands have been executed!'
+ expect(page).to have_content 'Commands applied'
expect(issuable.reload).to be_closed
end
@@ -114,7 +114,7 @@ shared_examples 'issuable record that supports slash commands in its description
write_note("/close")
expect(page).not_to have_content '/close'
- expect(page).not_to have_content 'Your commands have been executed!'
+ expect(page).not_to have_content 'Commands applied'
expect(issuable).to be_open
end
@@ -132,7 +132,7 @@ shared_examples 'issuable record that supports slash commands in its description
write_note("/reopen")
expect(page).not_to have_content '/reopen'
- expect(page).to have_content 'Your commands have been executed!'
+ expect(page).to have_content 'Commands applied'
expect(issuable.reload).to be_open
end
@@ -149,7 +149,7 @@ shared_examples 'issuable record that supports slash commands in its description
write_note("/reopen")
expect(page).not_to have_content '/reopen'
- expect(page).not_to have_content 'Your commands have been executed!'
+ expect(page).not_to have_content 'Commands applied'
expect(issuable).to be_closed
end
@@ -162,7 +162,7 @@ shared_examples 'issuable record that supports slash commands in its description
write_note("/title Awesome new title")
expect(page).not_to have_content '/title'
- expect(page).to have_content 'Your commands have been executed!'
+ expect(page).to have_content 'Commands applied'
expect(issuable.reload.title).to eq 'Awesome new title'
end
@@ -179,7 +179,7 @@ shared_examples 'issuable record that supports slash commands in its description
write_note("/title Awesome new title")
expect(page).not_to have_content '/title'
- expect(page).not_to have_content 'Your commands have been executed!'
+ expect(page).not_to have_content 'Commands applied'
expect(issuable.reload.title).not_to eq 'Awesome new title'
end
@@ -191,7 +191,7 @@ shared_examples 'issuable record that supports slash commands in its description
write_note("/todo")
expect(page).not_to have_content '/todo'
- expect(page).to have_content 'Your commands have been executed!'
+ expect(page).to have_content 'Commands applied'
todos = TodosFinder.new(master).execute
todo = todos.first
@@ -222,7 +222,7 @@ shared_examples 'issuable record that supports slash commands in its description
write_note("/done")
expect(page).not_to have_content '/done'
- expect(page).to have_content 'Your commands have been executed!'
+ expect(page).to have_content 'Commands applied'
expect(todo.reload).to be_done
end
@@ -235,7 +235,7 @@ shared_examples 'issuable record that supports slash commands in its description
write_note("/subscribe")
expect(page).not_to have_content '/subscribe'
- expect(page).to have_content 'Your commands have been executed!'
+ expect(page).to have_content 'Commands applied'
expect(issuable.subscribed?(master, project)).to be_truthy
end
@@ -252,7 +252,7 @@ shared_examples 'issuable record that supports slash commands in its description
write_note("/unsubscribe")
expect(page).not_to have_content '/unsubscribe'
- expect(page).to have_content 'Your commands have been executed!'
+ expect(page).to have_content 'Commands applied'
expect(issuable.subscribed?(master, project)).to be_falsy
end
diff --git a/spec/support/services/issuable_create_service_shared_examples.rb b/spec/support/services/issuable_create_service_shared_examples.rb
new file mode 100644
index 00000000000..93c0267d2db
--- /dev/null
+++ b/spec/support/services/issuable_create_service_shared_examples.rb
@@ -0,0 +1,52 @@
+shared_examples 'issuable create service' do
+ context 'asssignee_id' do
+ let(:assignee) { create(:user) }
+
+ before { project.team << [user, :master] }
+
+ it 'removes assignee_id when user id is invalid' do
+ opts = { title: 'Title', description: 'Description', assignee_id: -1 }
+
+ issuable = described_class.new(project, user, opts).execute
+
+ expect(issuable.assignee_id).to be_nil
+ end
+
+ it 'removes assignee_id when user id is 0' do
+ opts = { title: 'Title', description: 'Description', assignee_id: 0 }
+
+ issuable = described_class.new(project, user, opts).execute
+
+ expect(issuable.assignee_id).to be_nil
+ end
+
+ it 'saves assignee when user id is valid' do
+ project.team << [assignee, :master]
+ opts = { title: 'Title', description: 'Description', assignee_id: assignee.id }
+
+ issuable = described_class.new(project, user, opts).execute
+
+ expect(issuable.assignee_id).to eq(assignee.id)
+ end
+
+ context "when issuable feature is private" do
+ before do
+ project.project_feature.update(issues_access_level: ProjectFeature::PRIVATE)
+ project.project_feature.update(merge_requests_access_level: ProjectFeature::PRIVATE)
+ end
+
+ levels = [Gitlab::VisibilityLevel::INTERNAL, Gitlab::VisibilityLevel::PUBLIC]
+
+ levels.each do |level|
+ it "removes not authorized assignee when project is #{Gitlab::VisibilityLevel.level_name(level)}" do
+ project.update(visibility_level: level)
+ opts = { title: 'Title', description: 'Description', assignee_id: assignee.id }
+
+ issuable = described_class.new(project, user, opts).execute
+
+ expect(issuable.assignee_id).to be_nil
+ end
+ end
+ end
+ end
+end
diff --git a/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb b/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb
index 5f9645ed44f..dd54b0addda 100644
--- a/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb
+++ b/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb
@@ -11,6 +11,8 @@ shared_examples 'new issuable record that supports slash commands' do
let(:params) { base_params.merge(defined?(default_params) ? default_params : {}).merge(example_params) }
let(:issuable) { described_class.new(project, user, params).execute }
+ before { project.team << [assignee, :master ] }
+
context 'with labels in command only' do
let(:example_params) do
{
@@ -55,7 +57,7 @@ shared_examples 'new issuable record that supports slash commands' do
context 'with assignee and milestone in params and command' do
let(:example_params) do
{
- assignee: build_stubbed(:user),
+ assignee: create(:user),
milestone_id: double(:milestone),
description: %(/assign @#{assignee.username}\n/milestone %"#{milestone.name}")
}
diff --git a/spec/support/services/issuable_update_service_shared_examples.rb b/spec/support/services/issuable_update_service_shared_examples.rb
index a3336755773..49cea1e608c 100644
--- a/spec/support/services/issuable_update_service_shared_examples.rb
+++ b/spec/support/services/issuable_update_service_shared_examples.rb
@@ -1,4 +1,8 @@
shared_examples 'issuable update service' do
+ def update_issuable(opts)
+ described_class.new(project, user, opts).execute(open_issuable)
+ end
+
context 'changing state' do
before { expect(project).to receive(:execute_hooks).once }
@@ -14,4 +18,52 @@ shared_examples 'issuable update service' do
end
end
end
+
+ context 'asssignee_id' do
+ it 'does not update assignee when assignee_id is invalid' do
+ open_issuable.update(assignee_id: user.id)
+
+ update_issuable(assignee_id: -1)
+
+ expect(open_issuable.reload.assignee).to eq(user)
+ end
+
+ it 'unassigns assignee when user id is 0' do
+ open_issuable.update(assignee_id: user.id)
+
+ update_issuable(assignee_id: 0)
+
+ expect(open_issuable.assignee_id).to be_nil
+ end
+
+ it 'saves assignee when user id is valid' do
+ update_issuable(assignee_id: user.id)
+
+ expect(open_issuable.assignee_id).to eq(user.id)
+ end
+
+ it 'does not update assignee_id when user cannot read issue' do
+ non_member = create(:user)
+ original_assignee = open_issuable.assignee
+
+ update_issuable(assignee_id: non_member.id)
+
+ expect(open_issuable.assignee_id).to eq(original_assignee.id)
+ end
+
+ context "when issuable feature is private" do
+ levels = [Gitlab::VisibilityLevel::INTERNAL, Gitlab::VisibilityLevel::PUBLIC]
+
+ levels.each do |level|
+ it "does not update with unauthorized assignee when project is #{Gitlab::VisibilityLevel.level_name(level)}" do
+ assignee = create(:user)
+ project.update(visibility_level: level)
+ feature_visibility_attr = :"#{open_issuable.model_name.plural}_access_level"
+ project.project_feature.update_attribute(feature_visibility_attr, ProjectFeature::PRIVATE)
+
+ expect{ update_issuable(assignee_id: assignee) }.not_to change{ open_issuable.assignee }
+ end
+ end
+ end
+ end
end
diff --git a/spec/workers/project_cache_worker_spec.rb b/spec/workers/project_cache_worker_spec.rb
index 855c28b584e..f4f63b57a5f 100644
--- a/spec/workers/project_cache_worker_spec.rb
+++ b/spec/workers/project_cache_worker_spec.rb
@@ -1,8 +1,9 @@
require 'spec_helper'
describe ProjectCacheWorker do
- let(:project) { create(:project) }
let(:worker) { described_class.new }
+ let(:project) { create(:project) }
+ let(:statistics) { project.statistics }
describe '#perform' do
before do
@@ -12,7 +13,7 @@ describe ProjectCacheWorker do
context 'with a non-existing project' do
it 'does nothing' do
- expect(worker).not_to receive(:update_repository_size)
+ expect(worker).not_to receive(:update_statistics)
worker.perform(-1)
end
@@ -22,24 +23,19 @@ describe ProjectCacheWorker do
it 'does nothing' do
allow_any_instance_of(Repository).to receive(:exists?).and_return(false)
- expect(worker).not_to receive(:update_repository_size)
+ expect(worker).not_to receive(:update_statistics)
worker.perform(project.id)
end
end
context 'with an existing project' do
- it 'updates the repository size' do
- expect(worker).to receive(:update_repository_size).and_call_original
-
- worker.perform(project.id)
- end
-
- it 'updates the commit count' do
- expect_any_instance_of(Project).to receive(:update_commit_count).
- and_call_original
+ it 'updates the project statistics' do
+ expect(worker).to receive(:update_statistics)
+ .with(kind_of(Project), %i(repository_size))
+ .and_call_original
- worker.perform(project.id)
+ worker.perform(project.id, [], %w(repository_size))
end
it 'refreshes the method caches' do
@@ -47,33 +43,35 @@ describe ProjectCacheWorker do
with(%i(readme)).
and_call_original
- worker.perform(project.id, %i(readme))
+ worker.perform(project.id, %w(readme))
end
end
end
- describe '#update_repository_size' do
+ describe '#update_statistics' do
context 'when a lease could not be obtained' do
it 'does not update the repository size' do
allow(worker).to receive(:try_obtain_lease_for).
- with(project.id, :update_repository_size).
+ with(project.id, :update_statistics).
and_return(false)
- expect(project).not_to receive(:update_repository_size)
+ expect(statistics).not_to receive(:refresh!)
- worker.update_repository_size(project)
+ worker.update_statistics(project)
end
end
context 'when a lease could be obtained' do
- it 'updates the repository size' do
+ it 'updates the project statistics' do
allow(worker).to receive(:try_obtain_lease_for).
- with(project.id, :update_repository_size).
+ with(project.id, :update_statistics).
and_return(true)
- expect(project).to receive(:update_repository_size).and_call_original
+ expect(statistics).to receive(:refresh!)
+ .with(only: %i(repository_size))
+ .and_call_original
- worker.update_repository_size(project)
+ worker.update_statistics(project, %i(repository_size))
end
end
end
diff --git a/vendor/gitignore/Clojure.gitignore b/vendor/gitignore/Clojure.gitignore
index 7657a270c45..a9fe6fba80d 120000..100644
--- a/vendor/gitignore/Clojure.gitignore
+++ b/vendor/gitignore/Clojure.gitignore
@@ -1 +1,13 @@
-Leiningen.gitignore \ No newline at end of file
+pom.xml
+pom.xml.asc
+*.jar
+*.class
+/lib/
+/classes/
+/target/
+/checkouts/
+.lein-deps-sum
+.lein-repl-history
+.lein-plugins/
+.lein-failures
+.nrepl-port