summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG27
-rw-r--r--GITLAB_SHELL_VERSION2
-rw-r--r--Gemfile14
-rw-r--r--Gemfile.lock22
-rwxr-xr-xapp/assets/fonts/OFL.txt92
-rwxr-xr-xapp/assets/fonts/SourceSansPro-Black.ttfbin0 -> 148368 bytes
-rwxr-xr-xapp/assets/fonts/SourceSansPro-BlackItalic.ttfbin0 -> 116360 bytes
-rwxr-xr-xapp/assets/fonts/SourceSansPro-Bold.ttfbin0 -> 148932 bytes
-rwxr-xr-xapp/assets/fonts/SourceSansPro-BoldItalic.ttfbin0 -> 116192 bytes
-rwxr-xr-xapp/assets/fonts/SourceSansPro-ExtraLight.ttfbin0 -> 150528 bytes
-rwxr-xr-xapp/assets/fonts/SourceSansPro-ExtraLightItalic.ttfbin0 -> 117140 bytes
-rwxr-xr-xapp/assets/fonts/SourceSansPro-Italic.ttfbin0 -> 117328 bytes
-rwxr-xr-xapp/assets/fonts/SourceSansPro-Light.ttfbin0 -> 150244 bytes
-rwxr-xr-xapp/assets/fonts/SourceSansPro-LightItalic.ttfbin0 -> 116960 bytes
-rwxr-xr-xapp/assets/fonts/SourceSansPro-Regular.ttfbin0 -> 149972 bytes
-rwxr-xr-xapp/assets/fonts/SourceSansPro-Semibold.ttfbin0 -> 149636 bytes
-rwxr-xr-xapp/assets/fonts/SourceSansPro-SemiboldItalic.ttfbin0 -> 116424 bytes
-rw-r--r--app/assets/javascripts/activities.js.coffee4
-rw-r--r--app/assets/javascripts/application.js.coffee12
-rw-r--r--app/assets/javascripts/dispatcher.js.coffee4
-rw-r--r--app/assets/javascripts/notes.js.coffee1
-rw-r--r--app/assets/javascripts/syntax_highlight.coffee12
-rw-r--r--app/assets/javascripts/zen_mode.js.coffee2
-rw-r--r--app/assets/stylesheets/application.scss1
-rw-r--r--app/assets/stylesheets/base/fonts.scss25
-rw-r--r--app/assets/stylesheets/base/gl_bootstrap.scss6
-rw-r--r--app/assets/stylesheets/base/gl_variables.scss38
-rw-r--r--app/assets/stylesheets/base/layout.scss1
-rw-r--r--app/assets/stylesheets/base/mixins.scss98
-rw-r--r--app/assets/stylesheets/base/variables.scss25
-rw-r--r--app/assets/stylesheets/generic/avatar.scss7
-rw-r--r--app/assets/stylesheets/generic/blocks.scss39
-rw-r--r--app/assets/stylesheets/generic/buttons.scss16
-rw-r--r--app/assets/stylesheets/generic/common.scss41
-rw-r--r--app/assets/stylesheets/generic/filters.scss25
-rw-r--r--app/assets/stylesheets/generic/header.scss31
-rw-r--r--app/assets/stylesheets/generic/issue_box.scss9
-rw-r--r--app/assets/stylesheets/generic/lists.scss56
-rw-r--r--app/assets/stylesheets/generic/markdown_area.scss11
-rw-r--r--app/assets/stylesheets/generic/mobile.scss17
-rw-r--r--app/assets/stylesheets/generic/selects.scss8
-rw-r--r--app/assets/stylesheets/generic/sidebar.scss71
-rw-r--r--app/assets/stylesheets/generic/timeline.scss140
-rw-r--r--app/assets/stylesheets/generic/typography.scss5
-rw-r--r--app/assets/stylesheets/generic/zen.scss4
-rw-r--r--app/assets/stylesheets/highlight/dark.scss6
-rw-r--r--app/assets/stylesheets/highlight/monokai.scss6
-rw-r--r--app/assets/stylesheets/highlight/solarized_dark.scss5
-rw-r--r--app/assets/stylesheets/highlight/solarized_light.scss5
-rw-r--r--app/assets/stylesheets/highlight/white.scss5
-rw-r--r--app/assets/stylesheets/pages/commit.scss17
-rw-r--r--app/assets/stylesheets/pages/commits.scss6
-rw-r--r--app/assets/stylesheets/pages/dashboard.scss6
-rw-r--r--app/assets/stylesheets/pages/diff.scss14
-rw-r--r--app/assets/stylesheets/pages/events.scss86
-rw-r--r--app/assets/stylesheets/pages/issuable.scss46
-rw-r--r--app/assets/stylesheets/pages/issues.scss14
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss39
-rw-r--r--app/assets/stylesheets/pages/milestone.scss4
-rw-r--r--app/assets/stylesheets/pages/note_form.scss16
-rw-r--r--app/assets/stylesheets/pages/notes.scss29
-rw-r--r--app/assets/stylesheets/pages/projects.scss198
-rw-r--r--app/assets/stylesheets/pages/search.scss18
-rw-r--r--app/assets/stylesheets/pages/snippets.scss24
-rw-r--r--app/assets/stylesheets/pages/tree.scss6
-rw-r--r--app/assets/stylesheets/themes/gitlab-theme.scss14
-rw-r--r--app/controllers/admin/labels_controller.rb58
-rw-r--r--app/controllers/application_controller.rb16
-rw-r--r--app/controllers/dashboard_controller.rb11
-rw-r--r--app/controllers/import/fogbugz_controller.rb106
-rw-r--r--app/controllers/projects/raw_controller.rb5
-rw-r--r--app/controllers/projects/services_controller.rb2
-rw-r--r--app/controllers/projects/snippets_controller.rb11
-rw-r--r--app/controllers/projects/wikis_controller.rb1
-rw-r--r--app/controllers/sessions_controller.rb2
-rw-r--r--app/controllers/users_controller.rb4
-rw-r--r--app/finders/trending_projects_finder.rb18
-rw-r--r--app/models/abuse_report.rb12
-rw-r--r--app/models/application_setting.rb2
-rw-r--r--app/models/issue.rb27
-rw-r--r--app/models/label.rb8
-rw-r--r--app/models/merge_request.rb1
-rw-r--r--app/models/note.rb1
-rw-r--r--app/models/project.rb13
-rw-r--r--app/models/project_services/ci_service.rb37
-rw-r--r--app/models/project_services/drone_ci_service.rb190
-rw-r--r--app/models/sent_notification.rb13
-rw-r--r--app/models/service.rb1
-rw-r--r--app/models/user.rb114
-rw-r--r--app/services/notification_service.rb11
-rw-r--r--app/services/projects/create_service.rb2
-rw-r--r--app/services/projects/download_service.rb43
-rw-r--r--app/views/admin/labels/_form.html.haml35
-rw-r--r--app/views/admin/labels/_label.html.haml5
-rw-r--r--app/views/admin/labels/destroy.js.haml2
-rw-r--r--app/views/admin/labels/edit.html.haml9
-rw-r--r--app/views/admin/labels/index.html.haml16
-rw-r--r--app/views/admin/labels/new.html.haml7
-rw-r--r--app/views/dashboard/_activities.html.haml4
-rw-r--r--app/views/dashboard/_activity_head.html.haml7
-rw-r--r--app/views/dashboard/_groups_head.html.haml2
-rw-r--r--app/views/dashboard/_projects.html.haml5
-rw-r--r--app/views/dashboard/_projects_head.html.haml4
-rw-r--r--app/views/dashboard/activity.html.haml3
-rw-r--r--app/views/dashboard/groups/index.html.haml38
-rw-r--r--app/views/dashboard/issues.html.haml12
-rw-r--r--app/views/dashboard/merge_requests.html.haml7
-rw-r--r--app/views/dashboard/milestones/_milestone.html.haml24
-rw-r--r--app/views/dashboard/milestones/index.html.haml28
-rw-r--r--app/views/dashboard/projects/starred.html.haml20
-rw-r--r--app/views/dashboard/show.html.haml1
-rw-r--r--app/views/devise/sessions/_new_crowd.html.haml9
-rw-r--r--app/views/devise/shared/_signin_box.html.haml10
-rw-r--r--app/views/events/_commit.html.haml2
-rw-r--r--app/views/events/_event.html.haml4
-rw-r--r--app/views/events/event/_note.html.haml3
-rw-r--r--app/views/explore/groups/index.html.haml19
-rw-r--r--app/views/explore/projects/_filter.html.haml2
-rw-r--r--app/views/explore/projects/_projects.html.haml12
-rw-r--r--app/views/explore/projects/index.html.haml3
-rw-r--r--app/views/explore/projects/starred.html.haml8
-rw-r--r--app/views/explore/projects/trending.html.haml20
-rw-r--r--app/views/groups/_projects.html.haml2
-rw-r--r--app/views/groups/group_members/index.html.haml4
-rw-r--r--app/views/groups/issues.html.haml25
-rw-r--r--app/views/groups/merge_requests.html.haml23
-rw-r--r--app/views/groups/milestones/_milestone.html.haml36
-rw-r--r--app/views/groups/milestones/index.html.haml26
-rw-r--r--app/views/import/fogbugz/new.html.haml25
-rw-r--r--app/views/import/fogbugz/new_user_map.html.haml49
-rw-r--r--app/views/import/fogbugz/status.html.haml51
-rw-r--r--app/views/layouts/_head.html.haml1
-rw-r--r--app/views/layouts/_page.html.haml4
-rw-r--r--app/views/layouts/dashboard.html.haml3
-rw-r--r--app/views/layouts/explore.html.haml3
-rw-r--r--app/views/layouts/group.html.haml3
-rw-r--r--app/views/layouts/header/_default.html.haml4
-rw-r--r--app/views/layouts/header/_public.html.haml4
-rw-r--r--app/views/layouts/nav/_admin.html.haml6
-rw-r--r--app/views/layouts/nav/_dashboard.html.haml2
-rw-r--r--app/views/layouts/nav/_project.html.haml2
-rw-r--r--app/views/layouts/profile.html.haml4
-rw-r--r--app/views/layouts/snippets.html.haml2
-rw-r--r--app/views/profiles/notifications/show.html.haml2
-rw-r--r--app/views/profiles/preferences/show.html.haml8
-rw-r--r--app/views/profiles/show.html.haml5
-rw-r--r--app/views/projects/_activity.html.haml3
-rw-r--r--app/views/projects/_md_preview.html.haml4
-rw-r--r--app/views/projects/branches/_branch.html.haml13
-rw-r--r--app/views/projects/branches/_commit.html.haml7
-rw-r--r--app/views/projects/branches/index.html.haml8
-rw-r--r--app/views/projects/commit/_commit_box.html.haml2
-rw-r--r--app/views/projects/commits/_head.html.haml6
-rw-r--r--app/views/projects/commits/show.html.haml25
-rw-r--r--app/views/projects/compare/_form.html.haml2
-rw-r--r--app/views/projects/compare/index.html.haml7
-rw-r--r--app/views/projects/compare/show.html.haml12
-rw-r--r--app/views/projects/diffs/_diffs.html.haml4
-rw-r--r--app/views/projects/diffs/_stats.html.haml2
-rw-r--r--app/views/projects/empty.html.haml6
-rw-r--r--app/views/projects/issues/_discussion.html.haml23
-rw-r--r--app/views/projects/issues/_issue.html.haml2
-rw-r--r--app/views/projects/issues/_issues.html.haml11
-rw-r--r--app/views/projects/issues/index.html.haml4
-rw-r--r--app/views/projects/issues/show.html.haml32
-rw-r--r--app/views/projects/merge_requests/_discussion.html.haml17
-rw-r--r--app/views/projects/merge_requests/_merge_request.html.haml7
-rw-r--r--app/views/projects/merge_requests/_merge_requests.html.haml11
-rw-r--r--app/views/projects/merge_requests/_new_compare.html.haml2
-rw-r--r--app/views/projects/merge_requests/_new_submit.html.haml4
-rw-r--r--app/views/projects/merge_requests/_show.html.haml9
-rw-r--r--app/views/projects/merge_requests/index.html.haml4
-rw-r--r--app/views/projects/merge_requests/show/_how_to_merge.html.haml16
-rw-r--r--app/views/projects/merge_requests/show/_mr_box.html.haml21
-rw-r--r--app/views/projects/merge_requests/show/_mr_title.html.haml9
-rw-r--r--app/views/projects/merge_requests/widget/open/_accept.html.haml2
-rw-r--r--app/views/projects/milestones/_milestone.html.haml42
-rw-r--r--app/views/projects/milestones/index.html.haml25
-rw-r--r--app/views/projects/new.html.haml5
-rw-r--r--app/views/projects/notes/_note.html.haml11
-rw-r--r--app/views/projects/show.html.haml23
-rw-r--r--app/views/projects/snippets/_snippet.html.haml15
-rw-r--r--app/views/projects/snippets/index.html.haml3
-rw-r--r--app/views/projects/tags/_tag.html.haml12
-rw-r--r--app/views/projects/tags/index.html.haml11
-rw-r--r--app/views/projects/tree/_tree.html.haml2
-rw-r--r--app/views/projects/wikis/_main_links.html.haml11
-rw-r--r--app/views/projects/wikis/_nav.html.haml15
-rw-r--r--app/views/projects/wikis/git_access.html.haml19
-rw-r--r--app/views/projects/wikis/history.html.haml7
-rw-r--r--app/views/projects/wikis/pages.html.haml7
-rw-r--r--app/views/projects/wikis/show.html.haml21
-rw-r--r--app/views/search/_category.html.haml2
-rw-r--r--app/views/search/_filter.html.haml6
-rw-r--r--app/views/search/_form.html.haml12
-rw-r--r--app/views/search/_results.html.haml9
-rw-r--r--app/views/search/results/_blob.html.haml2
-rw-r--r--app/views/search/results/_project.html.haml6
-rw-r--r--app/views/search/results/_snippet_blob.html.haml2
-rw-r--r--app/views/search/results/_wiki_blob.html.haml2
-rw-r--r--app/views/search/show.html.haml2
-rw-r--r--app/views/shared/_clone_panel.html.haml6
-rw-r--r--app/views/shared/_event_filter.html.haml2
-rw-r--r--app/views/shared/_file_highlight.html.haml2
-rw-r--r--app/views/shared/_milestones_filter.html.haml7
-rw-r--r--app/views/shared/_project.html.haml20
-rw-r--r--app/views/shared/_projects_list.html.haml17
-rw-r--r--app/views/shared/groups/_group.html.haml22
-rw-r--r--app/views/shared/issuable/_filter.html.haml9
-rw-r--r--app/views/shared/projects/_list.html.haml19
-rw-r--r--app/views/shared/projects/_project.html.haml24
-rw-r--r--app/views/shared/snippets/_snippet.html.haml (renamed from app/views/snippets/_snippet.html.haml)18
-rw-r--r--app/views/snippets/_snippets.html.haml2
-rw-r--r--app/views/snippets/current_user_index.html.haml11
-rw-r--r--app/views/snippets/index.html.haml10
-rw-r--r--app/views/users/_projects.html.haml4
-rw-r--r--app/workers/merge_worker.rb4
-rw-r--r--app/workers/repository_import_worker.rb35
-rwxr-xr-xbin/daemon_with_pidfile33
-rwxr-xr-xbin/mail_room4
-rw-r--r--config/gitlab.yml.example5
-rw-r--r--config/initializers/1_settings.rb2
-rw-r--r--config/initializers/devise.rb2
-rw-r--r--config/initializers/doorkeeper.rb3
-rw-r--r--config/routes.rb40
-rw-r--r--db/migrate/20150817163600_deduplicate_user_identities.rb14
-rw-r--r--db/migrate/20150902001023_add_template_to_label.rb5
-rw-r--r--db/schema.rb3
-rw-r--r--doc/api/README.md1
-rw-r--r--doc/api/keys.md46
-rw-r--r--doc/api/services.md506
-rw-r--r--doc/development/rake_tasks.md6
-rw-r--r--doc/gitlab-basics/command-line-commands.md5
-rw-r--r--doc/install/installation.md31
-rw-r--r--doc/integration/crowd.md58
-rw-r--r--doc/integration/omniauth.md1
-rw-r--r--doc/update/6.x-or-7.x-to-7.14.md4
-rw-r--r--doc/update/7.13-to-7.14.md2
-rw-r--r--doc/update/7.14-to-8.0.md159
-rw-r--r--doc/web_hooks/web_hooks.md4
-rw-r--r--doc/workflow/importing/README.md1
-rw-r--r--doc/workflow/importing/fogbugz_importer/fogbugz_import_finished.pngbin0 -> 53276 bytes
-rw-r--r--doc/workflow/importing/fogbugz_importer/fogbugz_import_login.pngbin0 -> 44444 bytes
-rw-r--r--doc/workflow/importing/fogbugz_importer/fogbugz_import_select_fogbogz.pngbin0 -> 35415 bytes
-rw-r--r--doc/workflow/importing/fogbugz_importer/fogbugz_import_select_project.pngbin0 -> 62552 bytes
-rw-r--r--doc/workflow/importing/fogbugz_importer/fogbugz_import_user_map.pngbin0 -> 157856 bytes
-rw-r--r--doc/workflow/importing/import_projects_from_fogbugz.md29
-rw-r--r--doc/workflow/importing/import_projects_from_github.md2
-rw-r--r--features/admin/labels.feature38
-rw-r--r--features/login_form.feature5
-rw-r--r--features/project/wiki.feature12
-rw-r--r--features/steps/admin/labels.rb117
-rw-r--r--features/steps/dashboard/starred_projects.rb2
-rw-r--r--features/steps/login_form.rb25
-rw-r--r--features/steps/project/commits/commits.rb5
-rw-r--r--features/steps/project/merge_requests.rb7
-rw-r--r--features/steps/project/wiki.rb9
-rw-r--r--features/steps/shared/active_tab.rb4
-rw-r--r--features/steps/shared/diff_note.rb2
-rw-r--r--features/user.feature5
-rw-r--r--lib/api/api.rb1
-rw-r--r--lib/api/entities.rb4
-rw-r--r--lib/api/helpers.rb26
-rw-r--r--lib/api/keys.rb20
-rw-r--r--lib/api/services.rb86
-rw-r--r--lib/gitlab/backend/grack_auth.rb49
-rw-r--r--lib/gitlab/bitbucket_import/client.rb23
-rw-r--r--lib/gitlab/bitbucket_import/importer.rb34
-rw-r--r--lib/gitlab/color_schemes.rb67
-rw-r--r--lib/gitlab/current_settings.rb14
-rw-r--r--lib/gitlab/fogbugz_import/client.rb56
-rw-r--r--lib/gitlab/fogbugz_import/importer.rb298
-rw-r--r--lib/gitlab/fogbugz_import/project_creator.rb38
-rw-r--r--lib/gitlab/fogbugz_import/repository.rb31
-rw-r--r--lib/gitlab/github_import/importer.rb3
-rw-r--r--lib/gitlab/gitlab_import/importer.rb3
-rw-r--r--lib/gitlab/import_formatter.rb4
-rw-r--r--lib/gitlab/import_sources.rb1
-rw-r--r--lib/gitlab/ldap/user.rb11
-rw-r--r--lib/gitlab/markdown.rb65
-rw-r--r--lib/gitlab/markdown/autolink_filter.rb1
-rw-r--r--lib/gitlab/markdown/commit_range_reference_filter.rb2
-rw-r--r--lib/gitlab/markdown/commit_reference_filter.rb2
-rw-r--r--lib/gitlab/markdown/cross_project_reference.rb2
-rw-r--r--lib/gitlab/markdown/emoji_filter.rb3
-rw-r--r--lib/gitlab/markdown/external_issue_reference_filter.rb2
-rw-r--r--lib/gitlab/markdown/external_link_filter.rb1
-rw-r--r--lib/gitlab/markdown/issue_reference_filter.rb2
-rw-r--r--lib/gitlab/markdown/label_reference_filter.rb2
-rw-r--r--lib/gitlab/markdown/merge_request_reference_filter.rb2
-rw-r--r--lib/gitlab/markdown/reference_filter.rb4
-rw-r--r--lib/gitlab/markdown/relative_link_filter.rb1
-rw-r--r--lib/gitlab/markdown/sanitization_filter.rb1
-rw-r--r--lib/gitlab/markdown/snippet_reference_filter.rb2
-rw-r--r--lib/gitlab/markdown/syntax_highlight_filter.rb39
-rw-r--r--lib/gitlab/markdown/table_of_contents_filter.rb1
-rw-r--r--lib/gitlab/markdown/task_list_filter.rb1
-rw-r--r--lib/gitlab/markdown/user_reference_filter.rb2
-rw-r--r--lib/gitlab/reference_extractor.rb10
-rw-r--r--lib/gitlab/themes.rb18
-rw-r--r--lib/gitlab/url_builder.rb8
-rw-r--r--lib/redcarpet/render/gitlab_html.rb46
-rwxr-xr-xlib/support/init.d/gitlab74
-rwxr-xr-xlib/support/init.d/gitlab.default.example10
-rw-r--r--lib/support/nginx/gitlab53
-rw-r--r--lib/support/nginx/gitlab-ssl53
-rw-r--r--lib/tasks/gitlab/check.rake13
-rw-r--r--lib/tasks/services.rake89
-rw-r--r--spec/controllers/import/fogbugz_controller_spec.rb39
-rw-r--r--spec/controllers/projects/raw_controller_spec.rb37
-rw-r--r--spec/factories/abuse_reports.rb12
-rw-r--r--spec/factories/merge_requests.rb1
-rw-r--r--spec/factories/notes.rb1
-rw-r--r--spec/features/gitlab_flavored_markdown_spec.rb2
-rw-r--r--spec/features/markdown_spec.rb10
-rw-r--r--spec/helpers/events_helper_spec.rb5
-rw-r--r--spec/helpers/gitlab_markdown_helper_spec.rb13
-rw-r--r--spec/helpers/preferences_helper_spec.rb92
-rw-r--r--spec/javascripts/zen_mode_spec.js.coffee5
-rw-r--r--spec/lib/gitlab/bitbucket_import/client_spec.rb34
-rw-r--r--spec/lib/gitlab/color_schemes_spec.rb45
-rw-r--r--spec/lib/gitlab/ldap/user_spec.rb22
-rw-r--r--spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb5
-rw-r--r--spec/lib/gitlab/markdown/commit_reference_filter_spec.rb5
-rw-r--r--spec/lib/gitlab/markdown/external_issue_reference_filter_spec.rb5
-rw-r--r--spec/lib/gitlab/markdown/issue_reference_filter_spec.rb5
-rw-r--r--spec/lib/gitlab/markdown/label_reference_filter_spec.rb5
-rw-r--r--spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb5
-rw-r--r--spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb5
-rw-r--r--spec/lib/gitlab/markdown/user_reference_filter_spec.rb5
-rw-r--r--spec/lib/gitlab/themes_spec.rb3
-rw-r--r--spec/models/abuse_report_spec.rb12
-rw-r--r--spec/models/application_setting_spec.rb1
-rw-r--r--spec/models/issue_spec.rb27
-rw-r--r--spec/models/merge_request_spec.rb1
-rw-r--r--spec/models/note_spec.rb1
-rw-r--r--spec/models/project_services/drone_ci_service_spec.rb107
-rw-r--r--spec/models/user_spec.rb108
-rw-r--r--spec/requests/api/keys_spec.rb39
-rw-r--r--spec/requests/api/services_spec.rb99
-rw-r--r--spec/services/notification_service_spec.rb11
-rw-r--r--spec/services/projects/create_service_spec.rb8
-rw-r--r--spec/services/projects/download_service_spec.rb65
-rw-r--r--spec/support/services_shared_context.rb21
-rw-r--r--spec/workers/merge_worker_spec.rb28
-rw-r--r--spec/workers/post_receive_spec.rb2
346 files changed, 5118 insertions, 1761 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 6ed1716f8af..3a1cbe4bc56 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,13 @@
Please view this file on the master branch, on stable branches it's out of date.
v 8.0.0 (unreleased)
+ - Fix URL construction for merge requests, issues, notes, and commits for relative URL config (Stan Hu)
+ - Omit filename in Content-Disposition header in raw file download to avoid RFC 6266 encoding issues (Stan HU)
+ - Fix broken Wiki Page History (Stan Hu)
+ - Prevent anchors from being hidden by header (Stan Hu)
+ - Fix bug where only the first 15 Bitbucket issues would be imported (Stan Hu)
+ - Sort issues by creation date in Bitbucket importer (Stan Hu)
+ - Upgrade gitlab_git to 7.2.15 to fix `git blame` errors with ISO-encoded files (Stan Hu)
- Prevent too many redirects upon login when home page URL is set to external_url (Stan Hu)
- Improve dropdown positioning on the project home page (Hannes Rosenögger)
- Upgrade browser gem to 1.0.0 to avoid warning in IE11 compatibilty mode (Stan Hu)
@@ -16,9 +23,24 @@ v 8.0.0 (unreleased)
- Create cross-reference for closing references on commits pushed to non-default branches (Maël Valais)
- Ability to search milestones
- Gracefully handle SMTP user input errors (e.g. incorrect email addresses) to prevent Sidekiq retries (Stan Hu)
- - Move dashboard activity to separate page
+ - Move dashboard activity to separate page (for your projects and starred projects)
- Improve performance of git blame
- Limit content width to 1200px for most of pages to improve readability on big screens
+ - Fix 500 error when submit project snippet without body
+ - Improve search page usability
+ - Bring more UI consistency in way how projects, snippets and groups lists are rendered
+ - Make all profiles public
+ - Fixed login failure when extern_uid changes (Joel Koglin)
+ - Don't notify users without access to the project when they are (accidentally) mentioned in a note.
+ - Retrieving oauth token with LDAP credentials
+ - Load Application settings from running database unless env var USE_DB=false
+ - Added Drone CI integration (Kirill Zaitsev)
+ - Refactored service API and added automatically service docs generator (Kirill Zaitsev)
+ - Added web_url key project hook_attrs (Kirill Zaitsev)
+ - Add ability to get user information by ID of an SSH key via the API
+ - Fix bug which IE cannot show image at markdown when the image is raw file of gitlab
+ - Add support for Crowd
+ - Global Labels that are available to all projects
v 7.14.1
- Improve abuse reports management from admin area
@@ -26,6 +48,7 @@ v 7.14.1
- Only include base URL in OmniAuth full_host parameter (Stan Hu)
- Fix Error 500 in API when accessing a group that has an avatar (Stan Hu)
- Ability to enable SSL verification for Webhooks
+ - Add FogBugz project import (Jared Szechy)
v 7.14.0
- Fix bug where non-project members of the target project could set labels on new merge requests.
@@ -103,7 +126,7 @@ v 7.13.4
v 7.13.3
- Fix bug causing Bitbucket importer to crash when OAuth application had been removed.
- Allow users to send abuse reports
- - Remove satellites
+ - Remove satellites
- Link username to profile on Group Members page (Tom Webster)
v 7.13.2
diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION
index 2714f5313ae..57cf282ebbc 100644
--- a/GITLAB_SHELL_VERSION
+++ b/GITLAB_SHELL_VERSION
@@ -1 +1 @@
-2.6.4
+2.6.5
diff --git a/Gemfile b/Gemfile
index 0cb3aedc108..3c88322f637 100644
--- a/Gemfile
+++ b/Gemfile
@@ -34,6 +34,7 @@ gem 'omniauth-gitlab', '~> 1.0.0'
gem 'omniauth-bitbucket', '~> 0.0.2'
gem 'omniauth-saml', '~> 1.4.0'
gem 'doorkeeper', '~> 2.1.3'
+gem 'omniauth_crowd'
gem "rack-oauth2", "~> 1.0.5"
# Two-factor authentication
@@ -46,12 +47,7 @@ gem "browser", '~> 1.0.0'
# Extracting information from a git repository
# Provide access to Gitlab::Git library
-gem "gitlab_git", '~> 7.2.14'
-
-# Ruby/Rack Git Smart-HTTP Server Handler
-# GitLab fork with a lot of changes (improved thread-safety, better memory usage etc)
-# For full list of changes see https://github.com/SaitoWu/grack/compare/master...gitlabhq:master
-gem 'gitlab-grack', '~> 2.0.2', require: 'grack'
+gem "gitlab_git", '~> 7.2.15'
# LDAP Auth
# GitLab fork with several improvements to original library. For full list of changes
@@ -169,6 +165,9 @@ gem "slack-notifier", "~> 1.0.0"
# Asana integration
gem 'asana', '~> 0.0.6'
+# FogBugz integration
+gem 'ruby-fogbugz'
+
# d3
gem 'd3_rails', '~> 3.5.5'
@@ -275,6 +274,7 @@ group :test do
gem 'email_spec', '~> 1.6.0'
gem 'webmock', '~> 1.21.0'
gem 'test_after_commit', '~> 0.2.2'
+ gem 'sham_rack'
end
group :production do
@@ -285,7 +285,7 @@ gem "newrelic_rpm", '~> 3.9.4.245'
gem 'octokit', '~> 3.7.0'
-gem "mail_room", "~> 0.4.1"
+gem "mail_room", "~> 0.4.2"
gem 'email_reply_parser', '~> 0.5.8'
diff --git a/Gemfile.lock b/Gemfile.lock
index 1c64a622cd2..2c0d376f238 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -265,8 +265,6 @@ GEM
flowdock (~> 0.7)
gitlab-grit (>= 2.4.1)
multi_json
- gitlab-grack (2.0.2)
- rack (~> 1.5.1)
gitlab-grit (2.7.3)
charlock_holmes (~> 0.6)
diff-lcs (~> 1.1)
@@ -279,7 +277,7 @@ GEM
gitlab_ci_meta (4.0)
gitlab_emoji (0.1.0)
gemojione (~> 2.0)
- gitlab_git (7.2.14)
+ gitlab_git (7.2.15)
activesupport (~> 4.0)
charlock_holmes (~> 0.6)
gitlab-linguist (~> 3.0)
@@ -387,7 +385,7 @@ GEM
systemu (~> 2.6.2)
mail (2.6.3)
mime-types (>= 1.16, < 3)
- mail_room (0.4.1)
+ mail_room (0.4.2)
method_source (0.8.2)
mime-types (1.25.1)
mimemagic (0.3.0)
@@ -456,6 +454,10 @@ GEM
omniauth-twitter (1.0.1)
multi_json (~> 1.3)
omniauth-oauth (~> 1.0)
+ omniauth_crowd (2.2.3)
+ activesupport
+ nokogiri (>= 1.4.4)
+ omniauth (~> 1.0)
opennebula (4.12.1)
json
nokogiri
@@ -595,6 +597,8 @@ GEM
powerpack (~> 0.0.6)
rainbow (>= 1.99.1, < 3.0)
ruby-progressbar (~> 1.4)
+ ruby-fogbugz (0.1.1)
+ crack
ruby-progressbar (1.7.5)
ruby-saml (1.0.0)
nokogiri (>= 1.5.10)
@@ -629,6 +633,8 @@ GEM
thor (~> 0.14)
settingslogic (2.0.9)
sexp_processor (4.6.0)
+ sham_rack (1.3.6)
+ rack
shellany (0.0.1)
shoulda-matchers (2.8.0)
activesupport (>= 3.0.0)
@@ -818,11 +824,10 @@ DEPENDENCIES
gemnasium-gitlab-service (~> 0.2)
github-markup (~> 1.3.1)
gitlab-flowdock-git-hook (~> 1.0.1)
- gitlab-grack (~> 2.0.2)
gitlab-linguist (~> 3.0.1)
gitlab_ci_meta (~> 4.0)
gitlab_emoji (~> 0.1)
- gitlab_git (~> 7.2.14)
+ gitlab_git (~> 7.2.15)
gitlab_meta (= 7.0)
gitlab_omniauth-ldap (~> 1.2.1)
gollum-lib (~> 4.0.2)
@@ -842,7 +847,7 @@ DEPENDENCIES
jquery-ui-rails (~> 4.2.1)
kaminari (~> 0.15.1)
letter_opener (~> 1.1.2)
- mail_room (~> 0.4.1)
+ mail_room (~> 0.4.2)
minitest (~> 5.7.0)
mousetrap-rails (~> 1.4.6)
mysql2 (~> 0.3.16)
@@ -860,6 +865,7 @@ DEPENDENCIES
omniauth-saml (~> 1.4.0)
omniauth-shibboleth (~> 1.1.1)
omniauth-twitter (~> 1.0.1)
+ omniauth_crowd
org-ruby (~> 0.9.12)
paranoia (~> 2.0)
pg (~> 0.18.2)
@@ -882,12 +888,14 @@ DEPENDENCIES
rqrcode-rails3 (~> 0.1.7)
rspec-rails (~> 3.3.0)
rubocop (~> 0.28.0)
+ ruby-fogbugz
sanitize (~> 2.0)
sass-rails (~> 4.0.5)
sdoc (~> 0.3.20)
seed-fu (~> 2.3.5)
select2-rails (~> 3.5.9)
settingslogic (~> 2.0.9)
+ sham_rack
shoulda-matchers (~> 2.8.0)
sidekiq (~> 3.3)
sidetiq (~> 0.6.3)
diff --git a/app/assets/fonts/OFL.txt b/app/assets/fonts/OFL.txt
new file mode 100755
index 00000000000..a9b845ed1d4
--- /dev/null
+++ b/app/assets/fonts/OFL.txt
@@ -0,0 +1,92 @@
+Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries.
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/app/assets/fonts/SourceSansPro-Black.ttf b/app/assets/fonts/SourceSansPro-Black.ttf
new file mode 100755
index 00000000000..cb89a2d171e
--- /dev/null
+++ b/app/assets/fonts/SourceSansPro-Black.ttf
Binary files differ
diff --git a/app/assets/fonts/SourceSansPro-BlackItalic.ttf b/app/assets/fonts/SourceSansPro-BlackItalic.ttf
new file mode 100755
index 00000000000..c719243c0d6
--- /dev/null
+++ b/app/assets/fonts/SourceSansPro-BlackItalic.ttf
Binary files differ
diff --git a/app/assets/fonts/SourceSansPro-Bold.ttf b/app/assets/fonts/SourceSansPro-Bold.ttf
new file mode 100755
index 00000000000..50d81bdad58
--- /dev/null
+++ b/app/assets/fonts/SourceSansPro-Bold.ttf
Binary files differ
diff --git a/app/assets/fonts/SourceSansPro-BoldItalic.ttf b/app/assets/fonts/SourceSansPro-BoldItalic.ttf
new file mode 100755
index 00000000000..d20dd0c5eca
--- /dev/null
+++ b/app/assets/fonts/SourceSansPro-BoldItalic.ttf
Binary files differ
diff --git a/app/assets/fonts/SourceSansPro-ExtraLight.ttf b/app/assets/fonts/SourceSansPro-ExtraLight.ttf
new file mode 100755
index 00000000000..bb4176c6fff
--- /dev/null
+++ b/app/assets/fonts/SourceSansPro-ExtraLight.ttf
Binary files differ
diff --git a/app/assets/fonts/SourceSansPro-ExtraLightItalic.ttf b/app/assets/fonts/SourceSansPro-ExtraLightItalic.ttf
new file mode 100755
index 00000000000..2c34f3b8dc4
--- /dev/null
+++ b/app/assets/fonts/SourceSansPro-ExtraLightItalic.ttf
Binary files differ
diff --git a/app/assets/fonts/SourceSansPro-Italic.ttf b/app/assets/fonts/SourceSansPro-Italic.ttf
new file mode 100755
index 00000000000..e5a1a86e631
--- /dev/null
+++ b/app/assets/fonts/SourceSansPro-Italic.ttf
Binary files differ
diff --git a/app/assets/fonts/SourceSansPro-Light.ttf b/app/assets/fonts/SourceSansPro-Light.ttf
new file mode 100755
index 00000000000..5f64679f6b9
--- /dev/null
+++ b/app/assets/fonts/SourceSansPro-Light.ttf
Binary files differ
diff --git a/app/assets/fonts/SourceSansPro-LightItalic.ttf b/app/assets/fonts/SourceSansPro-LightItalic.ttf
new file mode 100755
index 00000000000..88a6778d24f
--- /dev/null
+++ b/app/assets/fonts/SourceSansPro-LightItalic.ttf
Binary files differ
diff --git a/app/assets/fonts/SourceSansPro-Regular.ttf b/app/assets/fonts/SourceSansPro-Regular.ttf
new file mode 100755
index 00000000000..91e9ea5757f
--- /dev/null
+++ b/app/assets/fonts/SourceSansPro-Regular.ttf
Binary files differ
diff --git a/app/assets/fonts/SourceSansPro-Semibold.ttf b/app/assets/fonts/SourceSansPro-Semibold.ttf
new file mode 100755
index 00000000000..5020594826b
--- /dev/null
+++ b/app/assets/fonts/SourceSansPro-Semibold.ttf
Binary files differ
diff --git a/app/assets/fonts/SourceSansPro-SemiboldItalic.ttf b/app/assets/fonts/SourceSansPro-SemiboldItalic.ttf
new file mode 100755
index 00000000000..2c5ad3008c3
--- /dev/null
+++ b/app/assets/fonts/SourceSansPro-SemiboldItalic.ttf
Binary files differ
diff --git a/app/assets/javascripts/activities.js.coffee b/app/assets/javascripts/activities.js.coffee
index 777c62dc1b7..63803747413 100644
--- a/app/assets/javascripts/activities.js.coffee
+++ b/app/assets/javascripts/activities.js.coffee
@@ -1,7 +1,7 @@
class @Activities
constructor: ->
Pager.init 20, true
- $(".event_filter_link").bind "click", (event) =>
+ $(".event-filter .btn").bind "click", (event) =>
event.preventDefault()
@toggleFilter($(event.currentTarget))
@reloadActivities()
@@ -12,7 +12,7 @@ class @Activities
toggleFilter: (sender) ->
- sender.parent().toggleClass "active"
+ sender.toggleClass "active"
event_filters = $.cookie("event_filter")
filter = sender.attr("id").split("_")[0]
if event_filters
diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee
index c263912b7ea..8e987ac4e83 100644
--- a/app/assets/javascripts/application.js.coffee
+++ b/app/assets/javascripts/application.js.coffee
@@ -94,16 +94,18 @@ window.unbindEvents = ->
$(document).off('scroll')
window.shiftWindow = ->
- scrollBy 0, -50
+ scrollBy 0, -100
document.addEventListener("page:fetch", unbindEvents)
-# Scroll the window to avoid the topnav bar
-# https://github.com/twitter/bootstrap/issues/1768
-if location.hash
- setTimeout shiftWindow, 1
window.addEventListener "hashchange", shiftWindow
+window.onload = ->
+ # Scroll the window to avoid the topnav bar
+ # https://github.com/twitter/bootstrap/issues/1768
+ if location.hash
+ setTimeout shiftWindow, 100
+
$ ->
$(".nicescroll").niceScroll(cursoropacitymax: '0.4', cursorcolor: '#FFF', cursorborder: "1px solid #FFF")
diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee
index 539041c2862..5bf0b302179 100644
--- a/app/assets/javascripts/dispatcher.js.coffee
+++ b/app/assets/javascripts/dispatcher.js.coffee
@@ -55,7 +55,6 @@ class Dispatcher
new Activities()
when 'dashboard:projects:starred'
new Activities()
- new ProjectsList()
when 'projects:commit:show'
new Commit()
new Diff()
@@ -70,7 +69,6 @@ class Dispatcher
when 'groups:show'
new Activities()
shortcut_handler = new ShortcutsNavigation()
- new ProjectsList()
when 'groups:group_members:index'
new GroupMembers()
new UsersSelect()
@@ -96,8 +94,6 @@ class Dispatcher
when 'users:show'
new User()
new Activities()
- when 'admin:users:show'
- new ProjectsList()
switch path.first()
when 'admin'
diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee
index 0021d17d85e..b7f2c63c5a7 100644
--- a/app/assets/javascripts/notes.js.coffee
+++ b/app/assets/javascripts/notes.js.coffee
@@ -123,6 +123,7 @@ class @Notes
if @isNewNote(note)
@note_ids.push(note.id)
$('ul.main-notes-list').append(note.html)
+ $('.js-syntax-highlight').syntaxHighlight()
@initTaskList()
###
diff --git a/app/assets/javascripts/syntax_highlight.coffee b/app/assets/javascripts/syntax_highlight.coffee
new file mode 100644
index 00000000000..71295cd4b08
--- /dev/null
+++ b/app/assets/javascripts/syntax_highlight.coffee
@@ -0,0 +1,12 @@
+# Applies a syntax highlighting color scheme CSS class to any element with the
+# `js-syntax-highlight` class
+#
+# ### Example Markup
+#
+# <div class="js-syntax-highlight"></div>
+#
+$.fn.syntaxHighlight = ->
+ $(this).addClass(gon.user_color_scheme)
+
+$(document).on 'ready page:load', ->
+ $('.js-syntax-highlight').syntaxHighlight()
diff --git a/app/assets/javascripts/zen_mode.js.coffee b/app/assets/javascripts/zen_mode.js.coffee
index 8a0564a9098..a1462cf3cae 100644
--- a/app/assets/javascripts/zen_mode.js.coffee
+++ b/app/assets/javascripts/zen_mode.js.coffee
@@ -38,6 +38,8 @@ class @ZenMode
@active_checkbox = $(checkbox)
@active_checkbox.prop('checked', true)
@active_zen_area = @active_checkbox.parent().find('textarea')
+ # Prevent a user-resized textarea from persisting to fullscreen
+ @active_zen_area.removeAttr('style')
@active_zen_area.focus()
exitZenMode: =>
diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss
index 1a5f11df7d1..46f7feddf8d 100644
--- a/app/assets/stylesheets/application.scss
+++ b/app/assets/stylesheets/application.scss
@@ -12,6 +12,7 @@
*/
+@import "base/fonts";
@import "base/variables";
@import "base/mixins";
@import "base/layout";
diff --git a/app/assets/stylesheets/base/fonts.scss b/app/assets/stylesheets/base/fonts.scss
new file mode 100644
index 00000000000..e214567eca1
--- /dev/null
+++ b/app/assets/stylesheets/base/fonts.scss
@@ -0,0 +1,25 @@
+/* latin-ext */
+@font-face {
+ font-family: 'Source Sans Pro';
+ font-style: normal;
+ font-weight: 300;
+ src: local('Source Sans Pro Light'), local('SourceSansPro-Light'), font-url('SourceSansPro-Light.ttf');
+}
+@font-face {
+ font-family: 'Source Sans Pro';
+ font-style: normal;
+ font-weight: 400;
+ src: local('Source Sans Pro'), local('SourceSansPro-Regular'), font-url('SourceSansPro-Regular.ttf');
+}
+@font-face {
+ font-family: 'Source Sans Pro';
+ font-style: normal;
+ font-weight: 600;
+ src: local('Source Sans Pro Semibold'), local('SourceSansPro-Semibold'), font-url('SourceSansPro-Semibold.ttf');
+}
+@font-face {
+ font-family: 'Source Sans Pro';
+ font-style: normal;
+ font-weight: 700;
+ src: local('Source Sans Pro Bold'), local('SourceSansPro-Bold'), font-url('SourceSansPro-Bold.ttf');
+}
diff --git a/app/assets/stylesheets/base/gl_bootstrap.scss b/app/assets/stylesheets/base/gl_bootstrap.scss
index 21acbfa5e5a..ae72c5b8d97 100644
--- a/app/assets/stylesheets/base/gl_bootstrap.scss
+++ b/app/assets/stylesheets/base/gl_bootstrap.scss
@@ -85,14 +85,14 @@
// Labels
.label {
padding: 2px 4px;
- font-size: 12px;
+ font-size: 13px;
font-style: normal;
font-weight: normal;
display: inline-block;
&.label-gray {
- background-color: #eee;
- color: #999;
+ background-color: #f8fafc;
+ color: $gl-gray;
text-shadow: none;
}
diff --git a/app/assets/stylesheets/base/gl_variables.scss b/app/assets/stylesheets/base/gl_variables.scss
index 56f4c794e1b..d18b48eaca9 100644
--- a/app/assets/stylesheets/base/gl_variables.scss
+++ b/app/assets/stylesheets/base/gl_variables.scss
@@ -22,6 +22,10 @@ $brand-info: $gl-info;
$brand-warning: $gl-warning;
$brand-danger: $gl-danger;
+$border-radius-base: 3px !default;
+$border-radius-large: 5px !default;
+$border-radius-small: 2px !default;
+
//== Scaffolding
//
@@ -42,17 +46,18 @@ $font-size-base: $gl-font-size;
//
//## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start).
-$padding-base-vertical: 6px;
-$padding-base-horizontal: 14px;
-
+$padding-base-vertical: 9px;
+$padding-base-horizontal: $gl-padding;
+$component-active-color: #fff;
+$component-active-bg: $brand-info;
//== Forms
//
//##
$input-color: $text-color;
-$input-border: #DDD;
-$input-border-focus: $brand-info;
+$input-border: #e7e9ed;
+$input-border-focus: #7F8FA4;
$legend-color: $text-color;
@@ -111,8 +116,8 @@ $alert-border-radius: 0;
$panel-border-radius: 0;
$panel-default-text: $text-color;
-$panel-default-border: $border-color;
-$panel-default-heading-bg: $background-color;
+$panel-default-border: #E7E9ED;
+$panel-default-heading-bg: #F8FAFC;
//== Wells
@@ -131,3 +136,22 @@ $code-bg: #f9f2f4;
$kbd-color: #fff;
$kbd-bg: #333;
+
+//== Buttons
+//
+//##
+$btn-default-color: $gl-text-color;
+$btn-default-bg: #fff;
+$btn-default-border: #e7e9ed;
+
+//== Nav
+//
+//##
+$nav-link-padding: 13px $gl-padding;
+
+//== Code
+//
+//##
+$pre-bg: #f8fafc !default;
+$pre-color: $gl-gray !default;
+$pre-border-color: #e7e9ed;
diff --git a/app/assets/stylesheets/base/layout.scss b/app/assets/stylesheets/base/layout.scss
index 734b95e26c0..b91c15d8910 100644
--- a/app/assets/stylesheets/base/layout.scss
+++ b/app/assets/stylesheets/base/layout.scss
@@ -21,7 +21,6 @@ html {
margin-top: 30px;
}
-
.container-limited {
max-width: $fixed-layout-width;
}
diff --git a/app/assets/stylesheets/base/mixins.scss b/app/assets/stylesheets/base/mixins.scss
index 7beef1845ef..0f661d6b1b6 100644
--- a/app/assets/stylesheets/base/mixins.scss
+++ b/app/assets/stylesheets/base/mixins.scss
@@ -55,8 +55,11 @@
}
@mixin md-typography {
- font-size: 15px;
- line-height: 1.5;
+ color: #444;
+
+ a {
+ color: #3084bb;
+ }
img {
max-width: 100%;
@@ -157,3 +160,94 @@
white-space: nowrap;
max-width: $max_width;
}
+
+/*
+ * Base mixin for lists in GitLab
+ */
+@mixin basic-list {
+ margin: 5px 0px;
+ padding: 0px;
+ list-style: none;
+
+ > li {
+ padding: 10px 0;
+ border-bottom: 1px solid #EEE;
+ overflow: hidden;
+ display: block;
+ margin: 0px;
+
+ &:last-child {
+ border-bottom: none;
+ }
+
+ &.active {
+ background: #f9f9f9;
+ a {
+ font-weight: bold;
+ }
+ }
+
+ &.hide {
+ display: none;
+ }
+
+ &.light {
+ a {
+ color: $gl-gray;
+ }
+ }
+ }
+}
+
+@mixin input-big {
+ height: 36px;
+ padding: 5px 10px;
+ font-size: 16px;
+ line-height: 24px;
+ color: #7f8fa4;
+ background-color: #fff;
+ border-color: #e7e9ed;
+}
+
+@mixin btn-big {
+ height: 36px;
+ padding: 5px 10px;
+ font-size: 16px;
+ line-height: 24px;
+}
+
+@mixin nav-menu {
+ padding: 0;
+ margin: 0;
+ list-style: none;
+ margin-top: 5px;
+ height: 56px;
+
+ li {
+ display: inline-block;
+
+ a {
+ padding: 14px;
+ font-size: 17px;
+ line-height: 28px;
+ color: #7f8fa4;
+ border-bottom: 2px solid transparent;
+
+ &:hover, &:active, &:focus {
+ text-decoration: none;
+ }
+ }
+
+ &.active a {
+ color: #4c4e54;
+ border-bottom: 2px solid #1cacfc;
+ }
+
+ .badge {
+ font-weight: normal;
+ background-color: #fff;
+ background-color: #eee;
+ color: #78a;
+ }
+ }
+}
diff --git a/app/assets/stylesheets/base/variables.scss b/app/assets/stylesheets/base/variables.scss
index 26d0a1e5363..21462b31127 100644
--- a/app/assets/stylesheets/base/variables.scss
+++ b/app/assets/stylesheets/base/variables.scss
@@ -1,27 +1,30 @@
-$style_color: #474D57;
$hover: #FFFAF1;
-$gl-text-color: #222222;
-$gl-link-color: #446e9b;
+$gl-text-color: #54565b;
+$gl-header-color: #4c4e54;
+$gl-link-color: #333c48;
$nprogress-color: #c0392b;
-$gl-font-size: 14px;
+$gl-font-size: 15px;
$list-font-size: 15px;
-$sidebar_collapsed_width: 52px;
+$sidebar_collapsed_width: 62px;
$sidebar_width: 230px;
$avatar_radius: 50%;
$code_font_size: 13px;
$code_line_height: 1.5;
-$border-color: #E5E5E5;
-$background-color: #f5f5f5;
-$header-height: 50px;
+$border-color: #E7E9ED;
+$background-color: #F8FAFC;
+$header-height: 58px;
$fixed-layout-width: 1200px;
+$gl-gray: #7f8fa4;
+$gl-padding: 16px;
+$gl-avatar-size: 46px;
/*
* State colors:
*/
$gl-primary: #446e9b;
-$gl-success: #019875;
-$gl-info: #029ACF;
+$gl-success: #44c679;
+$gl-info: #00aaff;
$gl-warning: #EB9532;
$gl-danger: #d9534f;
@@ -35,4 +38,4 @@ $deleted: #f77;
* Fonts
*/
$monospace_font: 'Menlo', 'Liberation Mono', 'Consolas', 'DejaVu Sans Mono', 'Ubuntu Mono', 'Courier New', 'andale mono', 'lucida console', monospace;
-$regular_font: "Helvetica Neue", Helvetica, Arial, sans-serif;
+$regular_font: 'Source Sans Pro', "Helvetica Neue", Helvetica, Arial, sans-serif;
diff --git a/app/assets/stylesheets/generic/avatar.scss b/app/assets/stylesheets/generic/avatar.scss
index 8595887c3b9..221cb6a04a5 100644
--- a/app/assets/stylesheets/generic/avatar.scss
+++ b/app/assets/stylesheets/generic/avatar.scss
@@ -23,8 +23,12 @@
&.s24 { width: 24px; height: 24px; margin-right: 8px; }
&.s26 { width: 26px; height: 26px; margin-right: 8px; }
&.s32 { width: 32px; height: 32px; margin-right: 10px; }
+ &.s36 { width: 36px; height: 36px; margin-right: 10px; }
+ &.s46 { width: 46px; height: 46px; margin-right: 15px; }
+ &.s48 { width: 48px; height: 48px; margin-right: 10px; }
&.s60 { width: 60px; height: 60px; margin-right: 12px; }
&.s90 { width: 90px; height: 90px; margin-right: 15px; }
+ &.s140 { width: 140px; height: 140px; margin-right: 20px; }
&.s160 { width: 160px; height: 160px; margin-right: 20px; }
}
@@ -38,5 +42,6 @@
&.s32 { font-size: 22px; line-height: 32px; }
&.s60 { font-size: 32px; line-height: 60px; }
&.s90 { font-size: 36px; line-height: 90px; }
- &.s160 { font-size: 96px; line-height: 1.33; }
+ &.s140 { font-size: 72px; line-height: 140px; }
+ &.s160 { font-size: 96px; line-height: 160px; }
}
diff --git a/app/assets/stylesheets/generic/blocks.scss b/app/assets/stylesheets/generic/blocks.scss
index 3536a68f416..27a4c4db8c8 100644
--- a/app/assets/stylesheets/generic/blocks.scss
+++ b/app/assets/stylesheets/generic/blocks.scss
@@ -1,19 +1,52 @@
.light-well {
- background: #f9f9f9;
+ background-color: #f8fafc;
padding: 15px;
}
.centered-light-block {
text-align: center;
- color: #888;
+ color: $gl-gray;
margin: 20px;
}
.nothing-here-block {
text-align: center;
padding: 20px;
- color: #666;
+ color: $gl-gray;
font-weight: normal;
font-size: 16px;
line-height: 36px;
}
+
+.gray-content-block {
+ margin: -$gl-padding;
+ background-color: #f8fafc;
+ padding: $gl-padding;
+ margin-bottom: 0px;
+ border-top: 1px solid #e7e9ed;
+ border-bottom: 1px solid #e7e9ed;
+ color: $gl-gray;
+
+ &.middle-block {
+ margin-top: 0;
+ margin-bottom: 0;
+ }
+
+ &.second-block {
+ margin-top: -1px;
+ margin-bottom: 0;
+ }
+
+ &.footer-block {
+ margin-top: 0;
+ margin-bottom: -$gl-padding;
+ }
+
+ .title {
+ color: $gl-text-color;
+ }
+
+ .oneline {
+ line-height: 44px;
+ }
+}
diff --git a/app/assets/stylesheets/generic/buttons.scss b/app/assets/stylesheets/generic/buttons.scss
index cd6bf64c0ae..e8237509092 100644
--- a/app/assets/stylesheets/generic/buttons.scss
+++ b/app/assets/stylesheets/generic/buttons.scss
@@ -72,3 +72,19 @@
}
}
}
+
+.btn-group-next {
+ .btn {
+ padding: 9px 0px;
+ font-size: 15px;
+ color: #7f8fa4;
+ border-color: #e7e9ed;
+ width: 140px;
+
+ &.active {
+ border-color: $gl-info;
+ background: $gl-info;
+ color: #fff;
+ }
+ }
+}
diff --git a/app/assets/stylesheets/generic/common.scss b/app/assets/stylesheets/generic/common.scss
index bf5c7a8d75e..5e191d5dd4a 100644
--- a/app/assets/stylesheets/generic/common.scss
+++ b/app/assets/stylesheets/generic/common.scss
@@ -1,5 +1,5 @@
/** COLORS **/
-.cgray { color: gray }
+.cgray { color: $gl-gray; }
.clgray { color: #BBB }
.cred { color: #D12F19 }
.cgreen { color: #4a2 }
@@ -7,6 +7,7 @@
/** COMMON CLASSES **/
.prepend-top-10 { margin-top:10px }
+.prepend-top-default { margin-top: $gl-padding; }
.prepend-top-20 { margin-top:20px }
.prepend-left-10 { margin-left:10px }
.prepend-left-20 { margin-left:20px }
@@ -20,10 +21,10 @@
.underlined-link { text-decoration: underline; }
.hint { font-style: italic; color: #999; }
-.light { color: #888 }
+.light { color: $gl-gray; }
.slead {
- color: #666;
+ color: $gl-gray;
font-size: 15px;
margin-bottom: 12px;
font-weight: normal;
@@ -74,8 +75,6 @@ pre {
color: $gl-link-color;
}
-.help li { color:$style_color; }
-
.back-link {
font-size: 14px;
}
@@ -132,10 +131,6 @@ p.time {
text-shadow: none;
}
-.highlight_word {
- background: #fafe3d;
-}
-
.thin_area{
height: 150px;
}
@@ -307,7 +302,7 @@ table {
}
.btn-sign-in {
- margin-top: 7px;
+ margin-top: 15px;
text-shadow: none;
}
@@ -359,14 +354,14 @@ table {
}
.description {
- font-size: 16px;
+ font-size: $gl-font-size;
color: #666;
margin-top: 8px;
}
}
.profiler-results {
- top: 50px !important;
+ top: 73px !important;
.profiler-button,
.profiler-controls {
@@ -375,21 +370,11 @@ table {
}
.center-top-menu {
- border-bottom: 1px solid #EEE;
- list-style: none;
+ @include nav-menu;
text-align: center;
- padding-bottom: 15px;
- margin-bottom: 15px;
-
- li {
- display: inline-block;
-
- a {
- padding: 10px;
- }
-
- &.active a {
- color: #666;
- }
- }
+ margin-top: 5px;
+ margin-bottom: $gl-padding;
+ height: 56px;
+ margin-top: -$gl-padding;
+ padding-top: $gl-padding;
}
diff --git a/app/assets/stylesheets/generic/filters.scss b/app/assets/stylesheets/generic/filters.scss
index bd93a79722d..8e6922c9231 100644
--- a/app/assets/stylesheets/generic/filters.scss
+++ b/app/assets/stylesheets/generic/filters.scss
@@ -2,31 +2,6 @@
margin-right: 15px;
}
-.issues-state-filters {
- li.active a {
- border-color: #DDD !important;
-
- &, &:hover, &:active, &.active {
- background: #f5f5f5 !important;
- border-bottom: 1px solid #f5f5f5 !important;
- }
- }
-}
-
-.issues-details-filters {
- font-size: 13px;
- background: #f5f5f5;
- margin: -10px 0;
- padding: 10px 15px;
- margin-top: -15px;
- border-left: 1px solid #DDD;
- border-right: 1px solid #DDD;
-
- .btn {
- font-size: 13px;
- }
-}
-
@media (min-width: 800px) {
.issues-filters,
.issues_bulk_update {
diff --git a/app/assets/stylesheets/generic/header.scss b/app/assets/stylesheets/generic/header.scss
index 6a29b32e196..b758a526fbb 100644
--- a/app/assets/stylesheets/generic/header.scss
+++ b/app/assets/stylesheets/generic/header.scss
@@ -24,29 +24,28 @@ header {
z-index: 100;
margin-bottom: 0;
min-height: $header-height;
+ background-color: #fff;
border: none;
border-bottom: 1px solid #EEE;
.container-fluid {
- background: #FFF;
width: 100% !important;
filter: none;
+ padding: 0;
.nav > li > a {
- color: #888;
- font-size: 14px;
+ color: #7f8fa4;
+ font-size: 18px;
padding: 0;
- background-color: #f5f5f5;
margin: ($header-height - 28) / 2 0;
margin-left: 10px;
- border-radius: 40px;
height: 28px;
width: 28px;
line-height: 28px;
text-align: center;
&:hover, &:focus, &:active {
- background-color: #EEE;
+ background-color: #FFF;
}
}
@@ -56,6 +55,7 @@ header {
border-radius: 0;
position: absolute;
right: 2px;
+ top: 15px;
&:hover {
background-color: #EEE;
@@ -70,16 +70,16 @@ header {
.title {
margin: 0;
overflow: hidden;
- font-size: 18px;
+ font-size: 19px;
line-height: $header-height;
- font-weight: bold;
- color: #444;
+ font-weight: normal;
+ color: #4c4e54;
text-overflow: ellipsis;
vertical-align: top;
white-space: nowrap;
a {
- color: #444;
+ color: #4c4e54;
&:hover {
text-decoration: underline;
}
@@ -94,7 +94,7 @@ header {
.search {
margin-right: 10px;
margin-left: 10px;
- margin-top: ($header-height - 28) / 2;
+ margin-top: ($header-height - 36) / 2;
form {
margin: 0;
@@ -105,13 +105,8 @@ header {
width: 220px;
background-image: image-url("icon-search.png");
background-repeat: no-repeat;
- background-position: 10px;
- height: inherit;
- padding: 4px 6px;
- padding-left: 25px;
- font-size: 13px;
- background-color: #f5f5f5;
- border-color: #f5f5f5;
+ background-position: 195px;
+ @include input-big;
&:focus {
@include box-shadow(none);
diff --git a/app/assets/stylesheets/generic/issue_box.scss b/app/assets/stylesheets/generic/issue_box.scss
index 869e586839b..b1fb87a6830 100644
--- a/app/assets/stylesheets/generic/issue_box.scss
+++ b/app/assets/stylesheets/generic/issue_box.scss
@@ -5,10 +5,13 @@
*/
.issue-box {
+ @include border-radius(3px);
+
display: inline-block;
- padding: 4px 13px;
+ padding: 10px $gl-padding;
font-weight: normal;
- margin-right: 5px;
+ margin-right: 10px;
+ font-size: $gl-font-size;
&.issue-box-closed {
background-color: $gl-danger;
@@ -21,7 +24,7 @@
}
&.issue-box-open {
- background-color: $gl-success;
+ background-color: #019875;
color: #FFF;
}
diff --git a/app/assets/stylesheets/generic/lists.scss b/app/assets/stylesheets/generic/lists.scss
index c502d953c75..3bfed8de772 100644
--- a/app/assets/stylesheets/generic/lists.scss
+++ b/app/assets/stylesheets/generic/lists.scss
@@ -49,8 +49,6 @@
}
}
- .author { color: #999; }
-
.list-item-name {
float: left;
position: relative;
@@ -71,15 +69,6 @@
font-size: $list-font-size;
line-height: 18px;
}
-
- .row_title {
- color: $gray-dark;
-
- &:hover {
- color: $text-color;
- text-decoration: underline;
- }
- }
}
}
@@ -93,28 +82,12 @@ ol, ul {
/** light list with border-bottom between li **/
ul.bordered-list {
- margin: 5px 0px;
- padding: 0px;
- li {
- padding: 5px 0;
- border-bottom: 1px solid #EEE;
- overflow: hidden;
- display: block;
- margin: 0px;
- &:last-child { border:none }
- &.active {
- background: #f9f9f9;
- a { font-weight: bold; }
- }
-
- &.light {
- a { color: #777; }
- }
- }
+ @include basic-list;
&.top-list {
li:first-child {
padding-top: 0;
+
h4, h5 {
margin-top: 0;
}
@@ -125,3 +98,28 @@ ul.bordered-list {
li.task-list-item {
list-style-type: none;
}
+
+ul.content-list {
+ @include basic-list;
+
+ margin: 0;
+ padding: 0;
+
+ > li {
+ padding: $gl-padding;
+ border-color: #f1f2f4;
+ margin-left: -$gl-padding;
+ margin-right: -$gl-padding;
+ color: $gl-gray;
+
+ .avatar {
+ margin-right: 15px;
+ }
+
+ .controls {
+ padding-top: 10px;
+ float: right;
+ }
+ }
+}
+
diff --git a/app/assets/stylesheets/generic/markdown_area.scss b/app/assets/stylesheets/generic/markdown_area.scss
index a4fc82e90bf..ed0333d2336 100644
--- a/app/assets/stylesheets/generic/markdown_area.scss
+++ b/app/assets/stylesheets/generic/markdown_area.scss
@@ -65,8 +65,11 @@
position: relative;
}
-.md-header ul {
- float: left;
+.md-header {
+ ul {
+ float: left;
+ margin-bottom: 1px;
+ }
}
.referenced-users {
@@ -80,7 +83,7 @@
.md-preview-holder {
background: #FFF;
border: 1px solid #ddd;
- min-height: 100px;
+ min-height: 169px;
padding: 5px;
box-shadow: none;
}
@@ -105,7 +108,7 @@
.markdown-area {
background: #FFF;
border: 1px solid #ddd;
- min-height: 100px;
+ min-height: 140px;
padding: 5px;
box-shadow: none;
width: 100%;
diff --git a/app/assets/stylesheets/generic/mobile.scss b/app/assets/stylesheets/generic/mobile.scss
index bb7b9356c70..36ae126f865 100644
--- a/app/assets/stylesheets/generic/mobile.scss
+++ b/app/assets/stylesheets/generic/mobile.scss
@@ -80,6 +80,23 @@
%ul.notes .note-role, .note-actions {
display: none;
}
+
+ .center-top-menu {
+ height: 45px;
+
+ li a {
+ font-size: 14px;
+ padding: 19px 10px;
+ }
+ }
+
+ .projects-search-form {
+ margin: 0 -5px !important;
+
+ .btn {
+ display: none;
+ }
+ }
}
@media (max-width: $screen-sm-max) {
diff --git a/app/assets/stylesheets/generic/selects.scss b/app/assets/stylesheets/generic/selects.scss
index d8e0dc028d1..f0860de1c49 100644
--- a/app/assets/stylesheets/generic/selects.scss
+++ b/app/assets/stylesheets/generic/selects.scss
@@ -3,9 +3,9 @@
.select2-choice {
background: #FFF;
border-color: #DDD;
- height: 34px;
- padding: 6px 14px;
- font-size: 14px;
+ height: 42px;
+ padding: 8px $gl-padding;
+ font-size: $gl-font-size;
line-height: 1.42857143;
@include border-radius(4px);
@@ -13,7 +13,7 @@
.select2-arrow {
background: #FFF;
border-left: none;
- padding-top: 3px;
+ padding-top: 5px;
}
}
}
diff --git a/app/assets/stylesheets/generic/sidebar.scss b/app/assets/stylesheets/generic/sidebar.scss
index 320bdb1c765..22720c2e1d5 100644
--- a/app/assets/stylesheets/generic/sidebar.scss
+++ b/app/assets/stylesheets/generic/sidebar.scss
@@ -18,13 +18,21 @@
}
.content-wrapper {
+ min-height: 100vh;
width: 100%;
padding: 20px;
- background: #FFF;
+ background: #f1f4f8;
+
+ .container-fluid {
+ background: #FFF;
+ padding: $gl-padding;
+ border: 1px solid #e7e9ed;
+ min-height: 90vh;
+ }
}
.nav-sidebar {
- margin-top: 29 + $header-height;
+ margin-top: 14 + $header-height;
margin-bottom: 50px;
transition-duration: .3s;
list-style: none;
@@ -43,13 +51,14 @@
}
a {
- padding: 8px 15px;
- font-size: 13px;
- line-height: 18px;
+ padding: 7px 15px;
+ font-size: $gl-font-size;
+ line-height: 24px;
color: $gray;
display: block;
text-decoration: none;
- padding-left: 16px;
+ padding-left: 22px;
+ font-weight: normal;
&:hover {
text-decoration: none;
@@ -60,9 +69,9 @@
}
i {
- width: 20px;
+ width: 16px;
color: $gray-light;
- margin-right: 23px;
+ margin-right: 13px;
}
.count {
@@ -108,17 +117,31 @@
}
@mixin folded-sidebar {
- padding-left: 50px;
+ padding-left: 60px;
transition-duration: .3s;
.sidebar-wrapper {
width: $sidebar_collapsed_width;
+ .header-logo {
+ width: $sidebar_collapsed_width;
+
+ a {
+ padding-left: 12px;
+
+ .gitlab-text-container {
+ display: none;
+ }
+ }
+ }
+
.nav-sidebar {
width: $sidebar_collapsed_width;
li a {
- padding-left: 16px;
+ span {
+ display: none;
+ }
}
}
@@ -128,21 +151,25 @@
}
.sidebar-user {
+ padding-left: 12px;
width: $sidebar_collapsed_width;
+
+ .username {
+ display: none;
+ }
}
}
}
.collapse-nav a {
+ width: $sidebar_width;
position: fixed;
- top: $header-height;
- left: 198px;
+ bottom: 0;
font-size: 13px;
background: transparent;
- width: 32px;
- height: 28px;
+ height: 40px;
text-align: center;
- line-height: 28px;
+ line-height: 40px;
transition-duration: .3s;
}
@@ -176,16 +203,18 @@
}
.sidebar-user {
+ padding: 9px 22px;
position: fixed;
- bottom: 0;
+ bottom: 40px;
width: $sidebar_width;
- padding: 10px;
overflow: hidden;
transition-duration: .3s;
.username {
- margin-top: 5px;
+ margin-left: 10px;
width: $sidebar_width - 2 * 10px;
+ font-size: 16px;
+ line-height: 34px;
}
}
@@ -202,7 +231,7 @@
float: left;
height: $header-height;
width: 100%;
- padding: ($header-height - 36 ) / 2 8px;
+ padding: 10px 22px;
overflow: hidden;
img {
@@ -219,8 +248,8 @@
float: left;
margin: 0;
margin-left: 14px;
- font-size: 18px;
- line-height: $header-height - 14;
+ font-size: 19px;
+ line-height: 41px;
font-weight: normal;
}
}
diff --git a/app/assets/stylesheets/generic/timeline.scss b/app/assets/stylesheets/generic/timeline.scss
index 97831eb7c27..74bbaabad39 100644
--- a/app/assets/stylesheets/generic/timeline.scss
+++ b/app/assets/stylesheets/generic/timeline.scss
@@ -1,119 +1,50 @@
.timeline {
- list-style: none;
- padding: 20px 0 20px;
- position: relative;
+ @include basic-list;
- &:before {
- top: 0;
- bottom: 0;
- position: absolute;
- content: " ";
- width: 3px;
- background-color: #eeeeee;
- margin-left: 29px;
- }
+ margin: 0;
+ padding: 0;
.timeline-entry {
- position: relative;
- margin-top: 5px;
- margin-left: 30px;
- margin-bottom: 10px;
- clear: both;
-
-
- &:target {
- .timeline-entry-inner .timeline-content {
- -webkit-animation:target-note 2s linear;
- background: $hover;
- }
+ padding: $gl-padding;
+ border-color: #f1f2f4;
+ margin-left: -$gl-padding;
+ margin-right: -$gl-padding;
+ color: $gl-gray;
+ border-bottom: 1px solid #f1f2f4;
+ border-right: 1px solid #f1f2f4;
+
+ &:last-child {
+ border-bottom: none;
}
- .timeline-entry-inner {
- position: relative;
- margin-left: -20px;
-
- &:before, &:after {
- content: " ";
- display: table;
- }
-
- .timeline-icon {
- margin-top: 2px;
- background: #fff;
- color: #737881;
- float: left;
- @include border-radius($avatar_radius);
- @include box-shadow(0 0 0 3px #EEE);
- overflow: hidden;
-
- .avatar {
- margin: 0;
- padding: 0;
- }
- }
-
- .timeline-content {
- position: relative;
- background: $background-color;
- padding: 10px 15px;
- margin-left: 60px;
-
- img {
- max-width: 100%;
- }
+ .avatar {
+ margin-right: 15px;
+ }
- &:after {
- content: '';
- display: block;
- position: absolute;
- width: 0;
- height: 0;
- border-style: solid;
- border-width: 9px 9px 9px 0;
- border-color: transparent $background-color transparent transparent;
- left: 0;
- top: 10px;
- margin-left: -9px;
- }
- }
+ .controls {
+ padding-top: 10px;
+ float: right;
}
}
- .system-note .timeline-entry-inner {
- .timeline-icon {
- background: none;
- margin-left: 12px;
- margin-top: 0;
- @include box-shadow(none);
-
- span {
- margin: 0 2px;
- font-size: 16px;
- color: #eeeeee;
- }
+ .note-text {
+ p:last-child {
+ margin-bottom: 0;
}
+ }
- .timeline-content {
- background: none;
- margin-left: 45px;
- padding: 0px 15px;
-
- &:after { border: 0; }
-
- .note-header {
- span { font-size: 12px; }
-
- .avatar {
- margin-right: 5px;
- }
- }
-
- .note-text {
- font-size: 12px;
- margin-left: 20px;
- }
+ .system-note {
+ .note-text {
+ color: $gl-gray !important;
}
}
+
+ .diff-file {
+ border: 1px solid $border-color;
+ border-bottom: none;
+ margin-left: 0;
+ margin-right: 0;
+ }
}
@media (max-width: $screen-xs-max) {
@@ -132,3 +63,8 @@
}
}
}
+
+.discussion .timeline-entry {
+ margin: 0;
+ border-right: none;
+}
diff --git a/app/assets/stylesheets/generic/typography.scss b/app/assets/stylesheets/generic/typography.scss
index 34b4ee3e17e..73034c84f9a 100644
--- a/app/assets/stylesheets/generic/typography.scss
+++ b/app/assets/stylesheets/generic/typography.scss
@@ -9,6 +9,11 @@
margin-bottom: 5px;
}
+h1, h2, h3, h4, h5, h6 {
+ color: $gl-header-color;
+ font-weight: 500;
+}
+
/** CODE **/
pre {
font-family: $monospace_font;
diff --git a/app/assets/stylesheets/generic/zen.scss b/app/assets/stylesheets/generic/zen.scss
index 7e86a0fe4b9..32e2c020e06 100644
--- a/app/assets/stylesheets/generic/zen.scss
+++ b/app/assets/stylesheets/generic/zen.scss
@@ -4,7 +4,7 @@
}
.zen-enter-link {
- color: #888;
+ color: $gl-gray;
position: absolute;
top: 0px;
right: 4px;
@@ -13,7 +13,7 @@
.zen-leave-link {
display: none;
- color: #888;
+ color: $gl-text-color;
position: absolute;
top: 10px;
right: 10px;
diff --git a/app/assets/stylesheets/highlight/dark.scss b/app/assets/stylesheets/highlight/dark.scss
index c8cb18ec35f..8323a8598ec 100644
--- a/app/assets/stylesheets/highlight/dark.scss
+++ b/app/assets/stylesheets/highlight/dark.scss
@@ -21,6 +21,12 @@ pre.code.highlight.dark,
background-color: #557 !important;
}
+ // Search result highlight
+ span.highlight_word {
+ background: #ffe792;
+ color: #000000;
+ }
+
.hll { background-color: #373b41 }
.c { color: #969896 } /* Comment */
.err { color: #cc6666 } /* Error */
diff --git a/app/assets/stylesheets/highlight/monokai.scss b/app/assets/stylesheets/highlight/monokai.scss
index 001e8b31020..e8381674336 100644
--- a/app/assets/stylesheets/highlight/monokai.scss
+++ b/app/assets/stylesheets/highlight/monokai.scss
@@ -21,6 +21,12 @@ pre.code.monokai,
background-color: #49483e !important;
}
+ // Search result highlight
+ span.highlight_word {
+ background: #ffe792;
+ color: #000000;
+ }
+
.hll { background-color: #49483e }
.c { color: #75715e } /* Comment */
.err { color: #960050; background-color: #1e0010 } /* Error */
diff --git a/app/assets/stylesheets/highlight/solarized_dark.scss b/app/assets/stylesheets/highlight/solarized_dark.scss
index f5b827e7c02..bd41480aefb 100644
--- a/app/assets/stylesheets/highlight/solarized_dark.scss
+++ b/app/assets/stylesheets/highlight/solarized_dark.scss
@@ -21,6 +21,11 @@ pre.code.highlight.solarized-dark,
background-color: #174652 !important;
}
+ // Search result highlight
+ span.highlight_word {
+ background: #094554;
+ }
+
/* Solarized Dark
For use with Jekyll and Pygments
diff --git a/app/assets/stylesheets/highlight/solarized_light.scss b/app/assets/stylesheets/highlight/solarized_light.scss
index 6b44c00c305..4cc62863870 100644
--- a/app/assets/stylesheets/highlight/solarized_light.scss
+++ b/app/assets/stylesheets/highlight/solarized_light.scss
@@ -21,6 +21,11 @@ pre.code.highlight.solarized-light,
background-color: #ddd8c5 !important;
}
+ // Search result highlight
+ span.highlight_word {
+ background: #eee8d5;
+ }
+
/* Solarized Light
For use with Jekyll and Pygments
diff --git a/app/assets/stylesheets/highlight/white.scss b/app/assets/stylesheets/highlight/white.scss
index a52ffc971d1..e0edfb80b42 100644
--- a/app/assets/stylesheets/highlight/white.scss
+++ b/app/assets/stylesheets/highlight/white.scss
@@ -21,6 +21,11 @@ pre.code.highlight.white,
background-color: #f8eec7 !important;
}
+ // Search result highlight
+ span.highlight_word {
+ background: #fafe3d;
+ }
+
.hll { background-color: #f8f8f8 }
.c { color: #999988; font-style: italic; }
.err { color: #a61717; background-color: #e3d2d2; }
diff --git a/app/assets/stylesheets/pages/commit.scss b/app/assets/stylesheets/pages/commit.scss
index e7125c03993..051ca3792c3 100644
--- a/app/assets/stylesheets/pages/commit.scss
+++ b/app/assets/stylesheets/pages/commit.scss
@@ -26,14 +26,6 @@
margin-top: 10px;
}
-.commit-stat-summary {
- color: #666;
- font-size: 14px;
- font-weight: normal;
- padding: 3px 0;
- margin-bottom: 10px;
-}
-
.commit-info-row {
margin-bottom: 10px;
.avatar {
@@ -47,11 +39,6 @@
}
.commit-box {
- margin: 10px 0;
- border-top: 1px solid #ddd;
- border-bottom: 1px solid #ddd;
- padding: 20px 0;
-
.commit-title {
margin: 0;
}
@@ -61,10 +48,6 @@
}
}
-.file-stats a {
- color: $style_color;
-}
-
.file-stats {
.new-file {
a {
diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss
index 359f4073e87..de2ae93df37 100644
--- a/app/assets/stylesheets/pages/commits.scss
+++ b/app/assets/stylesheets/pages/commits.scss
@@ -52,7 +52,7 @@ li.commit {
}
.commit-row-message {
- color: #444;
+ color: $gl-link-color;
&:hover {
text-decoration: underline;
@@ -88,12 +88,12 @@ li.commit {
}
.commit-row-info {
- color: #777;
+ color: $gl-gray;
line-height: 24px;
font-size: 13px;
a {
- color: #777;
+ color: $gl-gray;
}
.committed_ago {
diff --git a/app/assets/stylesheets/pages/dashboard.scss b/app/assets/stylesheets/pages/dashboard.scss
index c1103a1c2e6..25a86cd0f94 100644
--- a/app/assets/stylesheets/pages/dashboard.scss
+++ b/app/assets/stylesheets/pages/dashboard.scss
@@ -2,7 +2,7 @@
.side {
.panel {
.panel-heading {
- background: #EEE;
+ background: $background-color;
border-top-left-radius: 0;
}
border-top-left-radius: 0;
@@ -38,11 +38,11 @@
float: left;
.avatar {
- @include border-radius(0px);
+ @include border-radius(50%);
}
.identicon {
- line-height: 40px;
+ line-height: 46px;
}
}
diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss
index 1557c243db5..487b600e31d 100644
--- a/app/assets/stylesheets/pages/diff.scss
+++ b/app/assets/stylesheets/pages/diff.scss
@@ -1,6 +1,8 @@
.diff-file {
- border: 1px solid $border-color;
- margin-bottom: 1em;
+ margin-left: -16px;
+ margin-right: -16px;
+ border: none;
+ border-bottom: 1px solid #E7E9EE;
.diff-header {
position: relative;
@@ -45,7 +47,7 @@
overflow-y: hidden;
background: #FFF;
color: #333;
- font-size: $code_font_size;
+
.old {
span.idiff {
background-color: #f8cbcb;
@@ -82,7 +84,7 @@
border: none;
margin: 0px;
padding: 0px;
- td {
+ .line_holder td {
line-height: $code_line_height;
font-size: $code_font_size;
}
@@ -367,3 +369,7 @@
white-space: pre-wrap;
}
+.inline-parallel-buttons {
+ float: right;
+ margin-top: -5px;
+}
diff --git a/app/assets/stylesheets/pages/events.scss b/app/assets/stylesheets/pages/events.scss
index d4af7506d5b..ca2ee455423 100644
--- a/app/assets/stylesheets/pages/events.scss
+++ b/app/assets/stylesheets/pages/events.scss
@@ -1,70 +1,58 @@
/**
- * Events labels
- *
- */
-.event_label {
- &.pushed {
- padding: 0 2px;
- }
-
- &.opened {
- padding: 0 2px;
- }
-
- &.closed {
- padding: 0 2px;
- }
-
- &.merged {
- padding: 0 2px;
- }
-
- &.left,
- &.joined {
- padding: 0 2px;
- float: none;
- }
-}
-
-/**
* Dashboard events feed
*
*/
.event-item {
- &:first-child {
- padding-top: 0;
- }
+ font-size: $gl-font-size;
+ padding: $gl-padding;
+ margin-left: -$gl-padding;
+ margin-right: -$gl-padding;
+ border-bottom: 1px solid #f1f2f4;
+ color: #7f8fa4;
&.event-inline {
.avatar {
position: relative;
top: -2px;
}
+
+ .event-title {
+ line-height: 44px;
+ }
+
+ .event-item-timestamp {
+ line-height: 44px;
+ }
+ }
+
+ a {
+ color: #4c4e54;
+ }
+
+ .avatar {
+ margin-right: 15px;
}
- padding: 12px 0px;
- border-bottom: 1px solid #eee;
.event-title {
- max-width: 70%;
@include str-truncated(calc(100% - 174px));
- font-weight: 500;
- font-size: 14px;
+ font-weight: 600;
+
.author_name {
color: #333;
}
}
+
.event-body {
- font-size: 13px;
- margin-left: 35px;
+ margin-left: 63px;
margin-right: 80px;
- color: #777;
.event-note {
margin-top: 5px;
word-wrap: break-word;
.md {
- font-size: 13px;
+ color: #7f8fa4;
+ font-size: $gl-font-size;
iframe.twitter-share-button {
vertical-align: bottom;
@@ -94,7 +82,7 @@
.event-note-icon {
color: #777;
float: left;
- font-size: 16px;
+ font-size: $gl-font-size;
line-height: 16px;
margin-right: 5px;
}
@@ -116,7 +104,7 @@
&:last-child { border:none }
.event_commits {
- margin-top: 5px;
+ margin-top: 9px;
li {
&.commit {
@@ -125,10 +113,12 @@
padding-left: 0;
border: none;
.commit-row-title {
- font-size: 12px;
+ font-size: $gl-font-size;
}
}
+
&.commits-stat {
+ margin-top: 3px;
display: block;
padding: 3px;
padding-left: 0;
@@ -142,7 +132,6 @@
.event-item-timestamp {
float: right;
- color: #999;
line-height: 22px;
}
}
@@ -186,12 +175,3 @@
}
}
}
-
-.event_filter {
- li a {
- font-size: 13px;
- padding: 5px 10px;
- background: $background-color;
- margin-left: 4px;
- }
-}
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 586e7b5f8da..b5c61f7f91d 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -25,8 +25,6 @@
}
.issuable-context-title {
- font-size: 14px;
- line-height: 1.4;
margin-bottom: 5px;
.avatar {
@@ -34,14 +32,50 @@
}
label {
- color: #666;
+ color: $gl-gray;
font-weight: normal;
margin-right: 4px;
}
}
-.issuable-affix .context {
- font-size: 13px;
+.project-issuable-filter {
+ .controls {
+ float: right;
+ margin-top: 7px;
+ }
+
+ .center-top-menu {
+ text-align: left;
+ }
+}
- .btn { font-size: 13px; }
+.issuable-details {
+ .page-title {
+ margin-top: -15px;
+ padding: 10px 0;
+ margin-bottom: 0;
+ color: $gl-gray;
+ font-size: 16px;
+
+ .author {
+ color: $gl-gray;
+ }
+
+ .issue-id {
+ font-size: 19px;
+ color: $gl-text-color;
+ }
+ }
+
+ .issue-title {
+ margin: 0;
+ }
+
+ .description {
+ margin-top: 6px;
+
+ p:last-child {
+ margin-bottom: 0;
+ }
+ }
}
diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss
index 3572f33e91f..4bf58cb4a59 100644
--- a/app/assets/stylesheets/pages/issues.scss
+++ b/app/assets/stylesheets/pages/issues.scss
@@ -1,6 +1,6 @@
.issues-list {
.issue {
- padding: 10px 15px;
+ padding: 10px $gl-padding;
position: relative;
.issue-title {
@@ -10,8 +10,7 @@
}
.issue-info {
- color: #999;
- font-size: 13px;
+ color: $gl-gray;
}
.issue-check {
@@ -47,10 +46,6 @@
}
}
-.participants {
- margin-bottom: 20px;
-}
-
.issue-search-form {
margin: 0;
height: 24px;
@@ -137,11 +132,6 @@ form.edit-issue {
}
}
-h2.issue-title {
- margin-top: 0;
- font-weight: bold;
-}
-
.issue-form .select2-container {
width: 250px !important;
}
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index 10fce5b3daa..d8c8e5ad0a4 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -3,10 +3,10 @@
*
*/
.mr-state-widget {
- background: #FAFAFA;
+ background: #f8fafc;
margin-bottom: 20px;
- color: #666;
- border: 1px solid #e5e5e5;
+ color: $gl-gray;
+ border: 1px solid #eef0f2;
@include box-shadow(0 1px 1px rgba(0, 0, 0, 0.05));
@include border-radius(3px);
@@ -29,6 +29,14 @@
padding: 5px;
line-height: 20px;
+ &.right {
+ float: right;
+ padding-top: 12px;
+ a {
+ color: $gl-gray;
+ }
+ }
+
.remove_source_checkbox {
margin: 0;
}
@@ -36,7 +44,7 @@
}
.ci_widget {
- border-bottom: 1px solid #EEE;
+ border-bottom: 1px solid #eef0f2;
i {
margin-right: 4px;
@@ -89,20 +97,14 @@
}
}
-@media(min-width: $screen-sm-max) {
- .merge-request .merge-request-tabs{
- li {
- a {
- padding: 15px 40px;
- font-size: 14px;
- }
- }
- }
-}
-
.merge-request .merge-request-tabs{
- margin-top: 30px;
- margin-bottom: 20px;
+ @include nav-menu;
+ margin: -$gl-padding;
+ padding: $gl-padding;
+ text-align: center;
+ border-top: 1px solid #e7e9ed;
+ margin-top: 18px;
+ margin-bottom: 3px;
}
.mr_source_commit,
@@ -136,8 +138,7 @@
}
.merge-request-info {
- color: #999;
- font-size: 13px;
+ color: $gl-gray;
}
}
diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss
index 15e3948e402..e80dc9e84a1 100644
--- a/app/assets/stylesheets/pages/milestone.scss
+++ b/app/assets/stylesheets/pages/milestone.scss
@@ -6,4 +6,8 @@ li.milestone {
h4 {
font-weight: bold;
}
+
+ .progress {
+ height: 6px;
+ }
}
diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index 203f9374cee..b311d26d675 100644
--- a/app/assets/stylesheets/pages/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
@@ -72,9 +72,13 @@
.common-note-form {
margin: 0;
- background: #F9F9F9;
- padding: 5px;
- border: 1px solid #DDD;
+ background: #f8fafc;
+ padding: $gl-padding;
+ margin-left: -$gl-padding;
+ margin-right: -$gl-padding;
+ border-right: 1px solid #f1f2f4;
+ border-top: 1px solid #f1f2f4;
+ margin-bottom: -$gl-padding;
}
.note-form-actions {
@@ -142,9 +146,9 @@
}
.discussion-reply-holder {
- background: #f9f9f9;
+ background: $background-color;
padding: 10px 15px;
- border-top: 1px solid #DDD;
+ border-top: 1px solid $border-color;
}
}
@@ -166,6 +170,6 @@
background: #FFF;
padding: 5px;
margin-top: -11px;
- border: 1px solid #DDD;
+ border: 1px solid $border-color;
font-size: 13px;
}
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index 85c828ec1ad..2544356a5f6 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -14,6 +14,19 @@ ul.notes {
margin: 0px;
padding: 0px;
+ .system-note {
+ font-size: 14px;
+ padding-top: 10px;
+ padding-bottom: 10px;
+ background: #f8fafc;
+
+ .timeline-icon {
+ .avatar {
+ visibility: hidden;
+ }
+ }
+ }
+
.discussion-header,
.note-header {
@extend .cgray;
@@ -34,10 +47,8 @@ ul.notes {
content: "\00b7";
}
- font-size: 13px;
-
a {
- @extend .cgray;
+ color: $gl-gray;
&:hover {
text-decoration: underline;
@@ -45,8 +56,9 @@ ul.notes {
}
}
.author {
- color: #333;
- font-weight: bold;
+ color: #4c4e54;
+ margin-right: 3px;
+
&:hover {
color: $gl-link-color;
}
@@ -59,7 +71,7 @@ ul.notes {
margin-top: 1px;
border: 1px solid #bbb;
background-color: transparent;
- color: #999;
+ color: $gl-gray;
}
}
@@ -133,8 +145,6 @@ ul.notes {
}
.diff-file .notes_holder {
- font-size: 13px;
- line-height: 18px;
font-family: $regular_font;
td {
@@ -176,8 +186,7 @@ ul.notes {
a {
margin-left: 5px;
-
- color: #999;
+ color: $gl-gray;
i.fa {
font-size: 16px;
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index 4d065c3bdf6..361fd63bc79 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -16,6 +16,10 @@
.project-home-panel {
text-align: center;
+ background: #f7f8fa;
+ margin: -$gl-padding;
+ padding: $gl-padding;
+ padding-top: 40px;
.project-identicon-holder {
margin-bottom: 15px;
@@ -38,13 +42,12 @@
h1 {
margin: 0;
margin-bottom: 10px;
- font-size: 26px;
- font-weight: bold;
+ font-size: 23px;
+ font-weight: normal;
}
p {
- font-size: 18px;
- color: #666;
+ color: #7f8fa4;
display: inline;
}
}
@@ -52,6 +55,10 @@
.git-clone-holder {
max-width: 600px;
margin: 20px auto;
+
+ .form-control {
+ background: #FFF;
+ }
}
.visibility-level-label {
@@ -62,17 +69,18 @@
}
.project-repo-buttons {
- margin-top: 25px;
+ margin-top: $gl-padding;
margin-bottom: 25px;
.btn {
@extend .btn-info;
+ text-transform: uppercase;
+ font-size: 15px;
+ line-height: 20px;
+ padding: 8px 14px;
+ border-radius: 3px;
margin-left: 10px;
- font-weight: bold;
- font-size: 14px;
- line-height: 16px;
- padding: 8px 12px;
.count {
padding-left: 7px;
@@ -162,78 +170,6 @@ ul.nav.nav-projects-tabs {
margin: 0px;
}
-.my-projects,
-.public-projects {
- li {
- .project-info {
- margin-bottom: 10px;
- overflow: hidden;
- }
-
- .access-icon {
- color: #AAA;
- margin-left: 10px;
- i {
- color: #AAA;
- }
- }
- }
-}
-
-.public-clone {
- background: #EEE;
- color: #777;
- padding: 6px 10px;
- margin: 1px;
- font-weight: normal;
-}
-
-.public-projects .repo-info {
- color: #777;
-
- a {
- color: #777;
- }
-}
-
-.project-side {
- .project-fork-icon {
- float: left;
- font-size: 26px;
- margin-right: 10px;
- line-height: 1.5;
- }
-
- .panel {
- @include border-radius(3px);
-
- .panel-heading, .panel-footer {
- font-weight: normal;
- background-color: transparent;
- color: #666;
- border-color: #EEE;
- }
-
- .actions {
- margin-top: 10px;
- }
-
- .nav-pills a {
- padding: 10px;
- font-weight: bold;
- color: $gl-link-color;
- }
-
- .nav {
- margin-bottom: 15px;
- }
- }
-
- .ci-status-image {
- max-height: 22px;
- }
-}
-
.transfer-project .select2-container {
min-width: 200px;
}
@@ -256,10 +192,10 @@ ul.nav.nav-projects-tabs {
.breadcrumb.repo-breadcrumb {
padding: 0;
- line-height: 34px;
- background: white;
+ line-height: 42px;
+ background: transparent;
border: none;
- font-size: 16px;
+ margin: 0;
> li + li:before {
padding: 0 3px;
@@ -305,10 +241,23 @@ table.table.protected-branches-list tr.no-border {
.project-stats {
text-align: center;
+ margin-top: 0;
+ margin-bottom: 0;
+ padding-top: 5px;
+ padding-bottom: 0;
+
+ ul.nav-pills {
+ display:inline-block;
+ }
+
+ li {
+ display:inline;
+ }
- ul.nav-pills { display:inline-block; }
- li { display:inline; }
- a { float:left; }
+ a {
+ float:left;
+ font-size: 17px;
+ }
li.missing a {
color: #bbb;
@@ -325,32 +274,67 @@ pre.light-well {
}
.projects-search-form {
- max-width: 600px;
- margin: 0 auto;
- margin-bottom: 20px;
-
- input {
- border-color: #BBB;
- }
+ margin: -$gl-padding;
+ background-color: #f8fafc;
+ padding: $gl-padding;
+ margin-bottom: 0px;
+ border-top: 1px solid #e7e9ed;
+ border-bottom: 1px solid #e7e9ed;
}
-.project-row {
- .project-full-name {
- font-weight: bold;
- font-size: 15px;
- }
+/*
+ * Projects list rendered on dashboard and user page
+ */
+.projects-list {
+ @include basic-list;
+
+ .project-row {
+ padding: $gl-padding;
+ border-color: #f1f2f4;
+ margin-left: -$gl-padding;
+ margin-right: -$gl-padding;
+
+ &.no-description {
+ .project {
+ line-height: 44px;
+ }
+ }
- .project-description {
- color: #888;
- font-size: 13px;
+ .project-full-name {
+ @include str-truncated;
+ font-weight: 600;
+ color: #4c4e54;
+ }
- p {
- margin-bottom: 0;
- color: #888;
+ .pull-right.light {
+ line-height: 45px;
+ color: #7f8fa4;
+ }
+
+ .project-description {
+ color: #7f8fa4;
+
+ p {
+ @include str-truncated;
+ margin-bottom: 0;
+ color: #7f8fa4;
+ }
}
}
+
+ .bottom {
+ padding-top: $gl-padding;
+ padding-bottom: 0;
+ }
+}
+
+.panel .projects-list li {
+ padding: 10px 15px;
+ margin: 0;
}
-.my-projects .project-row {
- padding: 10px 0;
+.project-show-activity {
+ .activity-filter-block {
+ margin-top: -1px;
+ }
}
diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss
index bdaa17ac339..3aaa96da609 100644
--- a/app/assets/stylesheets/pages/search.scss
+++ b/app/assets/stylesheets/pages/search.scss
@@ -1,7 +1,19 @@
.search-results {
.search-result-row {
- border-bottom: 1px solid #EEE;
- padding-bottom: 10px;
- margin-bottom: 10px;
+ border-bottom: 1px solid #DDD;
+ padding-bottom: 15px;
+ margin-bottom: 15px;
}
}
+
+.search-holder {
+ max-width: 600px;
+ margin: 0 auto;
+ margin-bottom: 20px;
+
+ input {
+ border-color: #BBB;
+ font-weight: bold;
+ }
+}
+
diff --git a/app/assets/stylesheets/pages/snippets.scss b/app/assets/stylesheets/pages/snippets.scss
index d79591d9915..a3d7aba054d 100644
--- a/app/assets/stylesheets/pages/snippets.scss
+++ b/app/assets/stylesheets/pages/snippets.scss
@@ -6,3 +6,27 @@
.snippet-form-holder .file-holder .file-title {
padding: 2px;
}
+
+
+.snippet-row {
+ .snippet-title {
+ font-size: 15px;
+ font-weight: bold;
+ line-height: 20px;
+ margin-bottom: 2px;
+
+ .monospace {
+ font-weight: normal;
+ }
+ }
+
+ .snippet-info {
+ color: #888;
+ font-size: 13px;
+ line-height: 24px;
+
+ a {
+ color: #888;
+ }
+ }
+}
diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss
index 81e2aa7bb9c..587d09a04a5 100644
--- a/app/assets/stylesheets/pages/tree.scss
+++ b/app/assets/stylesheets/pages/tree.scss
@@ -63,15 +63,15 @@
padding-right: 8px;
.commit-author-name {
- color: gray;
+ color: $gl-gray;
}
}
.tree_commit {
- color: gray;
+ color: $gl-gray;
.tree-commit-link {
- color: gray;
+ color: $gl-gray;
&:hover {
text-decoration: underline;
diff --git a/app/assets/stylesheets/themes/gitlab-theme.scss b/app/assets/stylesheets/themes/gitlab-theme.scss
index 77b62c3153f..8d9a0aae568 100644
--- a/app/assets/stylesheets/themes/gitlab-theme.scss
+++ b/app/assets/stylesheets/themes/gitlab-theme.scss
@@ -9,15 +9,19 @@
@mixin gitlab-theme($color-light, $color, $color-darker, $color-dark) {
.page-with-sidebar {
.header-logo {
- background-color: $color-darker;
- border-color: $color-darker;
+ background-color: $color;
+ border-color: $color;
a {
color: $color-light;
+
+ h3 {
+ color: $color-light;
+ }
}
&:hover {
- background-color: $color-dark;
+ background-color: $color-darker;
a {
color: #FFF;
}
@@ -83,7 +87,7 @@
}
$theme-blue: #2980B9;
-$theme-charcoal: #474D57;
+$theme-charcoal: #333c47;
$theme-graphite: #888888;
$theme-gray: #373737;
$theme-green: #019875;
@@ -95,7 +99,7 @@ body {
}
&.ui_charcoal {
- @include gitlab-theme(#979DA7, $theme-charcoal, #373D47, #24272D);
+ @include gitlab-theme(#c5d0de, $theme-charcoal, #2b333d, #24272D);
}
&.ui_graphite {
diff --git a/app/controllers/admin/labels_controller.rb b/app/controllers/admin/labels_controller.rb
new file mode 100644
index 00000000000..3b070e65d0d
--- /dev/null
+++ b/app/controllers/admin/labels_controller.rb
@@ -0,0 +1,58 @@
+class Admin::LabelsController < Admin::ApplicationController
+ before_action :set_label, only: [:show, :edit, :update, :destroy]
+
+ def index
+ @labels = Label.templates.page(params[:page]).per(PER_PAGE)
+ end
+
+ def show
+ end
+
+ def new
+ @label = Label.new
+ end
+
+ def edit
+ end
+
+ def create
+ @label = Label.new(label_params)
+ @label.template = true
+
+ if @label.save
+ redirect_to admin_labels_url, notice: "Label was created"
+ else
+ render :new
+ end
+ end
+
+ def update
+ if @label.update(label_params)
+ redirect_to admin_labels_path, notice: 'label was successfully updated.'
+ else
+ render :edit
+ end
+ end
+
+ def destroy
+ @label.destroy
+ @labels = Label.templates
+
+ respond_to do |format|
+ format.html do
+ redirect_to(admin_labels_path, notice: 'Label was removed')
+ end
+ format.js
+ end
+ end
+
+ private
+
+ def set_label
+ @label = Label.find(params[:id])
+ end
+
+ def label_params
+ params[:label].permit(:title, :color)
+ end
+end
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index f029abc5013..9b6472a7b13 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -1,4 +1,5 @@
require 'gon'
+require 'fogbugz'
class ApplicationController < ActionController::Base
include Gitlab::CurrentSettings
@@ -20,7 +21,7 @@ class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
helper_method :abilities, :can?, :current_application_settings
- helper_method :import_sources_enabled?, :github_import_enabled?, :github_import_configured?, :gitlab_import_enabled?, :gitlab_import_configured?, :bitbucket_import_enabled?, :bitbucket_import_configured?, :gitorious_import_enabled?, :google_code_import_enabled?, :git_import_enabled?
+ helper_method :import_sources_enabled?, :github_import_enabled?, :github_import_configured?, :gitlab_import_enabled?, :gitlab_import_configured?, :bitbucket_import_enabled?, :bitbucket_import_configured?, :gitorious_import_enabled?, :google_code_import_enabled?, :fogbugz_import_enabled?, :git_import_enabled?
rescue_from Encoding::CompatibilityError do |exception|
log_exception(exception)
@@ -189,11 +190,12 @@ class ApplicationController < ActionController::Base
end
def add_gon_variables
+ gon.api_version = API::API.version
+ gon.default_avatar_url = URI::join(Gitlab.config.gitlab.url, ActionController::Base.helpers.image_path('no_avatar.png')).to_s
gon.default_issues_tracker = Project.new.default_issue_tracker.to_param
- gon.api_version = API::API.version
- gon.relative_url_root = Gitlab.config.gitlab.relative_url_root
- gon.default_avatar_url = URI::join(Gitlab.config.gitlab.url, ActionController::Base.helpers.image_path('no_avatar.png')).to_s
- gon.max_file_size = current_application_settings.max_attachment_size;
+ gon.max_file_size = current_application_settings.max_attachment_size
+ gon.relative_url_root = Gitlab.config.gitlab.relative_url_root
+ gon.user_color_scheme = Gitlab::ColorSchemes.for_user(current_user).css_class
if current_user
gon.current_user_id = current_user.id
@@ -333,6 +335,10 @@ class ApplicationController < ActionController::Base
current_application_settings.import_sources.include?('google_code')
end
+ def fogbugz_import_enabled?
+ current_application_settings.import_sources.include?('fogbugz')
+ end
+
def git_import_enabled?
current_application_settings.import_sources.include?('git')
end
diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb
index d745131694b..2bc2e5e58f5 100644
--- a/app/controllers/dashboard_controller.rb
+++ b/app/controllers/dashboard_controller.rb
@@ -1,5 +1,5 @@
class DashboardController < Dashboard::ApplicationController
- before_action :load_projects
+ before_action :load_projects, except: :activity
before_action :event_filter, only: :activity
respond_to :html
@@ -55,7 +55,14 @@ class DashboardController < Dashboard::ApplicationController
end
def load_events
- @events = Event.in_projects(current_user.authorized_projects.pluck(:id))
+ project_ids =
+ if params[:filter] == "starred"
+ current_user.starred_projects
+ else
+ current_user.authorized_projects
+ end.pluck(:id)
+
+ @events = Event.in_projects(project_ids)
@events = @event_filter.apply_filter(@events).with_associations
@events = @events.limit(20).offset(params[:offset] || 0)
end
diff --git a/app/controllers/import/fogbugz_controller.rb b/app/controllers/import/fogbugz_controller.rb
new file mode 100644
index 00000000000..bda534fb4de
--- /dev/null
+++ b/app/controllers/import/fogbugz_controller.rb
@@ -0,0 +1,106 @@
+class Import::FogbugzController < Import::BaseController
+ before_action :verify_fogbugz_import_enabled
+ before_action :user_map, only: [:new_user_map, :create_user_map]
+
+ # Doesn't work yet due to bug in ruby-fogbugz, see below
+ rescue_from Fogbugz::AuthenticationException, with: :fogbugz_unauthorized
+
+ def new
+
+ end
+
+ def callback
+ begin
+ res = Gitlab::FogbugzImport::Client.new(import_params.symbolize_keys)
+ rescue
+ # Needed until https://github.com/firmafon/ruby-fogbugz/pull/9 is merged
+ return redirect_to :back, alert: 'Could not authenticate with FogBugz, check your URL, email, and password'
+ end
+ session[:fogbugz_token] = res.get_token
+ session[:fogbugz_uri] = params[:uri]
+
+ redirect_to new_user_map_import_fogbugz_path
+ end
+
+ def new_user_map
+
+ end
+
+ def create_user_map
+ user_map = params[:users]
+
+ unless user_map.is_a?(Hash) && user_map.all? { |k, v| !v[:name].blank? }
+ flash.now[:alert] = 'All users must have a name.'
+
+ render 'new_user_map' and return
+ end
+
+ session[:fogbugz_user_map] = user_map
+
+ flash[:notice] = 'The user map has been saved. Continue by selecting the projects you want to import.'
+
+ redirect_to status_import_fogbugz_path
+ end
+
+ def status
+ unless client.valid?
+ return redirect_to new_import_fogbugz_path
+ end
+
+ @repos = client.repos
+
+ @already_added_projects = current_user.created_projects.where(import_type: 'fogbugz')
+ already_added_projects_names = @already_added_projects.pluck(:import_source)
+
+ @repos.reject! { |repo| already_added_projects_names.include? repo.name }
+ end
+
+ def jobs
+ jobs = current_user.created_projects.where(import_type: 'fogbugz').to_json(only: [:id, :import_status])
+ render json: jobs
+ end
+
+ def create
+ @repo_id = params[:repo_id]
+ repo = client.repo(@repo_id)
+ fb_session = { uri: session[:fogbugz_uri], token: session[:fogbugz_token] }
+ @target_namespace = current_user.namespace
+ @project_name = repo.name
+
+ namespace = @target_namespace
+
+ umap = session[:fogbugz_user_map] || client.user_map
+
+ @project = Gitlab::FogbugzImport::ProjectCreator.new(repo, fb_session, namespace, current_user, umap).execute
+ end
+
+ private
+
+ def client
+ @client ||= Gitlab::FogbugzImport::Client.new(token: session[:fogbugz_token], uri: session[:fogbugz_uri])
+ end
+
+ def user_map
+ @user_map ||= begin
+ user_map = client.user_map
+
+ stored_user_map = session[:fogbugz_user_map]
+ user_map.update(stored_user_map) if stored_user_map
+
+ user_map
+ end
+ end
+
+ def fogbugz_unauthorized(exception)
+ flash[:alert] = exception.message
+ redirect_to new_import_fogbugz_path
+ end
+
+ def import_params
+ params.permit(:uri, :email, :password)
+ end
+
+ def verify_fogbugz_import_enabled
+ not_found! unless fogbugz_import_enabled?
+ end
+end
diff --git a/app/controllers/projects/raw_controller.rb b/app/controllers/projects/raw_controller.rb
index 647c1454078..5f6fbce795e 100644
--- a/app/controllers/projects/raw_controller.rb
+++ b/app/controllers/projects/raw_controller.rb
@@ -17,8 +17,7 @@ class Projects::RawController < Projects::ApplicationController
send_data(
@blob.data,
type: type,
- disposition: 'inline',
- filename: @blob.name
+ disposition: 'inline'
)
else
not_found!
@@ -30,6 +29,8 @@ class Projects::RawController < Projects::ApplicationController
def get_blob_type
if @blob.text?
'text/plain; charset=utf-8'
+ elsif @blob.image?
+ @blob.content_type
else
'application/octet-stream'
end
diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb
index b0cf5866d41..3a22ed832ac 100644
--- a/app/controllers/projects/services_controller.rb
+++ b/app/controllers/projects/services_controller.rb
@@ -2,7 +2,7 @@ class Projects::ServicesController < Projects::ApplicationController
ALLOWED_PARAMS = [:title, :token, :type, :active, :api_key, :api_version, :subdomain,
:room, :recipients, :project_url, :webhook,
:user_key, :device, :priority, :sound, :bamboo_url, :username, :password,
- :build_key, :server, :teamcity_url, :build_type,
+ :build_key, :server, :teamcity_url, :drone_url, :build_type,
:description, :issues_url, :new_issue_url, :restrict_to_branch, :channel,
:colorize_messages, :channels,
:push_events, :issues_events, :merge_requests_events, :tag_push_events,
diff --git a/app/controllers/projects/snippets_controller.rb b/app/controllers/projects/snippets_controller.rb
index 64306637423..b07a2a8db2f 100644
--- a/app/controllers/projects/snippets_controller.rb
+++ b/app/controllers/projects/snippets_controller.rb
@@ -30,9 +30,14 @@ class Projects::SnippetsController < Projects::ApplicationController
def create
@snippet = CreateSnippetService.new(@project, current_user,
snippet_params).execute
- respond_with(@snippet,
- location: namespace_project_snippet_path(@project.namespace,
- @project, @snippet))
+
+ if @snippet.valid?
+ respond_with(@snippet,
+ location: namespace_project_snippet_path(@project.namespace,
+ @project, @snippet))
+ else
+ render :new
+ end
end
def edit
diff --git a/app/controllers/projects/wikis_controller.rb b/app/controllers/projects/wikis_controller.rb
index b2bf9fa05ea..51c26a6a465 100644
--- a/app/controllers/projects/wikis_controller.rb
+++ b/app/controllers/projects/wikis_controller.rb
@@ -5,7 +5,6 @@ class Projects::WikisController < Projects::ApplicationController
before_action :authorize_create_wiki!, only: [:edit, :create, :history]
before_action :authorize_admin_wiki!, only: :destroy
before_action :load_project_wiki
- #include WikiHelper
def pages
@wiki_pages = Kaminari.paginate_array(@project_wiki.pages).page(params[:page]).per(PER_PAGE)
diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb
index 8389f07a3bd..cfa565cd03e 100644
--- a/app/controllers/sessions_controller.rb
+++ b/app/controllers/sessions_controller.rb
@@ -8,6 +8,8 @@ class SessionsController < Devise::SessionsController
def new
if Gitlab.config.ldap.enabled
@ldap_servers = Gitlab::LDAP::Config.servers
+ else
+ @ldap_servers = []
end
super
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 2bb5c338cf6..1484356a7f4 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -51,10 +51,6 @@ class UsersController < ApplicationController
def set_user
@user = User.find_by_username!(params[:username])
-
- unless current_user || @user.public_profile?
- return authenticate_user!
- end
end
def authorized_projects_ids
diff --git a/app/finders/trending_projects_finder.rb b/app/finders/trending_projects_finder.rb
index a79bd47d986..f3f4d461efa 100644
--- a/app/finders/trending_projects_finder.rb
+++ b/app/finders/trending_projects_finder.rb
@@ -2,13 +2,21 @@ class TrendingProjectsFinder
def execute(current_user, start_date = nil)
start_date ||= Date.today - 1.month
- projects = projects_for(current_user)
-
# Determine trending projects based on comments count
# for period of time - ex. month
- projects.joins(:notes).where('notes.created_at > ?', start_date).
- select("projects.*, count(notes.id) as ncount").
- group("projects.id").reorder("ncount DESC")
+ trending_project_ids = Note.
+ select("notes.project_id, count(notes.project_id) as pcount").
+ where('notes.created_at > ?', start_date).
+ group("project_id").
+ reorder("pcount DESC").
+ map(&:project_id)
+
+ sql_order_ids = trending_project_ids.reverse.
+ map { |project_id| "id = #{project_id}" }.join(", ")
+
+ # Get list of projects that user allowed to see
+ projects = projects_for(current_user)
+ projects.where(id: trending_project_ids).reorder(sql_order_ids)
end
private
diff --git a/app/models/abuse_report.rb b/app/models/abuse_report.rb
index c8c39db11bc..07c87a7fe87 100644
--- a/app/models/abuse_report.rb
+++ b/app/models/abuse_report.rb
@@ -1,3 +1,15 @@
+# == Schema Information
+#
+# Table name: abuse_reports
+#
+# id :integer not null, primary key
+# reporter_id :integer
+# user_id :integer
+# message :text
+# created_at :datetime
+# updated_at :datetime
+#
+
class AbuseReport < ActiveRecord::Base
belongs_to :reporter, class_name: "User"
belongs_to :user
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index 8f27e35d723..c8841178e93 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -83,7 +83,7 @@ class ApplicationSetting < ActiveRecord::Base
default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'],
default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'],
restricted_signup_domains: Settings.gitlab['restricted_signup_domains'],
- import_sources: ['github','bitbucket','gitlab','gitorious','google_code','git']
+ import_sources: ['github','bitbucket','gitlab','gitorious','google_code','fogbugz','git']
)
end
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 2456b7d0dc1..fc7e9abe29e 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -2,19 +2,20 @@
#
# Table name: issues
#
-# id :integer not null, primary key
-# title :string(255)
-# assignee_id :integer
-# author_id :integer
-# project_id :integer
-# created_at :datetime
-# updated_at :datetime
-# position :integer default(0)
-# branch_name :string(255)
-# description :text
-# milestone_id :integer
-# state :string(255)
-# iid :integer
+# id :integer not null, primary key
+# title :string(255)
+# assignee_id :integer
+# author_id :integer
+# project_id :integer
+# created_at :datetime
+# updated_at :datetime
+# position :integer default(0)
+# branch_name :string(255)
+# description :text
+# milestone_id :integer
+# state :string(255)
+# iid :integer
+# updated_by_id :integer
#
require 'carrierwave/orm/activerecord'
diff --git a/app/models/label.rb b/app/models/label.rb
index 230631b5180..4a22bd53400 100644
--- a/app/models/label.rb
+++ b/app/models/label.rb
@@ -24,7 +24,7 @@ class Label < ActiveRecord::Base
validates :color,
format: { with: /\A#[0-9A-Fa-f]{6}\Z/ },
allow_blank: false
- validates :project, presence: true
+ validates :project, presence: true, unless: Proc.new { |service| service.template? }
# Don't allow '?', '&', and ',' for label titles
validates :title,
@@ -34,6 +34,8 @@ class Label < ActiveRecord::Base
default_scope { order(title: :asc) }
+ scope :templates, -> { where(template: true) }
+
alias_attribute :name, :title
def self.reference_prefix
@@ -78,4 +80,8 @@ class Label < ActiveRecord::Base
def open_issues_count
issues.opened.count
end
+
+ def template?
+ template
+ end
end
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 467b90861f9..93faa133875 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -19,6 +19,7 @@
# description :text
# position :integer default(0)
# locked_at :datetime
+# updated_by_id :integer
#
require Rails.root.join("app/models/commit")
diff --git a/app/models/note.rb b/app/models/note.rb
index 36cad8f583d..89d81ab1de2 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -15,6 +15,7 @@
# noteable_id :integer
# system :boolean default(FALSE), not null
# st_diff :text
+# updated_by_id :integer
#
require 'carrierwave/orm/activerecord'
diff --git a/app/models/project.rb b/app/models/project.rb
index f14cd884c89..495a863d93b 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -43,6 +43,8 @@ class Project < ActiveRecord::Base
extend Gitlab::ConfigHelper
extend Enumerize
+ UNKNOWN_IMPORT_URL = 'http://unknown.git'
+
default_value_for :archived, false
default_value_for :visibility_level, gitlab_config_features.visibility_level
default_value_for :issues_enabled, gitlab_config_features.issues
@@ -73,6 +75,7 @@ class Project < ActiveRecord::Base
has_many :services
has_one :gitlab_ci_service, dependent: :destroy
has_one :campfire_service, dependent: :destroy
+ has_one :drone_ci_service, dependent: :destroy
has_one :emails_on_push_service, dependent: :destroy
has_one :irker_service, dependent: :destroy
has_one :pivotaltracker_service, dependent: :destroy
@@ -400,6 +403,15 @@ class Project < ActiveRecord::Base
end
end
+ def create_labels
+ Label.templates.each do |label|
+ label = label.dup
+ label.template = nil
+ label.project_id = self.id
+ label.save
+ end
+ end
+
def find_service(list, name)
list.find { |service| service.to_param == name }
end
@@ -613,6 +625,7 @@ class Project < ActiveRecord::Base
name: name,
ssh_url: ssh_url_to_repo,
http_url: http_url_to_repo,
+ web_url: web_url,
namespace: namespace.name,
visibility_level: visibility_level
}
diff --git a/app/models/project_services/ci_service.rb b/app/models/project_services/ci_service.rb
index 803402c83ee..88186113c68 100644
--- a/app/models/project_services/ci_service.rb
+++ b/app/models/project_services/ci_service.rb
@@ -25,12 +25,24 @@ class CiService < Service
def category
:ci
end
-
+
+ def valid_token?(token)
+ self.respond_to?(:token) && self.token.present? && self.token == token
+ end
+
def supported_events
%w(push)
end
- # Return complete url to build page
+ def merge_request_page(iid, sha, ref)
+ commit_page(sha, ref)
+ end
+
+ def commit_page(sha, ref)
+ build_page(sha, ref)
+ end
+
+ # Return complete url to merge_request page
#
# Ex.
# http://jenkins.example.com:8888/job/test1/scm/bySHA1/12d65c
@@ -45,10 +57,27 @@ class CiService < Service
#
#
# Ex.
- # @service.commit_status('13be4ac')
+ # @service.merge_request_status(9, '13be4ac', 'dev')
+ # # => 'success'
+ #
+ # @service.merge_request_status(10, '2abe4ac', 'dev)
+ # # => 'running'
+ #
+ #
+ def merge_request_status(iid, sha, ref)
+ commit_status(sha, ref)
+ end
+
+ # Return string with build status or :error symbol
+ #
+ # Allowed states: 'success', 'failed', 'running', 'pending', 'skipped'
+ #
+ #
+ # Ex.
+ # @service.commit_status('13be4ac', 'master')
# # => 'success'
#
- # @service.commit_status('2abe4ac')
+ # @service.commit_status('2abe4ac', 'dev')
# # => 'running'
#
#
diff --git a/app/models/project_services/drone_ci_service.rb b/app/models/project_services/drone_ci_service.rb
new file mode 100644
index 00000000000..3e2b7faecdb
--- /dev/null
+++ b/app/models/project_services/drone_ci_service.rb
@@ -0,0 +1,190 @@
+# == Schema Information
+#
+# Table name: services
+#
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# project_id :integer
+# created_at :datetime
+# updated_at :datetime
+# active :boolean default(FALSE), not null
+# properties :text
+# template :boolean default(FALSE)
+# push_events :boolean default(TRUE)
+# issues_events :boolean default(TRUE)
+# merge_requests_events :boolean default(TRUE)
+# tag_push_events :boolean default(TRUE)
+# note_events :boolean default(TRUE), not null
+#
+
+class DroneCiService < CiService
+
+ prop_accessor :drone_url, :token, :enable_ssl_verification
+ validates :drone_url,
+ presence: true,
+ format: { with: /\A#{URI.regexp(%w(http https))}\z/, message: "should be a valid url" }, if: :activated?
+ validates :token,
+ presence: true,
+ format: { with: /\A([A-Za-z0-9]+)\z/ }, if: :activated?
+
+ after_save :compose_service_hook, if: :activated?
+
+ def compose_service_hook
+ hook = service_hook || build_service_hook
+ hook.url = [drone_url, "/api/hook", "?owner=#{project.namespace.path}", "&name=#{project.path}", "&access_token=#{token}"].join
+ hook.enable_ssl_verification = enable_ssl_verification
+ hook.save
+ end
+
+ def execute(data)
+ case data[:object_kind]
+ when 'push'
+ service_hook.execute(data) if push_valid?(data)
+ when 'merge_request'
+ service_hook.execute(data) if merge_request_valid?(data)
+ when 'tag_push'
+ service_hook.execute(data) if tag_push_valid?(data)
+ end
+ end
+
+ def allow_target_ci?
+ true
+ end
+
+ def supported_events
+ %w(push merge_request tag_push)
+ end
+
+ def merge_request_status_path(iid, sha = nil, ref = nil)
+ url = [drone_url,
+ "gitlab/#{project.namespace.path}/#{project.path}/pulls/#{iid}",
+ "?access_token=#{token}"]
+
+ URI.join(*url).to_s
+ end
+
+ def commit_status_path(sha, ref)
+ url = [drone_url,
+ "gitlab/#{project.namespace.path}/#{project.path}/commits/#{sha}",
+ "?branch=#{URI::encode(ref.to_s)}&access_token=#{token}"]
+
+ URI.join(*url).to_s
+ end
+
+ def merge_request_status(iid, sha, ref)
+ response = HTTParty.get(merge_request_status_path(iid), verify: enable_ssl_verification)
+
+ if response.code == 200 and response['status']
+ case response['status']
+ when 'killed'
+ :canceled
+ when 'failure', 'error'
+ # Because drone return error if some test env failed
+ :failed
+ else
+ response["status"]
+ end
+ else
+ :error
+ end
+ rescue Errno::ECONNREFUSED
+ :error
+ end
+
+ def commit_status(sha, ref)
+ response = HTTParty.get(commit_status_path(sha, ref), verify: enable_ssl_verification)
+
+ if response.code == 200 and response['status']
+ case response['status']
+ when 'killed'
+ :canceled
+ when 'failure', 'error'
+ # Because drone return error if some test env failed
+ :failed
+ else
+ response["status"]
+ end
+ else
+ :error
+ end
+ rescue Errno::ECONNREFUSED
+ :error
+ end
+
+ def merge_request_page(iid, sha, ref)
+ url = [drone_url,
+ "gitlab/#{project.namespace.path}/#{project.path}/redirect/pulls/#{iid}"]
+
+ URI.join(*url).to_s
+ end
+
+ def commit_page(sha, ref)
+ url = [drone_url,
+ "gitlab/#{project.namespace.path}/#{project.path}/redirect/commits/#{sha}",
+ "?branch=#{URI::encode(ref.to_s)}"]
+
+ URI.join(*url).to_s
+ end
+
+ def commit_coverage(sha, ref)
+ nil
+ end
+
+ def build_page(sha, ref)
+ commit_page(sha, ref)
+ end
+
+ def builds_path
+ url = [drone_url, "#{project.namespace.path}/#{project.path}"]
+
+ URI.join(*url).to_s
+ end
+
+ def status_img_path
+ url = [drone_url,
+ "api/badges/#{project.namespace.path}/#{project.path}/status.svg",
+ "?branch=#{URI::encode(project.default_branch)}"]
+
+ URI.join(*url).to_s
+ end
+
+ def title
+ 'Drone CI'
+ end
+
+ def description
+ 'Drone is a Continuous Integration platform built on Docker, written in Go'
+ end
+
+ def to_param
+ 'drone_ci'
+ end
+
+ def fields
+ [
+ { type: 'text', name: 'token', placeholder: 'Drone CI project specific token' },
+ { type: 'text', name: 'drone_url', placeholder: 'http://drone.example.com' },
+ { type: 'checkbox', name: 'enable_ssl_verification', title: "Enable SSL verification" }
+ ]
+ end
+
+ private
+
+ def tag_push_valid?(data)
+ data[:total_commits_count] > 0 && !Gitlab::Git.blank_ref?(data[:after])
+ end
+
+ def push_valid?(data)
+ opened_merge_requests = project.merge_requests.opened.where(source_project_id: project.id,
+ source_branch: Gitlab::Git.ref_name(data[:ref]))
+
+ opened_merge_requests.empty? && data[:total_commits_count] > 0 &&
+ !Gitlab::Git.blank_ref?(data[:after])
+ end
+
+ def merge_request_valid?(data)
+ ['opened', 'reopened'].include?(data[:object_attributes][:state]) &&
+ data[:object_attributes][:merge_status] == 'unchecked'
+ end
+end
diff --git a/app/models/sent_notification.rb b/app/models/sent_notification.rb
index 460ca40be3f..33b113a2a27 100644
--- a/app/models/sent_notification.rb
+++ b/app/models/sent_notification.rb
@@ -1,3 +1,16 @@
+# == Schema Information
+#
+# Table name: sent_notifications
+#
+# id :integer not null, primary key
+# project_id :integer
+# noteable_id :integer
+# noteable_type :string(255)
+# recipient_id :integer
+# commit_id :string(255)
+# reply_key :string(255) not null
+#
+
class SentNotification < ActiveRecord::Base
belongs_to :project
belongs_to :noteable, polymorphic: true
diff --git a/app/models/service.rb b/app/models/service.rb
index dcef2866c3b..60fcc9d2857 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -135,6 +135,7 @@ class Service < ActiveRecord::Base
buildkite
campfire
custom_issue_tracker
+ drone_ci
emails_on_push
external_wiki
flowdock
diff --git a/app/models/user.rb b/app/models/user.rb
index f70761074c5..bff8eeed96d 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -2,62 +2,58 @@
#
# Table name: users
#
-# id :integer not null, primary key
-# email :string(255) default(""), not null
-# encrypted_password :string(255) default(""), not null
-# reset_password_token :string(255)
-# reset_password_sent_at :datetime
-# remember_created_at :datetime
-# sign_in_count :integer default(0)
-# current_sign_in_at :datetime
-# last_sign_in_at :datetime
-# current_sign_in_ip :string(255)
-# last_sign_in_ip :string(255)
-# created_at :datetime
-# updated_at :datetime
-# name :string(255)
-# admin :boolean default(FALSE), not null
-# projects_limit :integer default(10)
-# skype :string(255) default(""), not null
-# linkedin :string(255) default(""), not null
-# twitter :string(255) default(""), not null
-# authentication_token :string(255)
-# theme_id :integer default(1), not null
-# bio :string(255)
-# failed_attempts :integer default(0)
-# locked_at :datetime
-# username :string(255)
-# can_create_group :boolean default(TRUE), not null
-# can_create_team :boolean default(TRUE), not null
-# state :string(255)
-# color_scheme_id :integer default(1), not null
-# notification_level :integer default(1), not null
-# password_expires_at :datetime
-# created_by_id :integer
-# last_credential_check_at :datetime
-# avatar :string(255)
-# confirmation_token :string(255)
-# confirmed_at :datetime
-# confirmation_sent_at :datetime
-# unconfirmed_email :string(255)
-# hide_no_ssh_key :boolean default(FALSE)
-# website_url :string(255) default(""), not null
-# github_access_token :string(255)
-# gitlab_access_token :string(255)
-# notification_email :string(255)
-# hide_no_password :boolean default(FALSE)
-# password_automatically_set :boolean default(FALSE)
-# bitbucket_access_token :string(255)
-# bitbucket_access_token_secret :string(255)
-# location :string(255)
-# encrypted_otp_secret :string(255)
-# encrypted_otp_secret_iv :string(255)
-# encrypted_otp_secret_salt :string(255)
-# otp_required_for_login :boolean default(FALSE), not null
-# otp_backup_codes :text
-# public_email :string(255) default(""), not null
-# dashboard :integer default(0)
-# project_view :integer default(0)
+# id :integer not null, primary key
+# email :string(255) default(""), not null
+# encrypted_password :string(255) default(""), not null
+# reset_password_token :string(255)
+# reset_password_sent_at :datetime
+# remember_created_at :datetime
+# sign_in_count :integer default(0)
+# current_sign_in_at :datetime
+# last_sign_in_at :datetime
+# current_sign_in_ip :string(255)
+# last_sign_in_ip :string(255)
+# created_at :datetime
+# updated_at :datetime
+# name :string(255)
+# admin :boolean default(FALSE), not null
+# projects_limit :integer default(10)
+# skype :string(255) default(""), not null
+# linkedin :string(255) default(""), not null
+# twitter :string(255) default(""), not null
+# authentication_token :string(255)
+# theme_id :integer default(1), not null
+# bio :string(255)
+# failed_attempts :integer default(0)
+# locked_at :datetime
+# username :string(255)
+# can_create_group :boolean default(TRUE), not null
+# can_create_team :boolean default(TRUE), not null
+# state :string(255)
+# color_scheme_id :integer default(1), not null
+# notification_level :integer default(1), not null
+# password_expires_at :datetime
+# created_by_id :integer
+# last_credential_check_at :datetime
+# avatar :string(255)
+# confirmation_token :string(255)
+# confirmed_at :datetime
+# confirmation_sent_at :datetime
+# unconfirmed_email :string(255)
+# hide_no_ssh_key :boolean default(FALSE)
+# website_url :string(255) default(""), not null
+# notification_email :string(255)
+# hide_no_password :boolean default(FALSE)
+# password_automatically_set :boolean default(FALSE)
+# location :string(255)
+# encrypted_otp_secret :string(255)
+# encrypted_otp_secret_iv :string(255)
+# encrypted_otp_secret_salt :string(255)
+# otp_required_for_login :boolean default(FALSE), not null
+# otp_backup_codes :text
+# public_email :string(255) default(""), not null
+# dashboard :integer default(0)
+# project_view :integer default(0)
#
require 'carrierwave/orm/activerecord'
@@ -104,7 +100,7 @@ class User < ActiveRecord::Base
# Profile
has_many :keys, dependent: :destroy
has_many :emails, dependent: :destroy
- has_many :identities, dependent: :destroy
+ has_many :identities, dependent: :destroy, autosave: true
# Groups
has_many :members, dependent: :destroy
@@ -637,10 +633,6 @@ class User < ActiveRecord::Base
email.start_with?('temp-email-for-oauth')
end
- def public_profile?
- authorized_projects.public_only.any?
- end
-
def avatar_url(size = nil)
if avatar.present?
[gitlab_config.url, avatar.url].join
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index 3735a136365..e294b23bc23 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -107,12 +107,17 @@ class NotificationService
recipients = []
+ mentioned_users = note.mentioned_users
+ mentioned_users.select! do |user|
+ user.can?(:read_project, note.project)
+ end
+
# Add all users participating in the thread (author, assignee, comment authors)
participants =
if target.respond_to?(:participants)
target.participants(note.author)
else
- note.mentioned_users
+ mentioned_users
end
recipients = recipients.concat(participants)
@@ -120,8 +125,8 @@ class NotificationService
recipients = add_project_watchers(recipients, note.project)
# Reject users with Mention notification level, except those mentioned in _this_ note.
- recipients = reject_mention_users(recipients - note.mentioned_users, note.project)
- recipients = recipients + note.mentioned_users
+ recipients = reject_mention_users(recipients - mentioned_users, note.project)
+ recipients = recipients + mentioned_users
recipients = reject_muted_users(recipients, note.project)
diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb
index b35aed005da..1bb2462565a 100644
--- a/app/services/projects/create_service.rb
+++ b/app/services/projects/create_service.rb
@@ -87,6 +87,8 @@ module Projects
@project.build_missing_services
+ @project.create_labels
+
event_service.create_project(@project, current_user)
system_hook_service.execute_hooks_for(@project, :create)
diff --git a/app/services/projects/download_service.rb b/app/services/projects/download_service.rb
new file mode 100644
index 00000000000..99f22293d0d
--- /dev/null
+++ b/app/services/projects/download_service.rb
@@ -0,0 +1,43 @@
+module Projects
+ class DownloadService < BaseService
+
+ WHITELIST = [
+ /^[^.]+\.fogbugz.com$/
+ ]
+
+ def initialize(project, url)
+ @project, @url = project, url
+ end
+
+ def execute
+ return nil unless valid_url?(@url)
+
+ uploader = FileUploader.new(@project)
+ uploader.download!(@url)
+ uploader.store!
+
+ filename = uploader.image? ? uploader.file.basename : uploader.file.filename
+
+ {
+ 'alt' => filename,
+ 'url' => uploader.secure_url,
+ 'is_image' => uploader.image?
+ }
+ end
+
+ private
+
+ def valid_url?(url)
+ url && http?(url) && valid_domain?(url)
+ end
+
+ def http?(url)
+ url =~ /\A#{URI::regexp(['http', 'https'])}\z/
+ end
+
+ def valid_domain?(url)
+ host = URI.parse(url).host
+ WHITELIST.any? { |entry| entry === host }
+ end
+ end
+end
diff --git a/app/views/admin/labels/_form.html.haml b/app/views/admin/labels/_form.html.haml
new file mode 100644
index 00000000000..ad58a3837f6
--- /dev/null
+++ b/app/views/admin/labels/_form.html.haml
@@ -0,0 +1,35 @@
+= form_for [:admin, @label], html: { class: 'form-horizontal label-form js-requires-input' } do |f|
+ -if @label.errors.any?
+ .row
+ .col-sm-offset-2.col-sm-10
+ .alert.alert-danger
+ - @label.errors.full_messages.each do |msg|
+ %span= msg
+ %br
+
+ .form-group
+ = f.label :title, class: 'control-label'
+ .col-sm-10
+ = f.text_field :title, class: "form-control", required: true
+ .form-group
+ = f.label :color, "Background Color", class: 'control-label'
+ .col-sm-10
+ .input-group
+ .input-group-addon.label-color-preview &nbsp;
+ = f.color_field :color, class: "form-control"
+ .help-block
+ Choose any color.
+ %br
+ Or you can choose one of suggested colors below
+
+ .suggest-colors
+ - suggested_colors.each do |color|
+ = link_to '#', style: "background-color: #{color}", data: { color: color } do
+ &nbsp;
+
+ .form-actions
+ = f.submit 'Save', class: 'btn btn-save js-save-button'
+ = link_to "Cancel", admin_labels_path, class: 'btn btn-cancel'
+
+:coffeescript
+ new Labels
diff --git a/app/views/admin/labels/_label.html.haml b/app/views/admin/labels/_label.html.haml
new file mode 100644
index 00000000000..596e06243dd
--- /dev/null
+++ b/app/views/admin/labels/_label.html.haml
@@ -0,0 +1,5 @@
+%li{id: dom_id(label)}
+ = render_colored_label(label)
+ .pull-right
+ = link_to 'Edit', edit_admin_label_path(label), class: 'btn btn-sm'
+ = link_to 'Remove', admin_label_path(label), class: 'btn btn-sm btn-remove remove-row', method: :delete, remote: true, data: {confirm: "Remove this label? Are you sure?"}
diff --git a/app/views/admin/labels/destroy.js.haml b/app/views/admin/labels/destroy.js.haml
new file mode 100644
index 00000000000..9d51762890f
--- /dev/null
+++ b/app/views/admin/labels/destroy.js.haml
@@ -0,0 +1,2 @@
+- if @labels.size == 0
+ $('.labels').load(document.URL + ' .light-well').hide().fadeIn(1000)
diff --git a/app/views/admin/labels/edit.html.haml b/app/views/admin/labels/edit.html.haml
new file mode 100644
index 00000000000..45c62a76259
--- /dev/null
+++ b/app/views/admin/labels/edit.html.haml
@@ -0,0 +1,9 @@
+- page_title "Edit", @label.name, "Labels"
+%h3
+ Edit label
+ %span.light #{@label.name}
+.back-link
+ = link_to admin_labels_path do
+ &larr; To labels list
+%hr
+= render 'form'
diff --git a/app/views/admin/labels/index.html.haml b/app/views/admin/labels/index.html.haml
new file mode 100644
index 00000000000..8b11c28c56e
--- /dev/null
+++ b/app/views/admin/labels/index.html.haml
@@ -0,0 +1,16 @@
+- page_title "Labels"
+= link_to new_admin_label_path, class: "pull-right btn btn-new" do
+ New label
+%h3.page-title
+ Labels
+%hr
+
+.labels
+ - if @labels.present?
+ %ul.bordered-list.manage-labels-list
+ = render @labels
+ = paginate @labels, theme: 'gitlab'
+ - else
+ .light-well
+ .nothing-here-block There are no any labels yet
+ \ No newline at end of file
diff --git a/app/views/admin/labels/new.html.haml b/app/views/admin/labels/new.html.haml
new file mode 100644
index 00000000000..8d298ad20f7
--- /dev/null
+++ b/app/views/admin/labels/new.html.haml
@@ -0,0 +1,7 @@
+- page_title "New Label"
+%h3 New label
+.back-link
+ = link_to admin_labels_path do
+ &larr; To labels list
+%hr
+= render 'form'
diff --git a/app/views/dashboard/_activities.html.haml b/app/views/dashboard/_activities.html.haml
index 213b5d65b3c..1db56542afd 100644
--- a/app/views/dashboard/_activities.html.haml
+++ b/app/views/dashboard/_activities.html.haml
@@ -1,13 +1,13 @@
.hidden-xs
= render "events/event_last_push", event: @last_push
+.gray-content-block
- if current_user
%ul.nav.nav-pills.event_filter.pull-right
%li.pull-right
= link_to dashboard_path(:atom, { private_token: current_user.private_token }), class: 'rss-btn' do
%i.fa.fa-rss
-
= render 'shared/event_filter'
- %hr
+
.content_list
= spinner
diff --git a/app/views/dashboard/_activity_head.html.haml b/app/views/dashboard/_activity_head.html.haml
new file mode 100644
index 00000000000..9f4be025bf2
--- /dev/null
+++ b/app/views/dashboard/_activity_head.html.haml
@@ -0,0 +1,7 @@
+%ul.center-top-menu
+ %li{ class: ("active" unless params[:filter]) }
+ = link_to activity_dashboard_path, class: 'shortcuts-activity', data: {placement: 'right'} do
+ Your Projects
+ %li{ class: ("active" if params[:filter] == 'starred') }
+ = link_to activity_dashboard_path(filter: 'starred'), data: {placement: 'right'} do
+ Starred Projects
diff --git a/app/views/dashboard/_groups_head.html.haml b/app/views/dashboard/_groups_head.html.haml
index 8a397a84e0e..dcd6c97d44d 100644
--- a/app/views/dashboard/_groups_head.html.haml
+++ b/app/views/dashboard/_groups_head.html.haml
@@ -3,5 +3,5 @@
= link_to dashboard_groups_path, title: 'Your groups', data: {placement: 'right'} do
Your Groups
= nav_link(page: [explore_groups_path]) do
- = link_to explore_groups_path, title: 'Explore groups', data: {toggle: 'tooltip', placement: 'bottom'} do
+ = link_to explore_groups_path, title: 'Explore groups', data: {placement: 'bottom'} do
Explore Groups
diff --git a/app/views/dashboard/_projects.html.haml b/app/views/dashboard/_projects.html.haml
index dc83d5343f2..ef9b9ce756a 100644
--- a/app/views/dashboard/_projects.html.haml
+++ b/app/views/dashboard/_projects.html.haml
@@ -7,7 +7,4 @@
= link_to new_project_path, class: 'btn btn-success' do
New project
- %ul.projects-list.bordered-list.my-projects
- - @projects.each do |project|
- %li.project-row
- = render partial: 'shared/project', locals: { project: project, avatar: true, stars: true }
+ = render 'shared/projects/list', projects: @projects
diff --git a/app/views/dashboard/_projects_head.html.haml b/app/views/dashboard/_projects_head.html.haml
index f7be194c696..13a5eae3cdc 100644
--- a/app/views/dashboard/_projects_head.html.haml
+++ b/app/views/dashboard/_projects_head.html.haml
@@ -5,6 +5,6 @@
= nav_link(page: starred_dashboard_projects_path) do
= link_to starred_dashboard_projects_path, title: 'Starred Projects', data: {placement: 'right'} do
Starred Projects
- = nav_link(page: [explore_root_path, trending_explore_projects_path, starred_explore_projects_path, explore_projects_path]) do
- = link_to explore_root_path, title: 'Explore', data: {toggle: 'tooltip', placement: 'bottom'} do
+ = nav_link(page: [explore_root_path, trending_explore_projects_path, starred_explore_projects_path, explore_projects_path], html_options: { class: 'hidden-xs' }) do
+ = link_to explore_root_path, title: 'Explore', data: {placement: 'right'} do
Explore Projects
diff --git a/app/views/dashboard/activity.html.haml b/app/views/dashboard/activity.html.haml
index 7a5a093add5..3e24338af64 100644
--- a/app/views/dashboard/activity.html.haml
+++ b/app/views/dashboard/activity.html.haml
@@ -2,5 +2,8 @@
- if current_user
= auto_discovery_link_tag(:atom, dashboard_url(format: :atom, private_token: current_user.private_token), title: "All activity")
+- header_title "Activity", activity_dashboard_path
+= render 'dashboard/activity_head'
+
%section.activities
= render 'activities'
diff --git a/app/views/dashboard/groups/index.html.haml b/app/views/dashboard/groups/index.html.haml
index 0860fe3c761..c249f5cacec 100644
--- a/app/views/dashboard/groups/index.html.haml
+++ b/app/views/dashboard/groups/index.html.haml
@@ -1,39 +1,19 @@
- page_title "Groups"
+- header_title "Groups", dashboard_groups_path
= render 'dashboard/groups_head'
-.slead
- Group members have access to all group projects.
+.gray-content-block
- if current_user.can_create_group?
%span.pull-right.hidden-xs
- = link_to new_group_path, class: "btn btn-new btn-sm" do
+ = link_to new_group_path, class: "btn btn-new" do
%i.fa.fa-plus
New Group
-.panel.panel-default
- .panel-heading
- %strong Groups
- (#{@group_members.count})
- %ul.well-list
- - @group_members.each do |group_member|
- - group = group_member.group
- %li
- .pull-right.hidden-xs
- - if can?(current_user, :admin_group, group)
- = link_to edit_group_path(group), class: "btn-sm btn btn-grouped" do
- %i.fa.fa-cogs
- Settings
-
- = link_to leave_group_group_members_path(group), data: { confirm: leave_group_message(group.name) }, method: :delete, class: "btn-sm btn btn-grouped", title: 'Leave this group' do
- %i.fa.fa-sign-out
- Leave
-
- = image_tag group_icon(group), class: "avatar s40 avatar-tile hidden-xs"
- = link_to group, class: 'group-name' do
- %strong= group.name
-
- as
- %strong #{group_member.human_access}
+ .title Welcome to the groups!
+ Group members have access to all group projects.
- %div.light
- #{pluralize(group.projects.count, "project")}, #{pluralize(group.users.count, "user")}
+%ul.content-list
+ - @group_members.each do |group_member|
+ - group = group_member.group
+ = render 'shared/groups/group', group: group, group_member: group_member
= paginate @group_members
diff --git a/app/views/dashboard/issues.html.haml b/app/views/dashboard/issues.html.haml
index 94318d1bcf5..cd602e897b7 100644
--- a/app/views/dashboard/issues.html.haml
+++ b/app/views/dashboard/issues.html.haml
@@ -1,21 +1,17 @@
- page_title "Issues"
+- header_title "Issues", issues_dashboard_path(assignee_id: current_user.id)
= content_for :meta_tags do
- if current_user
= auto_discovery_link_tag(:atom, issues_dashboard_url(format: :atom, private_token: current_user.private_token), title: "#{current_user.name} issues")
-%h3.page-title
- Issues
-
-%p.light
- List all issues from all projects you have access to.
-%hr
.append-bottom-20
.pull-right
- if current_user
- .hidden-xs.pull-left
- = link_to issues_dashboard_url(format: :atom, private_token: current_user.private_token), class: 'btn' do
+ .hidden-xs.pull-left.prepend-top-20
+ = link_to issues_dashboard_url(format: :atom, private_token: current_user.private_token), class: '' do
%i.fa.fa-rss
= render 'shared/issuable/filter', type: :issues
+
= render 'shared/issues'
diff --git a/app/views/dashboard/merge_requests.html.haml b/app/views/dashboard/merge_requests.html.haml
index 90611d562b0..d1f332fa0d3 100644
--- a/app/views/dashboard/merge_requests.html.haml
+++ b/app/views/dashboard/merge_requests.html.haml
@@ -1,11 +1,6 @@
- page_title "Merge Requests"
-%h3.page-title
- Merge Requests
+- header_title "Merge Requests", merge_requests_dashboard_path(assignee_id: current_user.id)
-
-%p.light
- List all merge requests from all projects you have access to.
-%hr
.append-bottom-20
= render 'shared/issuable/filter', type: :merge_requests
= render 'shared/merge_requests'
diff --git a/app/views/dashboard/milestones/_milestone.html.haml b/app/views/dashboard/milestones/_milestone.html.haml
index d6f3e029a38..55080d6b3fe 100644
--- a/app/views/dashboard/milestones/_milestone.html.haml
+++ b/app/views/dashboard/milestones/_milestone.html.haml
@@ -1,20 +1,22 @@
%li{class: "milestone milestone-#{milestone.closed? ? 'closed' : 'open'}", id: dom_id(milestone.milestones.first) }
- %h4
- = link_to_gfm truncate(milestone.title, length: 100), dashboard_milestone_path(milestone.safe_title, title: milestone.title)
+ .row
+ .col-sm-6
+ %strong
+ = link_to_gfm truncate(milestone.title, length: 100), dashboard_milestone_path(milestone.safe_title, title: milestone.title)
+ .col-sm-6
+ .pull-right.light #{milestone.percent_complete}% complete
.row
.col-sm-6
= link_to issues_dashboard_path(milestone_title: milestone.title) do
= pluralize milestone.issue_count, 'Issue'
- &nbsp;
+ &middot;
= link_to merge_requests_dashboard_path(milestone_title: milestone.title) do
= pluralize milestone.merge_requests_count, 'Merge Request'
- &nbsp;
- %span.light #{milestone.percent_complete}% complete
-
.col-sm-6
= milestone_progress_bar(milestone)
- %div
- - milestone.milestones.each do |milestone|
- = link_to milestone_path(milestone) do
- %span.label.label-gray
- = milestone.project.name_with_namespace
+ .row
+ .col-sm-6
+ - milestone.milestones.each do |milestone|
+ = link_to milestone_path(milestone) do
+ %span.label.label-gray
+ = milestone.project.name_with_namespace
diff --git a/app/views/dashboard/milestones/index.html.haml b/app/views/dashboard/milestones/index.html.haml
index 9a9a5e139a4..21b25c3986e 100644
--- a/app/views/dashboard/milestones/index.html.haml
+++ b/app/views/dashboard/milestones/index.html.haml
@@ -1,21 +1,19 @@
- page_title "Milestones"
-%h3.page-title
- Milestones
- %span.pull-right #{@dashboard_milestones.count} milestones
+- header_title "Milestones", dashboard_milestones_path
-%p.light
- List all milestones from all projects you have access to.
-
-%hr
= render 'shared/milestones_filter'
+
+.gray-content-block
+ .oneline
+ List all milestones from all projects you have access to.
+
.milestones
- .panel.panel-default
- %ul.well-list
- - if @dashboard_milestones.blank?
- %li
- .nothing-here-block No milestones to show
- - else
- - @dashboard_milestones.each do |milestone|
- = render 'milestone', milestone: milestone
+ %ul.content-list
+ - if @dashboard_milestones.blank?
+ %li
+ .nothing-here-block No milestones to show
+ - else
+ - @dashboard_milestones.each do |milestone|
+ = render 'milestone', milestone: milestone
= paginate @dashboard_milestones, theme: "gitlab"
diff --git a/app/views/dashboard/projects/starred.html.haml b/app/views/dashboard/projects/starred.html.haml
index 6dcfd497ed2..2fd7a1cf16c 100644
--- a/app/views/dashboard/projects/starred.html.haml
+++ b/app/views/dashboard/projects/starred.html.haml
@@ -1,25 +1,9 @@
- page_title "Starred Projects"
+- header_title "Projects", (current_user ? root_path : explore_root_path)
= render 'dashboard/projects_head'
- if @projects.any?
- = render 'shared/show_aside'
-
- .dashboard.row
- %section.activities.col-md-7
- = render 'dashboard/activities'
- %aside.col-md-5
- .panel.panel-default.projects-list-holder
- .panel-heading.clearfix
- .input-group
- = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control'
- - if current_user.can_create_project?
- %span.input-group-btn
- = link_to new_project_path, class: 'btn btn-success' do
- New project
-
- = render 'shared/projects_list', projects: @projects,
- projects_limit: 20, stars: true, avatar: false
-
+ = render 'dashboard/projects'
- else
%h3 You don't have starred projects yet
%p.slead Visit project page and press on star icon and it will appear on this page.
diff --git a/app/views/dashboard/show.html.haml b/app/views/dashboard/show.html.haml
index 4cf2feb9aa6..1d5324e0d72 100644
--- a/app/views/dashboard/show.html.haml
+++ b/app/views/dashboard/show.html.haml
@@ -2,6 +2,7 @@
- if current_user
= auto_discovery_link_tag(:atom, dashboard_url(format: :atom, private_token: current_user.private_token), title: "All activity")
+- header_title "Projects", (current_user ? root_path : explore_root_path)
= render 'dashboard/projects_head'
- if @last_push
diff --git a/app/views/devise/sessions/_new_crowd.html.haml b/app/views/devise/sessions/_new_crowd.html.haml
new file mode 100644
index 00000000000..4974bb7f7fb
--- /dev/null
+++ b/app/views/devise/sessions/_new_crowd.html.haml
@@ -0,0 +1,9 @@
+= form_tag(user_omniauth_authorize_path("crowd"), id: 'new_crowd_user' ) do
+ = text_field_tag :username, nil, {class: "form-control top", placeholder: "Username", autofocus: "autofocus"}
+ = password_field_tag :password, nil, {class: "form-control bottom", placeholder: "Password"}
+ - if devise_mapping.rememberable?
+ .remember-me.checkbox
+ %label{for: "remember_me"}
+ = check_box_tag :remember_me, '1', false, id: 'remember_me'
+ %span Remember me
+ = button_tag "Sign in", class: "btn-save btn" \ No newline at end of file
diff --git a/app/views/devise/shared/_signin_box.html.haml b/app/views/devise/shared/_signin_box.html.haml
index bb5e479697d..41ad2c231d4 100644
--- a/app/views/devise/shared/_signin_box.html.haml
+++ b/app/views/devise/shared/_signin_box.html.haml
@@ -8,15 +8,21 @@
.login-body
- if form_based_providers.any?
%ul.nav.nav-tabs
+ - if crowd_enabled?
+ %li.active
+ = link_to "Crowd", "#tab-crowd", 'data-toggle' => 'tab'
- @ldap_servers.each_with_index do |server, i|
- %li{class: (:active if i.zero?)}
+ %li{class: (:active if i.zero? && !crowd_enabled?)}
= link_to server['label'], "#tab-#{server['provider_name']}", 'data-toggle' => 'tab'
- if signin_enabled?
%li
= link_to 'Standard', '#tab-signin', 'data-toggle' => 'tab'
.tab-content
+ - if crowd_enabled?
+ %div.tab-pane.active{id: "tab-crowd"}
+ = render 'devise/sessions/new_crowd'
- @ldap_servers.each_with_index do |server, i|
- %div.tab-pane{id: "tab-#{server['provider_name']}", class: (:active if i.zero?)}
+ %div.tab-pane{id: "tab-#{server['provider_name']}", class: (:active if i.zero? && !crowd_enabled?)}
= render 'devise/sessions/new_ldap', server: server
- if signin_enabled?
%div#tab-signin.tab-pane
diff --git a/app/views/events/_commit.html.haml b/app/views/events/_commit.html.haml
index 742b74a67c7..ad63841ccf3 100644
--- a/app/views/events/_commit.html.haml
+++ b/app/views/events/_commit.html.haml
@@ -1,5 +1,5 @@
%li.commit
.commit-row-title
= link_to truncate_sha(commit[:id]), namespace_project_commit_path(project.namespace, project, commit[:id]), class: "commit_short_id", alt: ''
- &nbsp;
+ &middot;
= gfm event_commit_title(commit[:message]), project: project
diff --git a/app/views/events/_event.html.haml b/app/views/events/_event.html.haml
index 0faab4458e9..9aacc79d686 100644
--- a/app/views/events/_event.html.haml
+++ b/app/views/events/_event.html.haml
@@ -3,8 +3,8 @@
.event-item-timestamp
#{time_ago_with_tooltip(event.created_at)}
- = cache [event, "v1"] do
- = image_tag avatar_icon(event.author_email, 24), class: "avatar s24", alt:''
+ = cache [event, "v2.1"] do
+ = image_tag avatar_icon(event.author_email, 46), class: "avatar s46", alt:''
- if event.created_project?
= render "events/event/created_project", event: event
- elsif event.push?
diff --git a/app/views/events/event/_note.html.haml b/app/views/events/event/_note.html.haml
index 07bec1697f5..830fec0b4ab 100644
--- a/app/views/events/event/_note.html.haml
+++ b/app/views/events/event/_note.html.haml
@@ -4,7 +4,7 @@
= event.action_name
= event_note_title_html(event)
at
-
+
- if event.project
= link_to_project event.project
- else
@@ -13,7 +13,6 @@
.event-body
.event-note
.md
- %i.fa.fa-comment-o.event-note-icon
= event_note(event.target.note, project: event.project)
- note = event.target
- if note.attachment.url
diff --git a/app/views/explore/groups/index.html.haml b/app/views/explore/groups/index.html.haml
index 7dcefd330a1..e8a6752de8c 100644
--- a/app/views/explore/groups/index.html.haml
+++ b/app/views/explore/groups/index.html.haml
@@ -1,14 +1,15 @@
- page_title "Groups"
+- header_title "Groups", (current_user ? dashboard_groups_path : explore_groups_path)
- if current_user
= render 'dashboard/groups_head'
-.clearfix.append-bottom-10
+.gray-content-block.clearfix
.pull-left
= form_tag explore_groups_path, method: :get, class: 'form-inline form-tiny' do |f|
= hidden_field_tag :sort, @sort
.form-group
= search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input", id: "groups_search"
.form-group
- = button_tag 'Search', class: "btn btn-primary wide"
+ = button_tag 'Search', class: "btn btn-default"
.pull-right
.dropdown.inline
@@ -30,19 +31,9 @@
= link_to explore_groups_path(sort: sort_value_oldest_updated) do
= sort_title_oldest_updated
-%ul.bordered-list
+%ul.content-list
- @groups.each do |group|
- %li
- .clearfix
- %h4
- = link_to group_path(id: group.path) do
- = group.name
- .clearfix
- %p
- = truncate group.description, length: 150
- .clearfix
- %p.light
- #{pluralize(group.members.size, 'member')}, #{pluralize(group.projects.count, 'project')}
+ = render 'shared/groups/group', group: group
- unless @groups.present?
.nothing-here-block No public groups
diff --git a/app/views/explore/projects/_filter.html.haml b/app/views/explore/projects/_filter.html.haml
index 4b91291caf4..5a3d689d1e5 100644
--- a/app/views/explore/projects/_filter.html.haml
+++ b/app/views/explore/projects/_filter.html.haml
@@ -3,7 +3,7 @@
.form-group
= search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input", id: "projects_search"
.form-group
- = button_tag 'Search', class: "btn btn-primary wide"
+ = button_tag 'Search', class: "btn btn-success"
.pull-right.hidden-sm.hidden-xs
- if current_user
diff --git a/app/views/explore/projects/_projects.html.haml b/app/views/explore/projects/_projects.html.haml
index 22cc541115c..669079e9521 100644
--- a/app/views/explore/projects/_projects.html.haml
+++ b/app/views/explore/projects/_projects.html.haml
@@ -1,6 +1,6 @@
-%ul.projects-list.bordered-list.my-projects.public-projects
- - projects.each do |project|
- %li.project-row
- = render partial: 'shared/project', locals: { project: project, avatar: true, stars: true }
-- unless projects.present?
- .nothing-here-block No such projects
+- if projects.any?
+ .public-projects
+ = render 'shared/projects/list', projects: projects
+- else
+ .nothing-here-block
+ No such projects
diff --git a/app/views/explore/projects/index.html.haml b/app/views/explore/projects/index.html.haml
index 0cfdf5cfd15..9df5b3830a8 100644
--- a/app/views/explore/projects/index.html.haml
+++ b/app/views/explore/projects/index.html.haml
@@ -1,8 +1,7 @@
- page_title "Projects"
- if current_user
= render 'dashboard/projects_head'
-.clearfix
+.gray-content-block.clearfix
= render 'filter'
-%br
= render 'projects', projects: @projects
= paginate @projects, theme: "gitlab"
diff --git a/app/views/explore/projects/starred.html.haml b/app/views/explore/projects/starred.html.haml
index 4a9fcae4bed..a9df32f3d7d 100644
--- a/app/views/explore/projects/starred.html.haml
+++ b/app/views/explore/projects/starred.html.haml
@@ -1,11 +1,13 @@
- page_title "Starred Projects"
- if current_user
= render 'dashboard/projects_head'
+
.explore-trending-block
- .lead
- %i.fa.fa-star
- See most starred projects
+ .gray-content-block
.pull-right
= render 'explore/projects/dropdown'
+ .oneline
+ %i.fa.fa-star
+ See most starred projects
= render 'projects', projects: @starred_projects
= paginate @starred_projects, theme: 'gitlab'
diff --git a/app/views/explore/projects/trending.html.haml b/app/views/explore/projects/trending.html.haml
index 4c7e7d44733..c1ef06f6cdb 100644
--- a/app/views/explore/projects/trending.html.haml
+++ b/app/views/explore/projects/trending.html.haml
@@ -1,16 +1,18 @@
- page_title "Trending Projects"
- if current_user
= render 'dashboard/projects_head'
-.explore-title
- %h3
- Explore GitLab
- %p.lead
- Discover projects and groups. Share your projects with others
-%hr
+- else
+ .explore-title
+ %h3
+ Explore GitLab
+ %p.lead
+ Discover projects and groups. Share your projects with others
+ %br
.explore-trending-block
- .lead
- %i.fa.fa-comments-o
- See most discussed projects for last month
+ .gray-content-block
.pull-right
= render 'explore/projects/dropdown'
+ .oneline
+ %i.fa.fa-comments-o
+ See most discussed projects for last month
= render 'projects', projects: @trending_projects
diff --git a/app/views/groups/_projects.html.haml b/app/views/groups/_projects.html.haml
index 2ae51a1c8c0..9ac56b1e5fe 100644
--- a/app/views/groups/_projects.html.haml
+++ b/app/views/groups/_projects.html.haml
@@ -7,4 +7,4 @@
= link_to new_project_path(namespace_id: @group.id), class: 'btn btn-success' do
New project
- = render 'shared/projects_list', projects: @projects, projects_limit: 20
+ = render 'shared/projects/list', projects: @projects, projects_limit: 20, stars: false
diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml
index dba395cc8fa..3a6d07ebddf 100644
--- a/app/views/groups/group_members/index.html.haml
+++ b/app/views/groups/group_members/index.html.haml
@@ -1,15 +1,13 @@
- page_title "Members"
+- header_title group_title(@group, "Members", group_group_members_path(@group))
- show_roles = should_user_see_group_roles?(current_user, @group)
-%h3.page-title
- Group members
- if show_roles
%p.light
Members of group have access to all group projects.
Read more about permissions
%strong= link_to "here", help_page_path("permissions", "permissions"), class: "vlink"
-%hr
.clearfix.js-toggle-container
= form_tag group_group_members_path(@group), method: :get, class: 'form-inline member-search-form' do
diff --git a/app/views/groups/issues.html.haml b/app/views/groups/issues.html.haml
index f0d90782556..08d97e418a3 100644
--- a/app/views/groups/issues.html.haml
+++ b/app/views/groups/issues.html.haml
@@ -1,25 +1,24 @@
- page_title "Issues"
+- header_title group_title(@group, "Issues", issues_group_path(@group))
= content_for :meta_tags do
- if current_user
= auto_discovery_link_tag(:atom, issues_group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} issues")
-%h3.page-title
- Issues
-%p.light
- Only issues from
- %strong #{@group.name}
- group are listed here.
- - if current_user
- To see all issues you should visit #{link_to 'dashboard', issues_dashboard_path} page.
-%hr
-.append-bottom-20
+= render 'shared/issuable/filter', type: :issues
+.gray-content-block.second-block
.pull-right
- if current_user
.hidden-xs.pull-left
- = link_to issues_group_url(@group, format: :atom, private_token: current_user.private_token), class: 'btn' do
+ = link_to issues_group_url(@group, format: :atom, private_token: current_user.private_token) do
%i.fa.fa-rss
+ %div
+ Only issues from
+ %strong #{@group.name}
+ group are listed here.
+ - if current_user
+ To see all issues you should visit #{link_to 'dashboard', issues_dashboard_path} page.
- = render 'shared/issuable/filter', type: :issues
-= render 'shared/issues'
+.prepend-top-default
+ = render 'shared/issues'
diff --git a/app/views/groups/merge_requests.html.haml b/app/views/groups/merge_requests.html.haml
index ca85a158707..425ad8331bf 100644
--- a/app/views/groups/merge_requests.html.haml
+++ b/app/views/groups/merge_requests.html.haml
@@ -1,14 +1,13 @@
- page_title "Merge Requests"
-%h3.page-title
- Merge Requests
+- header_title group_title(@group, "Merge Requests", merge_requests_group_path(@group))
-%p.light
- Only merge requests from
- %strong #{@group.name}
- group are listed here.
- - if current_user
- To see all merge requests you should visit #{link_to 'dashboard', merge_requests_dashboard_path} page.
-%hr
-.append-bottom-20
- = render 'shared/issuable/filter', type: :merge_requests
-= render 'shared/merge_requests'
+= render 'shared/issuable/filter', type: :merge_requests
+.gray-content-block.second-block
+ %div
+ Only merge requests from
+ %strong #{@group.name}
+ group are listed here.
+ - if current_user
+ To see all merge requests you should visit #{link_to 'dashboard', merge_requests_dashboard_path} page.
+.prepend-top-default
+ = render 'shared/merge_requests'
diff --git a/app/views/groups/milestones/_milestone.html.haml b/app/views/groups/milestones/_milestone.html.haml
index ba30e6e07c6..41dffdd2fb8 100644
--- a/app/views/groups/milestones/_milestone.html.haml
+++ b/app/views/groups/milestones/_milestone.html.haml
@@ -1,25 +1,29 @@
%li{class: "milestone milestone-#{milestone.closed? ? 'closed' : 'open'}", id: dom_id(milestone.milestones.first) }
- .pull-right
- - if can?(current_user, :admin_group, @group)
- - if milestone.closed?
- = link_to 'Reopen Milestone', group_milestone_path(@group, milestone.safe_title, title: milestone.title, milestone: {state_event: :activate }), method: :put, class: "btn btn-sm btn-grouped btn-reopen"
- - else
- = link_to 'Close Milestone', group_milestone_path(@group, milestone.safe_title, title: milestone.title, milestone: {state_event: :close }), method: :put, class: "btn btn-sm btn-close"
- %h4
- = link_to_gfm truncate(milestone.title, length: 100), group_milestone_path(@group, milestone.safe_title, title: milestone.title)
+ .row
+ .col-sm-6
+ %strong
+ = link_to_gfm truncate(milestone.title, length: 100), group_milestone_path(@group, milestone.safe_title, title: milestone.title)
+ .col-sm-6
+ .pull-right.light #{milestone.percent_complete}% complete
.row
.col-sm-6
= link_to issues_group_path(@group, milestone_title: milestone.title) do
= pluralize milestone.issue_count, 'Issue'
- &nbsp;
+ &middot;
= link_to merge_requests_group_path(@group, milestone_title: milestone.title) do
= pluralize milestone.merge_requests_count, 'Merge Request'
- &nbsp;
- %span.light #{milestone.percent_complete}% complete
.col-sm-6
= milestone_progress_bar(milestone)
- %div
- - milestone.milestones.each do |milestone|
- = link_to milestone_path(milestone) do
- %span.label.label-gray
- = milestone.project.name
+ .row
+ .col-sm-6
+ %div
+ - milestone.milestones.each do |milestone|
+ = link_to milestone_path(milestone) do
+ %span.label.label-gray
+ = milestone.project.name
+ .col-sm-6
+ - if can?(current_user, :admin_group, @group)
+ - if milestone.closed?
+ = link_to 'Reopen Milestone', group_milestone_path(@group, milestone.safe_title, title: milestone.title, milestone: {state_event: :activate }), method: :put, class: "btn btn-xs btn-grouped btn-reopen"
+ - else
+ = link_to 'Close Milestone', group_milestone_path(@group, milestone.safe_title, title: milestone.title, milestone: {state_event: :close }), method: :put, class: "btn btn-xs btn-close"
diff --git a/app/views/groups/milestones/index.html.haml b/app/views/groups/milestones/index.html.haml
index 385222fa5b7..2bbcad5fdfb 100644
--- a/app/views/groups/milestones/index.html.haml
+++ b/app/views/groups/milestones/index.html.haml
@@ -1,23 +1,17 @@
- page_title "Milestones"
-%h3.page-title
- Milestones
- %span.pull-right #{@group_milestones.count} milestones
+- header_title group_title(@group, "Milestones", group_milestones_path(@group))
-%p.light
+= render 'shared/milestones_filter'
+.gray-content-block
Only milestones from
%strong #{@group.name}
group are listed here.
-
-%hr
-
-= render 'shared/milestones_filter'
.milestones
- .panel.panel-default
- %ul.well-list
- - if @group_milestones.blank?
- %li
- .nothing-here-block No milestones to show
- - else
- - @group_milestones.each do |milestone|
- = render 'milestone', milestone: milestone
+ %ul.content-list
+ - if @group_milestones.blank?
+ %li
+ .nothing-here-block No milestones to show
+ - else
+ - @group_milestones.each do |milestone|
+ = render 'milestone', milestone: milestone
= paginate @group_milestones, theme: "gitlab"
diff --git a/app/views/import/fogbugz/new.html.haml b/app/views/import/fogbugz/new.html.haml
new file mode 100644
index 00000000000..e1bb88ca4ed
--- /dev/null
+++ b/app/views/import/fogbugz/new.html.haml
@@ -0,0 +1,25 @@
+- page_title "FogBugz Import"
+%h3.page-title
+ %i.fa.fa-bug
+ Import projects from FogBugz
+%hr
+
+= form_tag callback_import_fogbugz_path, class: 'form-horizontal' do
+ %p
+ To get started you enter your FogBugz URL and login information below.
+ In the next steps, you'll be able to map users and select the projects
+ you want to import.
+ .form-group
+ = label_tag :uri, 'FogBugz URL', class: 'control-label'
+ .col-sm-4
+ = text_field_tag :uri, nil, placeholder: 'https://mycompany.fogbugz.com', class: 'form-control'
+ .form-group
+ = label_tag :email, 'FogBugz Email', class: 'control-label'
+ .col-sm-4
+ = text_field_tag :email, nil, class: 'form-control'
+ .form-group
+ = label_tag :password, 'FogBugz Password', class: 'control-label'
+ .col-sm-4
+ = password_field_tag :password, nil, class: 'form-control'
+ .form-actions
+ = submit_tag 'Continue to the next step', class: 'btn btn-create'
diff --git a/app/views/import/fogbugz/new_user_map.html.haml b/app/views/import/fogbugz/new_user_map.html.haml
new file mode 100644
index 00000000000..25cebfb3665
--- /dev/null
+++ b/app/views/import/fogbugz/new_user_map.html.haml
@@ -0,0 +1,49 @@
+- page_title 'User map', 'FogBugz import'
+%h3.page-title
+ %i.fa.fa-bug
+ Import projects from FogBugz
+%hr
+
+= form_tag create_user_map_import_fogbugz_path, class: 'form-horizontal' do
+ %p
+ Customize how FogBugz email addresses and usernames are imported into GitLab.
+ In the next step, you'll be able to select the projects you want to import.
+ %p
+ The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames wil be imported into GitLab. You can change this by populating the table below.
+ %ul
+ %li
+ %strong Default: Map a FogBugz account ID to a full name
+ %p
+ An empty GitLab User field will add the FogBugz user's full name
+ (e.g. "By John Smith") in the description of all issues and comments.
+ It will also associate and/or assign these issues and comments with
+ the project creator.
+ %li
+ %strong Map a FogBugz account ID to a GitLab user
+ %p
+ Selecting a GitLab user will add a link to the GitLab user in the descriptions
+ of issues and comments (e.g. "By <a href="#">@johnsmith</a>"). It will also
+ associate and/or assign these issues and comments with the selected user.
+
+ %table.table
+ %thead
+ %tr
+ %th ID
+ %th Name
+ %th Email
+ %th GitLab User
+ %tbody
+ - @user_map.each do |id, user|
+ %tr
+ %td= id
+ %td= text_field_tag "users[#{id}][name]", user[:name], class: 'form-control'
+ %td= text_field_tag "users[#{id}][email]", user[:email], class: 'form-control'
+ %td
+ = users_select_tag("users[#{id}][gitlab_user]", class: 'custom-form-control',
+ scope: :all, email_user: true, selected: user[:gitlab_user])
+
+ .form-actions
+ = submit_tag 'Continue to the next step', class: 'btn btn-create'
+
+:coffeescript
+ new UsersSelect()
diff --git a/app/views/import/fogbugz/status.html.haml b/app/views/import/fogbugz/status.html.haml
new file mode 100644
index 00000000000..f179ece402d
--- /dev/null
+++ b/app/views/import/fogbugz/status.html.haml
@@ -0,0 +1,51 @@
+- page_title "FogBugz import"
+%h3.page-title
+ %i.fa.fa-bug
+ Import projects from FogBugz
+
+- if @repos.any?
+ %p.light
+ Select projects you want to import.
+ %p.light
+ Optionally, you can
+ = link_to 'customize', new_user_map_import_fogbugz_path
+ how FogBugz email addresses and usernames are imported into GitLab.
+ %hr
+ %p
+ = button_tag 'Import all projects', class: 'btn btn-success js-import-all'
+
+%table.table.import-jobs
+ %thead
+ %tr
+ %th From FogBugz
+ %th To GitLab
+ %th Status
+ %tbody
+ - @already_added_projects.each do |project|
+ %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"}
+ %td
+ = project.import_source
+ %td
+ %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
+ %td.job-status
+ - if project.import_status == 'finished'
+ %span
+ %i.fa.fa-check
+ done
+ - elsif project.import_status == 'started'
+ %i.fa.fa-spinner.fa-spin
+ started
+ - else
+ = project.human_import_status_name
+
+ - @repos.each do |repo|
+ %tr{id: "repo_#{repo.id}"}
+ %td
+ = repo.name
+ %td.import-target
+ = "#{current_user.username}/#{repo.name}"
+ %td.import-actions.job-status
+ = button_tag "Import", class: "btn js-add-to-import"
+
+:coffeescript
+ new ImporterStatus("#{jobs_import_fogbugz_path}", "#{import_fogbugz_path}")
diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml
index 397649dacf8..c3b137e3ddf 100644
--- a/app/views/layouts/_head.html.haml
+++ b/app/views/layouts/_head.html.haml
@@ -3,6 +3,7 @@
%meta{charset: "utf-8"}
%meta{'http-equiv' => 'X-UA-Compatible', content: 'IE=edge'}
%meta{content: "GitLab Community Edition", name: "description"}
+ %meta{name: 'referrer', content: 'origin'}
%title= page_title
diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml
index 0104d7198df..c1746676ae2 100644
--- a/app/views/layouts/_page.html.haml
+++ b/app/views/layouts/_page.html.haml
@@ -14,12 +14,12 @@
= render partial: 'layouts/collapse_button'
- if current_user
= link_to current_user, class: 'sidebar-user' do
- = image_tag avatar_icon(current_user.email, 60), alt: 'User activity', class: 'avatar avatar s32'
+ = image_tag avatar_icon(current_user.email, 60), alt: 'User activity', class: 'avatar avatar s36'
.username
= current_user.username
.content-wrapper
+ = render "layouts/flash"
%div{ class: fluid_layout ? "container-fluid" : "container-fluid container-limited" }
.content
- = render "layouts/flash"
.clearfix
= yield
diff --git a/app/views/layouts/dashboard.html.haml b/app/views/layouts/dashboard.html.haml
index c72eca10bf4..fad7de69432 100644
--- a/app/views/layouts/dashboard.html.haml
+++ b/app/views/layouts/dashboard.html.haml
@@ -1,5 +1,6 @@
- page_title "Dashboard"
-- header_title "Dashboard", root_path
+- unless @header_title
+ - header_title "Dashboard", root_path
- sidebar "dashboard"
= render template: "layouts/application"
diff --git a/app/views/layouts/explore.html.haml b/app/views/layouts/explore.html.haml
index 17fee9c510d..9098554e6f0 100644
--- a/app/views/layouts/explore.html.haml
+++ b/app/views/layouts/explore.html.haml
@@ -1,6 +1,7 @@
- page_title "Explore"
- if current_user
- - header_title "Dashboard", root_path
+ - unless @header_title
+ - header_title "Projects", (current_user ? root_path : explore_root_path)
- else
- header_title "Explore GitLab", explore_root_path
- sidebar "dashboard"
diff --git a/app/views/layouts/group.html.haml b/app/views/layouts/group.html.haml
index db7dbf9bfe3..4f00d01d4cd 100644
--- a/app/views/layouts/group.html.haml
+++ b/app/views/layouts/group.html.haml
@@ -1,5 +1,6 @@
- page_title @group.name
-- header_title @group.name, group_path(@group)
+- unless @header_title
+ - header_title @group.name, group_path(@group)
- sidebar "group" unless sidebar
= render template: "layouts/application"
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
index 0b630b55c70..3892f71c0e3 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -1,5 +1,5 @@
%header.navbar.navbar-fixed-top.navbar-gitlab{ class: nav_header_class }
- %div{ class: fluid_layout ? "container-fluid" : "container-fluid container-limited" }
+ %div{ class: fluid_layout ? "container-fluid" : "container-fluid" }
.header-content
%button.navbar-toggle{type: 'button'}
%span.sr-only Toggle navigation
@@ -17,7 +17,7 @@
= link_to admin_root_path, title: 'Admin area', data: {toggle: 'tooltip', placement: 'bottom'} do
= icon('wrench fw')
- if current_user.can_create_project?
- %li.hidden-xs
+ %li
= link_to new_project_path, title: 'New project', data: {toggle: 'tooltip', placement: 'bottom'} do
= icon('plus fw')
%li
diff --git a/app/views/layouts/header/_public.html.haml b/app/views/layouts/header/_public.html.haml
index af4b9ba58f6..a6a26518a0e 100644
--- a/app/views/layouts/header/_public.html.haml
+++ b/app/views/layouts/header/_public.html.haml
@@ -1,9 +1,9 @@
%header.navbar.navbar-fixed-top.navbar-gitlab{ class: nav_header_class }
- %div{ class: fluid_layout ? "container-fluid" : "container-fluid container-limited" }
+ %div{ class: fluid_layout ? "container-fluid" : "container-fluid" }
.header-content
- unless current_controller?('sessions')
.pull-right
- = link_to "Sign in", new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in btn-success btn-sm'
+ = link_to "Sign in", new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in btn-success'
%h1.title= title
diff --git a/app/views/layouts/nav/_admin.html.haml b/app/views/layouts/nav/_admin.html.haml
index 2065be3828a..3fe0127041e 100644
--- a/app/views/layouts/nav/_admin.html.haml
+++ b/app/views/layouts/nav/_admin.html.haml
@@ -57,6 +57,12 @@
%span
Service Templates
+ = nav_link(controller: :labels) do
+ = link_to admin_labels_path, title: 'Labels', data: {placement: 'right'} do
+ = icon('tags fw')
+ %span
+ Labels
+
= nav_link(controller: :abuse_reports) do
= link_to admin_abuse_reports_path, title: "Abuse reports" do
= icon('exclamation-circle fw')
diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml
index d620c022273..0cf1c3d5d27 100644
--- a/app/views/layouts/nav/_dashboard.html.haml
+++ b/app/views/layouts/nav/_dashboard.html.haml
@@ -42,7 +42,7 @@
= link_to profile_path, title: 'Profile settings', data: {toggle: 'tooltip', placement: 'bottom'} do
= icon('user fw')
%span
- Profile
+ Profile Settings
= nav_link(controller: :help) do
= link_to help_path, title: 'Help', data: {placement: 'right'} do
= icon('question-circle fw')
diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml
index d17d1c5fbd4..5e7b902622b 100644
--- a/app/views/layouts/nav/_project.html.haml
+++ b/app/views/layouts/nav/_project.html.haml
@@ -100,7 +100,7 @@
- if project_nav_tab? :snippets
= nav_link(controller: :snippets) do
= link_to namespace_project_snippets_path(@project.namespace, @project), title: 'Snippets', class: 'shortcuts-snippets', data: {placement: 'right'} do
- = icon('file-text-o fw')
+ = icon('clipboard fw')
%span
Snippets
diff --git a/app/views/layouts/profile.html.haml b/app/views/layouts/profile.html.haml
index 3193206fe12..77d2ccbf762 100644
--- a/app/views/layouts/profile.html.haml
+++ b/app/views/layouts/profile.html.haml
@@ -1,5 +1,5 @@
-- page_title "Settings"
-- header_title "Settings", profile_path
+- page_title "Profile Settings"
+- header_title "Profile Settings", profile_path
- sidebar "profile"
= render template: "layouts/application"
diff --git a/app/views/layouts/snippets.html.haml b/app/views/layouts/snippets.html.haml
index b77fe09fc2a..d9c90d4fcef 100644
--- a/app/views/layouts/snippets.html.haml
+++ b/app/views/layouts/snippets.html.haml
@@ -1,6 +1,6 @@
- page_title 'Snippets'
- if current_user
- - header_title "Dashboard", root_path
+ - header_title "Snippets", user_snippets_path(current_user)
- else
- header_title 'Snippets', snippets_path
- sidebar "dashboard"
diff --git a/app/views/profiles/notifications/show.html.haml b/app/views/profiles/notifications/show.html.haml
index 9480a19f5b2..db7fa2eabe3 100644
--- a/app/views/profiles/notifications/show.html.haml
+++ b/app/views/profiles/notifications/show.html.haml
@@ -48,7 +48,7 @@
= f.radio_button :notification_level, Notification::N_WATCH
.level-title
Watch
- %p You will receive all notifications from projects in which you participate
+ %p You will receive notifications for any activity
.form-actions
= f.submit 'Save changes', class: "btn btn-create"
diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml
index 1134317ee06..aa0361a0a1b 100644
--- a/app/views/profiles/preferences/show.html.haml
+++ b/app/views/profiles/preferences/show.html.haml
@@ -22,11 +22,11 @@
.panel-heading
Syntax highlighting theme
.panel-body
- - color_schemes.each do |color_scheme_id, color_scheme|
+ - Gitlab::ColorSchemes.each do |scheme|
= label_tag do
- .preview= image_tag "#{color_scheme}-scheme-preview.png"
- = f.radio_button :color_scheme_id, color_scheme_id
- = color_scheme.tr('-_', ' ').titleize
+ .preview= image_tag "#{scheme.css_class}-scheme-preview.png"
+ = f.radio_button :color_scheme_id, scheme.id
+ = scheme.name
.panel.panel-default
.panel-heading
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index 9fdeddfcc7a..c519e52e596 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -100,11 +100,6 @@
%hr
= link_to 'Remove avatar', profile_avatar_path, data: { confirm: "Avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-sm remove-avatar"
- - if @user.public_profile?
- .alert.alert-info
- %h4 Public profile
- %p Your profile is publicly visible because you joined public project(s)
-
.row
.col-md-7
diff --git a/app/views/projects/_activity.html.haml b/app/views/projects/_activity.html.haml
index ee02b7f6a6c..1261f6254d7 100644
--- a/app/views/projects/_activity.html.haml
+++ b/app/views/projects/_activity.html.haml
@@ -1,5 +1,5 @@
= render 'projects/last_push'
-.hidden-xs
+.gray-content-block.activity-filter-block
- if current_user
%ul.nav.nav-pills.event_filter.pull-right
%li
@@ -7,7 +7,6 @@
%i.fa.fa-rss
= render 'shared/event_filter'
- %hr
.content_list{:"data-href" => activity_project_path(@project)}
= spinner
diff --git a/app/views/projects/_md_preview.html.haml b/app/views/projects/_md_preview.html.haml
index b7bca6dae09..507757f6a2b 100644
--- a/app/views/projects/_md_preview.html.haml
+++ b/app/views/projects/_md_preview.html.haml
@@ -1,6 +1,6 @@
.md-area
.md-header.clearfix
- %ul.nav.nav-tabs
+ %ul.center-top-menu
%li.active
= link_to '#md-write-holder', class: 'js-md-write-button', tabindex: '-1' do
Write
@@ -14,7 +14,7 @@
You are about to add
%strong
%span.js-referenced-users-count 0
- people
+ people
to the discussion. Proceed with caution.
%div
diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml
index a693c4b282f..cc0ec9483d2 100644
--- a/app/views/projects/branches/_branch.html.haml
+++ b/app/views/projects/branches/_branch.html.haml
@@ -1,20 +1,20 @@
- commit = @repository.commit(branch.target)
%li(class="js-branch-#{branch.name}")
- %h4
+ %div
= link_to namespace_project_tree_path(@project.namespace, @project, branch.name) do
%strong.str-truncated= branch.name
+ &nbsp;
- if branch.name == @repository.root_ref
- %span.label.label-info default
+ %span.label.label-primary default
- elsif @repository.merged_to_root_ref? branch.name
- %span.label.label-primary.has_tooltip(title="Merged into #{@repository.root_ref}")
- %i.fa.fa-check
+ %span.label.label-info.has_tooltip(title="Merged into #{@repository.root_ref}")
merged
- if @project.protected_branch? branch.name
%span.label.label-success
%i.fa.fa-lock
protected
- .pull-right
+ .controls.hidden-xs
- if create_mr_button?(@repository.root_ref, branch.name)
= link_to create_mr_path(@repository.root_ref, branch.name), class: 'btn btn-grouped btn-xs' do
= icon('plus')
@@ -30,8 +30,7 @@
= icon("trash-o")
- if commit
- %ul.list-unstyled
- = render 'projects/commits/inline_commit', commit: commit, project: @project
+ = render 'projects/branches/commit', commit: commit, project: @project
- else
%p
Cant find HEAD commit for this branch
diff --git a/app/views/projects/branches/_commit.html.haml b/app/views/projects/branches/_commit.html.haml
new file mode 100644
index 00000000000..68326e65d85
--- /dev/null
+++ b/app/views/projects/branches/_commit.html.haml
@@ -0,0 +1,7 @@
+.branch-commit.light
+ = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id"
+ &middot;
+ %span.str-truncated
+ = link_to_gfm commit.title, namespace_project_commit_path(project.namespace, project, commit.id), class: "commit-row-message"
+ &middot;
+ #{time_ago_with_tooltip(commit.committed_date)}
diff --git a/app/views/projects/branches/index.html.haml b/app/views/projects/branches/index.html.haml
index 80acc937908..6e2dc2d2710 100644
--- a/app/views/projects/branches/index.html.haml
+++ b/app/views/projects/branches/index.html.haml
@@ -1,7 +1,6 @@
- page_title "Branches"
= render "projects/commits/head"
-%h3.page-title
- Branches
+.gray-content-block
.pull-right
- if can? current_user, :push_code, @project
= link_to new_namespace_project_branch_path(@project.namespace, @project), class: 'btn btn-create' do
@@ -24,9 +23,10 @@
= sort_title_recently_updated
= link_to namespace_project_branches_path(sort: 'last_updated') do
= sort_title_oldest_updated
-%hr
+ .oneline
+ Protected branches can be managed in project settings
- unless @branches.empty?
- %ul.bordered-list.top-list.all-branches
+ %ul.content-list.all-branches
- @branches.each do |branch|
= render "projects/branches/branch", branch: branch
= paginate @branches, theme: 'gitlab'
diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml
index 3f645b81397..2ac79e87b4a 100644
--- a/app/views/projects/commit/_commit_box.html.haml
+++ b/app/views/projects/commit/_commit_box.html.haml
@@ -41,7 +41,7 @@
.commit-info-row.branches
%i.fa.fa-spinner.fa-spin
-.commit-box
+.commit-box.gray-content-block.middle-block
%h3.commit-title
= gfm escape_once(@commit.title)
- if @commit.description.present?
diff --git a/app/views/projects/commits/_head.html.haml b/app/views/projects/commits/_head.html.haml
index e3d8cd0fdd5..50c0fd6803d 100644
--- a/app/views/projects/commits/_head.html.haml
+++ b/app/views/projects/commits/_head.html.haml
@@ -1,22 +1,18 @@
-%ul.nav.nav-tabs
+%ul.center-top-menu
= nav_link(controller: [:commit, :commits]) do
= link_to namespace_project_commits_path(@project.namespace, @project, @ref || @repository.root_ref) do
- = icon("history")
Commits
%span.badge= number_with_delimiter(@repository.commit_count)
= nav_link(controller: :compare) do
= link_to namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: @ref || @repository.root_ref) do
- = icon("exchange")
Compare
= nav_link(html_options: {class: branches_tab_class}) do
= link_to namespace_project_branches_path(@project.namespace, @project) do
- = icon("code-fork")
Branches
%span.badge.js-totalbranch-count= @repository.branches.size
= nav_link(controller: :tags) do
= link_to namespace_project_tags_path(@project.namespace, @project) do
- = icon("tags")
Tags
%span.badge.js-totaltags-count= @repository.tags.length
diff --git a/app/views/projects/commits/show.html.haml b/app/views/projects/commits/show.html.haml
index 55054a31977..a01a99458a0 100644
--- a/app/views/projects/commits/show.html.haml
+++ b/app/views/projects/commits/show.html.haml
@@ -5,22 +5,23 @@
= render "head"
-.tree-ref-holder
- = render 'shared/ref_switcher', destination: 'commits'
+.gray-content-block
+ .tree-ref-holder
+ = render 'shared/ref_switcher', destination: 'commits'
-.commits-feed-holder.hidden-xs.hidden-sm
- - if create_mr_button?(@repository.root_ref, @ref)
- = link_to create_mr_path(@repository.root_ref, @ref), class: 'btn btn-success' do
- = icon('plus')
- Create Merge Request
+ .commits-feed-holder.hidden-xs.hidden-sm
+ - if create_mr_button?(@repository.root_ref, @ref)
+ = link_to create_mr_path(@repository.root_ref, @ref), class: 'btn btn-success' do
+ = icon('plus')
+ Create Merge Request
- - if current_user && current_user.private_token
- = link_to namespace_project_commits_path(@project.namespace, @project, @ref, {format: :atom, private_token: current_user.private_token}), title: "Commits Feed", class: 'prepend-left-10 btn' do
- = icon("rss")
+ - if current_user && current_user.private_token
+ = link_to namespace_project_commits_path(@project.namespace, @project, @ref, {format: :atom, private_token: current_user.private_token}), title: "Commits Feed", class: 'prepend-left-10 btn' do
+ = icon("rss")
-%ul.breadcrumb.repo-breadcrumb
- = commits_breadcrumbs
+ %ul.breadcrumb.repo-breadcrumb
+ = commits_breadcrumbs
%div{id: dom_id(@project)}
#commits-list= render "commits", project: @project
diff --git a/app/views/projects/compare/_form.html.haml b/app/views/projects/compare/_form.html.haml
index 3019893d12c..efc25eda26b 100644
--- a/app/views/projects/compare/_form.html.haml
+++ b/app/views/projects/compare/_form.html.haml
@@ -1,5 +1,5 @@
= form_tag namespace_project_compare_index_path(@project.namespace, @project), method: :post, class: 'form-inline js-requires-input' do
- .clearfix.append-bottom-20
+ .clearfix
- if params[:to] && params[:from]
= link_to 'switch', {from: params[:to], to: params[:from]}, {class: 'commits-compare-switch has_tooltip', title: 'Switch base of comparison'}
.form-group
diff --git a/app/views/projects/compare/index.html.haml b/app/views/projects/compare/index.html.haml
index d1e579a2ede..43d00726c48 100644
--- a/app/views/projects/compare/index.html.haml
+++ b/app/views/projects/compare/index.html.haml
@@ -1,9 +1,7 @@
- page_title "Compare"
= render "projects/commits/head"
-%h3.page-title
- Compare View
-%p.slead
+.gray-content-block
Compare branches, tags or commit ranges.
%br
Fill input field with commit id like
@@ -14,4 +12,5 @@
%br
Changes are shown <b>from</b> the version in the first field <b>to</b> the version in the second field.
-= render "form"
+.prepend-top-20
+ = render "form"
diff --git a/app/views/projects/compare/show.html.haml b/app/views/projects/compare/show.html.haml
index 3670dd5c13b..8800ffdf482 100644
--- a/app/views/projects/compare/show.html.haml
+++ b/app/views/projects/compare/show.html.haml
@@ -1,16 +1,16 @@
- page_title "#{params[:from]}...#{params[:to]}"
= render "projects/commits/head"
-%h3.page-title
- Compare View
-= render "form"
+.gray-content-block
+ = render "form"
- if @commits.present?
- = render "projects/commits/commit_list"
- = render "projects/diffs/diffs", diffs: @diffs, project: @project
+ .prepend-top-20
+ = render "projects/commits/commit_list"
+ = render "projects/diffs/diffs", diffs: @diffs, project: @project
- else
- .light-well
+ .light-well.prepend-top-20
.center
%h4
There isn't anything to compare.
diff --git a/app/views/projects/diffs/_diffs.html.haml b/app/views/projects/diffs/_diffs.html.haml
index 30943f49bba..2f24dc7c909 100644
--- a/app/views/projects/diffs/_diffs.html.haml
+++ b/app/views/projects/diffs/_diffs.html.haml
@@ -1,8 +1,8 @@
- if params[:view] == 'parallel'
- fluid_layout true
-.prepend-top-20.append-bottom-20
- .pull-right
+.gray-content-block.second-block
+ .inline-parallel-buttons
.btn-group
= inline_diff_btn
= parallel_diff_btn
diff --git a/app/views/projects/diffs/_stats.html.haml b/app/views/projects/diffs/_stats.html.haml
index 1625930615a..c4d7f26430b 100644
--- a/app/views/projects/diffs/_stats.html.haml
+++ b/app/views/projects/diffs/_stats.html.haml
@@ -10,7 +10,7 @@
and
%strong.cred #{@commit.stats.deletions} deletions
.file-stats.js-toggle-content.hide
- %ul.bordered-list
+ %ul
- diffs.each_with_index do |diff, i|
%li
- if diff.deleted_file
diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml
index e577d35d560..798f1c47da5 100644
--- a/app/views/projects/empty.html.haml
+++ b/app/views/projects/empty.html.haml
@@ -22,15 +22,15 @@
%h5 Git global setup
%pre.light-well
:preserve
- git config --global user.name "#{git_user_name}"
- git config --global user.email "#{git_user_email}"
+ git config --global user.name "#{h git_user_name}"
+ git config --global user.email "#{h git_user_email}"
%fieldset
%h5 Create a new repository
%pre.light-well
:preserve
git clone #{ content_tag(:span, default_url_to_repo, class: 'clone')}
- cd #{@project.path}
+ cd #{h @project.path}
touch README.md
git add README.md
git commit -m "add README"
diff --git a/app/views/projects/issues/_discussion.html.haml b/app/views/projects/issues/_discussion.html.haml
index f61ae957208..d4a98eca473 100644
--- a/app/views/projects/issues/_discussion.html.haml
+++ b/app/views/projects/issues/_discussion.html.haml
@@ -7,21 +7,24 @@
= render 'shared/show_aside'
+.gray-content-block.second-block
+ .row
+ .col-md-9
+ .votes-holder.pull-right
+ #votes= render 'votes/votes_block', votable: @issue
+ .participants
+ %span= pluralize(@participants.count, 'participant')
+ - @participants.each do |participant|
+ = link_to_member(@project, participant, name: false, size: 24)
+ .col-md-3
+ %span.slead.has_tooltip{title: 'Cross-project reference'}
+ = cross_project_reference(@project, @issue)
+
.row
%section.col-md-9
- .votes-holder.pull-right
- #votes= render 'votes/votes_block', votable: @issue
- .participants
- %span= pluralize(@participants.count, 'participant')
- - @participants.each do |participant|
- = link_to_member(@project, participant, name: false, size: 24)
.voting_notes#notes= render 'projects/notes/notes_with_form'
%aside.col-md-3
.issuable-affix
- .clearfix
- %span.slead.has_tooltip{title: 'Cross-project reference'}
- = cross_project_reference(@project, @issue)
- %hr
.context
= render 'shared/issuable/context', issuable: @issue
diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml
index b6910c8f796..55ce912829d 100644
--- a/app/views/projects/issues/_issue.html.haml
+++ b/app/views/projects/issues/_issue.html.haml
@@ -41,4 +41,4 @@
= issue.task_status
.pull-right.issue-updated-at
- %small updated #{time_ago_with_tooltip(issue.updated_at, placement: 'bottom', html_class: 'issue_update_ago')}
+ %span updated #{time_ago_with_tooltip(issue.updated_at, placement: 'bottom', html_class: 'issue_update_ago')}
diff --git a/app/views/projects/issues/_issues.html.haml b/app/views/projects/issues/_issues.html.haml
index 5d243adb5fe..a3399c57aa2 100644
--- a/app/views/projects/issues/_issues.html.haml
+++ b/app/views/projects/issues/_issues.html.haml
@@ -1,9 +1,8 @@
-.panel.panel-default
- %ul.well-list.issues-list
- = render @issues
- - if @issues.blank?
- %li
- .nothing-here-block No issues to show
+%ul.content-list.issues-list
+ = render @issues
+ - if @issues.blank?
+ %li
+ .nothing-here-block No issues to show
- if @issues.present?
.pull-right
diff --git a/app/views/projects/issues/index.html.haml b/app/views/projects/issues/index.html.haml
index d06225f5488..24314d11404 100644
--- a/app/views/projects/issues/index.html.haml
+++ b/app/views/projects/issues/index.html.haml
@@ -3,8 +3,8 @@
- if current_user
= auto_discovery_link_tag(:atom, namespace_project_issues_url(@project.namespace, @project, :atom, private_token: current_user.private_token), title: "#{@project.name} issues")
-.append-bottom-10
- .pull-right
+.project-issuable-filter
+ .controls
.pull-left
- if current_user
.hidden-xs.pull-left
diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml
index e7b14e7582c..09080642293 100644
--- a/app/views/projects/issues/show.html.haml
+++ b/app/views/projects/issues/show.html.haml
@@ -1,15 +1,16 @@
- page_title "#{@issue.title} (##{@issue.iid})", "Issues"
.issue
.issue-details.issuable-details
- %h4.page-title
+ .page-title
.issue-box{ class: issue_box_class(@issue) }
- if @issue.closed?
Closed
- else
Open
- Issue ##{@issue.iid}
- %small.creator
- &middot; created by #{link_to_member(@project, @issue.author)}
+ %span.issue-id Issue ##{@issue.iid}
+ %span.creator
+ &middot; created by #{link_to_member(@project, @issue.author, size: 24)}
+ &middot;
= time_ago_with_tooltip(@issue.created_at, placement: 'bottom', html_class: 'issue_created_ago')
- if @issue.updated_at != @issue.created_at
%span
@@ -32,18 +33,17 @@
= icon('pencil-square-o')
Edit
- %hr
- %h2.issue-title
- = gfm escape_once(@issue.title)
- %div
- - if @issue.description.present?
- .description{class: can?(current_user, :update_issue, @issue) ? 'js-task-list-container' : ''}
- .wiki
- = preserve do
- = markdown(@issue.description)
- %textarea.hidden.js-task-list-field
- = @issue.description
+ .gray-content-block.middle-block
+ %h2.issue-title
+ = gfm escape_once(@issue.title)
+ %div
+ - if @issue.description.present?
+ .description{class: can?(current_user, :update_issue, @issue) ? 'js-task-list-container' : ''}
+ .wiki
+ = preserve do
+ = markdown(@issue.description)
+ %textarea.hidden.js-task-list-field
+ = @issue.description
- %hr
.issue-discussion
= render 'projects/issues/discussion'
diff --git a/app/views/projects/merge_requests/_discussion.html.haml b/app/views/projects/merge_requests/_discussion.html.haml
index f855dfec321..38e66c3828b 100644
--- a/app/views/projects/merge_requests/_discussion.html.haml
+++ b/app/views/projects/merge_requests/_discussion.html.haml
@@ -7,18 +7,21 @@
= render 'shared/show_aside'
+.gray-content-block.second-block
+ .row
+ .col-md-9
+ .votes-holder.pull-right
+ #votes= render 'votes/votes_block', votable: @merge_request
+ = render "projects/merge_requests/show/participants"
+ .col-md-3
+ %span.slead.has_tooltip{:"data-original-title" => 'Cross-project reference'}
+ = cross_project_reference(@project, @merge_request)
+
.row
%section.col-md-9
- .votes-holder.pull-right
- #votes= render 'votes/votes_block', votable: @merge_request
- = render "projects/merge_requests/show/participants"
= render "projects/notes/notes_with_form"
%aside.col-md-3
.issuable-affix
- .clearfix
- %span.slead.has_tooltip{:"data-original-title" => 'Cross-project reference'}
- = cross_project_reference(@project, @merge_request)
- %hr
.context
= render 'shared/issuable/context', issuable: @merge_request
diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml
index 0bcd543fee7..25e4e8ba80d 100644
--- a/app/views/projects/merge_requests/_merge_request.html.haml
+++ b/app/views/projects/merge_requests/_merge_request.html.haml
@@ -14,11 +14,6 @@
%span
%i.fa.fa-ban
CLOSED
- - else
- %span.hidden-xs.hidden-sm
- %span.label-branch<
- %i.fa.fa-code-fork
- %span= merge_request.target_branch
- note_count = merge_request.mr_and_commit_notes.user.count
- if merge_request.assignee
&nbsp;
@@ -48,4 +43,4 @@
= merge_request.task_status
.pull-right.hidden-xs
- %small updated #{time_ago_with_tooltip(merge_request.updated_at, placement: 'bottom', html_class: 'merge_request_updated_ago')}
+ %span updated #{time_ago_with_tooltip(merge_request.updated_at, placement: 'bottom', html_class: 'merge_request_updated_ago')}
diff --git a/app/views/projects/merge_requests/_merge_requests.html.haml b/app/views/projects/merge_requests/_merge_requests.html.haml
index b8a0ca9a42f..d86707b3d97 100644
--- a/app/views/projects/merge_requests/_merge_requests.html.haml
+++ b/app/views/projects/merge_requests/_merge_requests.html.haml
@@ -1,9 +1,8 @@
-.panel.panel-default
- %ul.well-list.mr-list
- = render @merge_requests
- - if @merge_requests.blank?
- %li
- .nothing-here-block No merge requests to show
+%ul.content-list.mr-list
+ = render @merge_requests
+ - if @merge_requests.blank?
+ %li
+ .nothing-here-block No merge requests to show
- if @merge_requests.present?
.pull-right
diff --git a/app/views/projects/merge_requests/_new_compare.html.haml b/app/views/projects/merge_requests/_new_compare.html.haml
index 7709330611a..452006162db 100644
--- a/app/views/projects/merge_requests/_new_compare.html.haml
+++ b/app/views/projects/merge_requests/_new_compare.html.haml
@@ -37,7 +37,7 @@
%h4 Compare failed
%p We can't compare selected branches. It may be because of huge diff. Please try again or select different branches.
- else
- .light-well
+ .light-well.append-bottom-10
.center
%h4
There isn't anything to merge.
diff --git a/app/views/projects/merge_requests/_new_submit.html.haml b/app/views/projects/merge_requests/_new_submit.html.haml
index 76f44211dac..46aeecd8733 100644
--- a/app/views/projects/merge_requests/_new_submit.html.haml
+++ b/app/views/projects/merge_requests/_new_submit.html.haml
@@ -18,15 +18,13 @@
= f.hidden_field :target_branch
.mr-compare.merge-request
- %ul.nav.nav-tabs.merge-request-tabs
+ %ul.merge-request-tabs
%li.commits-tab
= link_to url_for(params), data: {target: '#commits', action: 'commits', toggle: 'tab'} do
- = icon('history')
Commits
%span.badge= @commits.size
%li.diffs-tab.active
= link_to url_for(params), data: {target: '#diffs', action: 'diffs', toggle: 'tab'} do
- = icon('list-alt')
Changes
%span.badge= @diffs.size
diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml
index ec1838eb489..61e04dce5ab 100644
--- a/app/views/projects/merge_requests/_show.html.haml
+++ b/app/views/projects/merge_requests/_show.html.haml
@@ -5,10 +5,8 @@
.merge-request{'data-url' => merge_request_path(@merge_request)}
.merge-request-details.issuable-details
= render "projects/merge_requests/show/mr_title"
- %hr
= render "projects/merge_requests/show/mr_box"
- %hr
- .append-bottom-20.mr-source-target
+ .append-bottom-20.mr-source-target.prepend-top-default
- if @merge_request.open?
.pull-right
- if @merge_request.source_branch_exists?
@@ -39,20 +37,17 @@
= link_to "command line", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal"
- if @commits.present?
- %ul.nav.nav-tabs.merge-request-tabs
+ %ul.merge-request-tabs
%li.notes-tab
= link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: '#notes', action: 'notes', toggle: 'tab'} do
- = icon('comments')
Discussion
%span.badge= @merge_request.mr_and_commit_notes.user.count
%li.commits-tab
= link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: '#commits', action: 'commits', toggle: 'tab'} do
- = icon('history')
Commits
%span.badge= @commits.size
%li.diffs-tab
= link_to diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: '#diffs', action: 'diffs', toggle: 'tab'} do
- = icon('list-alt')
Changes
%span.badge= @merge_request.diffs.size
diff --git a/app/views/projects/merge_requests/index.html.haml b/app/views/projects/merge_requests/index.html.haml
index 72fbe2e27a7..d3a576977c2 100644
--- a/app/views/projects/merge_requests/index.html.haml
+++ b/app/views/projects/merge_requests/index.html.haml
@@ -1,7 +1,7 @@
- page_title "Merge Requests"
= render 'projects/last_push'
-.append-bottom-10
- .pull-right
+.project-issuable-filter
+ .controls
= render 'shared/issuable/search_form', path: namespace_project_merge_requests_path(@project.namespace, @project)
- if can? current_user, :create_merge_request, @project
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 db1575f899a..f18cf96c17d 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
@@ -11,12 +11,12 @@
%pre.dark
- if @merge_request.for_fork?
:preserve
- git fetch #{@merge_request.source_project.http_url_to_repo} #{@merge_request.source_branch}
- git checkout -b #{@merge_request.source_project_path}-#{@merge_request.source_branch} FETCH_HEAD
+ git fetch #{h @merge_request.source_project.http_url_to_repo} #{h @merge_request.source_branch}
+ git checkout -b #{h @merge_request.source_project_path}-#{h @merge_request.source_branch} FETCH_HEAD
- else
:preserve
git fetch origin
- git checkout -b #{@merge_request.source_branch} origin/#{@merge_request.source_branch}
+ git checkout -b #{h @merge_request.source_branch} origin/#{h @merge_request.source_branch}
%p
%strong Step 2.
Review the changes locally
@@ -27,18 +27,18 @@
%pre.dark
- if @merge_request.for_fork?
:preserve
- git checkout #{@merge_request.target_branch}
- git merge --no-ff #{@merge_request.source_project_path}-#{@merge_request.source_branch}
+ git checkout #{h @merge_request.target_branch}
+ git merge --no-ff #{h @merge_request.source_project_path}-#{h @merge_request.source_branch}
- else
:preserve
- git checkout #{@merge_request.target_branch}
- git merge --no-ff #{@merge_request.source_branch}
+ git checkout #{h @merge_request.target_branch}
+ git merge --no-ff #{h @merge_request.source_branch}
%p
%strong Step 4.
Push the result of the merge to GitLab
%pre.dark
:preserve
- git push origin #{@merge_request.target_branch}
+ git push origin #{h @merge_request.target_branch}
- unless @merge_request.can_be_merged_by?(current_user)
%p
Note that pushing to GitLab requires write access to this repository.
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 e3cd4346872..b4f62a75890 100644
--- a/app/views/projects/merge_requests/show/_mr_box.html.haml
+++ b/app/views/projects/merge_requests/show/_mr_box.html.haml
@@ -1,11 +1,12 @@
-%h2.issue-title
- = gfm escape_once(@merge_request.title)
+.gray-content-block.middle-block
+ %h2.issue-title
+ = gfm escape_once(@merge_request.title)
-%div
- - if @merge_request.description.present?
- .description{class: can?(current_user, :update_merge_request, @merge_request) ? 'js-task-list-container' : ''}
- .wiki
- = preserve do
- = markdown(@merge_request.description)
- %textarea.hidden.js-task-list-field
- = @merge_request.description
+ %div
+ - if @merge_request.description.present?
+ .description{class: can?(current_user, :update_merge_request, @merge_request) ? 'js-task-list-container' : ''}
+ .wiki
+ = preserve do
+ = markdown(@merge_request.description)
+ %textarea.hidden.js-task-list-field
+ = @merge_request.description
diff --git a/app/views/projects/merge_requests/show/_mr_title.html.haml b/app/views/projects/merge_requests/show/_mr_title.html.haml
index 9a1eb36fc88..2bf9cd597a4 100644
--- a/app/views/projects/merge_requests/show/_mr_title.html.haml
+++ b/app/views/projects/merge_requests/show/_mr_title.html.haml
@@ -1,10 +1,11 @@
-%h4.page-title
+.page-title
.issue-box{ class: issue_box_class(@merge_request) }
= @merge_request.state_human_name
- Merge Request ##{@merge_request.iid}
- %small.creator
+ %span.issue-id Merge Request ##{@merge_request.iid}
+ %span.creator
+ &middot;
+ created by #{link_to_member(@project, @merge_request.author, size: 24)}
&middot;
- created by #{link_to_member(@project, @merge_request.author)}
= time_ago_with_tooltip(@merge_request.created_at)
- if @merge_request.updated_at != @merge_request.created_at
%span
diff --git a/app/views/projects/merge_requests/widget/open/_accept.html.haml b/app/views/projects/merge_requests/widget/open/_accept.html.haml
index b61e193fc42..613525437ab 100644
--- a/app/views/projects/merge_requests/widget/open/_accept.html.haml
+++ b/app/views/projects/merge_requests/widget/open/_accept.html.haml
@@ -9,7 +9,7 @@
= label_tag :should_remove_source_branch, class: "remove_source_checkbox" do
= check_box_tag :should_remove_source_branch
Remove source branch
- .accept-control
+ .accept-control.right
= link_to "#", class: "modify-merge-commit-link js-toggle-button" do
= icon('edit')
Modify commit message
diff --git a/app/views/projects/milestones/_milestone.html.haml b/app/views/projects/milestones/_milestone.html.haml
index 2ce5358fa74..5e93d55b1fb 100644
--- a/app/views/projects/milestones/_milestone.html.haml
+++ b/app/views/projects/milestones/_milestone.html.haml
@@ -1,28 +1,34 @@
%li{class: "milestone milestone-#{milestone.closed? ? 'closed' : 'open'}", id: dom_id(milestone) }
- .pull-right
- - if can?(current_user, :admin_milestone, milestone.project) and milestone.active?
- = link_to edit_namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone), class: "btn btn-sm edit-milestone-link btn-grouped" do
- %i.fa.fa-pencil-square-o
- Edit
- = link_to 'Close Milestone', namespace_project_milestone_path(@project.namespace, @project, milestone, milestone: {state_event: :close }), method: :put, remote: true, class: "btn btn-sm btn-close"
- = link_to namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-sm btn-remove" do
- %i.fa.fa-trash-o
- Remove
+ .row
+ .col-sm-6
+ %strong
+ = link_to_gfm truncate(milestone.title, length: 100), namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone)
- %h4
- = link_to_gfm truncate(milestone.title, length: 100), namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone)
- - if milestone.expired? and not milestone.closed?
- %span.cred (Expired)
- %small
- = milestone.expires_at
+ .col-sm-6
+ .pull-right.light #{milestone.percent_complete}% complete
.row
.col-sm-6
= link_to namespace_project_issues_path(milestone.project.namespace, milestone.project, milestone_title: milestone.title) do
= pluralize milestone.issues.count, 'Issue'
- &nbsp;
+ &middot;
= link_to namespace_project_merge_requests_path(milestone.project.namespace, milestone.project, milestone_title: milestone.title) do
= pluralize milestone.merge_requests.count, 'Merge Request'
- &nbsp;
- %span.light #{milestone.percent_complete}% complete
.col-sm-6
= milestone_progress_bar(milestone)
+
+ .row
+ .col-sm-6
+ - if milestone.expired? and not milestone.closed?
+ %span.cred (Expired)
+ - if milestone.expires_at
+ %span
+ = milestone.expires_at
+ .col-sm-6
+ - if can?(current_user, :admin_milestone, milestone.project) and milestone.active?
+ = link_to edit_namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone), class: "btn btn-xs edit-milestone-link btn-grouped" do
+ %i.fa.fa-pencil-square-o
+ Edit
+ = link_to 'Close Milestone', namespace_project_milestone_path(@project.namespace, @project, milestone, milestone: {state_event: :close }), method: :put, remote: true, class: "btn btn-xs btn-close"
+ = link_to namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-xs btn-remove" do
+ %i.fa.fa-trash-o
+ Remove
diff --git a/app/views/projects/milestones/index.html.haml b/app/views/projects/milestones/index.html.haml
index 995eecd7830..2b8fd671587 100644
--- a/app/views/projects/milestones/index.html.haml
+++ b/app/views/projects/milestones/index.html.haml
@@ -1,18 +1,21 @@
- page_title "Milestones"
-.pull-right
- - if can? current_user, :admin_milestone, @project
- = link_to new_namespace_project_milestone_path(@project.namespace, @project), class: "pull-right btn btn-new", title: "New Milestone" do
- %i.fa.fa-plus
- New Milestone
= render 'shared/milestones_filter'
+.gray-content-block
+ .pull-right
+ - if can? current_user, :admin_milestone, @project
+ = link_to new_namespace_project_milestone_path(@project.namespace, @project), class: "pull-right btn btn-new", title: "New Milestone" do
+ %i.fa.fa-plus
+ New Milestone
+ .oneline
+ Milestone allows you to group issues and set due date for it
+
.milestones
- .panel.panel-default
- %ul.well-list
- = render @milestones
+ %ul.content-list
+ = render @milestones
- - if @milestones.blank?
- %li
- .nothing-here-block No milestones to show
+ - if @milestones.blank?
+ %li
+ .nothing-here-block No milestones to show
= paginate @milestones, theme: "gitlab"
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index 636218368cc..bccea21e7a8 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -72,6 +72,11 @@
%i.fa.fa-google
Google Code
+ - if fogbugz_import_enabled?
+ = link_to new_import_fogbugz_path, class: 'btn import_fogbugz' do
+ %i.fa.fa-bug
+ Fogbugz
+
- if git_import_enabled?
= link_to "#", class: 'btn js-toggle-button import_git' do
%i.fa.fa-git
diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml
index de75d44fc41..9bfbde02ca2 100644
--- a/app/views/projects/notes/_note.html.haml
+++ b/app/views/projects/notes/_note.html.haml
@@ -1,11 +1,8 @@
%li.timeline-entry{ id: dom_id(note), class: [dom_class(note), "note-row-#{note.id}", ('system-note' if note.system)], data: { discussion: note.discussion_id } }
.timeline-entry-inner
.timeline-icon
- - if note.system
- %span= icon('circle')
- - else
- = link_to user_path(note.author) do
- = image_tag avatar_icon(note.author_email), class: 'avatar s40', alt: ''
+ = link_to user_path(note.author) do
+ = image_tag avatar_icon(note.author_email), class: 'avatar s40', alt: ''
.timeline-content
.note-header
- if note_editable?(note)
@@ -22,10 +19,6 @@
%span.note-role.label
= member.human_access
- - if note.system
- = link_to user_path(note.author) do
- = image_tag avatar_icon(note.author_email), class: 'avatar s16', alt: ''
-
= link_to_member(note.project, note.author, avatar: false)
%span.author-username
diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml
index 507f2c7beb0..d5c4ee71978 100644
--- a/app/views/projects/show.html.haml
+++ b/app/views/projects/show.html.haml
@@ -11,7 +11,7 @@
= render "home_panel"
-.project-stats
+.project-stats.gray-content-block
%ul.nav.nav-pills
%li
= link_to namespace_project_commits_path(@project.namespace, @project, @ref || @repository.root_ref) do
@@ -63,21 +63,22 @@
= icon("exclamation-triangle fw")
Archived project! Repository is read-only
-%hr
%section
- if prefer_readme?
- = render 'projects/readme'
+ .project-show-readme
+ = render 'projects/readme'
- else
- = render 'projects/activity'
+ .project-show-activity
+ = render 'projects/activity'
- if current_user
- access = user_max_access_in_project(current_user, @project)
- if access
- %hr
- %p.light
- You have #{access} access to this project.
- - if @project.project_member_by_id(current_user)
- = link_to leave_namespace_project_project_members_path(@project.namespace, @project),
- data: { confirm: leave_project_message(@project) }, method: :delete, title: 'Leave project', class: 'cred' do
- Leave this project \ No newline at end of file
+ .prepend-top-20
+ .gray-content-block.footer-block.center
+ You have #{access} access to this project.
+ - if @project.project_member_by_id(current_user)
+ = link_to leave_namespace_project_project_members_path(@project.namespace, @project),
+ data: { confirm: leave_project_message(@project) }, method: :delete, title: 'Leave project', class: 'cred' do
+ Leave this project
diff --git a/app/views/projects/snippets/_snippet.html.haml b/app/views/projects/snippets/_snippet.html.haml
deleted file mode 100644
index b2c35edc44c..00000000000
--- a/app/views/projects/snippets/_snippet.html.haml
+++ /dev/null
@@ -1,15 +0,0 @@
-%li
- %h4.snippet-title
- = link_to reliable_snippet_path(snippet) do
- = truncate(snippet.title, length: 60)
- %span.cgray.monospace.tiny.pull-right
- = snippet.file_name
-
- .snippet-info
- = "##{snippet.id}"
- %span
- by
- = image_tag avatar_icon(snippet.author_email), class: "avatar avatar-inline s16"
- = snippet.author_name
- %span.light
- #{time_ago_with_tooltip(snippet.created_at)}
diff --git a/app/views/projects/snippets/index.html.haml b/app/views/projects/snippets/index.html.haml
index 30081673ffc..45d4de6a385 100644
--- a/app/views/projects/snippets/index.html.haml
+++ b/app/views/projects/snippets/index.html.haml
@@ -8,9 +8,8 @@
%p.light
Share code pastes with others out of git repository
-%hr
%ul.bordered-list
- = render partial: "projects/snippets/snippet", collection: @snippets
+ = render partial: "shared/snippets/snippet", collection: @snippets
- if @snippets.empty?
%li
.nothing-here-block Nothing here.
diff --git a/app/views/projects/tags/_tag.html.haml b/app/views/projects/tags/_tag.html.haml
index 28ad272322f..2ca295fc5f3 100644
--- a/app/views/projects/tags/_tag.html.haml
+++ b/app/views/projects/tags/_tag.html.haml
@@ -1,13 +1,14 @@
- commit = @repository.commit(tag.target)
%li
- %h4
+ %div
= link_to namespace_project_commits_path(@project.namespace, @project, tag.name), class: "" do
- %i.fa.fa-tag
- = tag.name
+ %strong
+ %i.fa.fa-tag
+ = tag.name
- if tag.message.present?
&nbsp;
= strip_gpg_signature(tag.message)
- .pull-right
+ .controls
- if can? current_user, :download_code, @project
= render 'projects/repositories/download_archive', ref: tag.name, btn_class: 'btn-grouped btn-group-xs'
- if can?(current_user, :admin_project, @project)
@@ -15,8 +16,7 @@
%i.fa.fa-trash-o
- if commit
- %ul.list-unstyled
- = render 'projects/commits/inline_commit', commit: commit, project: @project
+ = render 'projects/branches/commit', commit: commit, project: @project
- else
%p
Cant find HEAD commit for this tag
diff --git a/app/views/projects/tags/index.html.haml b/app/views/projects/tags/index.html.haml
index d4652a47cba..1503a4330f4 100644
--- a/app/views/projects/tags/index.html.haml
+++ b/app/views/projects/tags/index.html.haml
@@ -1,21 +1,18 @@
- page_title "Tags"
= render "projects/commits/head"
-%h3.page-title
- Git Tags
+.gray-content-block
- if can? current_user, :push_code, @project
.pull-right
= link_to new_namespace_project_tag_path(@project.namespace, @project), class: 'btn btn-create new-tag-btn' do
%i.fa.fa-add-sign
New tag
-
-%p.light
- Tags give the ability to mark specific points in history as being important
-%hr
+ .oneline
+ Tags give the ability to mark specific points in history as being important
.tags
- unless @tags.empty?
- %ul.bordered-list
+ %ul.content-list
- @tags.each do |tag|
= render 'tag', tag: @repository.find_tag(tag)
diff --git a/app/views/projects/tree/_tree.html.haml b/app/views/projects/tree/_tree.html.haml
index 5048154cb2f..367a87927d7 100644
--- a/app/views/projects/tree/_tree.html.haml
+++ b/app/views/projects/tree/_tree.html.haml
@@ -14,7 +14,7 @@
%small
%i.fa.fa-plus
-%div#tree-content-holder.tree-content-holder
+%div#tree-content-holder.tree-content-holder.prepend-top-20
%table#tree-slider{class: "table_#{@hex_path} tree-table" }
%thead
%tr
diff --git a/app/views/projects/wikis/_main_links.html.haml b/app/views/projects/wikis/_main_links.html.haml
index 788bb8cf1e2..14f25822259 100644
--- a/app/views/projects/wikis/_main_links.html.haml
+++ b/app/views/projects/wikis/_main_links.html.haml
@@ -1,8 +1,15 @@
%span.pull-right
+ - if can?(current_user, :create_wiki, @project)
+ = link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new btn-grouped", "data-toggle" => "modal" do
+ %i.fa.fa-plus
+ New Page
+
- if (@page && @page.persisted?)
- = link_to history_namespace_project_wiki_path(@project.namespace, @project, @page), class: "btn btn-grouped" do
+ = link_to namespace_project_wiki_history_path(@project.namespace, @project, @page), class: "btn btn-grouped" do
Page History
- if can?(current_user, :create_wiki, @project)
- = link_to edit_namespace_project_wiki_path(@project.namespace, @project, @page), class: "btn btn-grouped" do
+ = link_to namespace_project_wiki_edit_path(@project.namespace, @project, @page), class: "btn btn-grouped" do
%i.fa.fa-pencil-square-o
Edit
+
+= render 'projects/wikis/new'
diff --git a/app/views/projects/wikis/_nav.html.haml b/app/views/projects/wikis/_nav.html.haml
index 804a1b52dbe..fffb4eb31ab 100644
--- a/app/views/projects/wikis/_nav.html.haml
+++ b/app/views/projects/wikis/_nav.html.haml
@@ -1,19 +1,10 @@
-%ul.nav.nav-tabs
+%ul.center-top-menu
= nav_link(html_options: {class: params[:id] == 'home' ? 'active' : '' }) do
= link_to 'Home', namespace_project_wiki_path(@project.namespace, @project, :home)
= nav_link(path: 'wikis#pages') do
- = link_to 'Pages', pages_namespace_project_wikis_path(@project.namespace, @project)
+ = link_to 'Pages', namespace_project_wiki_pages_path(@project.namespace, @project)
= nav_link(path: 'wikis#git_access') do
- = link_to git_access_namespace_project_wikis_path(@project.namespace, @project) do
- %i.fa.fa-download
+ = link_to namespace_project_wikis_git_access_path(@project.namespace, @project) do
Git Access
-
- - if can?(current_user, :create_wiki, @project)
- .pull-right
- = link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new", "data-toggle" => "modal" do
- %i.fa.fa-plus
- New Page
-
-= render 'projects/wikis/new'
diff --git a/app/views/projects/wikis/git_access.html.haml b/app/views/projects/wikis/git_access.html.haml
index 825f2a161c4..226fd3b2290 100644
--- a/app/views/projects/wikis/git_access.html.haml
+++ b/app/views/projects/wikis/git_access.html.haml
@@ -1,15 +1,16 @@
- page_title "Git Access", "Wiki"
= render 'nav'
-.row
- .col-sm-6
- %h3.page-title
- Git access for
- %strong= @project_wiki.path_with_namespace
+.gray-content-block
+ .row
+ .col-sm-6
+ %h3.page-title
+ Git access for
+ %strong= @project_wiki.path_with_namespace
- .col-sm-6
- = render "shared/clone_panel", project: @project_wiki
+ .col-sm-6
+ = render "shared/clone_panel", project: @project_wiki
-.git-empty
+.git-empty.prepend-top-default
%fieldset
%legend Install Gollum:
%pre.dark
@@ -20,7 +21,7 @@
%pre.dark
:preserve
git clone #{ content_tag(:span, default_url_to_repo(@project_wiki), class: 'clone')}
- cd #{@project_wiki.path}
+ cd #{h @project_wiki.path}
%legend Start Gollum And Edit Locally:
%pre.dark
diff --git a/app/views/projects/wikis/history.html.haml b/app/views/projects/wikis/history.html.haml
index 673ec2d20e5..7c81ad53d32 100644
--- a/app/views/projects/wikis/history.html.haml
+++ b/app/views/projects/wikis/history.html.haml
@@ -1,8 +1,9 @@
- page_title "History", @page.title, "Wiki"
= render 'nav'
-%h3.page-title
- %span.light History for
- = link_to @page.title, namespace_project_wiki_path(@project.namespace, @project, @page)
+.gray-content-block
+ %h3.page-title
+ %span.light History for
+ = link_to @page.title, namespace_project_wiki_path(@project.namespace, @project, @page)
%table.table
%thead
diff --git a/app/views/projects/wikis/pages.html.haml b/app/views/projects/wikis/pages.html.haml
index 890ff1aed73..7fb91507eb2 100644
--- a/app/views/projects/wikis/pages.html.haml
+++ b/app/views/projects/wikis/pages.html.haml
@@ -1,8 +1,9 @@
- page_title "All Pages", "Wiki"
= render 'nav'
-%h3.page-title
- All Pages
-%ul.bordered-list
+.gray-content-block
+ %h3.page-title
+ All Pages
+%ul.content-list
- @wiki_pages.each do |wiki_page|
%li
%h4
diff --git a/app/views/projects/wikis/show.html.haml b/app/views/projects/wikis/show.html.haml
index 5c4dd7f91ae..126810811ec 100644
--- a/app/views/projects/wikis/show.html.haml
+++ b/app/views/projects/wikis/show.html.haml
@@ -1,25 +1,26 @@
- page_title @page.title, "Wiki"
= render 'nav'
-%h3.page-title
- = @page.title
+
+.gray-content-block
= render 'main_links'
+ %h3.page-title
+ = @page.title.capitalize
-.wiki-last-edit-by
- Last edited by #{@page.commit.author.name} #{time_ago_with_tooltip(@page.commit.authored_date)}
+ .wiki-last-edit-by
+ Last edited by #{@page.commit.author.name} #{time_ago_with_tooltip(@page.commit.authored_date)}
- if @page.historical?
.warning_message
This is an old version of this page.
- You can view the #{link_to "most recent version", namespace_project_wiki_path(@project.namespace, @project, @page)} or browse the #{link_to "history", history_namespace_project_wiki_path(@project.namespace, @project, @page)}.
+ You can view the #{link_to "most recent version", namespace_project_wiki_path(@project.namespace, @project, @page)} or browse the #{link_to "history", namespace_project_wiki_history_path(@project.namespace, @project, @page)}.
-%hr
-.wiki-holder
+.wiki-holder.prepend-top-default
.wiki
= preserve do
= render_wiki_content(@page)
-%hr
-.wiki-last-edit-by
- Last edited by #{@page.commit.author.name} #{time_ago_with_tooltip(@page.commit.authored_date)}
+.gray-content-block.footer-block
+ .wiki-last-edit-by
+ Last edited by #{@page.commit.author.name} #{time_ago_with_tooltip(@page.commit.authored_date)}
diff --git a/app/views/search/_category.html.haml b/app/views/search/_category.html.haml
index a75cd7bd809..d637abfa76b 100644
--- a/app/views/search/_category.html.haml
+++ b/app/views/search/_category.html.haml
@@ -1,4 +1,4 @@
-%ul.nav.nav-pills.search-filter
+%ul.nav.nav-tabs.search-filter
- if @project
%li{class: ("active" if @scope == 'blobs')}
= link_to search_filter_path(scope: 'blobs') do
diff --git a/app/views/search/_filter.html.haml b/app/views/search/_filter.html.haml
index e2d0cab9e79..ec478a5963d 100644
--- a/app/views/search/_filter.html.haml
+++ b/app/views/search/_filter.html.haml
@@ -1,6 +1,5 @@
.dropdown.inline
- %button.dropdown-toggle.btn.btn{type: 'button', 'data-toggle' => 'dropdown'}
- %i.fa.fa-tags
+ %button.dropdown-toggle.btn.btn-sm{type: 'button', 'data-toggle' => 'dropdown'}
%span.light Group:
- if @group.present?
%strong= @group.name
@@ -17,8 +16,7 @@
= group.name
.dropdown.inline.prepend-left-10.project-filter
- %button.dropdown-toggle.btn.btn{type: 'button', 'data-toggle' => 'dropdown'}
- %i.fa.fa-tags
+ %button.dropdown-toggle.btn.btn-sm{type: 'button', 'data-toggle' => 'dropdown'}
%span.light Project:
- if @project.present?
%strong= @project.name_with_namespace
diff --git a/app/views/search/_form.html.haml b/app/views/search/_form.html.haml
index 5ee70be1ad6..3938c545cad 100644
--- a/app/views/search/_form.html.haml
+++ b/app/views/search/_form.html.haml
@@ -1,12 +1,14 @@
-= form_tag search_path, method: :get, class: 'form-inline' do |f|
+= form_tag search_path, method: :get do |f|
= hidden_field_tag :project_id, params[:project_id]
= hidden_field_tag :group_id, params[:group_id]
= hidden_field_tag :snippets, params[:snippets]
= hidden_field_tag :scope, params[:scope]
+
.search-holder.clearfix
- .form-group
+ .input-group
= search_field_tag :search, params[:search], placeholder: "Search for projects, issues etc", class: "form-control search-text-input", id: "dashboard_search", autofocus: true
- = button_tag 'Search', class: "btn btn-primary"
+ %span.input-group-btn
+ = button_tag 'Search', class: "btn btn-primary"
- unless params[:snippets].eql? 'true'
- .pull-right
- = render 'filter'
+ %br
+ = render 'filter'
diff --git a/app/views/search/_results.html.haml b/app/views/search/_results.html.haml
index 741c780ad96..2a38c98dcfc 100644
--- a/app/views/search/_results.html.haml
+++ b/app/views/search/_results.html.haml
@@ -1,7 +1,7 @@
- if @search_results.empty?
= render partial: "search/results/empty"
- else
- .light
+ %p.light
Search results for
%code
= @search_term
@@ -11,10 +11,13 @@
- elsif @group
in group #{link_to @group.name, @group}
- %br
.results.prepend-top-10
.search-results
- = render partial: "search/results/#{@scope.singularize}", collection: @objects
+ - if @scope == 'projects'
+ .term
+ = render 'shared/projects/list', projects: @objects
+ - else
+ = render partial: "search/results/#{@scope.singularize}", collection: @objects
= paginate @objects, theme: 'gitlab'
:javascript
diff --git a/app/views/search/results/_blob.html.haml b/app/views/search/results/_blob.html.haml
index 58f58eff54d..0fe8a3b490a 100644
--- a/app/views/search/results/_blob.html.haml
+++ b/app/views/search/results/_blob.html.haml
@@ -7,4 +7,4 @@
%strong
= blob.filename
.file-content.code.term
- = render 'shared/file_highlight', blob: blob, first_line_number: blob.startline, user_color_scheme_class: 'white'
+ = render 'shared/file_highlight', blob: blob, first_line_number: blob.startline
diff --git a/app/views/search/results/_project.html.haml b/app/views/search/results/_project.html.haml
deleted file mode 100644
index 195cf06c8ea..00000000000
--- a/app/views/search/results/_project.html.haml
+++ /dev/null
@@ -1,6 +0,0 @@
-.search-result-row
- %h4
- = link_to [project.namespace.becomes(Namespace), project] do
- %span.term= project.name_with_namespace
- - if project.description.present?
- %span.light.term= project.description
diff --git a/app/views/search/results/_snippet_blob.html.haml b/app/views/search/results/_snippet_blob.html.haml
index 95099853918..9a4f9fb9485 100644
--- a/app/views/search/results/_snippet_blob.html.haml
+++ b/app/views/search/results/_snippet_blob.html.haml
@@ -23,7 +23,7 @@
.nothing-here-block Empty file
- else
.file-content.code
- %div.highlighted-data{class: user_color_scheme_class}
+ %div.highlighted-data{ class: user_color_scheme }
.line-numbers
- snippet_blob[:snippet_chunks].each do |snippet|
- unless snippet[:data].empty?
diff --git a/app/views/search/results/_wiki_blob.html.haml b/app/views/search/results/_wiki_blob.html.haml
index c03438eb952..f5859481d46 100644
--- a/app/views/search/results/_wiki_blob.html.haml
+++ b/app/views/search/results/_wiki_blob.html.haml
@@ -7,4 +7,4 @@
%strong
= wiki_blob.filename
.file-content.code.term
- = render 'shared/file_highlight', blob: wiki_blob, first_line_number: wiki_blob.startline, user_color_scheme_class: 'white'
+ = render 'shared/file_highlight', blob: wiki_blob, first_line_number: wiki_blob.startline
diff --git a/app/views/search/show.html.haml b/app/views/search/show.html.haml
index 60f9e9ac9de..f4f3dcfc29f 100644
--- a/app/views/search/show.html.haml
+++ b/app/views/search/show.html.haml
@@ -1,7 +1,5 @@
- page_title @search_term
= render 'search/form'
-%hr
- if @search_term
= render 'search/category'
- %hr
= render 'search/results'
diff --git a/app/views/shared/_clone_panel.html.haml b/app/views/shared/_clone_panel.html.haml
index 07672359dba..2cd422e772a 100644
--- a/app/views/shared/_clone_panel.html.haml
+++ b/app/views/shared/_clone_panel.html.haml
@@ -4,7 +4,7 @@
.input-group-btn
%button{ |
type: 'button', |
- class: "btn btn-sm #{ 'active' if default_clone_protocol == 'ssh' }#{ ' has_tooltip' if current_user && current_user.require_ssh_key? }", |
+ class: "btn #{ 'active' if default_clone_protocol == 'ssh' }#{ ' has_tooltip' if current_user && current_user.require_ssh_key? }", |
:"data-clone" => project.ssh_url_to_repo, |
:"data-title" => "Add an SSH key to your profile<br> to pull or push via SSH",
:"data-html" => "true",
@@ -13,13 +13,13 @@
.input-group-btn
%button{ |
type: 'button', |
- class: "btn btn-sm #{ 'active' if default_clone_protocol == 'http' }#{ ' has_tooltip' if current_user && current_user.require_password? }", |
+ class: "btn #{ 'active' if default_clone_protocol == 'http' }#{ ' has_tooltip' if current_user && current_user.require_password? }", |
:"data-clone" => project.http_url_to_repo, |
:"data-title" => "Set a password on your account<br> to pull or push via #{gitlab_config.protocol.upcase}",
:"data-html" => "true",
:"data-container" => "body"}
= gitlab_config.protocol.upcase
- = text_field_tag :project_clone, default_url_to_repo(project), class: "js-select-on-focus form-control input-sm", readonly: true
+ = text_field_tag :project_clone, default_url_to_repo(project), class: "js-select-on-focus form-control", readonly: true
- if project.kind_of?(Project)
.input-group-addon
.visibility-level-label.has_tooltip{'data-title' => "#{visibility_level_label(project.visibility_level)} project" }
diff --git a/app/views/shared/_event_filter.html.haml b/app/views/shared/_event_filter.html.haml
index 334db60690d..8495774accc 100644
--- a/app/views/shared/_event_filter.html.haml
+++ b/app/views/shared/_event_filter.html.haml
@@ -1,4 +1,4 @@
-%ul.nav.nav-pills.event_filter
+.btn-group.btn-group-next.event-filter
= event_filter_link EventFilter.push, 'Push events'
= event_filter_link EventFilter.merged, 'Merge events'
= event_filter_link EventFilter.comments, 'Comments'
diff --git a/app/views/shared/_file_highlight.html.haml b/app/views/shared/_file_highlight.html.haml
index d6a2e177da1..57c3aff3e18 100644
--- a/app/views/shared/_file_highlight.html.haml
+++ b/app/views/shared/_file_highlight.html.haml
@@ -1,4 +1,4 @@
-.file-content.code{class: user_color_scheme_class}
+.file-content.code.js-syntax-highlight{ class: user_color_scheme }
.line-numbers
- if blob.data.present?
- blob.data.lines.each_index do |index|
diff --git a/app/views/shared/_milestones_filter.html.haml b/app/views/shared/_milestones_filter.html.haml
index f685ae7726c..cbdecda4fff 100644
--- a/app/views/shared/_milestones_filter.html.haml
+++ b/app/views/shared/_milestones_filter.html.haml
@@ -1,14 +1,11 @@
-.milestones-filters.append-bottom-10
- %ul.nav.nav-tabs
+.milestones-filters
+ %ul.center-top-menu
%li{class: ("active" if params[:state].blank? || params[:state] == 'opened')}
= link_to milestones_filter_path(state: 'opened') do
- %i.fa.fa-exclamation-circle
Open
%li{class: ("active" if params[:state] == 'closed')}
= link_to milestones_filter_path(state: 'closed') do
- %i.fa.fa-check-circle
Closed
%li{class: ("active" if params[:state] == 'all')}
= link_to milestones_filter_path(state: 'all') do
- %i.fa.fa-compass
All
diff --git a/app/views/shared/_project.html.haml b/app/views/shared/_project.html.haml
deleted file mode 100644
index 15df97b1333..00000000000
--- a/app/views/shared/_project.html.haml
+++ /dev/null
@@ -1,20 +0,0 @@
-= cache [project.namespace, project, controller.controller_name, controller.action_name] do
- = link_to project_path(project), class: dom_class(project) do
- - if avatar
- .dash-project-avatar
- = project_icon(project, alt: '', class: 'avatar project-avatar s40')
- %span.str-truncated.project-full-name
- %span.namespace-name
- - if project.namespace
- = project.namespace.human_name
- \/
- %span.project-name.filter-title
- = project.name
- - if stars
- %span.pull-right.light
- %i.fa.fa-star
- = project.star_count
- - if project.description.present?
- .project-description
- .str-truncated
- = markdown(project.description, pipeline: :description)
diff --git a/app/views/shared/_projects_list.html.haml b/app/views/shared/_projects_list.html.haml
deleted file mode 100644
index 4c58092af44..00000000000
--- a/app/views/shared/_projects_list.html.haml
+++ /dev/null
@@ -1,17 +0,0 @@
-- projects_limit = 20 unless local_assigns[:projects_limit]
-- avatar = true unless local_assigns[:avatar] == false
-- stars = false unless local_assigns[:stars] == true
-%ul.well-list.projects-list
- - projects.each_with_index do |project, i|
- %li{class: (i >= projects_limit) ? 'project-row hide' : 'project-row'}
- = render "shared/project", project: project, avatar: avatar, stars: stars
- - if projects.blank?
- %li
- .nothing-here-block There are no projects here.
- - if projects.count > projects_limit
- %li.bottom
- %span.light
- #{projects_limit} of #{pluralize(projects.count, 'project')} displayed.
- %span
- = link_to '#', class: 'js-expand' do
- Show all
diff --git a/app/views/shared/groups/_group.html.haml b/app/views/shared/groups/_group.html.haml
new file mode 100644
index 00000000000..a54c5fa8c33
--- /dev/null
+++ b/app/views/shared/groups/_group.html.haml
@@ -0,0 +1,22 @@
+- group_member = local_assigns[:group_member]
+%li
+ - if group_member
+ .controls.hidden-xs
+ - if can?(current_user, :admin_group, group)
+ = link_to edit_group_path(group), class: "btn-sm btn btn-grouped" do
+ %i.fa.fa-cogs
+
+ = link_to leave_group_group_members_path(group), data: { confirm: leave_group_message(group.name) }, method: :delete, class: "btn-sm btn btn-grouped", title: 'Leave this group' do
+ %i.fa.fa-sign-out
+
+ = image_tag group_icon(group), class: "avatar s46 hidden-xs"
+ = link_to group, class: 'group-name' do
+ %strong= group.name
+
+ - if group_member
+ as
+ %span #{group_member.human_access}
+
+ %div.light
+ #{pluralize(group.projects.count, "project")}, #{pluralize(group.users.count, "user")}
+
diff --git a/app/views/shared/issuable/_filter.html.haml b/app/views/shared/issuable/_filter.html.haml
index bcaa48c7a12..8f16773077e 100644
--- a/app/views/shared/issuable/_filter.html.haml
+++ b/app/views/shared/issuable/_filter.html.haml
@@ -1,33 +1,28 @@
.issues-filters
.issues-state-filters
- %ul.nav.nav-tabs
+ %ul.center-top-menu
%li{class: ("active" if params[:state] == 'opened')}
= link_to page_filter_path(state: 'opened') do
- = icon('exclamation-circle')
#{state_filters_text_for(:opened, @project)}
- if defined?(type) && type == :merge_requests
%li{class: ("active" if params[:state] == 'merged')}
= link_to page_filter_path(state: 'merged') do
- = icon('check-circle')
#{state_filters_text_for(:merged, @project)}
%li{class: ("active" if params[:state] == 'closed')}
= link_to page_filter_path(state: 'closed') do
- = icon('ban')
#{state_filters_text_for(:closed, @project)}
- else
%li{class: ("active" if params[:state] == 'closed')}
= link_to page_filter_path(state: 'closed') do
- = icon('check-circle')
#{state_filters_text_for(:closed, @project)}
%li{class: ("active" if params[:state] == 'all')}
= link_to page_filter_path(state: 'all') do
- = icon('compass')
#{state_filters_text_for(:all, @project)}
- .issues-details-filters
+ .issues-details-filters.gray-content-block
= form_tag page_filter_path(without: [:assignee_id, :author_id, :milestone_title, :label_name]), method: :get, class: 'filter-form' do
- if controller.controller_name == 'issues' && can?(current_user, :admin_issue, @project)
.check-all-holder
diff --git a/app/views/shared/projects/_list.html.haml b/app/views/shared/projects/_list.html.haml
new file mode 100644
index 00000000000..021e3b689a1
--- /dev/null
+++ b/app/views/shared/projects/_list.html.haml
@@ -0,0 +1,19 @@
+- projects_limit = 20 unless local_assigns[:projects_limit]
+- avatar = true unless local_assigns[:avatar] == false
+- stars = true unless local_assigns[:stars] == false
+
+%ul.projects-list
+ - projects.each_with_index do |project, i|
+ - css_class = (i >= projects_limit) ? 'hide' : nil
+ = render "shared/projects/project", project: project,
+ avatar: avatar, stars: stars, css_class: css_class
+
+ - if projects.count > projects_limit
+ %li.bottom.center
+ .light
+ #{projects_limit} of #{pluralize(projects.count, 'project')} displayed.
+ = link_to '#', class: 'js-expand' do
+ Show all
+
+:coffeescript
+ new ProjectsList()
diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml
new file mode 100644
index 00000000000..5318c6011f4
--- /dev/null
+++ b/app/views/shared/projects/_project.html.haml
@@ -0,0 +1,24 @@
+- avatar = true unless local_assigns[:avatar] == false
+- stars = true unless local_assigns[:stars] == false
+- css_class = '' unless local_assigns[:css_class]
+- css_class += " no-description" unless project.description.present?
+%li.project-row{ class: css_class }
+ = cache [project.namespace, project, controller.controller_name, controller.action_name, 'v2.1'] do
+ = link_to project_path(project), class: dom_class(project) do
+ - if avatar
+ .dash-project-avatar
+ = project_icon(project, alt: '', class: 'avatar project-avatar s46')
+ %span.project-full-name
+ %span.namespace-name
+ - if project.namespace
+ = project.namespace.human_name
+ \/
+ %span.project-name.filter-title
+ = project.name
+ - if stars
+ %span.pull-right.light
+ %i.fa.fa-star
+ = project.star_count
+ - if project.description.present?
+ .project-description
+ = markdown(project.description, pipeline: :description)
diff --git a/app/views/snippets/_snippet.html.haml b/app/views/shared/snippets/_snippet.html.haml
index 5bb28664349..69a713ad9aa 100644
--- a/app/views/snippets/_snippet.html.haml
+++ b/app/views/shared/snippets/_snippet.html.haml
@@ -1,12 +1,12 @@
-%li
- %h4.snippet-title
+%li.snippet-row
+ .snippet-title
= link_to reliable_snippet_path(snippet) do
= truncate(snippet.title, length: 60)
- if snippet.private?
%span.label.label-gray
%i.fa.fa-lock
private
- %span.cgray.monospace.tiny.pull-right
+ %span.monospace.pull-right
= snippet.file_name
%small.pull-right.cgray
@@ -14,10 +14,8 @@
= link_to snippet.project.name_with_namespace, namespace_project_path(snippet.project.namespace, snippet.project)
.snippet-info
- = "##{snippet.id}"
- %span
- by
- = link_to user_snippets_path(snippet.author) do
- = image_tag avatar_icon(snippet.author_email), class: "avatar avatar-inline s16", alt: ''
- = snippet.author_name
- %span.light #{time_ago_with_tooltip(snippet.created_at)}
+ = link_to user_snippets_path(snippet.author) do
+ = image_tag avatar_icon(snippet.author_email), class: "avatar s24", alt: ''
+ = snippet.author_name
+ authored #{time_ago_with_tooltip(snippet.created_at)}
+
diff --git a/app/views/snippets/_snippets.html.haml b/app/views/snippets/_snippets.html.haml
index 40df42b6cf5..d9aa4dd1d2e 100644
--- a/app/views/snippets/_snippets.html.haml
+++ b/app/views/snippets/_snippets.html.haml
@@ -1,5 +1,5 @@
%ul.bordered-list
- = render partial: 'snippet', collection: @snippets
+ = render partial: 'shared/snippets/snippet', collection: @snippets
- if @snippets.empty?
%li
.nothing-here-block Nothing here.
diff --git a/app/views/snippets/current_user_index.html.haml b/app/views/snippets/current_user_index.html.haml
index 62a967a2e06..d704407c4dd 100644
--- a/app/views/snippets/current_user_index.html.haml
+++ b/app/views/snippets/current_user_index.html.haml
@@ -1,14 +1,15 @@
- page_title "Your Snippets"
= render 'head'
-.slead
- Share code pastes with others out of git repository
-
+.gray-content-block
.pull-right
- = link_to new_snippet_path, class: "btn btn-new btn-sm", title: "New Snippet" do
+ = link_to new_snippet_path, class: "btn btn-new", title: "New Snippet" do
Add new snippet
-%ul.nav.nav-tabs
+ .oneline
+ Share code pastes with others out of git repository
+
+%ul.nav.nav-tabs.prepend-top-20
= nav_tab :scope, nil do
= link_to user_snippets_path(@user) do
All
diff --git a/app/views/snippets/index.html.haml b/app/views/snippets/index.html.haml
index 8608815e0a6..3b62dd2a6e1 100644
--- a/app/views/snippets/index.html.haml
+++ b/app/views/snippets/index.html.haml
@@ -2,8 +2,14 @@
- if current_user
= render 'head'
-.slead
- Public snippets created by you and other users are listed here
+.gray-content-block
+ - if current_user
+ .pull-right
+ = link_to new_snippet_path, class: "btn btn-new", title: "New Snippet" do
+ Add new snippet
+
+ .oneline
+ Public snippets created by you and other users are listed here
= render 'snippets'
diff --git a/app/views/users/_projects.html.haml b/app/views/users/_projects.html.haml
index 297fa537394..a126a858ea8 100644
--- a/app/views/users/_projects.html.haml
+++ b/app/views/users/_projects.html.haml
@@ -1,13 +1,13 @@
- if local_assigns.has_key?(:contributed_projects) && contributed_projects.present?
.panel.panel-default.contributed-projects
.panel-heading Projects contributed to
- = render 'shared/projects_list',
+ = render 'shared/projects/list',
projects: contributed_projects.sort_by(&:star_count).reverse,
projects_limit: 5, stars: true, avatar: false
- if local_assigns.has_key?(:projects) && projects.present?
.panel.panel-default
.panel-heading Personal projects
- = render 'shared/projects_list',
+ = render 'shared/projects/list',
projects: projects.sort_by(&:star_count).reverse,
projects_limit: 10, stars: true, avatar: false
diff --git a/app/workers/merge_worker.rb b/app/workers/merge_worker.rb
index 6a8665c179a..5d1a8555b7d 100644
--- a/app/workers/merge_worker.rb
+++ b/app/workers/merge_worker.rb
@@ -14,6 +14,10 @@ class MergeWorker
if result[:status] == :success && params[:should_remove_source_branch].present?
DeleteBranchService.new(merge_request.source_project, current_user).
execute(merge_request.source_branch)
+
+ merge_request.source_project.repository.expire_branch_names
end
+
+ result
end
end
diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb
index f2ba2e15e7b..ea2808045eb 100644
--- a/app/workers/repository_import_worker.rb
+++ b/app/workers/repository_import_worker.rb
@@ -7,22 +7,31 @@ class RepositoryImportWorker
def perform(project_id)
project = Project.find(project_id)
- import_result = gitlab_shell.send(:import_repository,
+ unless project.import_url == Project::UNKNOWN_IMPORT_URL
+ import_result = gitlab_shell.send(:import_repository,
project.path_with_namespace,
project.import_url)
- return project.import_fail unless import_result
+ return project.import_fail unless import_result
+ else
+ unless project.create_repository
+ return project.import_fail
+ end
+ end
- data_import_result = if project.import_type == 'github'
- Gitlab::GithubImport::Importer.new(project).execute
- elsif project.import_type == 'gitlab'
- Gitlab::GitlabImport::Importer.new(project).execute
- elsif project.import_type == 'bitbucket'
- Gitlab::BitbucketImport::Importer.new(project).execute
- elsif project.import_type == 'google_code'
- Gitlab::GoogleCodeImport::Importer.new(project).execute
- else
- true
- end
+ data_import_result = case project.import_type
+ when 'github'
+ Gitlab::GithubImport::Importer.new(project).execute
+ when 'gitlab'
+ Gitlab::GitlabImport::Importer.new(project).execute
+ when 'bitbucket'
+ Gitlab::BitbucketImport::Importer.new(project).execute
+ when 'google_code'
+ Gitlab::GoogleCodeImport::Importer.new(project).execute
+ when 'fogbugz'
+ Gitlab::FogbugzImport::Importer.new(project).execute
+ else
+ true
+ end
return project.import_fail unless data_import_result
Gitlab::BitbucketImport::KeyDeleter.new(project).execute if project.import_type == 'bitbucket'
diff --git a/bin/daemon_with_pidfile b/bin/daemon_with_pidfile
new file mode 100755
index 00000000000..f138c27a0e2
--- /dev/null
+++ b/bin/daemon_with_pidfile
@@ -0,0 +1,33 @@
+#!/usr/bin/env ruby
+# daemon_with_pidfile
+#
+# Daemonize, write a pidfile, and exec the remainder of the command line.
+
+def main(pidfile, cmd)
+ if middle_pid = Process.fork
+ # outer process
+ # Do not exit the outer process before the middle process finishes
+ Process.waitpid(middle_pid)
+ exit $?.exitstatus
+ end
+
+ if final_pid = Process.fork
+ # middle process
+ open(pidfile, 'w') { |f| f.puts final_pid }
+ exit
+ end
+
+ # Standard daemon things: become session leader, ignore SIGHUP, close stdin.
+ Signal.trap("HUP", "IGNORE")
+ Process.setsid
+ IO.new(0).close
+
+ exec(*cmd)
+end
+
+if ARGV.count < 2
+ abort "Usage: #$0 pidfile command [args...]"
+end
+
+pidfile = ARGV.shift
+main(pidfile, ARGV)
diff --git a/bin/mail_room b/bin/mail_room
index f4f1a170c04..74a84f5b2b4 100755
--- a/bin/mail_room
+++ b/bin/mail_room
@@ -19,9 +19,7 @@ get_mail_room_pid()
start()
{
- bundle exec mail_room -q -c $mail_room_config >> $mail_room_logfile 2>&1 &
- PID=$!
- echo $PID > $mail_room_pidfile
+ bin/daemon_with_pidfile $mail_room_pidfile bundle exec mail_room -q -c $mail_room_config >> $mail_room_logfile 2>&1
}
stop()
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index c7b60a1d4b1..9eb99dae456 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -246,6 +246,11 @@ production: &base
# issuer: 'https://gitlab.example.com',
# name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient'
# } }
+ # - { name: 'crowd',
+ # args: {
+ # crowd_server_url: 'CROWD SERVER URL',
+ # application_name: 'YOUR_APP_NAME',
+ # application_password: 'YOUR_APP_PASSWORD' } }
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index c47e5dab27c..689c3f3049d 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -158,7 +158,7 @@ Settings.gitlab.default_projects_features['snippets'] = false if Settings.
Settings.gitlab.default_projects_features['visibility_level'] = Settings.send(:verify_constant, Gitlab::VisibilityLevel, Settings.gitlab.default_projects_features['visibility_level'], Gitlab::VisibilityLevel::PRIVATE)
Settings.gitlab['repository_downloads_path'] = File.absolute_path(Settings.gitlab['repository_downloads_path'] || 'tmp/repositories', Rails.root)
Settings.gitlab['restricted_signup_domains'] ||= []
-Settings.gitlab['import_sources'] ||= ['github','bitbucket','gitlab','gitorious','google_code','git']
+Settings.gitlab['import_sources'] ||= ['github','bitbucket','gitlab','gitorious','google_code','fogbugz','git']
#
# Reply by email
diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb
index 091548348b1..2ce24592f8b 100644
--- a/config/initializers/devise.rb
+++ b/config/initializers/devise.rb
@@ -238,7 +238,7 @@ Devise.setup do |config|
provider_arguments.concat provider['args']
when Hash
# A Hash from the configuration will be passed as is.
- provider_arguments << provider['args']
+ provider_arguments << provider['args'].symbolize_keys
end
config.omniauth provider['name'].to_sym, *provider_arguments
diff --git a/config/initializers/doorkeeper.rb b/config/initializers/doorkeeper.rb
index 6139ddbe6cd..66ac88e9f4a 100644
--- a/config/initializers/doorkeeper.rb
+++ b/config/initializers/doorkeeper.rb
@@ -12,8 +12,7 @@ Doorkeeper.configure do
end
resource_owner_from_credentials do |routes|
- u = User.find_by(email: params[:username]) || User.find_by(username: params[:username])
- u if u && u.valid_password?(params[:password])
+ Gitlab::Auth.new.find(params[:username], params[:password])
end
# If you want to restrict access to the web interface for adding oauth authorized applications, you need to declare the block below.
diff --git a/config/routes.rb b/config/routes.rb
index 3a27571d62b..d484db559df 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -129,12 +129,7 @@ Gitlab::Application.routes.draw do
end
# Enable Grack support
- mount Grack::Bundle.new({
- git_path: Gitlab.config.git.bin_path,
- project_root: Gitlab.config.gitlab_shell.repos_path,
- upload_pack: Gitlab.config.gitlab_shell.upload_pack,
- receive_pack: Gitlab.config.gitlab_shell.receive_pack
- }), at: '/', constraints: lambda { |request| /[-\/\w\.]+\.git\//.match(request.path_info) }, via: [:get, :post]
+ mount Grack::AuthSpawner, at: '/', constraints: lambda { |request| /[-\/\w\.]+\.git\//.match(request.path_info) }, via: [:get, :post]
# Help
get 'help' => 'help#index'
@@ -203,6 +198,15 @@ Gitlab::Application.routes.draw do
get :new_user_map, path: :user_map
post :create_user_map, path: :user_map
end
+
+ resource :fogbugz, only: [:create, :new], controller: :fogbugz do
+ get :status
+ post :callback
+ get :jobs
+
+ get :new_user_map, path: :user_map
+ post :create_user_map, path: :user_map
+ end
end
#
@@ -305,6 +309,8 @@ Gitlab::Application.routes.draw do
resources :services
end
+ resources :labels
+
root to: 'dashboard#index'
end
@@ -510,16 +516,20 @@ Gitlab::Application.routes.draw do
end
end
- resources :wikis, only: [:show, :edit, :destroy, :create], constraints: { id: /[a-zA-Z.0-9_\-\/]+/ } do
- collection do
- get :pages
- put ':id' => 'wikis#update'
- get :git_access
- end
+ WIKI_SLUG_ID = { id: /[a-zA-Z.0-9_\-\/]+/ } unless defined? WIKI_SLUG_ID
- member do
- get 'history'
- end
+ scope do
+ # Order matters to give priority to these matches
+ get '/wikis/git_access', to: 'wikis#git_access'
+ get '/wikis/pages', to: 'wikis#pages', as: 'wiki_pages'
+ post '/wikis', to: 'wikis#create'
+
+ get '/wikis/*id/history', to: 'wikis#history', as: 'wiki_history', constraints: WIKI_SLUG_ID
+ get '/wikis/*id/edit', to: 'wikis#edit', as: 'wiki_edit', constraints: WIKI_SLUG_ID
+
+ get '/wikis/*id', to: 'wikis#show', as: 'wiki', constraints: WIKI_SLUG_ID
+ delete '/wikis/*id', to: 'wikis#destroy', constraints: WIKI_SLUG_ID
+ put '/wikis/*id', to: 'wikis#update', constraints: WIKI_SLUG_ID
end
resource :repository, only: [:show, :create] do
diff --git a/db/migrate/20150817163600_deduplicate_user_identities.rb b/db/migrate/20150817163600_deduplicate_user_identities.rb
new file mode 100644
index 00000000000..fab669c2905
--- /dev/null
+++ b/db/migrate/20150817163600_deduplicate_user_identities.rb
@@ -0,0 +1,14 @@
+class DeduplicateUserIdentities < ActiveRecord::Migration
+ def change
+ execute 'DROP TABLE IF EXISTS tt_migration_DeduplicateUserIdentities;'
+ execute 'CREATE TEMPORARY TABLE tt_migration_DeduplicateUserIdentities AS SELECT id,provider,user_id FROM identities;'
+ execute 'DELETE FROM identities WHERE id NOT IN ( SELECT MIN(id) FROM tt_migration_DeduplicateUserIdentities GROUP BY user_id, provider);'
+ execute 'DROP TABLE IF EXISTS tt_migration_DeduplicateUserIdentities;'
+ end
+
+ def down
+ # This is an irreversible migration;
+ # If someone is trying to rollback for other reasons, we should not throw an Exception.
+ # raise ActiveRecord::IrreversibleMigration
+ end
+end
diff --git a/db/migrate/20150902001023_add_template_to_label.rb b/db/migrate/20150902001023_add_template_to_label.rb
new file mode 100644
index 00000000000..bd381a97b69
--- /dev/null
+++ b/db/migrate/20150902001023_add_template_to_label.rb
@@ -0,0 +1,5 @@
+class AddTemplateToLabel < ActiveRecord::Migration
+ def change
+ add_column :labels, :template, :boolean, default: false
+ end
+end \ No newline at end of file
diff --git a/db/schema.rb b/db/schema.rb
index d7197d951a4..30b4832c1f3 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: 20150826001931) do
+ActiveRecord::Schema.define(version: 20150902001023) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -373,6 +373,7 @@ ActiveRecord::Schema.define(version: 20150826001931) do
t.integer "project_id"
t.datetime "created_at"
t.datetime "updated_at"
+ t.boolean "template", default: false
end
add_index "labels", ["project_id"], name: "index_labels_on_project_id", using: :btree
diff --git a/doc/api/README.md b/doc/api/README.md
index f369c3fd978..6b8528de50c 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -21,6 +21,7 @@
- [Groups](groups.md)
- [Namespaces](namespaces.md)
- [Settings](settings.md)
+- [Keys](keys.md)
## Clients
diff --git a/doc/api/keys.md b/doc/api/keys.md
new file mode 100644
index 00000000000..faa6f212b43
--- /dev/null
+++ b/doc/api/keys.md
@@ -0,0 +1,46 @@
+# Keys
+
+## Get SSH key with user by ID of an SSH key
+
+Get SSH key with user by ID of an SSH key. Note only administrators can lookup SSH key with user by ID of an SSH key.
+
+```
+GET /keys/:id
+```
+
+Parameters:
+
+- `id` (required) - The ID of an SSH key
+
+```json
+{
+ "id": 1,
+ "title": "Sample key 25",
+ "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt1256k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
+ "created_at": "2015-09-03T07:24:44.627Z",
+ "user": {
+ "name": "John Smith",
+ "username": "john_smith",
+ "id": 25,
+ "state": "active",
+ "avatar_url": "http://www.gravatar.com/avatar/cfa35b8cd2ec278026357769582fa563?s=40\u0026d=identicon",
+ "web_url": "http://localhost:3000/u/john_smith",
+ "created_at": "2015-09-03T07:24:01.670Z",
+ "is_admin": false,
+ "bio": null,
+ "skype": "",
+ "linkedin": "",
+ "twitter": "",
+ "website_url": "",
+ "email": "john@example.com",
+ "theme_id": 2,
+ "color_scheme_id": 1,
+ "projects_limit": 10,
+ "current_sign_in_at": null,
+ "identities": [],
+ "can_create_group": true,
+ "can_create_project": true,
+ "two_factor_enabled": false
+ }
+}
+```
diff --git a/doc/api/services.md b/doc/api/services.md
index cbf767d1b25..bc5049dd302 100644
--- a/doc/api/services.md
+++ b/doc/api/services.md
@@ -1,8 +1,296 @@
# Services
+## Asana
+
+Asana - Teamwork without email
+
+### Create/Edit Asana service
+
+Set Asana service for a project.
+
+> This service adds commit messages as comments to Asana tasks. Once enabled, commit messages are checked for Asana task URLs (for example, `https://app.asana.com/0/123456/987654`) or task IDs starting with # (for example, `#987654`). Every task ID found will get the commit comment added to it. You can also close a task with a message containing: `fix #123456`. You can find your Api Keys here: http://developer.asana.com/documentation/#api_keys
+
+```
+PUT /projects/:id/services/asana
+```
+
+Parameters:
+
+- `api_key` (**required**) - User API token. User must have access to task,all comments will be attributed to this user.
+- `restrict_to_branch` (optional) - Comma-separated list of branches which will beautomatically inspected. Leave blank to include all branches.
+
+### Delete Asana service
+
+Delete Asana service for a project.
+
+```
+DELETE /projects/:id/services/asana
+```
+
+## Assembla
+
+Project Management Software (Source Commits Endpoint)
+
+### Create/Edit Assembla service
+
+Set Assembla service for a project.
+
+```
+PUT /projects/:id/services/assembla
+```
+
+Parameters:
+
+- `token` (**required**)
+- `subdomain` (optional)
+
+### Delete Assembla service
+
+Delete Assembla service for a project.
+
+```
+DELETE /projects/:id/services/assembla
+```
+
+## Atlassian Bamboo CI
+
+A continuous integration and build server
+
+### Create/Edit Atlassian Bamboo CI service
+
+Set Atlassian Bamboo CI service for a project.
+
+> You must set up automatic revision labeling and a repository trigger in Bamboo.
+
+```
+PUT /projects/:id/services/bamboo
+```
+
+Parameters:
+
+- `bamboo_url` (**required**) - Bamboo root URL like https://bamboo.example.com
+- `build_key` (**required**) - Bamboo build plan key like KEY
+- `username` (**required**) - A user with API access, if applicable
+- `password` (**required**)
+
+### Delete Atlassian Bamboo CI service
+
+Delete Atlassian Bamboo CI service for a project.
+
+```
+DELETE /projects/:id/services/bamboo
+```
+
+## Buildkite
+
+Continuous integration and deployments
+
+### Create/Edit Buildkite service
+
+Set Buildkite service for a project.
+
+```
+PUT /projects/:id/services/buildkite
+```
+
+Parameters:
+
+- `token` (**required**) - Buildkite project GitLab token
+- `project_url` (**required**) - https://buildkite.com/example/project
+- `enable_ssl_verification` (optional) - Enable SSL verification
+
+### Delete Buildkite service
+
+Delete Buildkite service for a project.
+
+```
+DELETE /projects/:id/services/buildkite
+```
+
+## Campfire
+
+Simple web-based real-time group chat
+
+### Create/Edit Campfire service
+
+Set Campfire service for a project.
+
+```
+PUT /projects/:id/services/campfire
+```
+
+Parameters:
+
+- `token` (**required**)
+- `subdomain` (optional)
+- `room` (optional)
+
+### Delete Campfire service
+
+Delete Campfire service for a project.
+
+```
+DELETE /projects/:id/services/campfire
+```
+
+## Custom Issue Tracker
+
+Custom issue tracker
+
+### Create/Edit Custom Issue Tracker service
+
+Set Custom Issue Tracker service for a project.
+
+```
+PUT /projects/:id/services/custom-issue-tracker
+```
+
+Parameters:
+
+- `new_issue_url` (**required**) - New Issue url
+- `issues_url` (**required**) - Issue url
+- `project_url` (**required**) - Project url
+- `description` (optional) - Custom issue tracker
+- `title` (optional) - Custom Issue Tracker
+
+### Delete Custom Issue Tracker service
+
+Delete Custom Issue Tracker service for a project.
+
+```
+DELETE /projects/:id/services/custom-issue-tracker
+```
+
+## Drone CI
+
+Drone is a Continuous Integration platform built on Docker, written in Go
+
+### Create/Edit Drone CI service
+
+Set Drone CI service for a project.
+
+```
+PUT /projects/:id/services/drone-ci
+```
+
+Parameters:
+
+- `token` (**required**) - Drone CI project specific token
+- `drone_url` (**required**) - http://drone.example.com
+- `enable_ssl_verification` (optional) - Enable SSL verification
+
+### Delete Drone CI service
+
+Delete Drone CI service for a project.
+
+```
+DELETE /projects/:id/services/drone-ci
+```
+
+## Emails on push
+
+Email the commits and diff of each push to a list of recipients.
+
+### Create/Edit Emails on push service
+
+Set Emails on push service for a project.
+
+```
+PUT /projects/:id/services/emails-on-push
+```
+
+Parameters:
+
+- `recipients` (**required**) - Emails separated by whitespace
+- `disable_diffs` (optional) - Disable code diffs
+- `send_from_committer_email` (optional) - Send from committer
+
+### Delete Emails on push service
+
+Delete Emails on push service for a project.
+
+```
+DELETE /projects/:id/services/emails-on-push
+```
+
+## External Wiki
+
+Replaces the link to the internal wiki with a link to an external wiki.
+
+### Create/Edit External Wiki service
+
+Set External Wiki service for a project.
+
+```
+PUT /projects/:id/services/external-wiki
+```
+
+Parameters:
+
+- `external_wiki_url` (**required**) - The URL of the external Wiki
+
+### Delete External Wiki service
+
+Delete External Wiki service for a project.
+
+```
+DELETE /projects/:id/services/external-wiki
+```
+
+## Flowdock
+
+Flowdock is a collaboration web app for technical teams.
+
+### Create/Edit Flowdock service
+
+Set Flowdock service for a project.
+
+```
+PUT /projects/:id/services/flowdock
+```
+
+Parameters:
+
+- `token` (**required**) - Flowdock Git source token
+
+### Delete Flowdock service
+
+Delete Flowdock service for a project.
+
+```
+DELETE /projects/:id/services/flowdock
+```
+
+## Gemnasium
+
+Gemnasium monitors your project dependencies and alerts you about updates and security vulnerabilities.
+
+### Create/Edit Gemnasium service
+
+Set Gemnasium service for a project.
+
+```
+PUT /projects/:id/services/gemnasium
+```
+
+Parameters:
+
+- `api_key` (**required**) - Your personal API KEY on gemnasium.com
+- `token` (**required**) - The project's slug on gemnasium.com
+
+### Delete Gemnasium service
+
+Delete Gemnasium service for a project.
+
+```
+DELETE /projects/:id/services/gemnasium
+```
+
## GitLab CI
-### Edit GitLab CI service
+Continuous integration server from GitLab
+
+### Create/Edit GitLab CI service
Set GitLab CI service for a project.
@@ -12,12 +300,13 @@ PUT /projects/:id/services/gitlab-ci
Parameters:
-- `token` (required) - CI project token
-- `project_url` (required) - CI project URL
+- `token` (**required**) - GitLab CI project specific token
+- `project_url` (**required**) - http://ci.gitlabhq.com/projects/3
+- `enable_ssl_verification` (optional) - Enable SSL verification
### Delete GitLab CI service
-Delete GitLab CI service settings for a project.
+Delete GitLab CI service for a project.
```
DELETE /projects/:id/services/gitlab-ci
@@ -25,17 +314,24 @@ DELETE /projects/:id/services/gitlab-ci
## HipChat
-### Edit HipChat service
+Private group chat and IM
+
+### Create/Edit HipChat service
-Set HipChat service for project.
+Set HipChat service for a project.
```
PUT /projects/:id/services/hipchat
```
+
Parameters:
-- `token` (required) - HipChat token
-- `room` (required) - HipChat room name
+- `token` (**required**) - Room token
+- `color` (optional)
+- `notify` (optional)
+- `room` (optional) - Room name or ID
+- `api_version` (optional) - Leave blank for default (v2)
+- `server` (optional) - Leave blank for default. https://hipchat.example.com
### Delete HipChat service
@@ -44,3 +340,197 @@ Delete HipChat service for a project.
```
DELETE /projects/:id/services/hipchat
```
+
+## Irker (IRC gateway)
+
+Send IRC messages, on update, to a list of recipients through an Irker gateway.
+
+### Create/Edit Irker (IRC gateway) service
+
+Set Irker (IRC gateway) service for a project.
+
+> NOTE: Irker does NOT have built-in authentication, which makes it vulnerable to spamming IRC channels if it is hosted outside of a firewall. Please make sure you run the daemon within a secured network to prevent abuse. For more details, read: http://www.catb.org/~esr/irker/security.html.
+
+```
+PUT /projects/:id/services/irker
+```
+
+Parameters:
+
+- `recipients` (**required**) - Recipients/channels separated by whitespaces
+- `default_irc_uri` (optional) - irc://irc.network.net:6697/
+- `server_port` (optional) - 6659
+- `server_host` (optional) - localhost
+- `colorize_messages` (optional)
+
+### Delete Irker (IRC gateway) service
+
+Delete Irker (IRC gateway) service for a project.
+
+```
+DELETE /projects/:id/services/irker
+```
+
+## JIRA
+
+Jira issue tracker
+
+### Create/Edit JIRA service
+
+Set JIRA service for a project.
+
+> Setting `project_url`, `issues_url` and `new_issue_url` will allow a user to easily navigate to the Jira issue tracker. See the [integration doc](http://doc.gitlab.com/ce/integration/external-issue-tracker.html) for details. Support for referencing commits and automatic closing of Jira issues directly from GitLab is [available in GitLab EE.](http://doc.gitlab.com/ee/integration/jira.html)
+
+```
+PUT /projects/:id/services/jira
+```
+
+Parameters:
+
+- `new_issue_url` (**required**) - New Issue url
+- `project_url` (**required**) - Project url
+- `issues_url` (**required**) - Issue url
+- `description` (optional) - Jira issue tracker
+
+### Delete JIRA service
+
+Delete JIRA service for a project.
+
+```
+DELETE /projects/:id/services/jira
+```
+
+## PivotalTracker
+
+Project Management Software (Source Commits Endpoint)
+
+### Create/Edit PivotalTracker service
+
+Set PivotalTracker service for a project.
+
+```
+PUT /projects/:id/services/pivotaltracker
+```
+
+Parameters:
+
+- `token` (**required**)
+
+### Delete PivotalTracker service
+
+Delete PivotalTracker service for a project.
+
+```
+DELETE /projects/:id/services/pivotaltracker
+```
+
+## Pushover
+
+Pushover makes it easy to get real-time notifications on your Android device, iPhone, iPad, and Desktop.
+
+### Create/Edit Pushover service
+
+Set Pushover service for a project.
+
+```
+PUT /projects/:id/services/pushover
+```
+
+Parameters:
+
+- `api_key` (**required**) - Your application key
+- `user_key` (**required**) - Your user key
+- `priority` (**required**)
+- `device` (optional) - Leave blank for all active devices
+- `sound` (optional)
+
+### Delete Pushover service
+
+Delete Pushover service for a project.
+
+```
+DELETE /projects/:id/services/pushover
+```
+
+## Redmine
+
+Redmine issue tracker
+
+### Create/Edit Redmine service
+
+Set Redmine service for a project.
+
+```
+PUT /projects/:id/services/redmine
+```
+
+Parameters:
+
+- `new_issue_url` (**required**) - New Issue url
+- `project_url` (**required**) - Project url
+- `issues_url` (**required**) - Issue url
+- `description` (optional) - Redmine issue tracker
+
+### Delete Redmine service
+
+Delete Redmine service for a project.
+
+```
+DELETE /projects/:id/services/redmine
+```
+
+## Slack
+
+A team communication tool for the 21st century
+
+### Create/Edit Slack service
+
+Set Slack service for a project.
+
+```
+PUT /projects/:id/services/slack
+```
+
+Parameters:
+
+- `webhook` (**required**) - https://hooks.slack.com/services/...
+- `username` (optional) - username
+- `channel` (optional) - #channel
+
+### Delete Slack service
+
+Delete Slack service for a project.
+
+```
+DELETE /projects/:id/services/slack
+```
+
+## JetBrains TeamCity CI
+
+A continuous integration and build server
+
+### Create/Edit JetBrains TeamCity CI service
+
+Set JetBrains TeamCity CI service for a project.
+
+> The build configuration in Teamcity must use the build format number %build.vcs.number% you will also want to configure monitoring of all branches so merge requests build, that setting is in the vsc root advanced settings.
+
+```
+PUT /projects/:id/services/teamcity
+```
+
+Parameters:
+
+- `teamcity_url` (**required**) - TeamCity root URL like https://teamcity.example.com
+- `build_type` (**required**) - Build configuration ID
+- `username` (**required**) - A user with permissions to trigger a manual build
+- `password` (**required**)
+
+### Delete JetBrains TeamCity CI service
+
+Delete JetBrains TeamCity CI service for a project.
+
+```
+DELETE /projects/:id/services/teamcity
+```
+
diff --git a/doc/development/rake_tasks.md b/doc/development/rake_tasks.md
index 53f8095cb13..a4a980cf0e0 100644
--- a/doc/development/rake_tasks.md
+++ b/doc/development/rake_tasks.md
@@ -27,3 +27,9 @@ You can find results under the `doc/code` directory.
```
bundle exec rake gitlab:generate_docs
```
+
+## Generate API documentation for project services (e.g. Slack)
+
+```
+bundle exec rake services:doc
+```
diff --git a/doc/gitlab-basics/command-line-commands.md b/doc/gitlab-basics/command-line-commands.md
index b03cca4029c..addd3b6b6eb 100644
--- a/doc/gitlab-basics/command-line-commands.md
+++ b/doc/gitlab-basics/command-line-commands.md
@@ -72,3 +72,8 @@ You will be asked for an administrator’s password.
```
sudo
```
+
+### Tell where you are
+```
+pwd
+```
diff --git a/doc/install/installation.md b/doc/install/installation.md
index 73e36fa7e51..ee13b0f2537 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -125,13 +125,25 @@ Install the Bundler Gem:
sudo gem install bundler --no-ri --no-rdoc
-## 3. System Users
+## 3. Go
+
+Since GitLab 8.0, Git HTTP requests are handled by gitlab-git-http-server.
+This is a small daemon written in Go.
+To install gitlab-git-http-server we need a Go compiler.
+
+ curl -O --progress https://storage.googleapis.com/golang/go1.5.linux-amd64.tar.gz
+ echo '5817fa4b2252afdb02e11e8b9dc1d9173ef3bd5a go1.5.linux-amd64.tar.gz' | shasum -c - && \
+ sudo tar -C /usr/local -xzf go1.5.linux-amd64.tar.gz
+ sudo ln -sf /usr/local/go/bin/{go,godoc,gofmt} /usr/local/bin/
+ rm go1.5.linux-amd64.tar.gz
+
+## 4. System Users
Create a `git` user for GitLab:
sudo adduser --disabled-login --gecos 'GitLab' git
-## 4. Database
+## 5. Database
We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](database_mysql.md). *Note*: because we need to make use of extensions you need at least pgsql 9.1.
@@ -157,7 +169,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da
# Quit the database session
gitlabhq_production> \q
-## 5. Redis
+## 6. Redis
sudo apt-get install redis-server
@@ -187,7 +199,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da
# Add git to the redis group
sudo usermod -aG redis git
-## 6. GitLab
+## 7. GitLab
# We'll install GitLab into home directory of the user "git"
cd /home/git
@@ -287,7 +299,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da
GitLab Shell is an SSH access and repository management software developed specially for GitLab.
# Run the installation task for gitlab-shell (replace `REDIS_URL` if needed):
- sudo -u git -H bundle exec rake gitlab:shell:install[v2.6.3] REDIS_URL=unix:/var/run/redis/redis.sock RAILS_ENV=production
+ sudo -u git -H bundle exec rake gitlab:shell:install[v2.6.5] REDIS_URL=unix:/var/run/redis/redis.sock RAILS_ENV=production
# By default, the gitlab-shell config is generated from your main GitLab config.
# You can review (and modify) the gitlab-shell config as follows:
@@ -297,6 +309,13 @@ GitLab Shell is an SSH access and repository management software developed speci
**Note:** Make sure your hostname can be resolved on the machine itself by either a proper DNS record or an additional line in /etc/hosts ("127.0.0.1 hostname"). This might be necessary for example if you set up gitlab behind a reverse proxy. If the hostname cannot be resolved, the final installation check will fail with "Check GitLab API access: FAILED. code: 401" and pushing commits will be rejected with "[remote rejected] master -> master (hook declined)".
+### Install gitlab-git-http-server
+
+ cd /home/git
+ sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-git-http-server.git
+ cd gitlab-git-http-server
+ sudo -u git -H make
+
### Initialize Database and Activate Advanced Features
sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production
@@ -345,7 +364,7 @@ Check if GitLab and its environment are configured correctly:
# or
sudo /etc/init.d/gitlab restart
-## 7. Nginx
+## 8. Nginx
**Note:** Nginx is the officially supported web server for GitLab. If you cannot or do not want to use Nginx as your web server, have a look at the [GitLab recipes](https://gitlab.com/gitlab-org/gitlab-recipes/).
diff --git a/doc/integration/crowd.md b/doc/integration/crowd.md
new file mode 100644
index 00000000000..2ecc8795ac1
--- /dev/null
+++ b/doc/integration/crowd.md
@@ -0,0 +1,58 @@
+# Crowd OmniAuth Provider
+
+To enable the Crowd OmniAuth provider you must register your application with Crowd. To configure Crowd integration you need an application name and password.
+
+1. On your GitLab server, open the configuration file.
+
+ For omnibus package:
+
+ ```sh
+ sudo editor /etc/gitlab/gitlab.rb
+ ```
+
+ For instalations from source:
+
+ ```sh
+ cd /home/git/gitlab
+
+ sudo -u git -H editor config/gitlab.yml
+ ```
+
+1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings.
+
+1. Add the provider configuration:
+
+ For omnibus package:
+
+ ```ruby
+ gitlab_rails['omniauth_providers'] = [
+ {
+ "name" => "crowd",
+ "args" => {
+ "crowd_server_url" => "CROWD",
+ "application_name" => "YOUR_APP_NAME",
+ "application_password" => "YOUR_APP_PASSWORD"
+ }
+ }
+ ]
+ ```
+
+ For installations from source:
+
+ ```
+ - { name: 'crowd',
+ args: {
+ crowd_server_url: 'CROWD SERVER URL',
+ application_name: 'YOUR_APP_NAME',
+ application_password: 'YOUR_APP_PASSWORD' } }
+ ```
+
+1. Change 'YOUR_APP_NAME' to the application name from Crowd applications page.
+
+1. Change 'YOUR_APP_PASSWORD' to the application password you've set.
+
+1. Save the configuration file.
+
+1. Restart GitLab for the changes to take effect.
+
+On the sign in page there should now be a Crowd tab in the sign in form. \ No newline at end of file
diff --git a/doc/integration/omniauth.md b/doc/integration/omniauth.md
index 2010cb9b8a1..c5cecbc2f2d 100644
--- a/doc/integration/omniauth.md
+++ b/doc/integration/omniauth.md
@@ -76,6 +76,7 @@ Now we can choose one or more of the Supported Providers below to continue confi
- [Shibboleth](shibboleth.md)
- [Twitter](twitter.md)
- [SAML](saml.md)
+- [Crowd](crowd.md)
## Enable OmniAuth for an Existing User
diff --git a/doc/update/6.x-or-7.x-to-7.14.md b/doc/update/6.x-or-7.x-to-7.14.md
index a1488474f60..5bc1f84270a 100644
--- a/doc/update/6.x-or-7.x-to-7.14.md
+++ b/doc/update/6.x-or-7.x-to-7.14.md
@@ -127,7 +127,7 @@ sudo apt-get install nodejs
```bash
cd /home/git/gitlab-shell
sudo -u git -H git fetch
-sudo -u git -H git checkout v2.6.4
+sudo -u git -H git checkout v2.6.5
```
## 7. Install libs, migrations, etc.
@@ -167,7 +167,7 @@ git diff 6-0-stable:config/gitlab.yml.example 7.14-stable:config/gitlab.yml.exam
* Make `/home/git/gitlab/config/gitlab.yml` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-14-stable/config/gitlab.yml.example but with your settings.
* Make `/home/git/gitlab/config/unicorn.rb` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-14-stable/config/unicorn.rb.example but with your settings.
-* Make `/home/git/gitlab-shell/config.yml` the same as https://gitlab.com/gitlab-org/gitlab-shell/blob/v2.6.0/config.yml.example but with your settings.
+* Make `/home/git/gitlab-shell/config.yml` the same as https://gitlab.com/gitlab-org/gitlab-shell/blob/v2.6.5/config.yml.example but with your settings.
* Copy rack attack middleware config
```bash
diff --git a/doc/update/7.13-to-7.14.md b/doc/update/7.13-to-7.14.md
index 7c2d3f4498a..6dd9727fb49 100644
--- a/doc/update/7.13-to-7.14.md
+++ b/doc/update/7.13-to-7.14.md
@@ -63,7 +63,7 @@ sudo -u git -H git checkout 7-14-stable-ee
```bash
cd /home/git/gitlab-shell
sudo -u git -H git fetch
-sudo -u git -H git checkout v2.6.4
+sudo -u git -H git checkout v2.6.5
```
### 5. Install libs, migrations, etc.
diff --git a/doc/update/7.14-to-8.0.md b/doc/update/7.14-to-8.0.md
new file mode 100644
index 00000000000..29a38d07b3d
--- /dev/null
+++ b/doc/update/7.14-to-8.0.md
@@ -0,0 +1,159 @@
+# From 7.14 to 8.0
+
+### 0. Double-check your Git version
+
+**This notice applies only to /usr/local/bin/git**
+
+If you compiled Git from source on your GitLab server then please double-check
+that you are using a version that protects against CVE-2014-9390. For six
+months after this vulnerability became known the GitLab installation guide
+still contained instructions that would install the outdated, 'vulnerable' Git
+version 2.1.2.
+
+Run the following command to get your current Git version.
+
+```
+/usr/local/bin/git --version
+```
+
+If you see 'No such file or directory' then you did not install Git according
+to the outdated instructions from the GitLab installation guide and you can go
+to the next step 'Stop server' below.
+
+If you see a version string then it should be v1.8.5.6, v1.9.5, v2.0.5, v2.1.4,
+v2.2.1 or newer. You can use the [instructions in the GitLab source
+installation
+guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md#1-packages-dependencies)
+to install a newer version of Git.
+
+### 1. Stop server
+
+ sudo service gitlab stop
+
+### 2. Backup
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
+```
+
+### 3. Get latest code
+
+```bash
+sudo -u git -H git fetch --all
+sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically
+```
+
+For GitLab Community Edition:
+
+```bash
+sudo -u git -H git checkout 8-0-stable
+```
+
+OR
+
+For GitLab Enterprise Edition:
+
+```bash
+sudo -u git -H git checkout 8-0-stable-ee
+```
+
+### 4. Update gitlab-shell
+
+```bash
+cd /home/git/gitlab-shell
+sudo -u git -H git fetch
+sudo -u git -H git checkout v2.6.4
+```
+
+### 5. Install gitlab-git-http-server
+
+First we download Go 1.5 and install it into /usr/local/go.
+
+ curl -O --progress https://storage.googleapis.com/golang/go1.5.linux-amd64.tar.gz
+ echo '5817fa4b2252afdb02e11e8b9dc1d9173ef3bd5a go1.5.linux-amd64.tar.gz' | shasum -c - && \
+ sudo tar -C /usr/local -xzf go1.5.linux-amd64.tar.gz
+ sudo ln -sf /usr/local/go/bin/{go,godoc,gofmt} /usr/local/bin/
+ rm go1.5.linux-amd64.tar.gz
+
+Now we download gitlab-git-http-server and install it in /home/git/gitlab-git-http-server.
+
+ cd /home/git
+ sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-git-http-server.git
+ cd gitlab-git-http-server
+ sudo -u git -H make
+
+If you put your Git repositories in a directory different from /home/git/repositories, you need to tell gitlab-git-http-server about it via /etc/gitlab/default.
+See lib/support/init.d/gitlab.default.example for the options.
+
+### 6. Install libs, migrations, etc.
+
+```bash
+cd /home/git/gitlab
+
+# MySQL installations (note: the line below states '--without ... postgres')
+sudo -u git -H bundle install --without development test postgres --deployment
+
+# PostgreSQL installations (note: the line below states '--without ... mysql')
+sudo -u git -H bundle install --without development test mysql --deployment
+
+# Run database migrations
+sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
+
+# Clean up assets and cache
+sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production
+
+# Update init.d script
+sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
+```
+
+### 7. Update config files
+
+#### New configuration options for `gitlab.yml`
+
+There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them to your current `gitlab.yml`.
+
+```
+git diff origin/7-14-stable:config/gitlab.yml.example origin/8-0-stable:config/gitlab.yml.example
+``````
+
+#### New NGINX configuration
+
+Because of the new gitlab-git-http-server you need to update your NGINX configuration.
+If you skip this step 'git clone' and 'git push' over HTTP(S) will stop working.
+
+```
+# Remove '-ssl' twice in the diff command below if you use HTTP instead of HTTPS
+git diff origin/7-14-stable:lib/support/nginx/gitlab-ssl origin/8-0-stable:lib/support/nginx/gitlab-ssl
+```
+
+### 8. Start application
+
+ sudo service gitlab start
+ sudo service nginx restart
+
+### 9. Check application status
+
+Check if GitLab and its environment are configured correctly:
+
+ sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
+
+To make sure you didn't miss anything run a more thorough check with:
+
+ sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
+
+If all items are green, then congratulations, the upgrade is complete!
+
+## Things went south? Revert to previous version (7.14)
+
+### 1. Revert the code to the previous version
+Follow the [upgrade guide from 7.13 to 7.14](7.13-to-7.14.md), except for the database migration
+(The backup is already migrated to the previous version)
+
+### 2. Restore from the backup:
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
+```
+If you have more than one backup *.tar file(s) please add `BACKUP=timestamp_of_backup` to the command above.
diff --git a/doc/web_hooks/web_hooks.md b/doc/web_hooks/web_hooks.md
index 73717ffc7d6..09400d9b163 100644
--- a/doc/web_hooks/web_hooks.md
+++ b/doc/web_hooks/web_hooks.md
@@ -279,6 +279,7 @@ X-Gitlab-Event: Note Hook
"name": "Gitlab Test",
"ssh_url": "git@example.com:gitlab-org/gitlab-test.git",
"http_url": "http://example.com/gitlab-org/gitlab-test.git",
+ "web_url": "http://example.com/gitlab-org/gitlab-test",
"namespace": "Gitlab Org",
"visibility_level": 10
},
@@ -286,6 +287,7 @@ X-Gitlab-Event: Note Hook
"name": "Gitlab Test",
"ssh_url": "git@example.com:gitlab-org/gitlab-test.git",
"http_url": "http://example.com/gitlab-org/gitlab-test.git",
+ "web_url": "http://example.com/gitlab-org/gitlab-test",
"namespace": "Gitlab Org",
"visibility_level": 10
},
@@ -462,6 +464,7 @@ X-Gitlab-Event: Merge Request Hook
"name": "awesome_project",
"ssh_url": "ssh://git@example.com/awesome_space/awesome_project.git",
"http_url": "http://example.com/awesome_space/awesome_project.git",
+ "web_url": "http://example.com/awesome_space/awesome_project",
"visibility_level": 20,
"namespace": "awesome_space"
},
@@ -469,6 +472,7 @@ X-Gitlab-Event: Merge Request Hook
"name": "awesome_project",
"ssh_url": "ssh://git@example.com/awesome_space/awesome_project.git",
"http_url": "http://example.com/awesome_space/awesome_project.git",
+ "web_url": "http://example.com/awesome_space/awesome_project",
"visibility_level": 20,
"namespace": "awesome_space"
},
diff --git a/doc/workflow/importing/README.md b/doc/workflow/importing/README.md
index 5cde90993d2..7ccf06fbd60 100644
--- a/doc/workflow/importing/README.md
+++ b/doc/workflow/importing/README.md
@@ -3,6 +3,7 @@
1. [Bitbucket](import_projects_from_bitbucket.md)
2. [GitHub](import_projects_from_github.md)
3. [GitLab.com](import_projects_from_gitlab_com.md)
+4. [FogBugz](import_projects_from_fogbugz.md)
4. [SVN](migrating_from_svn.md)
### Note
diff --git a/doc/workflow/importing/fogbugz_importer/fogbugz_import_finished.png b/doc/workflow/importing/fogbugz_importer/fogbugz_import_finished.png
new file mode 100644
index 00000000000..205c515bd3f
--- /dev/null
+++ b/doc/workflow/importing/fogbugz_importer/fogbugz_import_finished.png
Binary files differ
diff --git a/doc/workflow/importing/fogbugz_importer/fogbugz_import_login.png b/doc/workflow/importing/fogbugz_importer/fogbugz_import_login.png
new file mode 100644
index 00000000000..a1e348d46ad
--- /dev/null
+++ b/doc/workflow/importing/fogbugz_importer/fogbugz_import_login.png
Binary files differ
diff --git a/doc/workflow/importing/fogbugz_importer/fogbugz_import_select_fogbogz.png b/doc/workflow/importing/fogbugz_importer/fogbugz_import_select_fogbogz.png
new file mode 100644
index 00000000000..ed362846909
--- /dev/null
+++ b/doc/workflow/importing/fogbugz_importer/fogbugz_import_select_fogbogz.png
Binary files differ
diff --git a/doc/workflow/importing/fogbugz_importer/fogbugz_import_select_project.png b/doc/workflow/importing/fogbugz_importer/fogbugz_import_select_project.png
new file mode 100644
index 00000000000..d2fbd0267bd
--- /dev/null
+++ b/doc/workflow/importing/fogbugz_importer/fogbugz_import_select_project.png
Binary files differ
diff --git a/doc/workflow/importing/fogbugz_importer/fogbugz_import_user_map.png b/doc/workflow/importing/fogbugz_importer/fogbugz_import_user_map.png
new file mode 100644
index 00000000000..b1cc4b58525
--- /dev/null
+++ b/doc/workflow/importing/fogbugz_importer/fogbugz_import_user_map.png
Binary files differ
diff --git a/doc/workflow/importing/import_projects_from_fogbugz.md b/doc/workflow/importing/import_projects_from_fogbugz.md
new file mode 100644
index 00000000000..71af0f9ea44
--- /dev/null
+++ b/doc/workflow/importing/import_projects_from_fogbugz.md
@@ -0,0 +1,29 @@
+# Import your project from FogBugz to GitLab
+
+It only takes a few simple steps to import your project from FogBugz.
+The importer will import all of your cases and comments with original case
+numbers and timestamps. You will also have the opportunity to map FogBugz
+users to GitLab users.
+
+* From your GitLab dashboard click 'New project'
+
+* Click on the 'FogBugz' button
+
+![FogBugz](fogbugz_importer/fogbugz_import_select_fogbogz.png)
+
+* Enter your FogBugz URL, email address, and password.
+
+![Login](fogbugz_importer/fogbugz_import_login.png)
+
+* Create mapping from FogBugz users to GitLab users.
+
+![User Map](fogbugz_importer/fogbugz_import_user_map.png)
+
+* Select the projects you wish to import by clicking the Import buttons
+
+![Import Project](fogbugz_importer/fogbugz_import_select_project.png)
+
+* Once the import has finished click the link to take you to the project
+dashboard. Follow the directions to push your existing repository.
+
+![Finished](fogbugz_importer/fogbugz_import_finished.png)
diff --git a/doc/workflow/importing/import_projects_from_github.md b/doc/workflow/importing/import_projects_from_github.md
index 3efa92cb868..2d77c6d1172 100644
--- a/doc/workflow/importing/import_projects_from_github.md
+++ b/doc/workflow/importing/import_projects_from_github.md
@@ -17,4 +17,4 @@ If you want to import from a GitHub Enterprise instance, you need to use GitLab
* To import a project, you can simple click "Add". The importer will import your repository and issues. Once the importer is done, a new GitLab project will be created with your imported data.
### Note
-When you import your projects from GitHub, it is not possible to keep your labels and milestones and issue numbers won't match. We are working on improving this in the near future.
+When you import your projects from GitHub, it is not possible to keep your labels and milestones. We are working on improving this in the near future.
diff --git a/features/admin/labels.feature b/features/admin/labels.feature
new file mode 100644
index 00000000000..1af0e700bd4
--- /dev/null
+++ b/features/admin/labels.feature
@@ -0,0 +1,38 @@
+Feature: Admin Issues Labels
+ Background:
+ Given I sign in as an admin
+ And I have labels: "bug", "feature", "enhancement"
+ Given I visit admin labels page
+
+ Scenario: I should see labels list
+ Then I should see label 'bug'
+ And I should see label 'feature'
+
+ Scenario: I create new label
+ Given I submit new label 'support'
+ Then I should see label 'support'
+
+ Scenario: I edit label
+ Given I visit 'bug' label edit page
+ When I change label 'bug' to 'fix'
+ Then I should not see label 'bug'
+ Then I should see label 'fix'
+
+ Scenario: I remove label
+ When I remove label 'bug'
+ Then I should not see label 'bug'
+
+ @javascript
+ Scenario: I delete all labels
+ When I delete all labels
+ Then I should see labels help message
+
+ Scenario: I create a label with invalid color
+ Given I visit admin new label page
+ When I submit new label with invalid color
+ Then I should see label color error message
+
+ Scenario: I create a label that already exists
+ Given I visit admin new label page
+ When I submit new label 'bug'
+ Then I should see label exist error message
diff --git a/features/login_form.feature b/features/login_form.feature
new file mode 100644
index 00000000000..b4d95754482
--- /dev/null
+++ b/features/login_form.feature
@@ -0,0 +1,5 @@
+Feature: Login form
+ Scenario: I see crowd form
+ Given Crowd integration enabled
+ When I visit sign in page
+ Then I should see Crowd login form \ No newline at end of file
diff --git a/features/project/wiki.feature b/features/project/wiki.feature
index 2ebfa3c1660..af970ecf2d0 100644
--- a/features/project/wiki.feature
+++ b/features/project/wiki.feature
@@ -91,3 +91,15 @@ Feature: Project Wiki
And I view the page history of a Wiki page that has a path
Then I should see a non-escaped path
And I should see the page history
+
+ @javascript
+ Scenario: View an old page version of a Wiki page
+ Given I create a New page with paths
+ And I click on the "Pages" button
+ And I edit the Wiki page with a path
+ Then I should see a non-escaped path
+ And I should see the Editing page
+ And I change the content
+ Then I click on Page History
+ And I should see the page history
+ And I should see a link with a version ID
diff --git a/features/steps/admin/labels.rb b/features/steps/admin/labels.rb
new file mode 100644
index 00000000000..d64380abf73
--- /dev/null
+++ b/features/steps/admin/labels.rb
@@ -0,0 +1,117 @@
+class Spinach::Features::AdminIssuesLabels < Spinach::FeatureSteps
+ include SharedAuthentication
+ include SharedProject
+ include SharedPaths
+
+ step 'I visit \'bug\' label edit page' do
+ visit edit_admin_label_path(bug_label)
+ end
+
+ step 'I visit admin new label page' do
+ visit new_admin_label_path
+ end
+
+ step 'I visit admin labels page' do
+ visit admin_labels_path
+ end
+
+ step 'I remove label \'bug\'' do
+ page.within "#label_#{bug_label.id}" do
+ click_link 'Remove'
+ end
+ end
+
+ step 'I have labels: "bug", "feature", "enhancement"' do
+ ["bug", "feature", "enhancement"].each do |title|
+ Label.create(title: title, template: true)
+ end
+ end
+
+ step 'I delete all labels' do
+ page.within '.labels' do
+ page.all('.btn-remove').each do |remove|
+ remove.click
+ sleep 0.05
+ end
+ end
+ end
+
+ step 'I should see labels help message' do
+ page.within '.labels' do
+ expect(page).to have_content 'There are no any labels yet'
+ end
+ end
+
+ step 'I submit new label \'support\'' do
+ visit new_admin_label_path
+ fill_in 'Title', with: 'support'
+ fill_in 'Background Color', with: '#F95610'
+ click_button 'Save'
+ end
+
+ step 'I submit new label \'bug\'' do
+ visit new_admin_label_path
+ fill_in 'Title', with: 'bug'
+ fill_in 'Background Color', with: '#F95610'
+ click_button 'Save'
+ end
+
+ step 'I submit new label with invalid color' do
+ visit new_admin_label_path
+ fill_in 'Title', with: 'support'
+ fill_in 'Background Color', with: '#12'
+ click_button 'Save'
+ end
+
+ step 'I should see label exist error message' do
+ page.within '.label-form' do
+ expect(page).to have_content 'Title has already been taken'
+ end
+ end
+
+ step 'I should see label color error message' do
+ page.within '.label-form' do
+ expect(page).to have_content 'Color is invalid'
+ end
+ end
+
+ step 'I should see label \'feature\'' do
+ page.within '.manage-labels-list' do
+ expect(page).to have_content 'feature'
+ end
+ end
+
+ step 'I should see label \'bug\'' do
+ page.within '.manage-labels-list' do
+ expect(page).to have_content 'bug'
+ end
+ end
+
+ step 'I should not see label \'bug\'' do
+ page.within '.manage-labels-list' do
+ expect(page).not_to have_content 'bug'
+ end
+ end
+
+ step 'I should see label \'support\'' do
+ page.within '.manage-labels-list' do
+ expect(page).to have_content 'support'
+ end
+ end
+
+ step 'I change label \'bug\' to \'fix\'' do
+ fill_in 'Title', with: 'fix'
+ fill_in 'Background Color', with: '#F15610'
+ click_button 'Save'
+ end
+
+ step 'I should see label \'fix\'' do
+ page.within '.manage-labels-list' do
+ expect(page).to have_content 'fix'
+ end
+ end
+
+ def bug_label
+ Label.templates.find_or_create_by(title: 'bug')
+ end
+end
diff --git a/features/steps/dashboard/starred_projects.rb b/features/steps/dashboard/starred_projects.rb
index 59c73fe63f2..c33813e550b 100644
--- a/features/steps/dashboard/starred_projects.rb
+++ b/features/steps/dashboard/starred_projects.rb
@@ -8,7 +8,7 @@ class Spinach::Features::DashboardStarredProjects < Spinach::FeatureSteps
end
step 'I should not see project "Shop"' do
- page.within 'aside' do
+ page.within '.projects-list' do
expect(page).not_to have_content('Shop')
end
end
diff --git a/features/steps/login_form.rb b/features/steps/login_form.rb
new file mode 100644
index 00000000000..b9ff6ae67fd
--- /dev/null
+++ b/features/steps/login_form.rb
@@ -0,0 +1,25 @@
+class Spinach::Features::LoginForm < Spinach::FeatureSteps
+ include SharedAuthentication
+ include SharedPaths
+ include SharedSnippet
+ include SharedUser
+ include SharedSearch
+
+ step 'Crowd integration enabled' do
+ @providers_orig = Gitlab::OAuth::Provider.providers
+ @omniauth_conf_orig = Gitlab.config.omniauth.enabled
+ expect(Gitlab::OAuth::Provider).to receive(:providers).and_return([:crowd])
+ allow_any_instance_of(ApplicationHelper).to receive(:user_omniauth_authorize_path).and_return(root_path)
+ expect(Gitlab.config.omniauth).to receive(:enabled).and_return(true)
+ end
+
+ step 'I should see Crowd login form' do
+ expect(page).to have_selector '#tab-crowd form'
+ Gitlab::OAuth::Provider.stub(:providers).and_return(@providers_orig)
+ Gitlab.config.omniauth.stub(:enabled).and_return(@omniauth_conf_orig)
+ end
+
+ step 'I visit sign in page' do
+ visit new_user_session_path
+ end
+end
diff --git a/features/steps/project/commits/commits.rb b/features/steps/project/commits/commits.rb
index a8532cc18d8..23e67371f96 100644
--- a/features/steps/project/commits/commits.rb
+++ b/features/steps/project/commits/commits.rb
@@ -52,7 +52,6 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps
end
step 'I see compared refs' do
- expect(page).to have_content "Compare View"
expect(page).to have_content "Commits (1)"
expect(page).to have_content "Showing 2 changed files"
end
@@ -98,4 +97,8 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps
step 'I see inline diff button' do
expect(page).to have_content "Inline"
end
+
+ step 'I click side-by-side diff button' do
+ find('#parallel-diff-btn').click
+ end
end
diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb
index 778dce06359..c92998631ff 100644
--- a/features/steps/project/merge_requests.rb
+++ b/features/steps/project/merge_requests.rb
@@ -128,7 +128,8 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
end
step 'I should see the proper Inline and Side-by-side links' do
- expect(page).to have_css('#commit-diff-viewtype', count: 2)
+ expect(page).to have_css('#parallel-diff-btn', count: 1)
+ expect(page).to have_css('#inline-diff-btn', count: 1)
end
step 'I switch to the merge request\'s comments tab' do
@@ -225,13 +226,13 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
step 'I click link "Hide inline discussion" of the second file' do
page.within '.files [id^=diff]:nth-child(2)' do
- find('.js-toggle-diff-comments').click
+ find('.js-toggle-diff-comments').trigger('click')
end
end
step 'I click link "Show inline discussion" of the second file' do
page.within '.files [id^=diff]:nth-child(2)' do
- find('.js-toggle-diff-comments').click
+ find('.js-toggle-diff-comments').trigger('click')
end
end
diff --git a/features/steps/project/wiki.rb b/features/steps/project/wiki.rb
index eebfaee1ede..02207dbffa6 100644
--- a/features/steps/project/wiki.rb
+++ b/features/steps/project/wiki.rb
@@ -3,7 +3,6 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps
include SharedProject
include SharedNote
include SharedPaths
- include WikiHelper
step 'I click on the Cancel button' do
page.within(:css, ".form-actions") do
@@ -165,6 +164,10 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps
click_on 'Page History'
end
+ step 'I click on Page History' do
+ click_on 'Page History'
+ end
+
step 'I should see the page history' do
expect(page).to have_content('History for')
end
@@ -174,6 +177,10 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps
click_button "Search"
end
+ step 'I should see a link with a version ID' do
+ find('a[href*="?version_id"]')
+ end
+
def wiki
@project_wiki = ProjectWiki.new(project, current_user)
end
diff --git a/features/steps/shared/active_tab.rb b/features/steps/shared/active_tab.rb
index 92e099315d8..eb2ccd9d01e 100644
--- a/features/steps/shared/active_tab.rb
+++ b/features/steps/shared/active_tab.rb
@@ -6,7 +6,7 @@ module SharedActiveTab
end
def ensure_active_sub_tab(content)
- expect(find('div.content ul.nav-tabs li.active')).to have_content(content)
+ expect(find('div.content ul.center-top-menu li.active')).to have_content(content)
end
def ensure_active_sub_nav(content)
@@ -18,7 +18,7 @@ module SharedActiveTab
end
step 'no other sub tabs should be active' do
- expect(page).to have_selector('div.content ul.nav-tabs li.active', count: 1)
+ expect(page).to have_selector('div.content ul.center-top-menu li.active', count: 1)
end
step 'no other sub navs should be active' do
diff --git a/features/steps/shared/diff_note.rb b/features/steps/shared/diff_note.rb
index 27a95aeb19a..72621911a37 100644
--- a/features/steps/shared/diff_note.rb
+++ b/features/steps/shared/diff_note.rb
@@ -198,7 +198,7 @@ module SharedDiffNote
end
step 'I click side-by-side diff button' do
- click_link "Side-by-side"
+ find('#parallel-diff-btn').trigger('click')
end
step 'I see side-by-side diff button' do
diff --git a/features/user.feature b/features/user.feature
index 69618e929c4..35eae842e77 100644
--- a/features/user.feature
+++ b/features/user.feature
@@ -14,11 +14,6 @@ Feature: User
And I should not see project "Internal"
And I should see project "Community"
- Scenario: I visit user "John Doe" page while not signed in when he is not authorized to a public project
- Given "John Doe" owns internal project "Internal"
- When I visit user "John Doe" page
- Then I should be redirected to sign in page
-
# Signed in as someone else
Scenario: I visit user "John Doe" page while signed in as someone else when he owns a public project
diff --git a/lib/api/api.rb b/lib/api/api.rb
index eebd44ea5b6..c09488d3547 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -50,5 +50,6 @@ module API
mount Branches
mount Labels
mount Settings
+ mount Keys
end
end
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index b434c4202de..33b6224a810 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -199,6 +199,10 @@ module API
expose :id, :title, :key, :created_at
end
+ class SSHKeyWithUser < SSHKey
+ expose :user, using: Entities::UserFull
+ end
+
class Note < Grape::Entity
expose :id
expose :note, as: :body
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 1ebf9a1f022..76c9cc2e3a4 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -55,6 +55,32 @@ module API
end
end
+ def project_service
+ @project_service ||= begin
+ underscored_service = params[:service_slug].underscore
+
+ if Service.available_services_names.include?(underscored_service)
+ user_project.build_missing_services
+
+ service_method = "#{underscored_service}_service"
+
+ send_service(service_method)
+ end
+ end
+
+ @project_service || not_found!("Service")
+ end
+
+ def send_service(service_method)
+ user_project.send(service_method)
+ end
+
+ def service_attributes
+ @service_attributes ||= project_service.fields.inject([]) do |arr, hash|
+ arr << hash[:name].to_sym
+ end
+ end
+
def find_group(id)
begin
group = Group.find(id)
diff --git a/lib/api/keys.rb b/lib/api/keys.rb
new file mode 100644
index 00000000000..2b723b79504
--- /dev/null
+++ b/lib/api/keys.rb
@@ -0,0 +1,20 @@
+module API
+ # Keys API
+ class Keys < Grape::API
+ before { authenticate! }
+
+ resource :keys do
+ # Get single ssh key by id. Only available to admin users.
+ #
+ # Example Request:
+ # GET /keys/:id
+ get ":id" do
+ authenticated_as_admin!
+
+ key = Key.find(params[:id])
+
+ present key, with: Entities::SSHKeyWithUser
+ end
+ end
+ end
+end
diff --git a/lib/api/services.rb b/lib/api/services.rb
index 3ad59cf3adf..73645cedea4 100644
--- a/lib/api/services.rb
+++ b/lib/api/services.rb
@@ -4,73 +4,49 @@ module API
before { authenticate! }
before { authorize_admin_project }
+
resource :projects do
- # Set GitLab CI service for project
- #
- # Parameters:
- # token (required) - CI project token
- # project_url (required) - CI project url
+ # Set <service_slug> service for project
#
# Example Request:
+ #
# PUT /projects/:id/services/gitlab-ci
- put ":id/services/gitlab-ci" do
- required_attributes! [:token, :project_url]
- attrs = attributes_for_keys [:token, :project_url]
- user_project.build_missing_services
-
- if user_project.gitlab_ci_service.update_attributes(attrs.merge(active: true))
- true
- else
- not_found!
- end
- end
-
- # Delete GitLab CI service settings
#
- # Example Request:
- # DELETE /projects/:id/services/gitlab-ci
- delete ":id/services/gitlab-ci" do
- if user_project.gitlab_ci_service
- user_project.gitlab_ci_service.update_attributes(
- active: false,
- token: nil,
- project_url: nil
- )
- end
- end
+ put ':id/services/:service_slug' do
+ if project_service
+ validators = project_service.class.validators.select do |s|
+ s.class == ActiveRecord::Validations::PresenceValidator &&
+ s.attributes != [:project_id]
+ end
- # Set Hipchat service for project
- #
- # Parameters:
- # token (required) - Hipchat token
- # room (required) - Hipchat room name
- #
- # Example Request:
- # PUT /projects/:id/services/hipchat
- put ':id/services/hipchat' do
- required_attributes! [:token, :room]
- attrs = attributes_for_keys [:token, :room]
- user_project.build_missing_services
+ required_attributes! validators.map(&:attributes).flatten.uniq
+ attrs = attributes_for_keys service_attributes
- if user_project.hipchat_service.update_attributes(
- attrs.merge(active: true))
- true
- else
- not_found!
+ if project_service.update_attributes(attrs.merge(active: true))
+ true
+ else
+ not_found!
+ end
end
end
- # Delete Hipchat service settings
+ # Delete <service_slug> service for project
#
# Example Request:
- # DELETE /projects/:id/services/hipchat
- delete ':id/services/hipchat' do
- if user_project.hipchat_service
- user_project.hipchat_service.update_attributes(
- active: false,
- token: nil,
- room: nil
- )
+ #
+ # DELETE /project/:id/services/gitlab-ci
+ #
+ delete ':id/services/:service_slug' do
+ if project_service
+ attrs = service_attributes.inject({}) do |hash, key|
+ hash.merge!(key => nil)
+ end
+
+ if project_service.update_attributes(attrs.merge(active: false))
+ true
+ else
+ not_found!
+ end
end
end
end
diff --git a/lib/gitlab/backend/grack_auth.rb b/lib/gitlab/backend/grack_auth.rb
index 12292f614e9..0353b3b7ed3 100644
--- a/lib/gitlab/backend/grack_auth.rb
+++ b/lib/gitlab/backend/grack_auth.rb
@@ -1,6 +1,14 @@
require_relative 'shell_env'
module Grack
+ class AuthSpawner
+ def self.call(env)
+ # Avoid issues with instance variables in Grack::Auth persisting across
+ # requests by creating a new instance for each request.
+ Auth.new({}).call(env)
+ end
+ end
+
class Auth < Rack::Auth::Basic
attr_accessor :user, :project, :env
@@ -10,7 +18,7 @@ module Grack
@request = Rack::Request.new(env)
@auth = Request.new(env)
- @gitlab_ci = false
+ @ci = false
# Need this patch due to the rails mount
# Need this if under RELATIVE_URL_ROOT
@@ -26,13 +34,9 @@ module Grack
auth!
if project && authorized_request?
- if ENV['GITLAB_GRACK_AUTH_ONLY'] == '1'
- # Tell gitlab-git-http-server the request is OK, and what the GL_ID is
- render_grack_auth_ok
- else
- @app.call(env)
- end
- elsif @user.nil? && !@gitlab_ci
+ # Tell gitlab-git-http-server the request is OK, and what the GL_ID is
+ render_grack_auth_ok
+ elsif @user.nil? && !@ci
unauthorized
else
render_not_found
@@ -51,8 +55,8 @@ module Grack
# Allow authentication for GitLab CI service
# if valid token passed
- if gitlab_ci_request?(login, password)
- @gitlab_ci = true
+ if ci_request?(login, password)
+ @ci = true
return
end
@@ -64,12 +68,17 @@ module Grack
end
end
- def gitlab_ci_request?(login, password)
- if login == "gitlab-ci-token" && project && project.gitlab_ci?
- token = project.gitlab_ci_service.token
+ def ci_request?(login, password)
+ matched_login = /(?<s>^[a-zA-Z]*-ci)-token$/.match(login)
+
+ if project && matched_login.present? && git_cmd == 'git-upload-pack'
+ underscored_service = matched_login['s'].underscore
- if token.present? && token == password && git_cmd == 'git-upload-pack'
- return true
+ if Service.available_services_names.include?(underscored_service)
+ service_method = "#{underscored_service}_service"
+ service = project.send(service_method)
+
+ return service && service.activated? && service.valid_token?(password)
end
end
@@ -128,11 +137,13 @@ module Grack
end
def authorized_request?
- return true if @gitlab_ci
+ return true if @ci
case git_cmd
when *Gitlab::GitAccess::DOWNLOAD_COMMANDS
- if user
+ if !Gitlab.config.gitlab_shell.upload_pack
+ false
+ elsif user
Gitlab::GitAccess.new(user, project).download_access_check.allowed?
elsif project.public?
# Allow clone/fetch for public projects
@@ -141,7 +152,9 @@ module Grack
false
end
when *Gitlab::GitAccess::PUSH_COMMANDS
- if user
+ if !Gitlab.config.gitlab_shell.receive_pack
+ false
+ elsif user
# Skip user authorization on upload request.
# It will be done by the pre-receive hook in the repository.
true
diff --git a/lib/gitlab/bitbucket_import/client.rb b/lib/gitlab/bitbucket_import/client.rb
index aec44b8c87b..d88a6eaac6b 100644
--- a/lib/gitlab/bitbucket_import/client.rb
+++ b/lib/gitlab/bitbucket_import/client.rb
@@ -52,11 +52,26 @@ module Gitlab
end
def issues(project_identifier)
- JSON.parse(get("/api/1.0/repositories/#{project_identifier}/issues").body)
+ all_issues = []
+ offset = 0
+ per_page = 50 # Maximum number allowed by Bitbucket
+ index = 0
+
+ begin
+ issues = JSON.parse(get(issue_api_endpoint(project_identifier, per_page, offset)).body)
+ # Find out how many total issues are present
+ total = issues["count"] if index == 0
+ all_issues.concat(issues["issues"])
+ offset += issues["issues"].count
+ index += 1
+ end while all_issues.count < total
+
+ all_issues
end
def issue_comments(project_identifier, issue_id)
- JSON.parse(get("/api/1.0/repositories/#{project_identifier}/issues/#{issue_id}/comments").body)
+ comments = JSON.parse(get("/api/1.0/repositories/#{project_identifier}/issues/#{issue_id}/comments").body)
+ comments.sort_by { |comment| comment["utc_created_on"] }
end
def project(project_identifier)
@@ -100,6 +115,10 @@ module Gitlab
response
end
+ def issue_api_endpoint(project_identifier, per_page, offset)
+ "/api/1.0/repositories/#{project_identifier}/issues?sort=utc_created_on&limit=#{per_page}&start=#{offset}"
+ end
+
def config
Gitlab.config.omniauth.providers.find { |provider| provider.name == "bitbucket"}
end
diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb
index d8a7d29f1bf..2355b3c6ddc 100644
--- a/lib/gitlab/bitbucket_import/importer.rb
+++ b/lib/gitlab/bitbucket_import/importer.rb
@@ -20,8 +20,18 @@ module Gitlab
#Issues && Comments
issues = client.issues(project_identifier)
- issues["issues"].each do |issue|
- body = @formatter.author_line(issue["reported_by"]["username"], issue["content"])
+ issues.each do |issue|
+ body = ''
+ reporter = nil
+ author = 'Anonymous'
+
+ if issue["reported_by"] && issue["reported_by"]["username"]
+ reporter = issue["reported_by"]["username"]
+ author = reporter
+ end
+
+ body = @formatter.author_line(author)
+ body += issue["content"]
comments = client.issue_comments(project_identifier, issue["local_id"])
@@ -30,14 +40,20 @@ module Gitlab
end
comments.each do |comment|
- body += @formatter.comment(comment["author_info"]["username"], comment["utc_created_on"], comment["content"])
+ author = 'Anonymous'
+
+ if comment["author_info"] && comment["author_info"]["username"]
+ author = comment["author_info"]["username"]
+ end
+
+ body += @formatter.comment(author, comment["utc_created_on"], comment["content"])
end
project.issues.create!(
description: body,
title: issue["title"],
state: %w(resolved invalid duplicate wontfix).include?(issue["status"]) ? 'closed' : 'opened',
- author_id: gl_user_id(project, issue["reported_by"]["username"])
+ author_id: gl_user_id(project, reporter)
)
end
@@ -47,9 +63,13 @@ module Gitlab
private
def gl_user_id(project, bitbucket_id)
- user = User.joins(:identities).find_by("identities.extern_uid = ? AND identities.provider = 'bitbucket'", bitbucket_id.to_s)
- (user && user.id) || project.creator_id
- end
+ if bitbucket_id
+ user = User.joins(:identities).find_by("identities.extern_uid = ? AND identities.provider = 'bitbucket'", bitbucket_id.to_s)
+ (user && user.id) || project.creator_id
+ else
+ project.creator_id
+ end
+ end
end
end
end
diff --git a/lib/gitlab/color_schemes.rb b/lib/gitlab/color_schemes.rb
new file mode 100644
index 00000000000..9c4664df903
--- /dev/null
+++ b/lib/gitlab/color_schemes.rb
@@ -0,0 +1,67 @@
+module Gitlab
+ # Module containing GitLab's syntax color scheme definitions and helper
+ # methods for accessing them.
+ module ColorSchemes
+ # Struct class representing a single Scheme
+ Scheme = Struct.new(:id, :name, :css_class)
+
+ SCHEMES = [
+ Scheme.new(1, 'White', 'white'),
+ Scheme.new(2, 'Dark', 'dark'),
+ Scheme.new(3, 'Solarized Light', 'solarized-light'),
+ Scheme.new(4, 'Solarized Dark', 'solarized-dark'),
+ Scheme.new(5, 'Monokai', 'monokai')
+ ].freeze
+
+ # Convenience method to get a space-separated String of all the color scheme
+ # classes that might be applied to a code block.
+ #
+ # Returns a String
+ def self.body_classes
+ SCHEMES.collect(&:css_class).uniq.join(' ')
+ end
+
+ # Get a Scheme by its ID
+ #
+ # If the ID is invalid, returns the default Scheme.
+ #
+ # id - Integer ID
+ #
+ # Returns a Scheme
+ def self.by_id(id)
+ SCHEMES.detect { |s| s.id == id } || default
+ end
+
+ # Returns the number of defined Schemes
+ def self.count
+ SCHEMES.size
+ end
+
+ # Get the default Scheme
+ #
+ # Returns a Scheme
+ def self.default
+ by_id(1)
+ end
+
+ # Iterate through each Scheme
+ #
+ # Yields the Scheme object
+ def self.each(&block)
+ SCHEMES.each(&block)
+ end
+
+ # Get the Scheme for the specified user, or the default
+ #
+ # user - User record
+ #
+ # Returns a Scheme
+ def self.for_user(user)
+ if user
+ by_id(user.color_scheme_id)
+ else
+ default
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb
index 1a2a50a14d0..0ea1b6a2f6f 100644
--- a/lib/gitlab/current_settings.rb
+++ b/lib/gitlab/current_settings.rb
@@ -4,7 +4,7 @@ module Gitlab
key = :current_application_settings
RequestStore.store[key] ||= begin
- if ActiveRecord::Base.connected? && ActiveRecord::Base.connection.table_exists?('application_settings')
+ if connect_to_db?
ApplicationSetting.current || ApplicationSetting.create_from_defaults
else
fake_application_settings
@@ -26,5 +26,17 @@ module Gitlab
import_sources: Settings.gitlab['import_sources']
)
end
+
+ private
+
+ def connect_to_db?
+ use_db = if ENV['USE_DB'] == "false"
+ false
+ else
+ true
+ end
+
+ use_db && ActiveRecord::Base.connection.active? && ActiveRecord::Base.connection.table_exists?('application_settings')
+ end
end
end
diff --git a/lib/gitlab/fogbugz_import/client.rb b/lib/gitlab/fogbugz_import/client.rb
new file mode 100644
index 00000000000..431d50882fd
--- /dev/null
+++ b/lib/gitlab/fogbugz_import/client.rb
@@ -0,0 +1,56 @@
+require 'fogbugz'
+
+module Gitlab
+ module FogbugzImport
+ class Client
+ attr_reader :api
+
+ def initialize(options = {})
+ if options[:uri] && options[:token]
+ @api = ::Fogbugz::Interface.new(options)
+ elsif options[:uri] && options[:email] && options[:password]
+ @api = ::Fogbugz::Interface.new(options)
+ @api.authenticate
+ @api
+ end
+ end
+
+ def get_token
+ @api.token
+ end
+
+ def valid?
+ !get_token.blank?
+ end
+
+ def user_map
+ users = {}
+ res = @api.command(:listPeople)
+ res['people']['person'].each do |user|
+ users[user['ixPerson']] = { name: user['sFullName'], email: user['sEmail'] }
+ end
+ users
+ end
+
+ def repos
+ res = @api.command(:listProjects)
+ @repos ||= res['projects']['project'].map { |proj| FogbugzImport::Repository.new(proj) }
+ end
+
+ def repo(id)
+ repos.find { |r| r.id.to_s == id.to_s }
+ end
+
+ def cases(project_id)
+ project_name = repo(project_id).name
+ res = @api.command(:search, q: "project:'#{project_name}'", cols: 'ixPersonAssignedTo,ixPersonOpenedBy,ixPersonClosedBy,sStatus,sPriority,sCategory,fOpen,sTitle,sLatestTextSummary,dtOpened,dtClosed,dtResolved,dtLastUpdated,events')
+ return [] unless res['cases']['count'].to_i > 0
+ res['cases']['case']
+ end
+
+ def categories
+ @api.command(:listCategories)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/fogbugz_import/importer.rb b/lib/gitlab/fogbugz_import/importer.rb
new file mode 100644
index 00000000000..61e08b23543
--- /dev/null
+++ b/lib/gitlab/fogbugz_import/importer.rb
@@ -0,0 +1,298 @@
+module Gitlab
+ module FogbugzImport
+ class Importer
+ attr_reader :project, :repo
+
+ def initialize(project)
+ @project = project
+
+ import_data = project.import_data.try(:data)
+ repo_data = import_data['repo'] if import_data
+ @repo = FogbugzImport::Repository.new(repo_data)
+
+ @known_labels = Set.new
+ end
+
+ def execute
+ return true unless repo.valid?
+
+ data = project.import_data.try(:data)
+
+ client = Gitlab::FogbugzImport::Client.new(token: data['fb_session']['token'], uri: data['fb_session']['uri'])
+
+ @cases = client.cases(@repo.id.to_i)
+ @categories = client.categories
+
+ import_cases
+
+ true
+ end
+
+ private
+
+ def user_map
+ @user_map ||= begin
+ user_map = Hash.new
+ import_data = project.import_data.try(:data)
+ stored_user_map = import_data['user_map'] if import_data
+ user_map.update(stored_user_map) if stored_user_map
+
+ user_map
+ end
+ end
+
+ def import_labels
+ @categories['categories']['category'].each do |label|
+ create_label(label['sCategory'])
+ @known_labels << name
+ end
+ end
+
+ def nice_label_color(name)
+ case name
+ when 'Blocker'
+ '#ff0000'
+ when 'Crash'
+ '#ffcfcf'
+ when 'Major'
+ '#deffcf'
+ when 'Minor'
+ '#cfe9ff'
+ when 'Bug'
+ '#d9534f'
+ when 'Feature'
+ '#44ad8e'
+ when 'Technical Task'
+ '#4b6dd0'
+ else
+ '#e2e2e2'
+ end
+ end
+
+ def create_label(name)
+ color = nice_label_color(name)
+ Label.create!(project_id: project.id, title: name, color: color)
+ end
+
+ def user_info(person_id)
+ user_hash = user_map[person_id.to_s]
+
+ user_name = ''
+ gitlab_id = nil
+
+ unless user_hash.nil?
+ user_name = user_hash['name']
+ if user = User.find_by(id: user_hash['gitlab_user'])
+ user_name = "@#{user.username}"
+ gitlab_id = user.id
+ end
+ end
+
+ { name: user_name, gitlab_id: gitlab_id }
+ end
+
+ def import_cases
+ return unless @cases
+
+ while bug = @cases.shift
+ author = user_info(bug['ixPersonOpenedBy'])[:name]
+ date = DateTime.parse(bug['dtOpened'])
+
+ comments = bug['events']['event']
+
+ content = format_content(opened_content(comments))
+ body = format_issue_body(author, date, content)
+
+ labels = []
+ [bug['sCategory'], bug['sPriority']].each do |label|
+ unless label.blank?
+ labels << label
+ unless @known_labels.include?(label)
+ create_label(label)
+ @known_labels << label
+ end
+ end
+ end
+
+ assignee_id = user_info(bug['ixPersonAssignedTo'])[:gitlab_id]
+ author_id = user_info(bug['ixPersonOpenedBy'])[:gitlab_id] || project.creator_id
+
+ issue = Issue.create!(
+ project_id: project.id,
+ title: bug['sTitle'],
+ description: body,
+ author_id: author_id,
+ assignee_id: assignee_id,
+ state: bug['fOpen'] == 'true' ? 'opened' : 'closed'
+ )
+ issue.add_labels_by_names(labels)
+
+ if issue.iid != bug['ixBug']
+ issue.update_attribute(:iid, bug['ixBug'])
+ end
+
+ import_issue_comments(issue, comments)
+
+ issue.update_attribute(:created_at, date)
+
+ last_update = DateTime.parse(bug['dtLastUpdated'])
+ issue.update_attribute(:updated_at, last_update)
+ end
+ end
+
+ def opened_content(comments)
+ while comment = comments.shift
+ if comment['sVerb'] == 'Opened'
+ return comment['s']
+ end
+ end
+ ''
+ end
+
+ def import_issue_comments(issue, comments)
+ Note.transaction do
+ while comment = comments.shift
+ verb = comment['sVerb']
+
+ next if verb == 'Opened' || verb === 'Closed'
+
+ content = format_content(comment['s'])
+ attachments = format_attachments(comment['rgAttachments'])
+ updates = format_updates(comment)
+
+ next if content.blank? && attachments.empty? && updates.empty?
+
+ author = user_info(comment['ixPerson'])[:name]
+ author_id = user_info(comment['ixPerson'])[:gitlab_id] || project.creator_id
+ date = DateTime.parse(comment['dt'])
+
+ body = format_issue_comment_body(
+ comment['ixBugEvent'],
+ author,
+ date,
+ content,
+ attachments,
+ updates
+ )
+
+ note = Note.create!(
+ project_id: project.id,
+ noteable_type: "Issue",
+ noteable_id: issue.id,
+ author_id: author_id,
+ note: body
+ )
+
+ note.update_attribute(:created_at, date)
+ note.update_attribute(:updated_at, date)
+ end
+ end
+ end
+
+ def linkify_issues(s)
+ s = s.gsub(/([Ii]ssue) ([0-9]+)/, '\1 #\2')
+ s = s.gsub(/([Cc]ase) ([0-9]+)/, '\1 #\2')
+ s
+ end
+
+ def escape_for_markdown(s)
+ s = s.gsub(/^#/, "\\#")
+ s = s.gsub(/^-/, "\\-")
+ s = s.gsub("`", "\\~")
+ s = s.gsub("\r", "")
+ s = s.gsub("\n", " \n")
+ s
+ end
+
+ def format_content(raw_content)
+ return raw_content if raw_content.nil?
+ linkify_issues(escape_for_markdown(raw_content))
+ end
+
+ def format_attachments(raw_attachments)
+ return [] unless raw_attachments
+
+ attachments = case raw_attachments['attachment']
+ when Array
+ raw_attachments['attachment']
+ when Hash
+ [raw_attachments['attachment']]
+ else
+ []
+ end
+
+ attachments.map! { |a| format_attachment(a) }
+ attachments.compact
+ end
+
+ def format_attachment(attachment)
+ link = build_attachment_url(attachment['sURL'])
+
+ res = ::Projects::DownloadService.new(project, link).execute
+
+ return nil if res.nil?
+
+ text = "[#{res['alt']}](#{res['url']})"
+ text = "!#{text}" if res['is_image']
+ text
+ end
+
+ def build_attachment_url(rel_url)
+ data = project.import_data.try(:data)
+ uri = data['fb_session']['uri']
+ token = data['fb_session']['token']
+ "#{uri}/#{rel_url}&token=#{token}"
+ end
+
+ def format_updates(comment)
+ updates = []
+
+ if comment['sChanges']
+ updates << "*Changes: #{linkify_issues(comment['sChanges'].chomp)}*"
+ end
+
+ if comment['evtDescription']
+ updates << "*#{comment['evtDescription']}*"
+ end
+
+ updates
+ end
+
+ def format_issue_body(author, date, content)
+ body = []
+ body << "*By #{author} on #{date} (imported from FogBugz)*"
+ body << '---'
+
+ if content.blank?
+ content = '*(No description has been entered for this issue)*'
+ end
+ body << content
+
+ body.join("\n\n")
+ end
+
+ def format_issue_comment_body(id, author, date, content, attachments, updates)
+ body = []
+ body << "*By #{author} on #{date} (imported from FogBugz)*"
+ body << '---'
+
+ if content.blank?
+ content = "*(No comment has been entered for this change)*"
+ end
+ body << content
+
+ if updates.any?
+ body << '---'
+ body += updates
+ end
+
+ if attachments.any?
+ body << '---'
+ body += attachments
+ end
+
+ body.join("\n\n")
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/fogbugz_import/project_creator.rb b/lib/gitlab/fogbugz_import/project_creator.rb
new file mode 100644
index 00000000000..f02ea43910f
--- /dev/null
+++ b/lib/gitlab/fogbugz_import/project_creator.rb
@@ -0,0 +1,38 @@
+module Gitlab
+ module FogbugzImport
+ class ProjectCreator
+ attr_reader :repo, :fb_session, :namespace, :current_user, :user_map
+
+ def initialize(repo, fb_session, namespace, current_user, user_map = nil)
+ @repo = repo
+ @fb_session = fb_session
+ @namespace = namespace
+ @current_user = current_user
+ @user_map = user_map
+ end
+
+ def execute
+ project = ::Projects::CreateService.new(current_user,
+ name: repo.safe_name,
+ path: repo.path,
+ namespace: namespace,
+ creator: current_user,
+ visibility_level: Gitlab::VisibilityLevel::INTERNAL,
+ import_type: 'fogbugz',
+ import_source: repo.name,
+ import_url: Project::UNKNOWN_IMPORT_URL
+ ).execute
+
+ import_data = project.create_import_data(
+ data: {
+ 'repo' => repo.raw_data,
+ 'user_map' => user_map,
+ 'fb_session' => fb_session
+ }
+ )
+
+ project
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/fogbugz_import/repository.rb b/lib/gitlab/fogbugz_import/repository.rb
new file mode 100644
index 00000000000..d1dc63db2b2
--- /dev/null
+++ b/lib/gitlab/fogbugz_import/repository.rb
@@ -0,0 +1,31 @@
+module Gitlab
+ module FogbugzImport
+ class Repository
+ attr_accessor :raw_data
+
+ def initialize(raw_data)
+ @raw_data = raw_data
+ end
+
+ def valid?
+ raw_data.is_a?(Hash)
+ end
+
+ def id
+ raw_data['ixProject']
+ end
+
+ def name
+ raw_data['sProject']
+ end
+
+ def safe_name
+ name.gsub(/[^\s\w.-]/, '')
+ end
+
+ def path
+ safe_name.gsub(/[\s]/, '_')
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb
index 8c106a61735..bd7340a80f1 100644
--- a/lib/gitlab/github_import/importer.rb
+++ b/lib/gitlab/github_import/importer.rb
@@ -18,7 +18,8 @@ module Gitlab
direction: :asc).each do |issue|
if issue.pull_request.nil?
- body = @formatter.author_line(issue.user.login, issue.body)
+ body = @formatter.author_line(issue.user.login)
+ body += issue.body
if issue.comments > 0
body += @formatter.comments_header
diff --git a/lib/gitlab/gitlab_import/importer.rb b/lib/gitlab/gitlab_import/importer.rb
index 50594d2b24f..e24b94d6159 100644
--- a/lib/gitlab/gitlab_import/importer.rb
+++ b/lib/gitlab/gitlab_import/importer.rb
@@ -18,7 +18,8 @@ module Gitlab
issues = client.issues(project_identifier)
issues.each do |issue|
- body = @formatter.author_line(issue["author"]["name"], issue["description"])
+ body = @formatter.author_line(issue["author"]["name"])
+ body += issue["description"]
comments = client.issue_comments(project_identifier, issue["id"])
diff --git a/lib/gitlab/import_formatter.rb b/lib/gitlab/import_formatter.rb
index 72e041a90b1..3e54456e936 100644
--- a/lib/gitlab/import_formatter.rb
+++ b/lib/gitlab/import_formatter.rb
@@ -8,8 +8,8 @@ module Gitlab
"\n\n\n**Imported comments:**\n"
end
- def author_line(author, body)
- "*Created by: #{author}*\n\n#{body}"
+ def author_line(author)
+ "*Created by: #{author}*\n\n"
end
end
end
diff --git a/lib/gitlab/import_sources.rb b/lib/gitlab/import_sources.rb
index 991b70aab6a..ccfdfbe73e8 100644
--- a/lib/gitlab/import_sources.rb
+++ b/lib/gitlab/import_sources.rb
@@ -19,6 +19,7 @@ module Gitlab
'GitLab.com' => 'gitlab',
'Gitorious.org' => 'gitorious',
'Google Code' => 'google_code',
+ 'FogBugz' => 'fogbugz',
'Any repo by URL' => 'git',
}
end
diff --git a/lib/gitlab/ldap/user.rb b/lib/gitlab/ldap/user.rb
index f7f3ba9ad7d..04a22237478 100644
--- a/lib/gitlab/ldap/user.rb
+++ b/lib/gitlab/ldap/user.rb
@@ -44,9 +44,14 @@ module Gitlab
gl_user.skip_reconfirmation!
gl_user.email = auth_hash.email
- # Build new identity only if we dont have have same one
- gl_user.identities.find_or_initialize_by(provider: auth_hash.provider,
- extern_uid: auth_hash.uid)
+ # find_or_initialize_by doesn't update `gl_user.identities`, and isn't autosaved.
+ identity = gl_user.identities.find { |identity| identity.provider == auth_hash.provider }
+ identity ||= gl_user.identities.build(provider: auth_hash.provider)
+
+ # For a new user set extern_uid to the LDAP DN
+ # For an existing user with matching email but changed DN, update the DN.
+ # For an existing user with no change in DN, this line changes nothing.
+ identity.extern_uid = auth_hash.uid
gl_user
end
diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb
index 9f6e19a09fd..097caf67a65 100644
--- a/lib/gitlab/markdown.rb
+++ b/lib/gitlab/markdown.rb
@@ -5,6 +5,32 @@ module Gitlab
#
# See the files in `lib/gitlab/markdown/` for specific processing information.
module Markdown
+ # Convert a Markdown String into an HTML-safe String of HTML
+ #
+ # markdown - Markdown String
+ # context - Hash of context options passed to our HTML Pipeline
+ #
+ # Returns an HTML-safe String
+ def self.render(markdown, context = {})
+ html = renderer.render(markdown)
+ html = gfm(html, context)
+
+ html.html_safe
+ end
+
+ # Convert a Markdown String into HTML without going through the HTML
+ # Pipeline.
+ #
+ # Note that because the pipeline is skipped, SanitizationFilter is as well.
+ # Do not output the result of this method to the user.
+ #
+ # markdown - Markdown String
+ #
+ # Returns a String
+ def self.render_without_gfm(markdown)
+ renderer.render(markdown)
+ end
+
# Provide autoload paths for filters to prevent a circular dependency error
autoload :AutolinkFilter, 'gitlab/markdown/autolink_filter'
autoload :CommitRangeReferenceFilter, 'gitlab/markdown/commit_range_reference_filter'
@@ -18,6 +44,7 @@ module Gitlab
autoload :RelativeLinkFilter, 'gitlab/markdown/relative_link_filter'
autoload :SanitizationFilter, 'gitlab/markdown/sanitization_filter'
autoload :SnippetReferenceFilter, 'gitlab/markdown/snippet_reference_filter'
+ autoload :SyntaxHighlightFilter, 'gitlab/markdown/syntax_highlight_filter'
autoload :TableOfContentsFilter, 'gitlab/markdown/table_of_contents_filter'
autoload :TaskListFilter, 'gitlab/markdown/task_list_filter'
autoload :UserReferenceFilter, 'gitlab/markdown/user_reference_filter'
@@ -28,8 +55,7 @@ module Gitlab
# options - A Hash of options used to customize output (default: {}):
# :xhtml - output XHTML instead of HTML
# :reference_only_path - Use relative path for reference links
- # html_options - extra options for the reference links as given to link_to
- def gfm(text, options = {}, html_options = {})
+ def self.gfm(text, options = {})
return text if text.nil?
# Duplicate the string so we don't alter the original, then call to_str
@@ -40,8 +66,8 @@ module Gitlab
options.reverse_merge!(
xhtml: false,
reference_only_path: true,
- project: @project,
- current_user: current_user
+ project: options[:project],
+ current_user: options[:current_user]
)
@pipeline ||= HTML::Pipeline.new(filters)
@@ -61,12 +87,11 @@ module Gitlab
current_user: options[:current_user],
only_path: options[:reference_only_path],
project: options[:project],
- reference_class: html_options[:class],
# RelativeLinkFilter
- ref: @ref,
- requested_path: @path,
- project_wiki: @project_wiki
+ ref: options[:ref],
+ requested_path: options[:path],
+ project_wiki: options[:project_wiki]
}
result = @pipeline.call(text, context)
@@ -83,14 +108,36 @@ module Gitlab
private
+ def self.renderer
+ @markdown ||= begin
+ renderer = Redcarpet::Render::HTML.new
+ Redcarpet::Markdown.new(renderer, redcarpet_options)
+ end
+ end
+
+ def self.redcarpet_options
+ # https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use
+ @redcarpet_options ||= {
+ fenced_code_blocks: true,
+ footnotes: true,
+ lax_spacing: true,
+ no_intra_emphasis: true,
+ space_after_headers: true,
+ strikethrough: true,
+ superscript: true,
+ tables: true
+ }.freeze
+ end
+
# Filters used in our pipeline
#
# SanitizationFilter should come first so that all generated reference HTML
# goes through untouched.
#
# See https://github.com/jch/html-pipeline#filters for more filters.
- def filters
+ def self.filters
[
+ Gitlab::Markdown::SyntaxHighlightFilter,
Gitlab::Markdown::SanitizationFilter,
Gitlab::Markdown::RelativeLinkFilter,
diff --git a/lib/gitlab/markdown/autolink_filter.rb b/lib/gitlab/markdown/autolink_filter.rb
index 541f1d88ffc..c37c3bc55bf 100644
--- a/lib/gitlab/markdown/autolink_filter.rb
+++ b/lib/gitlab/markdown/autolink_filter.rb
@@ -1,3 +1,4 @@
+require 'gitlab/markdown'
require 'html/pipeline/filter'
require 'uri'
diff --git a/lib/gitlab/markdown/commit_range_reference_filter.rb b/lib/gitlab/markdown/commit_range_reference_filter.rb
index 52efed66553..bb496135d92 100644
--- a/lib/gitlab/markdown/commit_range_reference_filter.rb
+++ b/lib/gitlab/markdown/commit_range_reference_filter.rb
@@ -1,3 +1,5 @@
+require 'gitlab/markdown'
+
module Gitlab
module Markdown
# HTML filter that replaces commit range references with links.
diff --git a/lib/gitlab/markdown/commit_reference_filter.rb b/lib/gitlab/markdown/commit_reference_filter.rb
index 066fb0c1853..fcbb2e936a5 100644
--- a/lib/gitlab/markdown/commit_reference_filter.rb
+++ b/lib/gitlab/markdown/commit_reference_filter.rb
@@ -1,3 +1,5 @@
+require 'gitlab/markdown'
+
module Gitlab
module Markdown
# HTML filter that replaces commit references with links.
diff --git a/lib/gitlab/markdown/cross_project_reference.rb b/lib/gitlab/markdown/cross_project_reference.rb
index 66c256c5104..855748fdccc 100644
--- a/lib/gitlab/markdown/cross_project_reference.rb
+++ b/lib/gitlab/markdown/cross_project_reference.rb
@@ -1,3 +1,5 @@
+require 'gitlab/markdown'
+
module Gitlab
module Markdown
# Common methods for ReferenceFilters that support an optional cross-project
diff --git a/lib/gitlab/markdown/emoji_filter.rb b/lib/gitlab/markdown/emoji_filter.rb
index 6794ab9c897..da10e4d3760 100644
--- a/lib/gitlab/markdown/emoji_filter.rb
+++ b/lib/gitlab/markdown/emoji_filter.rb
@@ -1,6 +1,7 @@
+require 'action_controller'
+require 'gitlab/markdown'
require 'gitlab_emoji'
require 'html/pipeline/filter'
-require 'action_controller'
module Gitlab
module Markdown
diff --git a/lib/gitlab/markdown/external_issue_reference_filter.rb b/lib/gitlab/markdown/external_issue_reference_filter.rb
index afd28dd8cf3..f7c43e1ca89 100644
--- a/lib/gitlab/markdown/external_issue_reference_filter.rb
+++ b/lib/gitlab/markdown/external_issue_reference_filter.rb
@@ -1,3 +1,5 @@
+require 'gitlab/markdown'
+
module Gitlab
module Markdown
# HTML filter that replaces external issue tracker references with links.
diff --git a/lib/gitlab/markdown/external_link_filter.rb b/lib/gitlab/markdown/external_link_filter.rb
index c539e0fb823..29e51b6ade6 100644
--- a/lib/gitlab/markdown/external_link_filter.rb
+++ b/lib/gitlab/markdown/external_link_filter.rb
@@ -1,3 +1,4 @@
+require 'gitlab/markdown'
require 'html/pipeline/filter'
module Gitlab
diff --git a/lib/gitlab/markdown/issue_reference_filter.rb b/lib/gitlab/markdown/issue_reference_filter.rb
index ab6f6bc1cf7..01320f80796 100644
--- a/lib/gitlab/markdown/issue_reference_filter.rb
+++ b/lib/gitlab/markdown/issue_reference_filter.rb
@@ -1,3 +1,5 @@
+require 'gitlab/markdown'
+
module Gitlab
module Markdown
# HTML filter that replaces issue references with links. References to
diff --git a/lib/gitlab/markdown/label_reference_filter.rb b/lib/gitlab/markdown/label_reference_filter.rb
index 76d56359693..1e5cb12071e 100644
--- a/lib/gitlab/markdown/label_reference_filter.rb
+++ b/lib/gitlab/markdown/label_reference_filter.rb
@@ -1,3 +1,5 @@
+require 'gitlab/markdown'
+
module Gitlab
module Markdown
# HTML filter that replaces label references with links.
diff --git a/lib/gitlab/markdown/merge_request_reference_filter.rb b/lib/gitlab/markdown/merge_request_reference_filter.rb
index c6313bab94a..ecbd263d0e0 100644
--- a/lib/gitlab/markdown/merge_request_reference_filter.rb
+++ b/lib/gitlab/markdown/merge_request_reference_filter.rb
@@ -1,3 +1,5 @@
+require 'gitlab/markdown'
+
module Gitlab
module Markdown
# HTML filter that replaces merge request references with links. References
diff --git a/lib/gitlab/markdown/reference_filter.rb b/lib/gitlab/markdown/reference_filter.rb
index 47ee1d99da3..9b293c957d6 100644
--- a/lib/gitlab/markdown/reference_filter.rb
+++ b/lib/gitlab/markdown/reference_filter.rb
@@ -1,4 +1,5 @@
require 'active_support/core_ext/string/output_safety'
+require 'gitlab/markdown'
require 'html/pipeline/filter'
module Gitlab
@@ -9,7 +10,6 @@ module Gitlab
#
# Context options:
# :project (required) - Current project, ignored if reference is cross-project.
- # :reference_class - Custom CSS class added to reference links.
# :only_path - Generate path-only links.
#
# Results:
@@ -70,7 +70,7 @@ module Gitlab
end
def reference_class(type)
- "gfm gfm-#{type} #{context[:reference_class]}".strip
+ "gfm gfm-#{type}"
end
# Iterate through the document's text nodes, yielding the current node's
diff --git a/lib/gitlab/markdown/relative_link_filter.rb b/lib/gitlab/markdown/relative_link_filter.rb
index 30f50b82996..8c5cf51bfe1 100644
--- a/lib/gitlab/markdown/relative_link_filter.rb
+++ b/lib/gitlab/markdown/relative_link_filter.rb
@@ -1,3 +1,4 @@
+require 'gitlab/markdown'
require 'html/pipeline/filter'
require 'uri'
diff --git a/lib/gitlab/markdown/sanitization_filter.rb b/lib/gitlab/markdown/sanitization_filter.rb
index 74b3a8d274f..68ed57f6257 100644
--- a/lib/gitlab/markdown/sanitization_filter.rb
+++ b/lib/gitlab/markdown/sanitization_filter.rb
@@ -1,3 +1,4 @@
+require 'gitlab/markdown'
require 'html/pipeline/filter'
require 'html/pipeline/sanitization_filter'
diff --git a/lib/gitlab/markdown/snippet_reference_filter.rb b/lib/gitlab/markdown/snippet_reference_filter.rb
index 1a2d0fcf98b..e2cf89cb1d8 100644
--- a/lib/gitlab/markdown/snippet_reference_filter.rb
+++ b/lib/gitlab/markdown/snippet_reference_filter.rb
@@ -1,3 +1,5 @@
+require 'gitlab/markdown'
+
module Gitlab
module Markdown
# HTML filter that replaces snippet references with links. References to
diff --git a/lib/gitlab/markdown/syntax_highlight_filter.rb b/lib/gitlab/markdown/syntax_highlight_filter.rb
new file mode 100644
index 00000000000..86f4385753a
--- /dev/null
+++ b/lib/gitlab/markdown/syntax_highlight_filter.rb
@@ -0,0 +1,39 @@
+require 'gitlab/markdown'
+require 'html/pipeline/filter'
+require 'rouge/plugins/redcarpet'
+
+module Gitlab
+ module Markdown
+ # HTML Filter to highlight fenced code blocks
+ #
+ class SyntaxHighlightFilter < HTML::Pipeline::Filter
+ include Rouge::Plugins::Redcarpet
+
+ def call
+ doc.search('pre > code').each do |node|
+ highlight_node(node)
+ end
+
+ doc
+ end
+
+ def highlight_node(node)
+ language = node.attr('class')
+ code = node.text
+
+ highlighted = block_code(code, language)
+
+ # Replace the parent `pre` element with the entire highlighted block
+ node.parent.replace(highlighted)
+ end
+
+ private
+
+ # Override Rouge::Plugins::Redcarpet#rouge_formatter
+ def rouge_formatter(lexer)
+ Rouge::Formatters::HTMLGitlab.new(
+ cssclass: "code highlight js-syntax-highlight #{lexer.tag}")
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/markdown/table_of_contents_filter.rb b/lib/gitlab/markdown/table_of_contents_filter.rb
index 38887c9778c..bbb3bf7fc8b 100644
--- a/lib/gitlab/markdown/table_of_contents_filter.rb
+++ b/lib/gitlab/markdown/table_of_contents_filter.rb
@@ -1,3 +1,4 @@
+require 'gitlab/markdown'
require 'html/pipeline/filter'
module Gitlab
diff --git a/lib/gitlab/markdown/task_list_filter.rb b/lib/gitlab/markdown/task_list_filter.rb
index c6eb2e2bf6d..2f133ae8500 100644
--- a/lib/gitlab/markdown/task_list_filter.rb
+++ b/lib/gitlab/markdown/task_list_filter.rb
@@ -1,3 +1,4 @@
+require 'gitlab/markdown'
require 'task_list/filter'
module Gitlab
diff --git a/lib/gitlab/markdown/user_reference_filter.rb b/lib/gitlab/markdown/user_reference_filter.rb
index 4e1cce2a0c1..6f436ea7167 100644
--- a/lib/gitlab/markdown/user_reference_filter.rb
+++ b/lib/gitlab/markdown/user_reference_filter.rb
@@ -1,3 +1,5 @@
+require 'gitlab/markdown'
+
module Gitlab
module Markdown
# HTML filter that replaces user or group references with links.
diff --git a/lib/gitlab/reference_extractor.rb b/lib/gitlab/reference_extractor.rb
index e836b05ff25..0961bd80421 100644
--- a/lib/gitlab/reference_extractor.rb
+++ b/lib/gitlab/reference_extractor.rb
@@ -1,3 +1,5 @@
+require 'gitlab/markdown'
+
module Gitlab
# Extract possible GFM references from an arbitrary String for further processing.
class ReferenceExtractor
@@ -10,7 +12,7 @@ module Gitlab
def analyze(text)
references.clear
- @text = markdown.render(text.dup)
+ @text = Gitlab::Markdown.render_without_gfm(text)
end
%i(user label issue merge_request snippet commit commit_range).each do |type|
@@ -21,10 +23,6 @@ module Gitlab
private
- def markdown
- @markdown ||= Redcarpet::Markdown.new(Redcarpet::Render::HTML, GitlabMarkdownHelper::MARKDOWN_OPTIONS)
- end
-
def references
@references ||= Hash.new do |references, type|
type = type.to_sym
@@ -42,7 +40,7 @@ module Gitlab
# Returns the results Array for the requested filter type
def pipeline_result(filter_type)
klass = filter_type.to_s.camelize + 'ReferenceFilter'
- filter = "Gitlab::Markdown::#{klass}".constantize
+ filter = Gitlab::Markdown.const_get(klass)
context = {
project: project,
diff --git a/lib/gitlab/themes.rb b/lib/gitlab/themes.rb
index 5209df92795..83f91de810c 100644
--- a/lib/gitlab/themes.rb
+++ b/lib/gitlab/themes.rb
@@ -37,6 +37,11 @@ module Gitlab
THEMES.detect { |t| t.id == id } || default
end
+ # Returns the number of defined Themes
+ def self.count
+ THEMES.size
+ end
+
# Get the default Theme
#
# Returns a Theme
@@ -51,6 +56,19 @@ module Gitlab
THEMES.each(&block)
end
+ # Get the Theme for the specified user, or the default
+ #
+ # user - User record
+ #
+ # Returns a Theme
+ def self.for_user(user)
+ if user
+ by_id(user.theme_id)
+ else
+ default
+ end
+ end
+
private
def self.default_id
diff --git a/lib/gitlab/url_builder.rb b/lib/gitlab/url_builder.rb
index 95a455b5dd7..6f0d02cafd1 100644
--- a/lib/gitlab/url_builder.rb
+++ b/lib/gitlab/url_builder.rb
@@ -23,12 +23,12 @@ module Gitlab
def build_issue_url(id)
issue = Issue.find(id)
- issue_url(issue, host: Gitlab.config.gitlab['url'])
+ issue_url(issue)
end
def build_merge_request_url(id)
merge_request = MergeRequest.find(id)
- merge_request_url(merge_request, host: Gitlab.config.gitlab['url'])
+ merge_request_url(merge_request)
end
def build_note_url(id)
@@ -37,22 +37,18 @@ module Gitlab
namespace_project_commit_url(namespace_id: note.project.namespace,
id: note.commit_id,
project_id: note.project,
- host: Gitlab.config.gitlab['url'],
anchor: "note_#{note.id}")
elsif note.for_issue?
issue = Issue.find(note.noteable_id)
issue_url(issue,
- host: Gitlab.config.gitlab['url'],
anchor: "note_#{note.id}")
elsif note.for_merge_request?
merge_request = MergeRequest.find(note.noteable_id)
merge_request_url(merge_request,
- host: Gitlab.config.gitlab['url'],
anchor: "note_#{note.id}")
elsif note.for_project_snippet?
snippet = Snippet.find(note.noteable_id)
project_snippet_url(snippet,
- host: Gitlab.config.gitlab['url'],
anchor: "note_#{note.id}")
end
end
diff --git a/lib/redcarpet/render/gitlab_html.rb b/lib/redcarpet/render/gitlab_html.rb
deleted file mode 100644
index f57b56cbdf0..00000000000
--- a/lib/redcarpet/render/gitlab_html.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-require 'active_support/core_ext/string/output_safety'
-
-class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
- attr_reader :template
- alias_method :h, :template
-
- def initialize(template, color_scheme, options = {})
- @template = template
- @color_scheme = color_scheme
- @options = options.dup
-
- @options.reverse_merge!(
- # Handled further down the line by Gitlab::Markdown::SanitizationFilter
- escape_html: false,
- project: @template.instance_variable_get("@project")
- )
-
- super(options)
- end
-
- def normal_text(text)
- ERB::Util.html_escape_once(text)
- end
-
- # Stolen from Rouge::Plugins::Redcarpet as this module is not required
- # from Rouge's gem root.
- def block_code(code, language)
- lexer = Rouge::Lexer.find_fancy(language, code) || Rouge::Lexers::PlainText
-
- # XXX HACK: Redcarpet strips hard tabs out of code blocks,
- # so we assume you're not using leading spaces that aren't tabs,
- # and just replace them here.
- if lexer.tag == 'make'
- code.gsub!(/^ /, "\t")
- end
-
- formatter = Rouge::Formatters::HTMLGitlab.new(
- cssclass: "code highlight #{@color_scheme} #{lexer.tag}"
- )
- formatter.format(lexer.lex(code))
- end
-
- def postprocess(full_document)
- h.gfm(full_document, @options)
- end
-end
diff --git a/lib/support/init.d/gitlab b/lib/support/init.d/gitlab
index 457bd31e23b..a80e7e77430 100755
--- a/lib/support/init.d/gitlab
+++ b/lib/support/init.d/gitlab
@@ -37,6 +37,10 @@ web_server_pid_path="$pid_path/unicorn.pid"
sidekiq_pid_path="$pid_path/sidekiq.pid"
mail_room_enabled=false
mail_room_pid_path="$pid_path/mail_room.pid"
+gitlab_git_http_server_pid_path="$pid_path/gitlab-git-http-server.pid"
+gitlab_git_http_server_options="-listenUmask 0 -listenNetwork unix -listenAddr $socket_path/gitlab-git-http-server.socket -authBackend http://127.0.0.1:8080"
+gitlab_git_http_server_repo_root='/home/git/repositories'
+gitlab_git_http_server_log="$app_root/log/gitlab-git-http-server.log"
shell_path="/bin/bash"
# Read configuration variable file if it is present
@@ -72,6 +76,11 @@ check_pids(){
else
spid=0
fi
+ if [ -f "$gitlab_git_http_server_pid_path" ]; then
+ hpid=$(cat "$gitlab_git_http_server_pid_path")
+ else
+ hpid=0
+ fi
if [ "$mail_room_enabled" = true ]; then
if [ -f "$mail_room_pid_path" ]; then
mpid=$(cat "$mail_room_pid_path")
@@ -85,7 +94,7 @@ check_pids(){
wait_for_pids(){
# We are sleeping a bit here mostly because sidekiq is slow at writing it's pid
i=0;
- while [ ! -f $web_server_pid_path ] || [ ! -f $sidekiq_pid_path ] || { [ "$mail_room_enabled" = true ] && [ ! -f $mail_room_pid_path ]; }; do
+ while [ ! -f $web_server_pid_path ] || [ ! -f $sidekiq_pid_path ] || [ ! -f $gitlab_git_http_server_pid_path ] || { [ "$mail_room_enabled" = true ] && [ ! -f $mail_room_pid_path ]; }; do
sleep 0.1;
i=$((i+1))
if [ $((i%10)) = 0 ]; then
@@ -120,6 +129,12 @@ check_status(){
else
sidekiq_status="-1"
fi
+ if [ $hpid -ne 0 ]; then
+ kill -0 "$hpid" 2>/dev/null
+ gitlab_git_http_server_status="$?"
+ else
+ gitlab_git_http_server_status="-1"
+ fi
if [ "$mail_room_enabled" = true ]; then
if [ $mpid -ne 0 ]; then
kill -0 "$mpid" 2>/dev/null
@@ -128,7 +143,7 @@ check_status(){
mail_room_status="-1"
fi
fi
- if [ $web_status = 0 ] && [ $sidekiq_status = 0 ] && { [ "$mail_room_enabled" != true ] || [ $mail_room_status = 0 ]; }; then
+ if [ $web_status = 0 ] && [ $sidekiq_status = 0 ] && [ $gitlab_git_http_server_status = 0 ] && { [ "$mail_room_enabled" != true ] || [ $mail_room_status = 0 ]; }; then
gitlab_status=0
else
# http://refspecs.linuxbase.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html
@@ -156,6 +171,13 @@ check_stale_pids(){
exit 1
fi
fi
+ if [ "$hpid" != "0" ] && [ "$gitlab_git_http_server_status" != "0" ]; then
+ echo "Removing stale gitlab-git-http-server pid. This is most likely caused by gitlab-git-http-server crashing the last time it ran."
+ if ! rm "$gitlab_git_http_server_pid_path"; then
+ echo "Unable to remove stale pid, exiting"
+ exit 1
+ fi
+ fi
if [ "$mail_room_enabled" = true ] && [ "$mpid" != "0" ] && [ "$mail_room_status" != "0" ]; then
echo "Removing stale MailRoom job dispatcher pid. This is most likely caused by MailRoom crashing the last time it ran."
if ! rm "$mail_room_pid_path"; then
@@ -168,7 +190,7 @@ check_stale_pids(){
## If no parts of the service is running, bail out.
exit_if_not_running(){
check_stale_pids
- if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; }; then
+ if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_git_http_server_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; }; then
echo "GitLab is not running."
exit
fi
@@ -184,6 +206,9 @@ start_gitlab() {
if [ "$sidekiq_status" != "0" ]; then
echo "Starting GitLab Sidekiq"
fi
+ if [ "$gitlab_git_http_server_status" != "0" ]; then
+ echo "Starting gitlab-git-http-server"
+ fi
if [ "$mail_room_enabled" = true ] && [ "$mail_room_status" != "0" ]; then
echo "Starting GitLab MailRoom"
fi
@@ -205,6 +230,17 @@ start_gitlab() {
RAILS_ENV=$RAILS_ENV bin/background_jobs start &
fi
+ if [ "$gitlab_git_http_server_status" = "0" ]; then
+ echo "The gitlab-git-http-server is already running with pid $spid, not restarting"
+ else
+ # No need to remove a socket, gitlab-git-http-server does this itself
+ $app_root/bin/daemon_with_pidfile $gitlab_git_http_server_pid_path \
+ $app_root/../gitlab-git-http-server/gitlab-git-http-server \
+ $gitlab_git_http_server_options \
+ $gitlab_git_http_server_repo_root \
+ >> $gitlab_git_http_server_log 2>&1 &
+ fi
+
if [ "$mail_room_enabled" = true ]; then
# If MailRoom is already running, don't start it again.
if [ "$mail_room_status" = "0" ]; then
@@ -226,33 +262,27 @@ stop_gitlab() {
if [ "$web_status" = "0" ]; then
echo "Shutting down GitLab Unicorn"
+ RAILS_ENV=$RAILS_ENV bin/web stop
fi
if [ "$sidekiq_status" = "0" ]; then
echo "Shutting down GitLab Sidekiq"
- fi
- if [ "$mail_room_enabled" = true ] && [ "$mail_room_status" = "0" ]; then
- echo "Shutting down GitLab MailRoom"
- fi
-
- # If the Unicorn web server is running, tell it to stop;
- if [ "$web_status" = "0" ]; then
- RAILS_ENV=$RAILS_ENV bin/web stop
- fi
- # And do the same thing for the Sidekiq.
- if [ "$sidekiq_status" = "0" ]; then
RAILS_ENV=$RAILS_ENV bin/background_jobs stop
fi
- # And do the same thing for the MailRoom.
+ if [ "$gitlab_git_http_server_status" = "0" ]; then
+ echo "Shutting down gitlab-git-http-server"
+ kill -- $(cat $gitlab_git_http_server_pid_path)
+ fi
if [ "$mail_room_enabled" = true ] && [ "$mail_room_status" = "0" ]; then
+ echo "Shutting down GitLab MailRoom"
RAILS_ENV=$RAILS_ENV bin/mail_room stop
fi
# If something needs to be stopped, lets wait for it to stop. Never use SIGKILL in a script.
- while [ "$web_status" = "0" ] || [ "$sidekiq_status" = "0" ] || { [ "$mail_room_enabled" = true ] && [ "$mail_room_status" = "0" ]; }; do
+ while [ "$web_status" = "0" ] || [ "$sidekiq_status" = "0" ] || [ "$gitlab_git_http_server_status" = "0" ] || { [ "$mail_room_enabled" = true ] && [ "$mail_room_status" = "0" ]; }; do
sleep 1
check_status
printf "."
- if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; }; then
+ if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_git_http_server_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; }; then
printf "\n"
break
fi
@@ -262,6 +292,7 @@ stop_gitlab() {
# Cleaning up unused pids
rm "$web_server_pid_path" 2>/dev/null
# rm "$sidekiq_pid_path" 2>/dev/null # Sidekiq seems to be cleaning up it's own pid.
+ rm -f "$gitlab_git_http_server_pid_path"
if [ "$mail_room_enabled" = true ]; then
rm "$mail_room_pid_path" 2>/dev/null
fi
@@ -272,7 +303,7 @@ stop_gitlab() {
## Prints the status of GitLab and it's components.
print_status() {
check_status
- if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; }; then
+ if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_git_http_server_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; }; then
echo "GitLab is not running."
return
fi
@@ -286,6 +317,11 @@ print_status() {
else
printf "The GitLab Sidekiq job dispatcher is \033[31mnot running\033[0m.\n"
fi
+ if [ "$gitlab_git_http_server_status" = "0" ]; then
+ echo "The gitlab-git-http-server with pid $hpid is running."
+ else
+ printf "The gitlab-git-http-server is \033[31mnot running\033[0m.\n"
+ fi
if [ "$mail_room_enabled" = true ]; then
if [ "$mail_room_status" = "0" ]; then
echo "The GitLab MailRoom email processor with pid $mpid is running."
@@ -324,7 +360,7 @@ reload_gitlab(){
## Restarts Sidekiq and Unicorn.
restart_gitlab(){
check_status
- if [ "$web_status" = "0" ] || [ "$sidekiq_status" = "0" ] || { [ "$mail_room_enabled" = true ] && [ "$mail_room_status" = "0" ]; }; then
+ if [ "$web_status" = "0" ] || [ "$sidekiq_status" = "0" ] || [ "$gitlab_git_http_server" = "0" ] || { [ "$mail_room_enabled" = true ] && [ "$mail_room_status" = "0" ]; }; then
stop_gitlab
fi
start_gitlab
diff --git a/lib/support/init.d/gitlab.default.example b/lib/support/init.d/gitlab.default.example
index fd70cb7cc74..aab5acaa72c 100755
--- a/lib/support/init.d/gitlab.default.example
+++ b/lib/support/init.d/gitlab.default.example
@@ -30,6 +30,16 @@ web_server_pid_path="$pid_path/unicorn.pid"
# The default is "$pid_path/sidekiq.pid"
sidekiq_pid_path="$pid_path/sidekiq.pid"
+gitlab_git_http_server_pid_path="$pid_path/gitlab-git-http-server.pid"
+# The -listenXxx settings determine where gitlab-git-http-server
+# listens for connections from NGINX. To listen on localhost:8181, write
+# '-listenNetwork tcp -listenAddr localhost:8181'.
+# The -authBackend setting tells gitlab-git-http-server where it can reach
+# Unicorn.
+gitlab_git_http_server_options="-listenUmask 0 -listenNetwork unix -listenAddr $socket_path/gitlab-git-http-server.socket -authBackend http://127.0.0.1:8080"
+gitlab_git_http_server_repo_root="/home/git/repositories"
+gitlab_git_http_server_log="$app_root/log/gitlab-git-http-server.log"
+
# mail_room_enabled specifies whether mail_room, which is used to process incoming email, is enabled.
# This is required for the Reply by email feature.
# The default is "false"
diff --git a/lib/support/nginx/gitlab b/lib/support/nginx/gitlab
index efa0898900f..7218a4d2f20 100644
--- a/lib/support/nginx/gitlab
+++ b/lib/support/nginx/gitlab
@@ -38,10 +38,9 @@ upstream gitlab {
server unix:/home/git/gitlab/tmp/sockets/gitlab.socket fail_timeout=0;
}
-## Experimental: gitlab-git-http-server
-# upstream gitlab-git-http-server {
-# server localhost:8181;
-# }
+upstream gitlab-git-http-server {
+ server unix:/home/git/gitlab/tmp/sockets/gitlab-git-http-server.socket fail_timeout=0;
+}
## Normal HTTP host
server {
@@ -114,25 +113,33 @@ server {
proxy_pass http://gitlab;
}
- ## Experimental: send Git HTTP traffic to gitlab-git-http-server instead of Unicorn
- # location ~ [-\/\w\.]+\.git\/ {
- # ## If you use HTTPS make sure you disable gzip compression
- # ## to be safe against BREACH attack.
- # # gzip off;
-
- # ## https://github.com/gitlabhq/gitlabhq/issues/694
- # ## Some requests take more than 30 seconds.
- # proxy_read_timeout 300;
- # proxy_connect_timeout 300;
- # proxy_redirect off;
-
- # proxy_set_header Host $http_host;
- # proxy_set_header X-Real-IP $remote_addr;
- # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- # proxy_set_header X-Forwarded-Proto $scheme;
-
- # proxy_pass http://gitlab-git-http-server;
- # }
+ location ~ [-\/\w\.]+\.git\/ {
+ ## If you use HTTPS make sure you disable gzip compression
+ ## to be safe against BREACH attack.
+ # gzip off;
+
+ ## https://github.com/gitlabhq/gitlabhq/issues/694
+ ## Some requests take more than 30 seconds.
+ proxy_read_timeout 300;
+ proxy_connect_timeout 300;
+ proxy_redirect off;
+
+ # Do not buffer Git HTTP responses
+ proxy_buffering off;
+
+ # The following settings only work with NGINX 1.7.11 or newer
+ #
+ # # Pass chunked request bodies to gitlab-git-http-server as-is
+ # proxy_request_buffering off;
+ # proxy_http_version 1.1;
+
+ proxy_set_header Host $http_host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+
+ proxy_pass http://gitlab-git-http-server;
+ }
## Enable gzip compression as per rails guide:
## http://guides.rubyonrails.org/asset_pipeline.html#gzip-compression
diff --git a/lib/support/nginx/gitlab-ssl b/lib/support/nginx/gitlab-ssl
index 314525518f1..7dabfba87e2 100644
--- a/lib/support/nginx/gitlab-ssl
+++ b/lib/support/nginx/gitlab-ssl
@@ -42,10 +42,9 @@ upstream gitlab {
server unix:/home/git/gitlab/tmp/sockets/gitlab.socket fail_timeout=0;
}
-## Experimental: gitlab-git-http-server
-# upstream gitlab-git-http-server {
-# server localhost:8181;
-# }
+upstream gitlab-git-http-server {
+ server unix:/home/git/gitlab/tmp/sockets/gitlab-git-http-server.socket fail_timeout=0;
+}
## Redirects all HTTP traffic to the HTTPS host
server {
@@ -161,25 +160,33 @@ server {
proxy_pass http://gitlab;
}
- ## Experimental: send Git HTTP traffic to gitlab-git-http-server instead of Unicorn
- # location ~ [-\/\w\.]+\.git\/ {
- # ## If you use HTTPS make sure you disable gzip compression
- # ## to be safe against BREACH attack.
- # gzip off;
-
- # ## https://github.com/gitlabhq/gitlabhq/issues/694
- # ## Some requests take more than 30 seconds.
- # proxy_read_timeout 300;
- # proxy_connect_timeout 300;
- # proxy_redirect off;
-
- # proxy_set_header Host $http_host;
- # proxy_set_header X-Real-IP $remote_addr;
- # proxy_set_header X-Forwarded-Ssl on;
- # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- # proxy_set_header X-Forwarded-Proto $scheme;
- # proxy_pass http://gitlab-git-http-server;
- # }
+ location ~ [-\/\w\.]+\.git\/ {
+ ## If you use HTTPS make sure you disable gzip compression
+ ## to be safe against BREACH attack.
+ gzip off;
+
+ ## https://github.com/gitlabhq/gitlabhq/issues/694
+ ## Some requests take more than 30 seconds.
+ proxy_read_timeout 300;
+ proxy_connect_timeout 300;
+ proxy_redirect off;
+
+ # Do not buffer Git HTTP responses
+ proxy_buffering off;
+
+ # The following settings only work with NGINX 1.7.11 or newer
+ #
+ # # Pass chunked request bodies to gitlab-git-http-server as-is
+ # proxy_request_buffering off;
+ # proxy_http_version 1.1;
+
+ proxy_set_header Host $http_host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-Ssl on;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ proxy_pass http://gitlab-git-http-server;
+ }
## Enable gzip compression as per rails guide:
## http://guides.rubyonrails.org/asset_pipeline.html#gzip-compression
diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake
index 2b9688c1b40..b8eb13a4fea 100644
--- a/lib/tasks/gitlab/check.rake
+++ b/lib/tasks/gitlab/check.rake
@@ -278,7 +278,7 @@ namespace :gitlab do
fix_and_rerun
end
end
-
+
def check_uploads
print "Uploads directory setup correctly? ... "
@@ -331,15 +331,18 @@ namespace :gitlab do
end
def check_redis_version
- print "Redis version >= 2.0.0? ... "
+ min_redis_version = "2.4.0"
+ print "Redis version >= #{min_redis_version}? ... "
redis_version = run(%W(redis-cli --version))
- if redis_version.try(:match, /redis-cli 2.\d.\d/) || redis_version.try(:match, /redis-cli 3.\d.\d/)
+ redis_version = redis_version.try(:match, /redis-cli (.*)/)
+ if redis_version &&
+ (Gem::Version.new(redis_version[1]) > Gem::Version.new(min_redis_version))
puts "yes".green
else
puts "no".red
try_fixing_it(
- "Update your redis server to a version >= 2.0.0"
+ "Update your redis server to a version >= #{min_redis_version}"
)
for_more_information(
"gitlab-public-wiki/wiki/Trouble-Shooting-Guide in section sidekiq"
@@ -488,7 +491,7 @@ namespace :gitlab do
else
puts "wrong or missing hooks".red
try_fixing_it(
- sudo_gitlab("#{gitlab_shell_path}/bin/create-hooks"),
+ sudo_gitlab("#{File.join(gitlab_shell_path, 'bin/create-hooks')}"),
'Check the hooks_path in config/gitlab.yml',
'Check your gitlab-shell installation'
)
diff --git a/lib/tasks/services.rake b/lib/tasks/services.rake
new file mode 100644
index 00000000000..3f276a5e12e
--- /dev/null
+++ b/lib/tasks/services.rake
@@ -0,0 +1,89 @@
+services_template = <<-ERB
+# Services
+
+<% services.each do |service| %>
+## <%= service[:title] %>
+
+
+<% unless service[:description].blank? %>
+<%= service[:description] %>
+<% end %>
+
+
+### Create/Edit <%= service[:title] %> service
+
+Set <%= service[:title] %> service for a project.
+<% unless service[:help].blank? %>
+
+> <%= service[:help].gsub("\n", ' ') %>
+
+<% end %>
+
+```
+PUT /projects/:id/services/<%= service[:dashed_name] %>
+
+```
+
+Parameters:
+
+<% service[:params].each do |param| %>
+- `<%= param[:name] %>` <%= param[:required] ? "(**required**)" : "(optional)" %><%= [" -", param[:description]].join(" ").gsub("\n", '') unless param[:description].blank? %>
+
+<% end %>
+
+### Delete <%= service[:title] %> service
+
+Delete <%= service[:title] %> service for a project.
+
+```
+DELETE /projects/:id/services/<%= service[:dashed_name] %>
+
+```
+
+<% end %>
+ERB
+
+namespace :services do
+ task doc: :environment do
+ services = Service.available_services_names.map do |s|
+ service_start = Time.now
+ klass = "#{s}_service".classify.constantize
+
+ service = klass.new
+
+ service_hash = {}
+
+ service_hash[:title] = service.title
+ service_hash[:dashed_name] = s.dasherize
+ service_hash[:description] = service.description
+ service_hash[:help] = service.help
+ service_hash[:params] = service.fields.map do |p|
+ param_hash = {}
+
+ param_hash[:name] = p[:name]
+ param_hash[:description] = p[:placeholder] || p[:title]
+ param_hash[:required] = klass.validators_on(p[:name].to_sym).any? do |v|
+ v.class == ActiveRecord::Validations::PresenceValidator
+ end
+
+ param_hash
+ end.sort_by { |p| p[:required] ? 0 : 1 }
+
+ puts "Collected data for: #{service.title}, #{Time.now-service_start}"
+ service_hash
+ end
+
+ doc_start = Time.now
+ doc_path = File.join(Rails.root, 'doc', 'api', 'services.md')
+
+ result = ERB.new(services_template, 0 , '>')
+ .result(OpenStruct.new(services: services).instance_eval { binding })
+
+ File.open(doc_path, 'w') do |f|
+ f.write result
+ end
+
+ puts "write a new service.md to: #{doc_path.to_s}, #{Time.now-doc_start}"
+
+ end
+end
diff --git a/spec/controllers/import/fogbugz_controller_spec.rb b/spec/controllers/import/fogbugz_controller_spec.rb
new file mode 100644
index 00000000000..27b11267d2a
--- /dev/null
+++ b/spec/controllers/import/fogbugz_controller_spec.rb
@@ -0,0 +1,39 @@
+require 'spec_helper'
+require_relative 'import_spec_helper'
+
+describe Import::FogbugzController do
+ include ImportSpecHelper
+
+ let(:user) { create(:user) }
+
+ before do
+ sign_in(user)
+ end
+
+ describe 'GET status' do
+ before do
+ @repo = OpenStruct.new(name: 'vim')
+ stub_client(valid?: true)
+ end
+
+ it 'assigns variables' do
+ @project = create(:project, import_type: 'fogbugz', creator_id: user.id)
+ stub_client(repos: [@repo])
+
+ get :status
+
+ expect(assigns(:already_added_projects)).to eq([@project])
+ expect(assigns(:repos)).to eq([@repo])
+ end
+
+ it 'does not show already added project' do
+ @project = create(:project, import_type: 'fogbugz', creator_id: user.id, import_source: 'vim')
+ stub_client(repos: [@repo])
+
+ get :status
+
+ expect(assigns(:already_added_projects)).to eq([@project])
+ expect(assigns(:repos)).to eq([])
+ end
+ end
+end
diff --git a/spec/controllers/projects/raw_controller_spec.rb b/spec/controllers/projects/raw_controller_spec.rb
new file mode 100644
index 00000000000..c114f342021
--- /dev/null
+++ b/spec/controllers/projects/raw_controller_spec.rb
@@ -0,0 +1,37 @@
+require 'spec_helper'
+
+describe Projects::RawController do
+ let(:public_project) { create(:project, :public) }
+
+ describe "#show" do
+ context 'regular filename' do
+ let(:id) { 'master/README.md' }
+
+ it 'delivers ASCII file' do
+ get(:show,
+ namespace_id: public_project.namespace.to_param,
+ project_id: public_project.to_param,
+ id: id)
+
+ expect(response.status).to eq(200)
+ expect(response.header['Content-Type']).to eq('text/plain; charset=utf-8')
+ expect(response.header['Content-Disposition']).
+ to eq("inline")
+ end
+ end
+
+ context 'image header' do
+ let(:id) { 'master/files/images/6049019_460s.jpg' }
+
+ it 'set image content type header' do
+ get(:show,
+ namespace_id: public_project.namespace.to_param,
+ project_id: public_project.to_param,
+ id: id)
+
+ expect(response.status).to eq(200)
+ expect(response.header['Content-Type']).to eq('image/jpeg')
+ end
+ end
+ end
+end
diff --git a/spec/factories/abuse_reports.rb b/spec/factories/abuse_reports.rb
index 29fcbc5e197..8d287ded292 100644
--- a/spec/factories/abuse_reports.rb
+++ b/spec/factories/abuse_reports.rb
@@ -1,3 +1,15 @@
+# == Schema Information
+#
+# Table name: abuse_reports
+#
+# id :integer not null, primary key
+# reporter_id :integer
+# user_id :integer
+# message :text
+# created_at :datetime
+# updated_at :datetime
+#
+
# Read about factories at https://github.com/thoughtbot/factory_girl
FactoryGirl.define do
diff --git a/spec/factories/merge_requests.rb b/spec/factories/merge_requests.rb
index 3b7adfe4398..6080d0ccdef 100644
--- a/spec/factories/merge_requests.rb
+++ b/spec/factories/merge_requests.rb
@@ -19,6 +19,7 @@
# description :text
# position :integer default(0)
# locked_at :datetime
+# updated_by_id :integer
#
FactoryGirl.define do
diff --git a/spec/factories/notes.rb b/spec/factories/notes.rb
index e1009d5916e..9d777ddfccd 100644
--- a/spec/factories/notes.rb
+++ b/spec/factories/notes.rb
@@ -15,6 +15,7 @@
# noteable_id :integer
# system :boolean default(FALSE), not null
# st_diff :text
+# updated_by_id :integer
#
require_relative '../support/repo_helpers'
diff --git a/spec/features/gitlab_flavored_markdown_spec.rb b/spec/features/gitlab_flavored_markdown_spec.rb
index 0c1bc53cdb5..7852c39fee2 100644
--- a/spec/features/gitlab_flavored_markdown_spec.rb
+++ b/spec/features/gitlab_flavored_markdown_spec.rb
@@ -77,7 +77,7 @@ describe "GitLab Flavored Markdown", feature: true do
it "should render details in issues#show" do
visit namespace_project_issue_path(project.namespace, project, @issue)
- expect(page).to have_link("@#{fred.username}")
+ expect(page).to have_link(fred.to_reference)
end
end
diff --git a/spec/features/markdown_spec.rb b/spec/features/markdown_spec.rb
index 3da4dfc2b23..c557a1061af 100644
--- a/spec/features/markdown_spec.rb
+++ b/spec/features/markdown_spec.rb
@@ -64,8 +64,8 @@ describe 'GitLab Markdown', feature: true do
it 'parses fenced code blocks' do
aggregate_failures do
- expect(doc).to have_selector('pre.code.highlight.white.c')
- expect(doc).to have_selector('pre.code.highlight.white.python')
+ expect(doc).to have_selector('pre.code.highlight.js-syntax-highlight.c')
+ expect(doc).to have_selector('pre.code.highlight.js-syntax-highlight.python')
end
end
@@ -179,7 +179,7 @@ describe 'GitLab Markdown', feature: true do
before(:all) do
@feat = MarkdownFeature.new
- # `gfm` helper depends on a `@project` variable
+ # `markdown` helper expects a `@project` variable
@project = @feat.project
@html = markdown(@feat.raw_markdown)
@@ -224,8 +224,4 @@ describe 'GitLab Markdown', feature: true do
def current_user
@feat.user
end
-
- def user_color_scheme_class
- :white
- end
end
diff --git a/spec/helpers/events_helper_spec.rb b/spec/helpers/events_helper_spec.rb
index da58ab98462..e68a5ec29ab 100644
--- a/spec/helpers/events_helper_spec.rb
+++ b/spec/helpers/events_helper_spec.rb
@@ -28,8 +28,7 @@ describe EventsHelper do
it 'should display the first line of a code block' do
input = "```\nCode block\nwith two lines\n```"
- expected = '<pre class="code highlight white plaintext"><code>' \
- 'Code block...</code></pre>'
+ expected = %r{<pre.+><code>Code block\.\.\.</code></pre>}
expect(event_note(input)).to match(expected)
end
@@ -55,7 +54,7 @@ describe EventsHelper do
it 'should preserve code color scheme' do
input = "```ruby\ndef test\n 'hello world'\nend\n```"
- expected = '<pre class="code highlight white ruby">' \
+ expected = '<pre class="code highlight js-syntax-highlight ruby">' \
"<code><span class=\"k\">def</span> <span class=\"nf\">test</span>\n" \
" <span class=\"s1\">\'hello world\'</span>\n" \
"<span class=\"k\">end</span>" \
diff --git a/spec/helpers/gitlab_markdown_helper_spec.rb b/spec/helpers/gitlab_markdown_helper_spec.rb
index a42ccb9b501..5639b3db913 100644
--- a/spec/helpers/gitlab_markdown_helper_spec.rb
+++ b/spec/helpers/gitlab_markdown_helper_spec.rb
@@ -19,28 +19,23 @@ describe GitlabMarkdownHelper do
@project = project
end
- describe "#gfm" do
- it "should forward HTML options to links" do
- expect(gfm("Fixed in #{commit.id}", { project: @project }, class: 'foo')).
- to have_selector('a.gfm.foo')
- end
-
+ describe "#markdown" do
describe "referencing multiple objects" do
let(:actual) { "#{merge_request.to_reference} -> #{commit.to_reference} -> #{issue.to_reference}" }
it "should link to the merge request" do
expected = namespace_project_merge_request_path(project.namespace, project, merge_request)
- expect(gfm(actual)).to match(expected)
+ expect(markdown(actual)).to match(expected)
end
it "should link to the commit" do
expected = namespace_project_commit_path(project.namespace, project, commit)
- expect(gfm(actual)).to match(expected)
+ expect(markdown(actual)).to match(expected)
end
it "should link to the issue" do
expected = namespace_project_issue_path(project.namespace, project, issue)
- expect(gfm(actual)).to match(expected)
+ expect(markdown(actual)).to match(expected)
end
end
end
diff --git a/spec/helpers/preferences_helper_spec.rb b/spec/helpers/preferences_helper_spec.rb
index d814b562113..06f69262b71 100644
--- a/spec/helpers/preferences_helper_spec.rb
+++ b/spec/helpers/preferences_helper_spec.rb
@@ -1,72 +1,82 @@
require 'spec_helper'
describe PreferencesHelper do
+ describe 'dashboard_choices' do
+ it 'raises an exception when defined choices may be missing' do
+ expect(User).to receive(:dashboards).and_return(foo: 'foo')
+ expect { helper.dashboard_choices }.to raise_error(RuntimeError)
+ end
+
+ it 'raises an exception when defined choices may be using the wrong key' do
+ expect(User).to receive(:dashboards).and_return(foo: 'foo', bar: 'bar')
+ expect { helper.dashboard_choices }.to raise_error(KeyError)
+ end
+
+ it 'provides better option descriptions' do
+ expect(helper.dashboard_choices).to match_array [
+ ['Your Projects (default)', 'projects'],
+ ['Starred Projects', 'stars']
+ ]
+ end
+ end
+
describe 'user_application_theme' do
context 'with a user' do
it "returns user's theme's css_class" do
- user = double('user', theme_id: 3)
- allow(self).to receive(:current_user).and_return(user)
- expect(user_application_theme).to eq 'ui_green'
+ stub_user(theme_id: 3)
+
+ expect(helper.user_application_theme).to eq 'ui_green'
end
it 'returns the default when id is invalid' do
- user = double('user', theme_id: Gitlab::Themes::THEMES.size + 5)
+ stub_user(theme_id: Gitlab::Themes.count + 5)
allow(Gitlab.config.gitlab).to receive(:default_theme).and_return(2)
- allow(self).to receive(:current_user).and_return(user)
- expect(user_application_theme).to eq 'ui_charcoal'
+ expect(helper.user_application_theme).to eq 'ui_charcoal'
end
end
context 'without a user' do
- before do
- allow(self).to receive(:current_user).and_return(nil)
- end
-
it 'returns the default theme' do
- expect(user_application_theme).to eq Gitlab::Themes.default.css_class
+ stub_user
+
+ expect(helper.user_application_theme).to eq Gitlab::Themes.default.css_class
end
end
end
- describe 'dashboard_choices' do
- it 'raises an exception when defined choices may be missing' do
- expect(User).to receive(:dashboards).and_return(foo: 'foo')
- expect { dashboard_choices }.to raise_error(RuntimeError)
- end
+ describe 'user_color_scheme' do
+ context 'with a user' do
+ it "returns user's scheme's css_class" do
+ allow(helper).to receive(:current_user).
+ and_return(double(color_scheme_id: 3))
- it 'raises an exception when defined choices may be using the wrong key' do
- expect(User).to receive(:dashboards).and_return(foo: 'foo', bar: 'bar')
- expect { dashboard_choices }.to raise_error(KeyError)
- end
+ expect(helper.user_color_scheme).to eq 'solarized-light'
+ end
- it 'provides better option descriptions' do
- expect(dashboard_choices).to match_array [
- ['Your Projects (default)', 'projects'],
- ['Starred Projects', 'stars']
- ]
+ it 'returns the default when id is invalid' do
+ allow(helper).to receive(:current_user).
+ and_return(double(color_scheme_id: Gitlab::ColorSchemes.count + 5))
+ end
end
- end
- describe 'user_color_scheme_class' do
- context 'with current_user is nil' do
- it 'should return a string' do
- allow(self).to receive(:current_user).and_return(nil)
- expect(user_color_scheme_class).to be_kind_of(String)
+ context 'without a user' do
+ it 'returns the default theme' do
+ stub_user
+
+ expect(helper.user_color_scheme).
+ to eq Gitlab::ColorSchemes.default.css_class
end
end
+ end
- context 'with a current_user' do
- (1..5).each do |color_scheme_id|
- context "with color_scheme_id == #{color_scheme_id}" do
- it 'should return a string' do
- current_user = double(color_scheme_id: color_scheme_id)
- allow(self).to receive(:current_user).and_return(current_user)
- expect(user_color_scheme_class).to be_kind_of(String)
- end
- end
- end
+ def stub_user(messages = {})
+ if messages.empty?
+ allow(helper).to receive(:current_user).and_return(nil)
+ else
+ allow(helper).to receive(:current_user).
+ and_return(double('user', messages))
end
end
end
diff --git a/spec/javascripts/zen_mode_spec.js.coffee b/spec/javascripts/zen_mode_spec.js.coffee
index 1f4ea58ad48..4cb3836755f 100644
--- a/spec/javascripts/zen_mode_spec.js.coffee
+++ b/spec/javascripts/zen_mode_spec.js.coffee
@@ -29,6 +29,11 @@ describe 'ZenMode', ->
enterZen()
expect(Mousetrap.pause).toHaveBeenCalled()
+ it 'removes textarea styling', ->
+ $('textarea').attr('style', 'height: 400px')
+ enterZen()
+ expect('textarea').not.toHaveAttr('style')
+
describe 'in use', ->
beforeEach ->
enterZen()
diff --git a/spec/lib/gitlab/bitbucket_import/client_spec.rb b/spec/lib/gitlab/bitbucket_import/client_spec.rb
index dd450e9967b..dfe58637eee 100644
--- a/spec/lib/gitlab/bitbucket_import/client_spec.rb
+++ b/spec/lib/gitlab/bitbucket_import/client_spec.rb
@@ -14,4 +14,38 @@ describe Gitlab::BitbucketImport::Client do
expect(key).to be_kind_of(Symbol)
end
end
+
+ context 'issues' do
+ let(:per_page) { 50 }
+ let(:count) { 95 }
+ let(:sample_issues) do
+ issues = []
+
+ count.times do |i|
+ issues << { local_id: i }
+ end
+
+ issues
+ end
+ let(:first_sample_data) { { count: count, issues: sample_issues[0..per_page - 1] } }
+ let(:second_sample_data) { { count: count, issues: sample_issues[per_page..count] } }
+ let(:project_id) { 'namespace/repo' }
+
+ it 'retrieves issues over a number of pages' do
+ stub_request(:get,
+ "https://bitbucket.org/api/1.0/repositories/#{project_id}/issues?limit=50&sort=utc_created_on&start=0").
+ to_return(status: 200,
+ body: first_sample_data.to_json,
+ headers: {})
+
+ stub_request(:get,
+ "https://bitbucket.org/api/1.0/repositories/#{project_id}/issues?limit=50&sort=utc_created_on&start=50").
+ to_return(status: 200,
+ body: second_sample_data.to_json,
+ headers: {})
+
+ issues = client.issues(project_id)
+ expect(issues.count).to eq(95)
+ end
+ end
end
diff --git a/spec/lib/gitlab/color_schemes_spec.rb b/spec/lib/gitlab/color_schemes_spec.rb
new file mode 100644
index 00000000000..c7be45dbcd3
--- /dev/null
+++ b/spec/lib/gitlab/color_schemes_spec.rb
@@ -0,0 +1,45 @@
+require 'spec_helper'
+
+describe Gitlab::ColorSchemes do
+ describe '.body_classes' do
+ it 'returns a space-separated list of class names' do
+ css = described_class.body_classes
+
+ expect(css).to include('white')
+ expect(css).to include(' solarized-light ')
+ expect(css).to include(' monokai')
+ end
+ end
+
+ describe '.by_id' do
+ it 'returns a scheme by its ID' do
+ expect(described_class.by_id(1).name).to eq 'White'
+ expect(described_class.by_id(4).name).to eq 'Solarized Dark'
+ end
+ end
+
+ describe '.default' do
+ it 'returns the default scheme' do
+ expect(described_class.default.id).to eq 1
+ end
+ end
+
+ describe '.each' do
+ it 'passes the block to the SCHEMES Array' do
+ ids = []
+ described_class.each { |scheme| ids << scheme.id }
+ expect(ids).not_to be_empty
+ end
+ end
+
+ describe '.for_user' do
+ it 'returns default when user is nil' do
+ expect(described_class.for_user(nil).id).to eq 1
+ end
+
+ it "returns user's preferred color scheme" do
+ user = double(color_scheme_id: 5)
+ expect(described_class.for_user(user).id).to eq 5
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ldap/user_spec.rb b/spec/lib/gitlab/ldap/user_spec.rb
index 7cfca96f4e0..84d9fb54b61 100644
--- a/spec/lib/gitlab/ldap/user_spec.rb
+++ b/spec/lib/gitlab/ldap/user_spec.rb
@@ -47,6 +47,28 @@ describe Gitlab::LDAP::User do
expect(existing_user.ldap_identity.provider).to eql 'ldapmain'
end
+ it 'connects to existing ldap user if the extern_uid changes' do
+ existing_user = create(:omniauth_user, email: 'john@example.com', extern_uid: 'old-uid', provider: 'ldapmain')
+ expect{ ldap_user.save }.not_to change{ User.count }
+
+ existing_user.reload
+ expect(existing_user.ldap_identity.extern_uid).to eql 'my-uid'
+ expect(existing_user.ldap_identity.provider).to eql 'ldapmain'
+ expect(existing_user.id).to eql ldap_user.gl_user.id
+ end
+
+ it 'maintains an identity per provider' do
+ existing_user = create(:omniauth_user, email: 'john@example.com', provider: 'twitter')
+ expect(existing_user.identities.count).to eql(1)
+
+ ldap_user.save
+ expect(ldap_user.gl_user.identities.count).to eql(2)
+
+ # Expect that find_by provider only returns a single instance of an identity and not an Enumerable
+ expect(ldap_user.gl_user.identities.find_by(provider: 'twitter')).to be_instance_of Identity
+ expect(ldap_user.gl_user.identities.find_by(provider: auth_hash.provider)).to be_instance_of Identity
+ end
+
it "creates a new user if not found" do
expect{ ldap_user.save }.to change{ User.count }.by(1)
end
diff --git a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb
index 58155284486..3c6c84a0416 100644
--- a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb
@@ -75,11 +75,6 @@ module Gitlab::Markdown
expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-commit_range'
end
- it 'includes an optional custom class' do
- doc = filter("See #{reference}", reference_class: 'custom')
- expect(doc.css('a').first.attr('class')).to include 'custom'
- end
-
it 'includes a data-project-id attribute' do
doc = filter("See #{reference}")
link = doc.css('a').first
diff --git a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb
index 05a02de4669..9ed438252b3 100644
--- a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb
@@ -71,11 +71,6 @@ module Gitlab::Markdown
expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-commit'
end
- it 'includes an optional custom class' do
- doc = filter("See #{reference}", reference_class: 'custom')
- expect(doc.css('a').first.attr('class')).to include 'custom'
- end
-
it 'includes a data-project-id attribute' do
doc = filter("See #{reference}")
link = doc.css('a').first
diff --git a/spec/lib/gitlab/markdown/external_issue_reference_filter_spec.rb b/spec/lib/gitlab/markdown/external_issue_reference_filter_spec.rb
index f16095bc2b2..d8c2970b6bd 100644
--- a/spec/lib/gitlab/markdown/external_issue_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/external_issue_reference_filter_spec.rb
@@ -68,11 +68,6 @@ module Gitlab::Markdown
expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-issue'
end
- it 'includes an optional custom class' do
- doc = filter("Issue #{reference}", reference_class: 'custom')
- expect(doc.css('a').first.attr('class')).to include 'custom'
- end
-
it 'supports an :only_path context' do
doc = filter("Issue #{reference}", only_path: true)
link = doc.css('a').first.attr('href')
diff --git a/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb b/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb
index 35b1ba5f132..1dd54f58588 100644
--- a/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb
@@ -68,11 +68,6 @@ module Gitlab::Markdown
expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-issue'
end
- it 'includes an optional custom class' do
- doc = filter("Issue #{reference}", reference_class: 'custom')
- expect(doc.css('a').first.attr('class')).to include 'custom'
- end
-
it 'includes a data-project-id attribute' do
doc = filter("Issue #{reference}")
link = doc.css('a').first
diff --git a/spec/lib/gitlab/markdown/label_reference_filter_spec.rb b/spec/lib/gitlab/markdown/label_reference_filter_spec.rb
index fabe0411e46..e32089de376 100644
--- a/spec/lib/gitlab/markdown/label_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/label_reference_filter_spec.rb
@@ -25,11 +25,6 @@ module Gitlab::Markdown
expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-label'
end
- it 'includes an optional custom class' do
- doc = filter("Label #{reference}", reference_class: 'custom')
- expect(doc.css('a').first.attr('class')).to include 'custom'
- end
-
it 'includes a data-project-id attribute' do
doc = filter("Label #{reference}")
link = doc.css('a').first
diff --git a/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb b/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb
index 5cef52b1916..66616b93368 100644
--- a/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb
@@ -56,11 +56,6 @@ module Gitlab::Markdown
expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-merge_request'
end
- it 'includes an optional custom class' do
- doc = filter("Merge #{reference}", reference_class: 'custom')
- expect(doc.css('a').first.attr('class')).to include 'custom'
- end
-
it 'includes a data-project-id attribute' do
doc = filter("Merge #{reference}")
link = doc.css('a').first
diff --git a/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb b/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb
index 678b171e99e..fd3f0d20fad 100644
--- a/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb
@@ -55,11 +55,6 @@ module Gitlab::Markdown
expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-snippet'
end
- it 'includes an optional custom class' do
- doc = filter("Snippet #{reference}", reference_class: 'custom')
- expect(doc.css('a').first.attr('class')).to include 'custom'
- end
-
it 'includes a data-project-id attribute' do
doc = filter("Snippet #{reference}")
link = doc.css('a').first
diff --git a/spec/lib/gitlab/markdown/user_reference_filter_spec.rb b/spec/lib/gitlab/markdown/user_reference_filter_spec.rb
index 02d923b036c..b2155fab59b 100644
--- a/spec/lib/gitlab/markdown/user_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/user_reference_filter_spec.rb
@@ -130,11 +130,6 @@ module Gitlab::Markdown
expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-project_member'
end
- it 'includes an optional custom class' do
- doc = filter("Hey #{reference}", reference_class: 'custom')
- expect(doc.css('a').first.attr('class')).to include 'custom'
- end
-
it 'supports an :only_path context' do
doc = filter("Hey #{reference}", only_path: true)
link = doc.css('a').first.attr('href')
diff --git a/spec/lib/gitlab/themes_spec.rb b/spec/lib/gitlab/themes_spec.rb
index 9c6c3fd8104..e554458e41c 100644
--- a/spec/lib/gitlab/themes_spec.rb
+++ b/spec/lib/gitlab/themes_spec.rb
@@ -43,9 +43,6 @@ describe Gitlab::Themes do
ids = []
described_class.each { |theme| ids << theme.id }
expect(ids).not_to be_empty
-
- # TODO (rspeicher): RSpec 3.x
- # expect(described_class.each).to yield_with_arg(described_class::Theme)
end
end
end
diff --git a/spec/models/abuse_report_spec.rb b/spec/models/abuse_report_spec.rb
index d83004a8388..635a6e2518c 100644
--- a/spec/models/abuse_report_spec.rb
+++ b/spec/models/abuse_report_spec.rb
@@ -1,3 +1,15 @@
+# == Schema Information
+#
+# Table name: abuse_reports
+#
+# id :integer not null, primary key
+# reporter_id :integer
+# user_id :integer
+# message :text
+# created_at :datetime
+# updated_at :datetime
+#
+
require 'rails_helper'
RSpec.describe AbuseReport, type: :model do
diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb
index bc14ff98fd8..de0b2ef4cda 100644
--- a/spec/models/application_setting_spec.rb
+++ b/spec/models/application_setting_spec.rb
@@ -22,6 +22,7 @@
# user_oauth_applications :boolean default(TRUE)
# after_sign_out_path :string(255)
# session_expire_delay :integer default(10080), not null
+# import_sources :text
#
require 'spec_helper'
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index 9bac451c28c..cf336d82957 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -2,19 +2,20 @@
#
# Table name: issues
#
-# id :integer not null, primary key
-# title :string(255)
-# assignee_id :integer
-# author_id :integer
-# project_id :integer
-# created_at :datetime
-# updated_at :datetime
-# position :integer default(0)
-# branch_name :string(255)
-# description :text
-# milestone_id :integer
-# state :string(255)
-# iid :integer
+# id :integer not null, primary key
+# title :string(255)
+# assignee_id :integer
+# author_id :integer
+# project_id :integer
+# created_at :datetime
+# updated_at :datetime
+# position :integer default(0)
+# branch_name :string(255)
+# description :text
+# milestone_id :integer
+# state :string(255)
+# iid :integer
+# updated_by_id :integer
#
require 'spec_helper'
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index b91687bc09f..17a49013d25 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -19,6 +19,7 @@
# description :text
# position :integer default(0)
# locked_at :datetime
+# updated_by_id :integer
#
require 'spec_helper'
diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb
index 331505a01b3..3a0b194ba1e 100644
--- a/spec/models/note_spec.rb
+++ b/spec/models/note_spec.rb
@@ -15,6 +15,7 @@
# noteable_id :integer
# system :boolean default(FALSE), not null
# st_diff :text
+# updated_by_id :integer
#
require 'spec_helper'
diff --git a/spec/models/project_services/drone_ci_service_spec.rb b/spec/models/project_services/drone_ci_service_spec.rb
new file mode 100644
index 00000000000..bad9a9e6e1a
--- /dev/null
+++ b/spec/models/project_services/drone_ci_service_spec.rb
@@ -0,0 +1,107 @@
+# == Schema Information
+#
+# Table name: services
+#
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# project_id :integer
+# created_at :datetime
+# updated_at :datetime
+# active :boolean default(FALSE), not null
+# properties :text
+# template :boolean default(FALSE)
+# push_events :boolean default(TRUE)
+# issues_events :boolean default(TRUE)
+# merge_requests_events :boolean default(TRUE)
+# tag_push_events :boolean default(TRUE)
+# note_events :boolean default(TRUE), not null
+#
+
+require 'spec_helper'
+
+describe DroneCiService do
+ describe 'associations' do
+ it { is_expected.to belong_to(:project) }
+ it { is_expected.to have_one(:service_hook) }
+ end
+
+ describe 'validations' do
+ context 'active' do
+ before { allow(subject).to receive(:activated?).and_return(true) }
+
+ it { is_expected.to validate_presence_of(:token) }
+ it { is_expected.to validate_presence_of(:drone_url) }
+ it { is_expected.to allow_value('ewf9843kdnfdfs89234n').for(:token) }
+ it { is_expected.to allow_value('http://ci.example.com').for(:drone_url) }
+ it { is_expected.not_to allow_value('token with spaces').for(:token) }
+ it { is_expected.not_to allow_value('token/with%spaces').for(:token) }
+ it { is_expected.not_to allow_value('this is not url').for(:drone_url) }
+ it { is_expected.not_to allow_value('http//noturl').for(:drone_url) }
+ it { is_expected.not_to allow_value('ftp://ci.example.com').for(:drone_url) }
+ end
+
+ context 'inactive' do
+ before { allow(subject).to receive(:activated?).and_return(false) }
+
+ it { is_expected.not_to validate_presence_of(:token) }
+ it { is_expected.not_to validate_presence_of(:drone_url) }
+ it { is_expected.to allow_value('ewf9843kdnfdfs89234n').for(:token) }
+ it { is_expected.to allow_value('http://drone.example.com').for(:drone_url) }
+ it { is_expected.to allow_value('token with spaces').for(:token) }
+ it { is_expected.to allow_value('ftp://drone.example.com').for(:drone_url) }
+ end
+ end
+
+ shared_context :drone_ci_service do
+ let(:drone) { DroneCiService.new }
+ let(:project) { create(:project, name: 'project') }
+ let(:path) { "#{project.namespace.path}/#{project.path}" }
+ let(:drone_url) { 'http://drone.example.com' }
+ let(:sha) { '2ab7834c' }
+ let(:branch) { 'dev' }
+ let(:token) { 'secret' }
+ let(:iid) { rand(1..9999) }
+
+ before(:each) do
+ allow(drone).to receive_messages(
+ project_id: project.id,
+ project: project,
+ active: true,
+ drone_url: drone_url,
+ token: token
+ )
+ end
+ end
+
+ describe "service page/path methods" do
+ include_context :drone_ci_service
+
+ # URL's
+ let(:commit_page) { "#{drone_url}/gitlab/#{path}/redirect/commits/#{sha}?branch=#{branch}" }
+ let(:merge_request_page) { "#{drone_url}/gitlab/#{path}/redirect/pulls/#{iid}" }
+ let(:commit_status_path) { "#{drone_url}/gitlab/#{path}/commits/#{sha}?branch=#{branch}&access_token=#{token}" }
+ let(:merge_request_status_path) { "#{drone_url}/gitlab/#{path}/pulls/#{iid}?access_token=#{token}" }
+
+ it { expect(drone.build_page(sha, branch)).to eq(commit_page) }
+ it { expect(drone.commit_page(sha, branch)).to eq(commit_page) }
+ it { expect(drone.merge_request_page(iid, sha, branch)).to eq(merge_request_page) }
+ it { expect(drone.commit_status_path(sha, branch)).to eq(commit_status_path) }
+ it { expect(drone.merge_request_status_path(iid, sha, branch)).to eq(merge_request_status_path) }
+ end
+
+ describe "execute" do
+ include_context :drone_ci_service
+
+ let(:user) { create(:user, username: 'username') }
+ let(:push_sample_data) { Gitlab::PushDataBuilder.build_sample(project, user) }
+
+ it do
+ service_hook = double
+ expect(service_hook).to receive(:execute)
+ expect(drone).to receive(:service_hook).and_return(service_hook)
+
+ drone.execute(push_sample_data)
+ end
+ end
+end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index a46e789eab4..eeb9069aa17 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -2,62 +2,58 @@
#
# Table name: users
#
-# id :integer not null, primary key
-# email :string(255) default(""), not null
-# encrypted_password :string(255) default(""), not null
-# reset_password_token :string(255)
-# reset_password_sent_at :datetime
-# remember_created_at :datetime
-# sign_in_count :integer default(0)
-# current_sign_in_at :datetime
-# last_sign_in_at :datetime
-# current_sign_in_ip :string(255)
-# last_sign_in_ip :string(255)
-# created_at :datetime
-# updated_at :datetime
-# name :string(255)
-# admin :boolean default(FALSE), not null
-# projects_limit :integer default(10)
-# skype :string(255) default(""), not null
-# linkedin :string(255) default(""), not null
-# twitter :string(255) default(""), not null
-# authentication_token :string(255)
-# theme_id :integer default(1), not null
-# bio :string(255)
-# failed_attempts :integer default(0)
-# locked_at :datetime
-# username :string(255)
-# can_create_group :boolean default(TRUE), not null
-# can_create_team :boolean default(TRUE), not null
-# state :string(255)
-# color_scheme_id :integer default(1), not null
-# notification_level :integer default(1), not null
-# password_expires_at :datetime
-# created_by_id :integer
-# last_credential_check_at :datetime
-# avatar :string(255)
-# confirmation_token :string(255)
-# confirmed_at :datetime
-# confirmation_sent_at :datetime
-# unconfirmed_email :string(255)
-# hide_no_ssh_key :boolean default(FALSE)
-# website_url :string(255) default(""), not null
-# github_access_token :string(255)
-# gitlab_access_token :string(255)
-# notification_email :string(255)
-# hide_no_password :boolean default(FALSE)
-# password_automatically_set :boolean default(FALSE)
-# bitbucket_access_token :string(255)
-# bitbucket_access_token_secret :string(255)
-# location :string(255)
-# encrypted_otp_secret :string(255)
-# encrypted_otp_secret_iv :string(255)
-# encrypted_otp_secret_salt :string(255)
-# otp_required_for_login :boolean default(FALSE), not null
-# otp_backup_codes :text
-# public_email :string(255) default(""), not null
-# dashboard :integer default(0)
-# project_view :integer default(0)
+# id :integer not null, primary key
+# email :string(255) default(""), not null
+# encrypted_password :string(255) default(""), not null
+# reset_password_token :string(255)
+# reset_password_sent_at :datetime
+# remember_created_at :datetime
+# sign_in_count :integer default(0)
+# current_sign_in_at :datetime
+# last_sign_in_at :datetime
+# current_sign_in_ip :string(255)
+# last_sign_in_ip :string(255)
+# created_at :datetime
+# updated_at :datetime
+# name :string(255)
+# admin :boolean default(FALSE), not null
+# projects_limit :integer default(10)
+# skype :string(255) default(""), not null
+# linkedin :string(255) default(""), not null
+# twitter :string(255) default(""), not null
+# authentication_token :string(255)
+# theme_id :integer default(1), not null
+# bio :string(255)
+# failed_attempts :integer default(0)
+# locked_at :datetime
+# username :string(255)
+# can_create_group :boolean default(TRUE), not null
+# can_create_team :boolean default(TRUE), not null
+# state :string(255)
+# color_scheme_id :integer default(1), not null
+# notification_level :integer default(1), not null
+# password_expires_at :datetime
+# created_by_id :integer
+# last_credential_check_at :datetime
+# avatar :string(255)
+# confirmation_token :string(255)
+# confirmed_at :datetime
+# confirmation_sent_at :datetime
+# unconfirmed_email :string(255)
+# hide_no_ssh_key :boolean default(FALSE)
+# website_url :string(255) default(""), not null
+# notification_email :string(255)
+# hide_no_password :boolean default(FALSE)
+# password_automatically_set :boolean default(FALSE)
+# location :string(255)
+# encrypted_otp_secret :string(255)
+# encrypted_otp_secret_iv :string(255)
+# encrypted_otp_secret_salt :string(255)
+# otp_required_for_login :boolean default(FALSE), not null
+# otp_backup_codes :text
+# public_email :string(255) default(""), not null
+# dashboard :integer default(0)
+# project_view :integer default(0)
#
require 'spec_helper'
diff --git a/spec/requests/api/keys_spec.rb b/spec/requests/api/keys_spec.rb
new file mode 100644
index 00000000000..d2b87f88712
--- /dev/null
+++ b/spec/requests/api/keys_spec.rb
@@ -0,0 +1,39 @@
+require 'spec_helper'
+
+describe API::API, api: true do
+ include ApiHelpers
+
+ let(:user) { create(:user) }
+ let(:admin) { create(:admin) }
+ let(:key) { create(:key, user: user) }
+ let(:email) { create(:email, user: user) }
+
+ describe 'GET /keys/:uid' do
+ before { admin }
+
+ context 'when unauthenticated' do
+ it 'should return authentication error' do
+ get api("/keys/#{key.id}")
+ expect(response.status).to eq(401)
+ end
+ end
+
+ context 'when authenticated' do
+ it 'should return 404 for non-existing key' do
+ get api('/keys/999999', admin)
+ expect(response.status).to eq(404)
+ expect(json_response['message']).to eq('404 Not found')
+ end
+
+ it 'should return single ssh key with user information' do
+ user.keys << key
+ user.save
+ get api("/keys/#{key.id}", admin)
+ expect(response.status).to eq(200)
+ expect(json_response['title']).to eq(key.title)
+ expect(json_response['user']['id']).to eq(user.id)
+ expect(json_response['user']['username']).to eq(user.username)
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/services_spec.rb b/spec/requests/api/services_spec.rb
index 6d29a28580a..c297904614a 100644
--- a/spec/requests/api/services_spec.rb
+++ b/spec/requests/api/services_spec.rb
@@ -5,64 +5,47 @@ describe API::API, api: true do
let(:user) { create(:user) }
let(:project) {create(:project, creator_id: user.id, namespace: user.namespace) }
- describe "POST /projects/:id/services/gitlab-ci" do
- it "should update gitlab-ci settings" do
- put api("/projects/#{project.id}/services/gitlab-ci", user), token: 'secrettoken', project_url: "http://ci.example.com/projects/1"
-
- expect(response.status).to eq(200)
- end
-
- it "should return if required fields missing" do
- put api("/projects/#{project.id}/services/gitlab-ci", user), project_url: "http://ci.example.com/projects/1", active: true
-
- expect(response.status).to eq(400)
- end
-
- it "should return if the format of token is invalid" do
- put api("/projects/#{project.id}/services/gitlab-ci", user), token: 'token-with dashes and spaces%', project_url: "http://ci.example.com/projects/1", active: true
-
- expect(response.status).to eq(404)
- end
-
- it "should return if the format of token is invalid" do
- put api("/projects/#{project.id}/services/gitlab-ci", user), token: 'token-with dashes and spaces%', project_url: "ftp://ci.example/projects/1", active: true
-
- expect(response.status).to eq(404)
- end
- end
-
- describe "DELETE /projects/:id/services/gitlab-ci" do
- it "should update gitlab-ci settings" do
- delete api("/projects/#{project.id}/services/gitlab-ci", user)
-
- expect(response.status).to eq(200)
- expect(project.gitlab_ci_service).to be_nil
- end
- end
-
- describe 'PUT /projects/:id/services/hipchat' do
- it 'should update hipchat settings' do
- put api("/projects/#{project.id}/services/hipchat", user),
- token: 'secret-token', room: 'test'
-
- expect(response.status).to eq(200)
- expect(project.hipchat_service).not_to be_nil
- end
-
- it 'should return if required fields missing' do
- put api("/projects/#{project.id}/services/gitlab-ci", user),
- token: 'secret-token', active: true
-
- expect(response.status).to eq(400)
- end
- end
-
- describe 'DELETE /projects/:id/services/hipchat' do
- it 'should delete hipchat settings' do
- delete api("/projects/#{project.id}/services/hipchat", user)
-
- expect(response.status).to eq(200)
- expect(project.hipchat_service).to be_nil
+ Service.available_services_names.each do |service|
+ describe "PUT /projects/:id/services/#{service.dasherize}" do
+ include_context service
+
+ it "should update #{service} settings" do
+ put api("/projects/#{project.id}/services/#{dashed_service}", user), service_attrs
+
+ expect(response.status).to eq(200)
+ end
+
+ it "should return if required fields missing" do
+ attrs = service_attrs
+
+ required_attributes = service_attrs_list.select do |attr|
+ service_klass.validators_on(attr).any? do |v|
+ v.class == ActiveRecord::Validations::PresenceValidator
+ end
+ end
+
+ if required_attributes.empty?
+ expected_code = 200
+ else
+ attrs.delete(required_attributes.shuffle.first)
+ expected_code = 400
+ end
+
+ put api("/projects/#{project.id}/services/#{dashed_service}", user), attrs
+
+ expect(response.status).to eq(expected_code)
+ end
+ end
+
+ describe "DELETE /projects/:id/services/#{service.dasherize}" do
+ include_context service
+
+ it "should delete #{service}" do
+ delete api("/projects/#{project.id}/services/#{dashed_service}", user)
+
+ expect(response.status).to eq(200)
+ expect(project.send(service_method).activated?).to be_falsey
+ end
end
end
end
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index 9da6c9dc949..8865335d0d1 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -31,13 +31,16 @@ describe NotificationService do
describe 'Notes' do
context 'issue note' do
- let(:project) { create(:empty_project, :public) }
+ let(:project) { create(:empty_project, :private) }
let(:issue) { create(:issue, project: project, assignee: create(:user)) }
let(:mentioned_issue) { create(:issue, assignee: issue.assignee) }
- let(:note) { create(:note_on_issue, noteable: issue, project_id: issue.project_id, note: '@mention referenced') }
+ let(:note) { create(:note_on_issue, noteable: issue, project_id: issue.project_id, note: '@mention referenced, @outsider also') }
before do
build_team(note.project)
+ project.team << [issue.author, :master]
+ project.team << [issue.assignee, :master]
+ project.team << [note.author, :master]
end
describe :new_note do
@@ -53,6 +56,7 @@ describe NotificationService do
should_not_email(@u_participating.id)
should_not_email(@u_disabled.id)
should_not_email(@unsubscriber.id)
+ should_not_email(@u_outsider_mentioned)
notification.new_note(note)
end
@@ -444,12 +448,15 @@ describe NotificationService do
@u_mentioned = create(:user, username: 'mention', notification_level: Notification::N_MENTION)
@u_committer = create(:user, username: 'committer')
@u_not_mentioned = create(:user, username: 'regular', notification_level: Notification::N_PARTICIPATING)
+ @u_outsider_mentioned = create(:user, username: 'outsider')
project.team << [@u_watcher, :master]
project.team << [@u_participating, :master]
+ project.team << [@u_participant_mentioned, :master]
project.team << [@u_disabled, :master]
project.team << [@u_mentioned, :master]
project.team << [@u_committer, :master]
+ project.team << [@u_not_mentioned, :master]
end
def add_users_with_subscription(project, issuable)
diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb
index 66cdfd5d758..ff4ed2dd484 100644
--- a/spec/services/projects/create_service_spec.rb
+++ b/spec/services/projects/create_service_spec.rb
@@ -17,6 +17,14 @@ describe Projects::CreateService do
expect(project.services).not_to be_empty
end
+ it 'creates labels on Project creation if there are templates' do
+ Label.create(title: "bug", template: true)
+ project = create_project(@user, @opts)
+ project.reload
+
+ expect(project.labels).not_to be_empty
+ end
+
context 'user namespace' do
before do
@project = create_project(@user, @opts)
diff --git a/spec/services/projects/download_service_spec.rb b/spec/services/projects/download_service_spec.rb
new file mode 100644
index 00000000000..f12e09c58c3
--- /dev/null
+++ b/spec/services/projects/download_service_spec.rb
@@ -0,0 +1,65 @@
+require 'spec_helper'
+
+describe Projects::DownloadService do
+ describe 'File service' do
+ before do
+ @user = create :user
+ @project = create :project, creator_id: @user.id, namespace: @user.namespace
+ end
+
+ context 'for a URL that is not on whitelist' do
+ before do
+ url = 'https://code.jquery.com/jquery-2.1.4.min.js'
+ @link_to_file = download_file(@project, url)
+ end
+
+ it { expect(@link_to_file).to eq(nil) }
+ end
+
+ context 'for URLs that are on the whitelist' do
+ before do
+ sham_rack_app = ShamRack.at('mycompany.fogbugz.com').stub
+ sham_rack_app.register_resource('/rails_sample.jpg', File.read(Rails.root + 'spec/fixtures/rails_sample.jpg'), 'image/jpg')
+ sham_rack_app.register_resource('/doc_sample.txt', File.read(Rails.root + 'spec/fixtures/doc_sample.txt'), 'text/plain')
+ end
+
+ after do
+ ShamRack.unmount_all
+ end
+
+ context 'an image file' do
+ before do
+ url = 'http://mycompany.fogbugz.com/rails_sample.jpg'
+ @link_to_file = download_file(@project, url)
+ end
+
+ it { expect(@link_to_file).to have_key('alt') }
+ it { expect(@link_to_file).to have_key('url') }
+ it { expect(@link_to_file).to have_key('is_image') }
+ it { expect(@link_to_file['is_image']).to be true }
+ it { expect(@link_to_file['url']).to match("/#{@project.path_with_namespace}") }
+ it { expect(@link_to_file['url']).to match('rails_sample.jpg') }
+ it { expect(@link_to_file['alt']).to eq('rails_sample') }
+ end
+
+ context 'a txt file' do
+ before do
+ url = 'http://mycompany.fogbugz.com/doc_sample.txt'
+ @link_to_file = download_file(@project, url)
+ end
+
+ it { expect(@link_to_file).to have_key('alt') }
+ it { expect(@link_to_file).to have_key('url') }
+ it { expect(@link_to_file).to have_key('is_image') }
+ it { expect(@link_to_file['is_image']).to be false }
+ it { expect(@link_to_file['url']).to match("/#{@project.path_with_namespace}") }
+ it { expect(@link_to_file['url']).to match('doc_sample.txt') }
+ it { expect(@link_to_file['alt']).to eq('doc_sample.txt') }
+ end
+ end
+ end
+
+ def download_file(repository, url)
+ Projects::DownloadService.new(repository, url).execute
+ end
+end
diff --git a/spec/support/services_shared_context.rb b/spec/support/services_shared_context.rb
new file mode 100644
index 00000000000..4d007ae55ee
--- /dev/null
+++ b/spec/support/services_shared_context.rb
@@ -0,0 +1,21 @@
+Service.available_services_names.each do |service|
+ shared_context service do
+ let(:dashed_service) { service.dasherize }
+ let(:service_method) { "#{service}_service".to_sym }
+ let(:service_klass) { "#{service}_service".classify.constantize }
+ let(:service_attrs_list) { service_klass.new.fields.inject([]) {|arr, hash| arr << hash[:name].to_sym } }
+ let(:service_attrs) do
+ service_attrs_list.inject({}) do |hash, k|
+ if k =~ /^(token*|.*_token|.*_key)/
+ hash.merge!(k => 'secrettoken')
+ elsif k =~ /^(.*_url|url|webhook)/
+ hash.merge!(k => "http://example.com")
+ elsif service == 'irker' && k == :recipients
+ hash.merge!(k => 'irc://irc.network.net:666/#channel')
+ else
+ hash.merge!(k => "someword")
+ end
+ end
+ end
+ end
+end
diff --git a/spec/workers/merge_worker_spec.rb b/spec/workers/merge_worker_spec.rb
new file mode 100644
index 00000000000..b11c5de94e3
--- /dev/null
+++ b/spec/workers/merge_worker_spec.rb
@@ -0,0 +1,28 @@
+require 'spec_helper'
+
+describe MergeWorker do
+ describe "remove source branch" do
+ let!(:merge_request) { create(:merge_request, source_branch: "markdown") }
+ let!(:source_project) { merge_request.source_project }
+ let!(:project) { merge_request.project }
+ let!(:author) { merge_request.author }
+
+ before do
+ source_project.team << [author, :master]
+ source_project.repository.expire_branch_names
+ end
+
+ it 'clears cache of source repo after removing source branch' do
+ expect(source_project.repository.branch_names).to include('markdown')
+
+ MergeWorker.new.perform(
+ merge_request.id, merge_request.author_id,
+ commit_message: 'wow such merge',
+ should_remove_source_branch: true)
+
+ merge_request.reload
+ expect(merge_request).to be_merged
+ expect(source_project.repository.branch_names).not_to include('markdown')
+ end
+ end
+end
diff --git a/spec/workers/post_receive_spec.rb b/spec/workers/post_receive_spec.rb
index 46eae9ab081..e4151b9bb6a 100644
--- a/spec/workers/post_receive_spec.rb
+++ b/spec/workers/post_receive_spec.rb
@@ -4,7 +4,7 @@ describe PostReceive do
let(:changes) { "123456 789012 refs/heads/tést\n654321 210987 refs/tags/tag" }
let(:wrongly_encoded_changes) { changes.encode("ISO-8859-1").force_encoding("UTF-8") }
let(:base64_changes) { Base64.encode64(wrongly_encoded_changes) }
-
+
context "as a resque worker" do
it "reponds to #perform" do
expect(PostReceive.new).to respond_to(:perform)