summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG48
-rw-r--r--CONTRIBUTING.md2
-rw-r--r--Gemfile11
-rw-r--r--Gemfile.lock39
-rw-r--r--app/assets/javascripts/autosave.js.coffee12
-rw-r--r--app/assets/javascripts/blob/blob.js.coffee2
-rw-r--r--app/assets/javascripts/blob/edit_blob.js.coffee2
-rw-r--r--app/assets/javascripts/blob/new_blob.js.coffee2
-rw-r--r--app/assets/javascripts/dashboard.js.coffee29
-rw-r--r--app/assets/javascripts/dispatcher.js.coffee10
-rw-r--r--app/assets/javascripts/importer_status.js.coffee10
-rw-r--r--app/assets/javascripts/issues.js.coffee2
-rw-r--r--app/assets/javascripts/merge_requests.js.coffee37
-rw-r--r--app/assets/javascripts/milestone.js.coffee7
-rw-r--r--app/assets/javascripts/project_show.js.coffee2
-rw-r--r--app/assets/javascripts/projects_list.js.coffee24
-rw-r--r--app/assets/javascripts/user.js.coffee1
-rw-r--r--app/assets/stylesheets/application.scss11
-rw-r--r--app/assets/stylesheets/base/gl_bootstrap.scss (renamed from app/assets/stylesheets/gl_bootstrap.scss)139
-rw-r--r--app/assets/stylesheets/base/gl_variables.scss870
-rw-r--r--app/assets/stylesheets/base/layout.scss (renamed from app/assets/stylesheets/main/layout.scss)2
-rw-r--r--app/assets/stylesheets/base/mixins.scss (renamed from app/assets/stylesheets/main/mixins.scss)19
-rw-r--r--app/assets/stylesheets/base/variables.scss37
-rw-r--r--app/assets/stylesheets/generic/buttons.scss136
-rw-r--r--app/assets/stylesheets/generic/common.scss27
-rw-r--r--app/assets/stylesheets/generic/forms.scss2
-rw-r--r--app/assets/stylesheets/generic/gfm.scss2
-rw-r--r--app/assets/stylesheets/generic/issue_box.scss6
-rw-r--r--app/assets/stylesheets/generic/jquery.scss4
-rw-r--r--app/assets/stylesheets/generic/markdown_area.scss10
-rw-r--r--app/assets/stylesheets/generic/mobile.scss6
-rw-r--r--app/assets/stylesheets/generic/nav_sidebar.scss (renamed from app/assets/stylesheets/sections/nav_sidebar.scss)16
-rw-r--r--app/assets/stylesheets/generic/selects.scss9
-rw-r--r--app/assets/stylesheets/generic/typography.scss72
-rw-r--r--app/assets/stylesheets/highlight/dark.scss8
-rw-r--r--app/assets/stylesheets/highlight/monokai.scss6
-rw-r--r--app/assets/stylesheets/highlight/solarized_dark.scss8
-rw-r--r--app/assets/stylesheets/highlight/solarized_light.scss8
-rw-r--r--app/assets/stylesheets/highlight/white.scss6
-rw-r--r--app/assets/stylesheets/main/fonts.scss3
-rw-r--r--app/assets/stylesheets/main/variables.scss63
-rw-r--r--app/assets/stylesheets/pages/admin.scss (renamed from app/assets/stylesheets/sections/admin.scss)11
-rw-r--r--app/assets/stylesheets/pages/commit.scss (renamed from app/assets/stylesheets/sections/commit.scss)9
-rw-r--r--app/assets/stylesheets/pages/commits.scss (renamed from app/assets/stylesheets/sections/commits.scss)1
-rw-r--r--app/assets/stylesheets/pages/dashboard.scss (renamed from app/assets/stylesheets/sections/dashboard.scss)35
-rw-r--r--app/assets/stylesheets/pages/diff.scss (renamed from app/assets/stylesheets/sections/diff.scss)0
-rw-r--r--app/assets/stylesheets/pages/editor.scss (renamed from app/assets/stylesheets/sections/editor.scss)0
-rw-r--r--app/assets/stylesheets/pages/errors.scss (renamed from app/assets/stylesheets/sections/errors.scss)0
-rw-r--r--app/assets/stylesheets/pages/events.scss (renamed from app/assets/stylesheets/sections/events.scss)5
-rw-r--r--app/assets/stylesheets/pages/explore.scss (renamed from app/assets/stylesheets/sections/explore.scss)0
-rw-r--r--app/assets/stylesheets/pages/graph.scss (renamed from app/assets/stylesheets/sections/graph.scss)0
-rw-r--r--app/assets/stylesheets/pages/groups.scss (renamed from app/assets/stylesheets/sections/groups.scss)0
-rw-r--r--app/assets/stylesheets/pages/header.scss (renamed from app/assets/stylesheets/sections/header.scss)0
-rw-r--r--app/assets/stylesheets/pages/help.scss (renamed from app/assets/stylesheets/sections/help.scss)2
-rw-r--r--app/assets/stylesheets/pages/import.scss (renamed from app/assets/stylesheets/sections/import.scss)0
-rw-r--r--app/assets/stylesheets/pages/issuable.scss (renamed from app/assets/stylesheets/sections/issuable.scss)0
-rw-r--r--app/assets/stylesheets/pages/issues.scss (renamed from app/assets/stylesheets/sections/issues.scss)3
-rw-r--r--app/assets/stylesheets/pages/labels.scss (renamed from app/assets/stylesheets/sections/labels.scss)0
-rw-r--r--app/assets/stylesheets/pages/login.scss (renamed from app/assets/stylesheets/sections/login.scss)0
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss (renamed from app/assets/stylesheets/sections/merge_requests.scss)17
-rw-r--r--app/assets/stylesheets/pages/milestone.scss9
-rw-r--r--app/assets/stylesheets/pages/note_form.scss (renamed from app/assets/stylesheets/sections/note_form.scss)0
-rw-r--r--app/assets/stylesheets/pages/notes.scss (renamed from app/assets/stylesheets/sections/notes.scss)18
-rw-r--r--app/assets/stylesheets/pages/notifications.scss (renamed from app/assets/stylesheets/sections/notifications.scss)6
-rw-r--r--app/assets/stylesheets/pages/profile.scss (renamed from app/assets/stylesheets/sections/profile.scss)4
-rw-r--r--app/assets/stylesheets/pages/projects.scss (renamed from app/assets/stylesheets/sections/projects.scss)25
-rw-r--r--app/assets/stylesheets/pages/search.scss (renamed from app/assets/stylesheets/sections/search.scss)0
-rw-r--r--app/assets/stylesheets/pages/snippets.scss (renamed from app/assets/stylesheets/sections/snippets.scss)0
-rw-r--r--app/assets/stylesheets/pages/stat_graph.scss (renamed from app/assets/stylesheets/sections/stat_graph.scss)0
-rw-r--r--app/assets/stylesheets/pages/themes.scss (renamed from app/assets/stylesheets/sections/themes.scss)0
-rw-r--r--app/assets/stylesheets/pages/tree.scss (renamed from app/assets/stylesheets/sections/tree.scss)16
-rw-r--r--app/assets/stylesheets/pages/ui_dev_kit.scss9
-rw-r--r--app/assets/stylesheets/pages/votes.scss (renamed from app/assets/stylesheets/sections/votes.scss)0
-rw-r--r--app/assets/stylesheets/pages/wiki.scss (renamed from app/assets/stylesheets/sections/wiki.scss)0
-rw-r--r--app/assets/stylesheets/sections/markdown_area.scss9
-rw-r--r--app/assets/stylesheets/sections/milestone.scss3
-rw-r--r--app/assets/stylesheets/themes/ui_blue.scss6
-rw-r--r--app/controllers/admin/groups_controller.rb6
-rw-r--r--app/controllers/admin/projects_controller.rb6
-rw-r--r--app/controllers/admin/services_controller.rb3
-rw-r--r--app/controllers/admin/users_controller.rb4
-rw-r--r--app/controllers/application_controller.rb2
-rw-r--r--app/controllers/dashboard/groups_controller.rb (renamed from app/controllers/profiles/groups_controller.rb)8
-rw-r--r--app/controllers/dashboard/milestones_controller.rb2
-rw-r--r--app/controllers/dashboard/projects_controller.rb27
-rw-r--r--app/controllers/dashboard_controller.rb39
-rw-r--r--app/controllers/explore/groups_controller.rb2
-rw-r--r--app/controllers/explore/projects_controller.rb9
-rw-r--r--app/controllers/groups/application_controller.rb10
-rw-r--r--app/controllers/groups/group_members_controller.rb8
-rw-r--r--app/controllers/groups/milestones_controller.rb2
-rw-r--r--app/controllers/groups_controller.rb12
-rw-r--r--app/controllers/help_controller.rb3
-rw-r--r--app/controllers/import/base_controller.rb18
-rw-r--r--app/controllers/import/github_controller.rb2
-rw-r--r--app/controllers/profiles_controller.rb2
-rw-r--r--app/controllers/projects/branches_controller.rb2
-rw-r--r--app/controllers/projects/imports_controller.rb2
-rw-r--r--app/controllers/projects/issues_controller.rb13
-rw-r--r--app/controllers/projects/labels_controller.rb2
-rw-r--r--app/controllers/projects/merge_requests_controller.rb27
-rw-r--r--app/controllers/projects/milestones_controller.rb2
-rw-r--r--app/controllers/projects/services_controller.rb3
-rw-r--r--app/controllers/projects/tags_controller.rb4
-rw-r--r--app/controllers/projects/wikis_controller.rb2
-rw-r--r--app/controllers/projects_controller.rb10
-rw-r--r--app/controllers/snippets_controller.rb4
-rw-r--r--app/controllers/uploads_controller.rb48
-rw-r--r--app/helpers/application_helper.rb9
-rw-r--r--app/helpers/dashboard_helper.rb16
-rw-r--r--app/helpers/emails_helper.rb25
-rw-r--r--app/helpers/events_helper.rb2
-rw-r--r--app/helpers/explore_helper.rb17
-rw-r--r--app/helpers/gitlab_markdown_helper.rb6
-rw-r--r--app/helpers/gitlab_routing_helper.rb4
-rw-r--r--app/helpers/groups_helper.rb12
-rw-r--r--app/helpers/milestones_helper.rb11
-rw-r--r--app/helpers/namespaces_helper.rb2
-rw-r--r--app/helpers/projects_helper.rb10
-rw-r--r--app/mailers/emails/projects.rb26
-rw-r--r--app/mailers/notify.rb24
-rw-r--r--app/models/application_setting.rb22
-rw-r--r--app/models/concerns/mentionable.rb1
-rw-r--r--app/models/event.rb12
-rw-r--r--app/models/identity.rb2
-rw-r--r--app/models/note.rb39
-rw-r--r--app/models/project_services/asana_service.rb22
-rw-r--r--app/models/project_services/assembla_service.rb19
-rw-r--r--app/models/project_services/bamboo_service.rb19
-rw-r--r--app/models/project_services/buildbox_service.rb20
-rw-r--r--app/models/project_services/campfire_service.rb25
-rw-r--r--app/models/project_services/ci_service.rb26
-rw-r--r--app/models/project_services/custom_issue_tracker_service.rb22
-rw-r--r--app/models/project_services/emails_on_push_service.rb47
-rw-r--r--app/models/project_services/flowdock_service.rb26
-rw-r--r--app/models/project_services/gemnasium_service.rb26
-rw-r--r--app/models/project_services/gitlab_ci_service.rb26
-rw-r--r--app/models/project_services/gitlab_issue_tracker_service.rb19
-rw-r--r--app/models/project_services/hipchat_service.rb190
-rw-r--r--app/models/project_services/irker_service.rb23
-rw-r--r--app/models/project_services/issue_tracker_service.rb43
-rw-r--r--app/models/project_services/jira_service.rb19
-rw-r--r--app/models/project_services/pivotaltracker_service.rb19
-rw-r--r--app/models/project_services/pushover_service.rb25
-rw-r--r--app/models/project_services/redmine_service.rb19
-rw-r--r--app/models/project_services/slack_service.rb26
-rw-r--r--app/models/project_services/slack_service/issue_message.rb8
-rw-r--r--app/models/project_services/slack_service/merge_message.rb14
-rw-r--r--app/models/project_services/slack_service/note_message.rb82
-rw-r--r--app/models/project_services/slack_service/push_message.rb18
-rw-r--r--app/models/project_services/teamcity_service.rb21
-rw-r--r--app/models/repository.rb3
-rw-r--r--app/models/service.rb28
-rw-r--r--app/models/snippet.rb4
-rw-r--r--app/models/user.rb112
-rw-r--r--app/services/create_tag_service.rb4
-rw-r--r--app/services/event_create_service.rb8
-rw-r--r--app/services/git_push_service.rb20
-rw-r--r--app/services/issues/bulk_update_service.rb35
-rw-r--r--app/services/merge_requests/refresh_service.rb12
-rw-r--r--app/services/notes/create_service.rb12
-rw-r--r--app/services/notification_service.rb1
-rw-r--r--app/services/system_hooks_service.rb2
-rw-r--r--app/views/admin/application_settings/_form.html.haml52
-rw-r--r--app/views/admin/dashboard/index.html.haml4
-rw-r--r--app/views/admin/groups/_form.html.haml2
-rw-r--r--app/views/admin/groups/show.html.haml2
-rw-r--r--app/views/admin/projects/index.html.haml4
-rw-r--r--app/views/admin/services/_form.html.haml8
-rw-r--r--app/views/admin/users/index.html.haml4
-rw-r--r--app/views/admin/users/show.html.haml76
-rw-r--r--app/views/dashboard/_groups.html.haml21
-rw-r--r--app/views/dashboard/_project.html.haml14
-rw-r--r--app/views/dashboard/_projects.html.haml19
-rw-r--r--app/views/dashboard/_projects_filter.html.haml100
-rw-r--r--app/views/dashboard/_sidebar.html.haml17
-rw-r--r--app/views/dashboard/_zero_authorized_projects.html.haml5
-rw-r--r--app/views/dashboard/groups/index.html.haml (renamed from app/views/profiles/groups/index.html.haml)3
-rw-r--r--app/views/dashboard/milestones/index.html.haml3
-rw-r--r--app/views/dashboard/milestones/show.html.haml3
-rw-r--r--app/views/dashboard/projects.html.haml60
-rw-r--r--app/views/dashboard/projects/starred.html.haml23
-rw-r--r--app/views/dashboard/show.html.haml2
-rw-r--r--app/views/events/_events.html.haml2
-rw-r--r--app/views/explore/projects/_filter.html.haml67
-rw-r--r--app/views/explore/projects/index.html.haml27
-rw-r--r--app/views/groups/_projects.html.haml31
-rw-r--r--app/views/groups/edit.html.haml2
-rw-r--r--app/views/groups/group_members/_group_member.html.haml5
-rw-r--r--app/views/groups/milestones/index.html.haml3
-rw-r--r--app/views/groups/milestones/show.html.haml3
-rw-r--r--app/views/groups/projects.html.haml2
-rw-r--r--app/views/groups/show.html.haml2
-rw-r--r--app/views/help/ui.html.haml208
-rw-r--r--app/views/import/bitbucket/status.html.haml2
-rw-r--r--app/views/import/github/status.html.haml2
-rw-r--r--app/views/import/gitlab/status.html.haml2
-rw-r--r--app/views/import/gitorious/status.html.haml2
-rw-r--r--app/views/layouts/_public_head_panel.html.haml2
-rw-r--r--app/views/layouts/nav/_dashboard.html.haml16
-rw-r--r--app/views/layouts/nav/_profile.html.haml5
-rw-r--r--app/views/layouts/notify.html.haml14
-rw-r--r--app/views/notify/_note_message.html.haml2
-rw-r--r--app/views/notify/new_issue_email.html.haml2
-rw-r--r--app/views/notify/new_merge_request_email.html.haml2
-rw-r--r--app/views/notify/repository_push_email.html.haml68
-rw-r--r--app/views/notify/repository_push_email.text.haml52
-rw-r--r--app/views/profiles/accounts/show.html.haml2
-rw-r--r--app/views/profiles/design.html.haml5
-rw-r--r--app/views/profiles/notifications/show.html.haml2
-rw-r--r--app/views/profiles/passwords/edit.html.haml2
-rw-r--r--app/views/profiles/show.html.haml10
-rw-r--r--app/views/projects/_bitbucket_import_modal.html.haml2
-rw-r--r--app/views/projects/_issuable_form.html.haml2
-rw-r--r--app/views/projects/blob/_blob.html.haml2
-rw-r--r--app/views/projects/diffs/_stats.html.haml2
-rw-r--r--app/views/projects/diffs/_warning.html.haml2
-rw-r--r--app/views/projects/imports/new.html.haml2
-rw-r--r--app/views/projects/issues/index.html.haml13
-rw-r--r--app/views/projects/labels/_form.html.haml2
-rw-r--r--app/views/projects/merge_requests/_merge_requests.html.haml13
-rw-r--r--app/views/projects/merge_requests/_new_submit.html.haml4
-rw-r--r--app/views/projects/merge_requests/index.html.haml31
-rw-r--r--app/views/projects/merge_requests/show/_diffs.html.haml2
-rw-r--r--app/views/projects/milestones/_merge_request.html.haml3
-rw-r--r--app/views/projects/milestones/_milestone.html.haml3
-rw-r--r--app/views/projects/milestones/show.html.haml3
-rw-r--r--app/views/projects/new.html.haml4
-rw-r--r--app/views/projects/protected_branches/index.html.haml2
-rw-r--r--app/views/projects/services/_form.html.haml20
-rw-r--r--app/views/projects/show.html.haml2
-rw-r--r--app/views/projects/team_members/_team_member.html.haml5
-rw-r--r--app/views/shared/_group_form.html.haml2
-rw-r--r--app/views/shared/_issuable_search_form.html.haml9
-rw-r--r--app/views/shared/_no_ssh.html.haml6
-rw-r--r--app/views/shared/_project.html.haml21
-rw-r--r--app/views/shared/_projects_list.html.haml17
-rw-r--r--app/views/users/_groups.html.haml2
-rw-r--r--app/views/users/_profile.html.haml2
-rw-r--r--app/views/users/_projects.html.haml20
-rw-r--r--app/views/users/show.html.haml15
-rw-r--r--app/workers/emails_on_push_worker.rb26
-rw-r--r--app/workers/irker_worker.rb6
-rw-r--r--app/workers/post_receive.rb8
-rw-r--r--config/application.rb2
-rw-r--r--config/gitlab.yml.example1
-rw-r--r--config/initializers/1_settings.rb1
-rw-r--r--config/initializers/smtp_settings.rb.sample5
-rw-r--r--config/initializers/timeout.rb8
-rw-r--r--config/routes.rb27
-rw-r--r--config/unicorn.rb.example20
-rw-r--r--db/fixtures/development/05_users.rb27
-rw-r--r--db/migrate/20150225065047_add_note_events_to_services.rb5
-rw-r--r--db/migrate/20150306023106_fix_namespace_duplication.rb21
-rw-r--r--db/migrate/20150306023112_add_unique_index_to_namespace.rb9
-rw-r--r--db/schema.rb9
-rw-r--r--doc/development/README.md1
-rw-r--r--doc/development/ui_guide.md12
-rw-r--r--doc/integration/github.md4
-rw-r--r--doc/integration/gitlab.md4
-rw-r--r--doc/integration/google.md2
-rw-r--r--doc/markdown/markdown.md20
-rw-r--r--doc/release/monthly.md6
-rw-r--r--doc/release/patch.md2
-rw-r--r--doc/release/security.md4
-rw-r--r--docker/Dockerfile2
-rw-r--r--features/dashboard/archived_projects.feature5
-rw-r--r--features/dashboard/group.feature (renamed from features/profile/group.feature)26
-rw-r--r--features/dashboard/projects.feature9
-rw-r--r--features/dashboard/starred_projects.feature12
-rw-r--r--features/groups.feature8
-rw-r--r--features/project/archived.feature9
-rw-r--r--features/project/merge_requests.feature7
-rw-r--r--features/steps/dashboard/group.rb (renamed from features/steps/profile/group.rb)21
-rw-r--r--features/steps/dashboard/projects.rb11
-rw-r--r--features/steps/dashboard/starred_projects.rb15
-rw-r--r--features/steps/groups.rb19
-rw-r--r--features/steps/project/merge_requests.rb4
-rw-r--r--features/steps/shared/active_tab.rb2
-rw-r--r--features/steps/shared/paths.rb20
-rw-r--r--lib/api/helpers.rb5
-rw-r--r--lib/gitlab/git.rb20
-rw-r--r--lib/gitlab/git_access.rb10
-rw-r--r--lib/gitlab/ldap/access.rb9
-rw-r--r--lib/gitlab/ldap/authentication.rb2
-rw-r--r--lib/gitlab/ldap/person.rb2
-rw-r--r--lib/gitlab/markdown.rb28
-rw-r--r--lib/gitlab/middleware/timeout.rb13
-rw-r--r--lib/gitlab/note_data_builder.rb77
-rw-r--r--lib/gitlab/push_data_builder.rb8
-rw-r--r--lib/gitlab/reference_extractor.rb16
-rw-r--r--lib/gitlab/theme.rb4
-rw-r--r--lib/gitlab/url_builder.rb29
-rw-r--r--lib/redcarpet/render/gitlab_html.rb13
-rw-r--r--lib/tasks/gitlab/check.rake5
-rw-r--r--public/503.html13
-rw-r--r--spec/controllers/import/github_controller_spec.rb7
-rw-r--r--spec/controllers/uploads_controller_spec.rb296
-rw-r--r--spec/factories/merge_requests.rb2
-rw-r--r--spec/factories/notes.rb6
-rw-r--r--spec/features/security/dashboard_access_spec.rb12
-rw-r--r--spec/features/security/profile_access_spec.rb107
-rw-r--r--spec/helpers/application_helper_spec.rb18
-rw-r--r--spec/helpers/events_helper_spec.rb12
-rw-r--r--spec/helpers/gitlab_markdown_helper_spec.rb54
-rw-r--r--spec/helpers/groups_helper.rb21
-rw-r--r--spec/lib/gitlab/ldap/access_spec.rb7
-rw-r--r--spec/lib/gitlab/note_data_builder_spec.rb73
-rw-r--r--spec/lib/gitlab/reference_extractor_spec.rb19
-rw-r--r--spec/lib/gitlab/url_builder_spec.rb58
-rw-r--r--spec/mailers/notify_spec.rb144
-rw-r--r--spec/models/application_setting_spec.rb21
-rw-r--r--spec/models/asana_service_spec.rb21
-rw-r--r--spec/models/project_services/assembla_service_spec.rb11
-rw-r--r--spec/models/project_services/buildbox_service_spec.rb11
-rw-r--r--spec/models/project_services/flowdock_service_spec.rb11
-rw-r--r--spec/models/project_services/gemnasium_service_spec.rb11
-rw-r--r--spec/models/project_services/gitlab_ci_service_spec.rb11
-rw-r--r--spec/models/project_services/gitlab_issue_tracker_service_spec.rb23
-rw-r--r--spec/models/project_services/hipchat_service_spec.rb217
-rw-r--r--spec/models/project_services/irker_service_spec.rb21
-rw-r--r--spec/models/project_services/jira_service_spec.rb21
-rw-r--r--spec/models/project_services/pushover_service_spec.rb11
-rw-r--r--spec/models/project_services/slack_service/issue_message_spec.rb11
-rw-r--r--spec/models/project_services/slack_service/merge_message_spec.rb13
-rw-r--r--spec/models/project_services/slack_service/note_message_spec.rb129
-rw-r--r--spec/models/project_services/slack_service/push_message_spec.rb24
-rw-r--r--spec/models/project_services/slack_service_spec.rb78
-rw-r--r--spec/models/repository_spec.rb23
-rw-r--r--spec/models/service_spec.rb26
-rw-r--r--spec/models/user_spec.rb88
-rw-r--r--spec/services/git_push_service_spec.rb9
-rw-r--r--spec/services/issues/bulk_update_service_spec.rb28
-rw-r--r--spec/services/merge_requests/refresh_service_spec.rb2
-rw-r--r--spec/support/repo_helpers.rb19
335 files changed, 4845 insertions, 2131 deletions
diff --git a/CHANGELOG b/CHANGELOG
index de13ef3da55..97376c85ece 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,8 +1,14 @@
Please view this file on the master branch, on stable branches it's out of date.
v 7.9.0 (unreleased)
+ - Fix broken email images (Hannes Rosenögger)
+ - Fix mass SQL statements on initial push (Hannes Rosenögger)
+ - Add tag push notifications and normalize HipChat and Slack messages to be consistent (Stan Hu)
+ - Add comment notification events to HipChat and Slack services (Stan Hu)
+ - Add issue and merge request events to HipChat and Slack services (Stan Hu)
- Fix merge request URL passed to Webhooks. (Stan Hu)
- Fix bug that caused a server error when editing a comment to "+1" or "-1" (Stan Hu)
+ - Fix code preview theme setting for comments, issues, merge requests, and snippets (Stan Hu)
- Move labels/milestones tabs to sidebar
- Upgrade Rails gem to version 4.1.9.
- Improve error messages for file edit failures
@@ -13,6 +19,7 @@ v 7.9.0 (unreleased)
- Fix ordering of imported but unchanged projects (Marco Wessel)
- Mobile UI improvements: make aside content expandable
- Expose avatar_url in projects API
+ - Fix checkbox alignment on the application settings page.
- Generalize image upload in drag and drop in markdown to all files (Hannes Rosenögger)
- Fix mass-unassignment of issues (Robert Speicher)
- Allow user confirmation to be skipped for new users via API
@@ -24,6 +31,43 @@ v 7.9.0 (unreleased)
- Add Bitbucket omniauth provider.
- Add Bitbucket importer.
- Support referencing issues to a project whose name starts with a digit
+ - Condense commits already in target branch when updating merge request source branch.
+ - Send notifications and leave system comments when bulk updating issues.
+ - Automatically link commit ranges to compare page: sha1...sha4 or sha1..sha4 (includes sha1 in comparison)
+ - Move groups page from profile to dashboard
+ - Starred projects page at dashboard
+ - Blocking user does not remove him/her from project/groups but show blocked label
+ - Change subject of EmailsOnPush emails to include namespace, project and branch.
+ - Change subject of EmailsOnPush emails to include first commit message when multiple were pushed.
+ - Remove confusing footer from EmailsOnPush mail body.
+ - Add list of changed files to EmailsOnPush emails.
+ - Add option to send EmailsOnPush emails from committer email if domain matches.
+ - Add option to disable code diffs in EmailOnPush emails.
+ - Wrap commit message in EmailsOnPush email.
+ - Send EmailsOnPush emails when deleting commits using force push.
+ - Fix EmailsOnPush email comparison link to include first commit.
+ - Fix highliht of selected lines in file
+ - Reject access to group/project avatar if the user doesn't have access.
+ - Add database migration to clean group duplicates with same path and name (Make sure you have a backup before update)
+ - Add GitLab active users count to rake gitlab:check
+ - Starred projects page at dashboard
+ - Make email display name configurable
+ - Improve json validation in hook data
+ - Use Emoji One
+ - Updated emoji help documentation to properly reference EmojiOne.
+ - Fix missing GitHub organisation repositories on import page.
+ - Added blue thmeme
+ - Remove annoying notice messages when create/update merge request
+ - Allow smb:// links in Markdown text.
+ - Filter merge request by title or description at Merge Requests page
+ - Block user if he/she was blocked in Active Directory
+
+v 7.8.4
+ - Fix issue_tracker_id substitution in custom issue trackers
+ - Fix path and name duplication in namespaces
+
+v 7.8.3
+ - Bump version of gitlab_git fixing annotated tags without message
v 7.8.2
- Fix service migration issue when upgrading from versions prior to 7.3
@@ -32,7 +76,9 @@ v 7.8.2
- Fix response of push to repository to return "Not found" if user doesn't have access
- Fix check if user is allowed to view the file attachment
- Fix import check for case sensetive namespaces
- - Added issue and merge request events to Slack service (Stan Hu)
+ - Increase timeout for Git-over-HTTP requests to 1 hour since large pulls/pushes can take a long time.
+ - Properly handle autosave local storage exceptions.
+ - Escape wildcards when searching LDAP by username.
v 7.8.1
- Fix run of custom post receive hooks
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 73a8f9eb49f..42b5ce22e32 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -63,6 +63,8 @@ Merge requests can be filed either at [gitlab.com](https://gitlab.com/gitlab-org
If you are new to GitLab development (or web development in general), search for the label `easyfix` ([gitlab.com](https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=easyfix), [github](https://github.com/gitlabhq/gitlabhq/labels/easyfix)). Those are issues easy to fix, marked by the GitLab core-team. If you are unsure how to proceed but want to help, mention one of the core-team members to give you a hint.
+To start with GitLab download the [GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit) and see [Development section](doc/development/README.md) in the help file.
+
### Merge request guidelines
If you can, please submit a merge request with the fix or improvements including tests. If you don't know how to fix the issue but can write a test that exposes the issue we will accept that as well. In general bug fixes that include a regression test are merged quickly while new features without proper tests are least likely to receive timely feedback. The workflow to make a merge request is as follows:
diff --git a/Gemfile b/Gemfile
index d711efe6215..064ed0958a7 100644
--- a/Gemfile
+++ b/Gemfile
@@ -39,7 +39,7 @@ gem "browser"
# Extracting information from a git repository
# Provide access to Gitlab::Git library
-gem "gitlab_git", '7.0.0.rc15'
+gem "gitlab_git", '7.1.0'
# Ruby/Rack Git Smart-HTTP Server Handler
gem 'gitlab-grack', '~> 2.0.0.rc2', require: 'grack'
@@ -88,7 +88,7 @@ gem "six"
gem "seed-fu"
# Markup pipeline for GitLab
-gem 'html-pipeline-gitlab', '~> 0.1.0'
+gem 'html-pipeline-gitlab', '~> 0.1'
# Markdown to HTML
gem "github-markup"
@@ -177,6 +177,9 @@ gem 'ace-rails-ap'
# Keyboard shortcuts
gem 'mousetrap-rails'
+# Shutting down requests that take too long
+gem "slowpoke"
+
gem "sass-rails", '~> 4.0.2'
gem "coffee-rails"
gem "uglifier"
@@ -191,7 +194,7 @@ gem "jquery-scrollto-rails"
gem "raphael-rails", "~> 2.1.2"
gem 'bootstrap-sass', '~> 3.0'
gem "font-awesome-rails", '~> 4.2'
-gem "gitlab_emoji", "~> 0.0.1.1"
+gem "gitlab_emoji", "~> 0.1"
gem "gon", '~> 5.0.0'
gem 'nprogress-rails'
gem 'request_store'
@@ -250,7 +253,7 @@ group :development, :test do
gem 'jasmine', '2.0.2'
- gem "spring", '1.3.1'
+ gem "spring", '~> 1.3.1'
gem "spring-commands-rspec", '1.0.4'
gem "spring-commands-spinach", '1.0.0'
end
diff --git a/Gemfile.lock b/Gemfile.lock
index 1413e967416..c847424a7c4 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -144,11 +144,10 @@ GEM
email_spec (1.5.0)
launchy (~> 2.1)
mail (~> 2.2)
- emoji (1.0.1)
- json
enumerize (0.7.0)
activesupport (>= 3.2)
equalizer (0.0.8)
+ errbase (0.0.2)
erubis (2.7.0)
escape_utils (0.2.4)
eventmachine (1.0.4)
@@ -190,8 +189,10 @@ GEM
dotenv (>= 0.7)
thor (>= 0.13.6)
formatador (0.2.4)
- gemnasium-gitlab-service (0.2.4)
+ gemnasium-gitlab-service (0.2.5)
rugged (~> 0.21)
+ gemojione (2.0.0)
+ json
gherkin-ruby (0.3.1)
racc
github-markup (1.3.1)
@@ -210,9 +211,9 @@ GEM
charlock_holmes (~> 0.6.6)
escape_utils (~> 0.2.4)
mime-types (~> 1.19)
- gitlab_emoji (0.0.1.1)
- emoji (~> 1.0.1)
- gitlab_git (7.0.0.rc15)
+ gitlab_emoji (0.1.0)
+ gemojione (~> 2.0)
+ gitlab_git (7.1.0)
activesupport (~> 4.0)
charlock_holmes (~> 0.6)
gitlab-linguist (~> 3.0)
@@ -277,10 +278,11 @@ GEM
html-pipeline (1.11.0)
activesupport (>= 2)
nokogiri (~> 1.4)
- html-pipeline-gitlab (0.1.5)
+ html-pipeline-gitlab (0.2.0)
actionpack (~> 4)
- gitlab_emoji (~> 0.0.1)
+ gitlab_emoji (~> 0.1)
html-pipeline (~> 1.11.0)
+ mime-types
sanitize (~> 2.1)
http_parser.rb (0.5.3)
httparty (0.13.0)
@@ -428,6 +430,7 @@ GEM
rack
rack-test (0.6.3)
rack (>= 1.0)
+ rack-timeout (0.2.0)
rails (4.1.9)
actionmailer (= 4.1.9)
actionpack (= 4.1.9)
@@ -481,6 +484,8 @@ GEM
rest-client (1.6.7)
mime-types (>= 1.16)
rinku (1.7.3)
+ robustly (0.0.3)
+ errbase
rouge (1.7.4)
rspec (2.99.0)
rspec-core (~> 2.99.0)
@@ -516,7 +521,7 @@ GEM
rubyntlm (0.4.0)
rubypants (0.2.0)
rugged (0.21.4)
- rugments (1.0.0.beta3)
+ rugments (1.0.0.beta4)
safe_yaml (0.9.7)
sanitize (2.1.0)
nokogiri (>= 1.4.4)
@@ -563,6 +568,9 @@ GEM
temple (~> 0.6.6)
tilt (>= 1.3.3, < 2.1)
slop (3.6.0)
+ slowpoke (0.0.5)
+ rack-timeout (>= 0.1.0)
+ robustly
spinach (0.8.7)
colorize (= 0.5.8)
gherkin-ruby (>= 0.3.1)
@@ -570,7 +578,7 @@ GEM
capybara (>= 2.0.0)
railties (>= 3)
spinach (>= 0.4)
- spring (1.3.1)
+ spring (1.3.3)
spring-commands-rspec (1.0.4)
spring (>= 0.9.1)
spring-commands-spinach (1.0.0)
@@ -600,7 +608,7 @@ GEM
eventmachine (>= 1.0.0)
rack (>= 1.0.0)
thor (0.19.1)
- thread_safe (0.3.4)
+ thread_safe (0.3.5)
tilt (1.4.1)
timers (4.0.1)
hitimes
@@ -700,8 +708,8 @@ DEPENDENCIES
gitlab-flowdock-git-hook (~> 0.4.2)
gitlab-grack (~> 2.0.0.rc2)
gitlab-linguist (~> 3.0.1)
- gitlab_emoji (~> 0.0.1.1)
- gitlab_git (= 7.0.0.rc15)
+ gitlab_emoji (~> 0.1)
+ gitlab_git (= 7.1.0)
gitlab_meta (= 7.0)
gitlab_omniauth-ldap (= 1.2.0)
gollum-lib (~> 4.0.0)
@@ -713,7 +721,7 @@ DEPENDENCIES
guard-spinach
haml-rails
hipchat (~> 1.4.0)
- html-pipeline-gitlab (~> 0.1.0)
+ html-pipeline-gitlab (~> 0.1)
httparty
jasmine (= 2.0.2)
jquery-atwho-rails (~> 0.3.3)
@@ -772,8 +780,9 @@ DEPENDENCIES
six
slack-notifier (~> 1.0.0)
slim
+ slowpoke
spinach-rails
- spring (= 1.3.1)
+ spring (~> 1.3.1)
spring-commands-rspec (= 1.0.4)
spring-commands-spinach (= 1.0.0)
stamp
diff --git a/app/assets/javascripts/autosave.js.coffee b/app/assets/javascripts/autosave.js.coffee
index 3450f4b55f7..5d3fe81da74 100644
--- a/app/assets/javascripts/autosave.js.coffee
+++ b/app/assets/javascripts/autosave.js.coffee
@@ -14,7 +14,11 @@ class @Autosave
restore: ->
return unless window.localStorage?
- text = window.localStorage.getItem @key
+ try
+ text = window.localStorage.getItem @key
+ catch
+ return
+
@field.val text if text?.length > 0
@field.trigger "input"
@@ -23,11 +27,13 @@ class @Autosave
text = @field.val()
if text?.length > 0
- window.localStorage.setItem @key, text
+ try
+ window.localStorage.setItem @key, text
else
@reset()
reset: ->
return unless window.localStorage?
- window.localStorage.removeItem @key \ No newline at end of file
+ try
+ window.localStorage.removeItem @key
diff --git a/app/assets/javascripts/blob/blob.js.coffee b/app/assets/javascripts/blob/blob.js.coffee
index a5f15f80c5c..37a175fdbc7 100644
--- a/app/assets/javascripts/blob/blob.js.coffee
+++ b/app/assets/javascripts/blob/blob.js.coffee
@@ -26,7 +26,7 @@ class @BlobView
unless isNaN first_line
$("#tree-content-holder .highlight .line").removeClass("hll")
$("#LC#{line}").addClass("hll") for line in [first_line..last_line]
- $.scrollTo("#L#{first_line}") unless e?
+ $.scrollTo("#L#{first_line}", offset: -50) unless e?
# parse selected lines from hash
# always return first and last line (initialized to NaN)
diff --git a/app/assets/javascripts/blob/edit_blob.js.coffee b/app/assets/javascripts/blob/edit_blob.js.coffee
index 6914ca759f6..2e91a06daa8 100644
--- a/app/assets/javascripts/blob/edit_blob.js.coffee
+++ b/app/assets/javascripts/blob/edit_blob.js.coffee
@@ -15,7 +15,7 @@ class @EditBlob
$(".js-commit-button").click ->
$("#file-content").val editor.getValue()
$(".file-editor form").submit()
- return
+ return false
editModePanes = $(".js-edit-mode-pane")
editModeLinks = $(".js-edit-mode a")
diff --git a/app/assets/javascripts/blob/new_blob.js.coffee b/app/assets/javascripts/blob/new_blob.js.coffee
index a6e27116b40..ab8f98715e8 100644
--- a/app/assets/javascripts/blob/new_blob.js.coffee
+++ b/app/assets/javascripts/blob/new_blob.js.coffee
@@ -15,7 +15,7 @@ class @NewBlob
$(".js-commit-button").click ->
$("#file-content").val editor.getValue()
$(".file-editor form").submit()
- return
+ return false
editor: ->
return @editor
diff --git a/app/assets/javascripts/dashboard.js.coffee b/app/assets/javascripts/dashboard.js.coffee
index 6ef5a539b8f..00ee503ff16 100644
--- a/app/assets/javascripts/dashboard.js.coffee
+++ b/app/assets/javascripts/dashboard.js.coffee
@@ -1,30 +1,3 @@
class @Dashboard
constructor: ->
- @initSidebarTab()
-
- $(".dash-filter").keyup ->
- terms = $(this).val()
- uiBox = $(this).parents('.panel').first()
- if terms == "" || terms == undefined
- uiBox.find(".dash-list li").show()
- else
- uiBox.find(".dash-list li").each (index) ->
- name = $(this).find(".filter-title").text()
-
- if name.toLowerCase().search(terms.toLowerCase()) == -1
- $(this).hide()
- else
- $(this).show()
-
-
-
- initSidebarTab: ->
- key = "dashboard_sidebar_filter"
-
- # store selection in cookie
- $('.dash-sidebar-tabs a').on 'click', (e) ->
- $.cookie(key, $(e.target).attr('id'))
-
- # show tab from cookie
- sidebar_filter = $.cookie(key)
- $("#" + sidebar_filter).tab('show') if sidebar_filter
+ new ProjectsList()
diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee
index 591a3749a93..e1015a63d52 100644
--- a/app/assets/javascripts/dispatcher.js.coffee
+++ b/app/assets/javascripts/dispatcher.js.coffee
@@ -52,9 +52,13 @@ class Dispatcher
new ZenMode()
when 'projects:merge_requests:index'
shortcut_handler = new ShortcutsNavigation()
+ MergeRequests.init()
when 'dashboard:show'
new Dashboard()
new Activities()
+ when 'dashboard:projects:starred'
+ new Activities()
+ new ProjectsList()
when 'projects:commit:show'
new Commit()
new Diff()
@@ -62,9 +66,13 @@ class Dispatcher
shortcut_handler = new ShortcutsNavigation()
when 'projects:commits:show'
shortcut_handler = new ShortcutsNavigation()
- when 'groups:show', 'projects:show'
+ when 'projects:show'
+ new Activities()
+ shortcut_handler = new ShortcutsNavigation()
+ when 'groups:show'
new Activities()
shortcut_handler = new ShortcutsNavigation()
+ new ProjectsList()
when 'groups:members'
new GroupMembers()
new UsersSelect()
diff --git a/app/assets/javascripts/importer_status.js.coffee b/app/assets/javascripts/importer_status.js.coffee
index e0e7771ab20..be8d225e73b 100644
--- a/app/assets/javascripts/importer_status.js.coffee
+++ b/app/assets/javascripts/importer_status.js.coffee
@@ -16,20 +16,20 @@ class @ImporterStatus
$(".js-import-all").click (event) =>
$(".js-add-to-import").each ->
$(this).click()
-
+
setAutoUpdate: ->
setInterval (=>
$.get @jobs_url, (data) =>
$.each data, (i, job) =>
job_item = $("#project_" + job.id)
status_field = job_item.find(".job-status")
-
+
if job.import_status == 'finished'
job_item.removeClass("active").addClass("success")
- status_field.html('<span class="cgreen"><i class="fa fa-check"></i> done</span>')
+ status_field.html('<span><i class="fa fa-check"></i> done</span>')
else if job.import_status == 'started'
status_field.html("<i class='fa fa-spinner fa-spin'></i> started")
else
status_field.html(job.import_status)
-
- ), 4000 \ No newline at end of file
+
+ ), 4000
diff --git a/app/assets/javascripts/issues.js.coffee b/app/assets/javascripts/issues.js.coffee
index 6513f4bcefc..40bb9e9cb0c 100644
--- a/app/assets/javascripts/issues.js.coffee
+++ b/app/assets/javascripts/issues.js.coffee
@@ -47,7 +47,7 @@
initSearch: ->
@timer = null
$("#issue_search").keyup ->
- clearTimeout(@timer);
+ clearTimeout(@timer)
@timer = setTimeout(Issues.filterResults, 500)
filterResults: =>
diff --git a/app/assets/javascripts/merge_requests.js.coffee b/app/assets/javascripts/merge_requests.js.coffee
index 9201c84c5ed..83434c1b9ba 100644
--- a/app/assets/javascripts/merge_requests.js.coffee
+++ b/app/assets/javascripts/merge_requests.js.coffee
@@ -1,8 +1,35 @@
#
# * Filter merge requests
#
-@merge_requestsPage = ->
- $('#assignee_id').select2()
- $('#milestone_id').select2()
- $('#milestone_id, #assignee_id').on 'change', ->
- $(this).closest('form').submit()
+@MergeRequests =
+ init: ->
+ MergeRequests.initSearch()
+
+ # Make sure we trigger ajax request only after user stop typing
+ initSearch: ->
+ @timer = null
+ $("#issue_search").keyup ->
+ clearTimeout(@timer)
+ @timer = setTimeout(MergeRequests.filterResults, 500)
+
+ filterResults: =>
+ form = $("#issue_search_form")
+ search = $("#issue_search").val()
+ $('.merge-requests-holder').css("opacity", '0.5')
+ issues_url = form.attr('action') + '? '+ form.serialize()
+
+ $.ajax
+ type: "GET"
+ url: form.attr('action')
+ data: form.serialize()
+ complete: ->
+ $('.merge-requests-holder').css("opacity", '1.0')
+ success: (data) ->
+ $('.merge-requests-holder').html(data.html)
+ # Change url so if user reload a page - search results are saved
+ History.replaceState {page: issues_url}, document.title, issues_url
+ MergeRequests.reload()
+ dataType: "json"
+
+ reload: ->
+ $('#filter_issue_search').val($('#issue_search').val())
diff --git a/app/assets/javascripts/milestone.js.coffee b/app/assets/javascripts/milestone.js.coffee
index c42f31933d3..d644d50b669 100644
--- a/app/assets/javascripts/milestone.js.coffee
+++ b/app/assets/javascripts/milestone.js.coffee
@@ -49,6 +49,13 @@ class @Milestone
data: data
success: (data) ->
if data.saved == true
+ if data.assignee_avatar_url
+ img_tag = $('<img/>')
+ img_tag.attr('src', data.assignee_avatar_url)
+ img_tag.addClass('avatar s16')
+ $(li).find('.assignee-icon').html(img_tag)
+ else
+ $(li).find('.assignee-icon').html('')
$(li).effect 'highlight'
else
new Flash("Issue update failed", 'alert')
diff --git a/app/assets/javascripts/project_show.js.coffee b/app/assets/javascripts/project_show.js.coffee
index d0eaaad92b8..6828ae471e5 100644
--- a/app/assets/javascripts/project_show.js.coffee
+++ b/app/assets/javascripts/project_show.js.coffee
@@ -6,7 +6,7 @@ class @ProjectShow
new Flash('Star toggle failed. Try again later.', 'alert')
$("a[data-toggle='tab']").on "shown.bs.tab", (e) ->
- $.cookie "default_view", $(e.target).attr("href"), { expires: 30 }
+ $.cookie "default_view", $(e.target).attr("href"), { expires: 30, path: '/' }
defaultView = $.cookie("default_view")
if defaultView
diff --git a/app/assets/javascripts/projects_list.js.coffee b/app/assets/javascripts/projects_list.js.coffee
new file mode 100644
index 00000000000..c0e36d1ccc5
--- /dev/null
+++ b/app/assets/javascripts/projects_list.js.coffee
@@ -0,0 +1,24 @@
+class @ProjectsList
+ constructor: ->
+ $(".projects-list .js-expand").on 'click', (e) ->
+ e.preventDefault()
+ list = $(this).closest('.projects-list')
+ list.find("li").show()
+ list.find("li.bottom").hide()
+
+ $(".projects-list-filter").keyup ->
+ terms = $(this).val()
+ uiBox = $(this).closest('.panel')
+ if terms == "" || terms == undefined
+ uiBox.find(".projects-list li").show()
+ else
+ uiBox.find(".projects-list li").each (index) ->
+ name = $(this).find(".filter-title").text()
+
+ if name.toLowerCase().search(terms.toLowerCase()) == -1
+ $(this).hide()
+ else
+ $(this).show()
+ uiBox.find(".projects-list li.bottom").hide()
+
+
diff --git a/app/assets/javascripts/user.js.coffee b/app/assets/javascripts/user.js.coffee
index 8a2e2421c2e..d0d81f96921 100644
--- a/app/assets/javascripts/user.js.coffee
+++ b/app/assets/javascripts/user.js.coffee
@@ -1,3 +1,4 @@
class @User
constructor: ->
$('.profile-groups-avatars').tooltip("placement": "top")
+ new ProjectsList()
diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss
index e5bb5e21bb0..015ff2ce4ec 100644
--- a/app/assets/stylesheets/application.scss
+++ b/app/assets/stylesheets/application.scss
@@ -11,12 +11,17 @@
*= require cal-heatmap
*/
-@import "main/*";
+
+@import "base/variables";
+@import "base/mixins";
+@import "base/layout";
+
/**
* Customized Twitter bootstrap
*/
-@import 'gl_bootstrap';
+@import 'base/gl_variables';
+@import 'base/gl_bootstrap';
/**
* NProgress load bar css
@@ -39,7 +44,7 @@
* Page specific styles (issues, projects etc):
*/
-@import "sections/*";
+@import "pages/*";
/**
* Code highlight
diff --git a/app/assets/stylesheets/gl_bootstrap.scss b/app/assets/stylesheets/base/gl_bootstrap.scss
index 6efa56544a5..16581e9ebf2 100644
--- a/app/assets/stylesheets/gl_bootstrap.scss
+++ b/app/assets/stylesheets/base/gl_bootstrap.scss
@@ -3,11 +3,6 @@
*
*/
-$font-size-base: 13px !default;
-$nav-pills-active-link-hover-bg: $bg_primary;
-$pagination-active-bg: $bg_primary;
-$list-group-active-bg: $bg_primary;
-
// Core variables and mixins
@import "bootstrap/variables";
@import "bootstrap/mixins";
@@ -23,6 +18,7 @@ $list-group-active-bg: $bg_primary;
@import "bootstrap/grid";
@import "bootstrap/tables";
@import "bootstrap/forms";
+@import "bootstrap/buttons";
// Components
@import "bootstrap/component-animations";
@@ -134,10 +130,6 @@ $list-group-active-bg: $bg_primary;
}
}
}
-
- &.nav-small-tabs > li > a {
- padding: 6px 9px;
- }
}
.nav-tabs > li > a,
@@ -145,61 +137,6 @@ $list-group-active-bg: $bg_primary;
color: #666;
}
-.nav-compact > li > a {
- padding: 6px 12px;
-}
-
-.nav-small > li > a {
- padding: 3px 5px;
- font-size: 12px;
-}
-
-
-/*
- * Callouts from Bootstrap3 docs
- *
- * Not quite alerts, but custom and helpful notes for folks reading the docs.
- * Requires a base and modifier class.
- */
-
-/* Common styles for all types */
-.bs-callout {
- margin: 20px 0;
- padding: 20px;
- border-left: 3px solid #eee;
- color: #666;
- background: #f9f9f9;
-}
-.bs-callout h4 {
- margin-top: 0;
- margin-bottom: 5px;
-}
-.bs-callout p:last-child {
- margin-bottom: 0;
-}
-
-/* Variations */
-.bs-callout-danger {
- background-color: #fdf7f7;
- border-color: #eed3d7;
- color: #b94a48;
-}
-.bs-callout-warning {
- background-color: #faf8f0;
- border-color: #faebcc;
- color: #8a6d3b;
-}
-.bs-callout-info {
- background-color: #f4f8fa;
- border-color: #bce8f1;
- color: #34789a;
-}
-.bs-callout-success {
- background-color: #dff0d8;
- border-color: #5cA64d;
- color: #3c763d;
-}
-
/**
* fix to keep tooltips position in top navigation bar
*
@@ -214,16 +151,13 @@ $list-group-active-bg: $bg_primary;
*
*/
.panel {
- @include border-radius(0px);
-
.panel-heading {
- @include border-radius(0px);
font-size: 14px;
line-height: 18px;
.panel-head-actions {
position: relative;
- top: -7px;
+ top: -6px;
float: right;
}
}
@@ -256,40 +190,57 @@ $list-group-active-bg: $bg_primary;
}
}
-.panel-default {
- .panel-heading {
- background-color: #EEE;
+.alert {
+ a {
+ @extend .alert-link;
+ color: #fff;
+ text-decoration: underline;
}
}
-.panel-danger {
- @include panel-colored;
- .panel-heading {
- color: $border_danger;
- border-color: $border_danger;
- }
+// Typography =================================================================
+
+.text-primary,
+.text-primary:hover {
+ color: $brand-primary;
}
-.panel-success {
- @include panel-colored;
- .panel-heading {
- color: $border_success;
- border-color: $border_success;
- }
+.text-success,
+.text-success:hover {
+ color: $brand-success;
}
-.panel-primary {
- @include panel-colored;
- .panel-heading {
- color: $border_primary;
- border-color: $border_primary;
- }
+.text-danger,
+.text-danger:hover {
+ color: $brand-danger;
}
-.panel-warning {
- @include panel-colored;
- .panel-heading {
- color: $border_warning;
- border-color: $border_warning;
+.text-warning,
+.text-warning:hover {
+ color: $brand-warning;
+}
+
+.text-info,
+.text-info:hover {
+ color: $brand-info;
+}
+
+// Tables =====================================================================
+
+table.table {
+ .dropdown-menu a {
+ text-decoration: none;
+ }
+
+ .success,
+ .warning,
+ .danger,
+ .info {
+ color: #fff;
+
+ a:not(.btn) {
+ text-decoration: underline;
+ color: #fff;
+ }
}
}
diff --git a/app/assets/stylesheets/base/gl_variables.scss b/app/assets/stylesheets/base/gl_variables.scss
new file mode 100644
index 00000000000..ea230646a89
--- /dev/null
+++ b/app/assets/stylesheets/base/gl_variables.scss
@@ -0,0 +1,870 @@
+// Override Bootstrap variables here (defaults from bootstrap-sass v3.3.3):
+
+//
+// Variables
+// --------------------------------------------------
+
+
+//== Colors
+//
+//## Gray and brand colors for use across Bootstrap.
+
+// $gray-base: #000
+// $gray-darker: lighten($gray-base, 13.5%) // #222
+// $gray-dark: lighten($gray-base, 20%) // #333
+// $gray: lighten($gray-base, 33.5%) // #555
+// $gray-light: lighten($gray-base, 46.7%) // #777
+// $gray-lighter: lighten($gray-base, 93.5%) // #eee
+$gray-base: #000;
+$gray-darker: lighten($gray-base, 13.5%); // #222
+$gray-dark: #7b8a8b; // #333
+$gray: #95a5a6; // #555
+$gray-light: #b4bcc2; // #999
+$gray-lighter: #ecf0f1; // #eee
+
+$brand-primary: $gl-primary;
+$brand-success: $gl-success;
+$brand-info: $gl-info;
+$brand-warning: $gl-warning;
+$brand-danger: $gl-danger;
+
+
+//== Scaffolding
+//
+//## Settings for some of the most global styles.
+
+//** Background color for `<body>`.
+// $body-bg: #fff
+//** Global text color on `<body>`.
+$text-color: $brand-primary;
+
+//** Global textual link color.
+$link-color: $gl-link-color;
+//** Link hover color set via `darken()` function.
+// $link-hover-color: darken($link-color, 15%)
+//** Link hover decoration.
+// $link-hover-decoration: underline
+
+
+//== Typography
+//
+//## Font, line-height, and color for body text, headings, and more.
+
+// $font-family-sans-serif: "Helvetica Neue", Helvetica, Arial, sans-serif
+// $font-family-serif: Georgia, "Times New Roman", Times, serif
+//** Default monospace fonts for `<code>`, `<kbd>`, and `<pre>`.
+// $font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace
+// $font-family-base: $font-family-sans-serif
+
+$font-size-base: $gl-font-size;
+// $font-size-large: ceil(($font-size-base * 1.25)) // ~18px
+// $font-size-small: ceil(($font-size-base * 0.85)) // ~12px
+
+// $font-size-h1: floor(($font-size-base * 2.6)) // ~36px
+// $font-size-h2: floor(($font-size-base * 2.15)) // ~30px
+// $font-size-h3: ceil(($font-size-base * 1.7)) // ~24px
+// $font-size-h4: ceil(($font-size-base * 1.25)) // ~18px
+// $font-size-h5: $font-size-base
+// $font-size-h6: ceil(($font-size-base * 0.85)) // ~12px
+
+//** Unit-less `line-height` for use in components like buttons.
+// $line-height-base: 1.428571429 // 20/14
+//** Computed "line-height" (`font-size` * `line-height`) for use with `margin`, `padding`, etc.
+// $line-height-computed: floor(($font-size-base * $line-height-base)) // ~20px
+
+//** By default, this inherits from the `<body>`.
+// $headings-font-family: inherit
+// $headings-font-weight: 500
+// $headings-line-height: 1.1
+// $headings-color: inherit
+
+
+//== Iconography
+//
+//## Specify custom location and filename of the included Glyphicons icon font. Useful for those including Bootstrap via Bower.
+
+//** Load fonts from this directory.
+
+// [converter] If $bootstrap-sass-asset-helper if used, provide path relative to the assets load path.
+// [converter] This is because some asset helpers, such as Sprockets, do not work with file-relative paths.
+// $icon-font-path: if($bootstrap-sass-asset-helper, "bootstrap/", "../fonts/bootstrap/")
+
+//** File name for all font files.
+// $icon-font-name: "glyphicons-halflings-regular"
+//** Element ID within SVG icon file.
+// $icon-font-svg-id: "glyphicons_halflingsregular"
+
+
+//== Components
+//
+//## 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-large-vertical: 10px
+// $padding-large-horizontal: 16px
+
+// $padding-small-vertical: 5px
+// $padding-small-horizontal: 10px
+
+// $padding-xs-vertical: 1px
+// $padding-xs-horizontal: 5px
+
+// $line-height-large: 1.3333333 // extra decimals for Win 8.1 Chrome
+// $line-height-small: 1.5
+
+// $border-radius-base: 4px
+// $border-radius-large: 6px
+// $border-radius-small: 3px
+
+//** Global color for active items (e.g., navs or dropdowns).
+// $component-active-color: #fff
+//** Global background color for active items (e.g., navs or dropdowns).
+// $component-active-bg: $brand-primary
+
+//** Width of the `border` for generating carets that indicator dropdowns.
+// $caret-width-base: 4px
+//** Carets increase slightly in size for larger components.
+// $caret-width-large: 5px
+
+
+//== Tables
+//
+//## Customizes the `.table` component with basic values, each used across all table variations.
+
+//** Padding for `<th>`s and `<td>`s.
+// $table-cell-padding: 8px
+//** Padding for cells in `.table-condensed`.
+// $table-condensed-cell-padding: 5px
+
+//** Default background color used for all tables.
+// $table-bg: transparent
+//** Background color used for `.table-striped`.
+// $table-bg-accent: #f9f9f9
+//** Background color used for `.table-hover`.
+// $table-bg-hover: #f5f5f5
+// $table-bg-active: $table-bg-hover
+
+//** Border color for table and cell borders.
+// $table-border-color: #ddd
+
+
+//== Buttons
+//
+//## For each of Bootstrap's buttons, define text, background and border color.
+
+// $btn-font-weight: normal
+
+// $btn-default-color: #333
+// $btn-default-bg: #fff
+// $btn-default-border: #ccc
+
+// $btn-primary-color: #fff
+// $btn-primary-bg: $brand-primary
+// $btn-primary-border: darken($btn-primary-bg, 5%)
+
+// $btn-success-color: #fff
+// $btn-success-bg: $brand-success
+// $btn-success-border: darken($btn-success-bg, 5%)
+
+// $btn-info-color: #fff
+// $btn-info-bg: $brand-info
+// $btn-info-border: darken($btn-info-bg, 5%)
+
+// $btn-warning-color: #fff
+// $btn-warning-bg: $brand-warning
+// $btn-warning-border: darken($btn-warning-bg, 5%)
+
+// $btn-danger-color: #fff
+// $btn-danger-bg: $brand-danger
+// $btn-danger-border: darken($btn-danger-bg, 5%)
+
+// $btn-link-disabled-color: $gray-light
+
+
+//== Forms
+//
+//##
+
+//** `<input>` background color
+// $input-bg: #fff
+//** `<input disabled>` background color
+// $input-bg-disabled: $gray-lighter
+
+//** Text color for `<input>`s
+$input-color: $text-color;
+//** `<input>` border color
+$input-border: #dce4ec;
+
+// TODO: Rename `$input-border-radius` to `$input-border-radius-base` in v4
+//** Default `.form-control` border radius
+// This has no effect on `<select>`s in some browsers, due to the limited stylability of `<select>`s in CSS.
+// $input-border-radius: $border-radius-base
+//** Large `.form-control` border radius
+// $input-border-radius-large: $border-radius-large
+//** Small `.form-control` border radius
+// $input-border-radius-small: $border-radius-small
+
+//** Border color for inputs on focus
+$input-border-focus: $brand-info;
+
+//** Placeholder text color
+// $input-color-placeholder: #999
+
+//** Default `.form-control` height
+// $input-height-base: ($line-height-computed + ($padding-base-vertical * 2) + 2)
+//** Large `.form-control` height
+// $input-height-large: (ceil($font-size-large * $line-height-large) + ($padding-large-vertical * 2) + 2)
+//** Small `.form-control` height
+// $input-height-small: (floor($font-size-small * $line-height-small) + ($padding-small-vertical * 2) + 2)
+
+$legend-color: $text-color;
+// $legend-border-color: #e5e5e5
+
+//** Background color for textual input addons
+// $input-group-addon-bg: $gray-lighter
+//** Border color for textual input addons
+// $input-group-addon-border-color: $input-border
+
+//** Disabled cursor for form controls and buttons.
+// $cursor-disabled: not-allowed
+
+
+//== Dropdowns
+//
+//## Dropdown menu container and contents.
+
+//** Background for the dropdown menu.
+// $dropdown-bg: #fff
+//** Dropdown menu `border-color`.
+// $dropdown-border: rgba(0,0,0,.15)
+//** Dropdown menu `border-color` **for IE8**.
+// $dropdown-fallback-border: #ccc
+//** Divider color for between dropdown items.
+// $dropdown-divider-bg: #e5e5e5
+
+//** Dropdown link text color.
+// $dropdown-link-color: $gray-dark
+//** Hover color for dropdown links.
+// $dropdown-link-hover-color: darken($gray-dark, 5%)
+//** Hover background for dropdown links.
+// $dropdown-link-hover-bg: #f5f5f5
+
+//** Active dropdown menu item text color.
+// $dropdown-link-active-color: $component-active-color
+//** Active dropdown menu item background color.
+// $dropdown-link-active-bg: $component-active-bg
+
+//** Disabled dropdown menu item background color.
+// $dropdown-link-disabled-color: $gray-light
+
+//** Text color for headers within dropdown menus.
+// $dropdown-header-color: $gray-light
+
+//** Deprecated `$dropdown-caret-color` as of v3.1.0
+// $dropdown-caret-color: #000
+
+
+//-- Z-index master list
+//
+// Warning: Avoid customizing these values. They're used for a bird's eye view
+// of components dependent on the z-axis and are designed to all work together.
+//
+// Note: These variables are not generated into the Customizer.
+
+// $zindex-navbar: 1000
+// $zindex-dropdown: 1000
+// $zindex-popover: 1060
+// $zindex-tooltip: 1070
+// $zindex-navbar-fixed: 1030
+// $zindex-modal: 1040
+
+
+//== Media queries breakpoints
+//
+//## Define the breakpoints at which your layout will change, adapting to different screen sizes.
+
+// Extra small screen / phone
+//** Deprecated `$screen-xs` as of v3.0.1
+// $screen-xs: 480px
+//** Deprecated `$screen-xs-min` as of v3.2.0
+// $screen-xs-min: $screen-xs
+//** Deprecated `$screen-phone` as of v3.0.1
+// $screen-phone: $screen-xs-min
+
+// Small screen / tablet
+//** Deprecated `$screen-sm` as of v3.0.1
+// $screen-sm: 768px
+// $screen-sm-min: $screen-sm
+//** Deprecated `$screen-tablet` as of v3.0.1
+// $screen-tablet: $screen-sm-min
+
+// Medium screen / desktop
+//** Deprecated `$screen-md` as of v3.0.1
+// $screen-md: 992px
+// $screen-md-min: $screen-md
+//** Deprecated `$screen-desktop` as of v3.0.1
+// $screen-desktop: $screen-md-min
+
+// Large screen / wide desktop
+//** Deprecated `$screen-lg` as of v3.0.1
+// $screen-lg: 1200px
+// $screen-lg-min: $screen-lg
+//** Deprecated `$screen-lg-desktop` as of v3.0.1
+// $screen-lg-desktop: $screen-lg-min
+
+// So media queries don't overlap when required, provide a maximum
+// $screen-xs-max: ($screen-sm-min - 1)
+// $screen-sm-max: ($screen-md-min - 1)
+// $screen-md-max: ($screen-lg-min - 1)
+
+
+//== Grid system
+//
+//## Define your custom responsive grid.
+
+//** Number of columns in the grid.
+// $grid-columns: 12
+//** Padding between columns. Gets divided in half for the left and right.
+// $grid-gutter-width: 30px
+// Navbar collapse
+//** Point at which the navbar becomes uncollapsed.
+// $grid-float-breakpoint: $screen-sm-min
+//** Point at which the navbar begins collapsing.
+// $grid-float-breakpoint-max: ($grid-float-breakpoint - 1)
+
+
+//== Container sizes
+//
+//## Define the maximum width of `.container` for different screen sizes.
+
+// Small screen / tablet
+// $container-tablet: (720px + $grid-gutter-width)
+//** For `$screen-sm-min` and up.
+// $container-sm: $container-tablet
+
+// Medium screen / desktop
+// $container-desktop: (940px + $grid-gutter-width)
+//** For `$screen-md-min` and up.
+// $container-md: $container-desktop
+
+// Large screen / wide desktop
+// $container-large-desktop: (1140px + $grid-gutter-width)
+//** For `$screen-lg-min` and up.
+// $container-lg: $container-large-desktop
+
+
+//== Navbar
+//
+//##
+
+// Basics of a navbar
+// $navbar-height: 50px
+// $navbar-margin-bottom: $line-height-computed
+// $navbar-border-radius: $border-radius-base
+// $navbar-padding-horizontal: floor(($grid-gutter-width / 2))
+// $navbar-padding-vertical: (($navbar-height - $line-height-computed) / 2)
+// $navbar-collapse-max-height: 340px
+
+// $navbar-default-color: #777
+// $navbar-default-bg: #f8f8f8
+// $navbar-default-border: darken($navbar-default-bg, 6.5%)
+
+// Navbar links
+// $navbar-default-link-color: #777
+// $navbar-default-link-hover-color: #333
+// $navbar-default-link-hover-bg: transparent
+// $navbar-default-link-active-color: #555
+// $navbar-default-link-active-bg: darken($navbar-default-bg, 6.5%)
+// $navbar-default-link-disabled-color: #ccc
+// $navbar-default-link-disabled-bg: transparent
+
+// Navbar brand label
+// $navbar-default-brand-color: $navbar-default-link-color
+// $navbar-default-brand-hover-color: darken($navbar-default-brand-color, 10%)
+// $navbar-default-brand-hover-bg: transparent
+
+// Navbar toggle
+// $navbar-default-toggle-hover-bg: #ddd
+// $navbar-default-toggle-icon-bar-bg: #888
+// $navbar-default-toggle-border-color: #ddd
+
+
+// Inverted navbar
+// Reset inverted navbar basics
+// $navbar-inverse-color: lighten($gray-light, 15%)
+// $navbar-inverse-bg: #222
+// $navbar-inverse-border: darken($navbar-inverse-bg, 10%)
+
+// Inverted navbar links
+// $navbar-inverse-link-color: lighten($gray-light, 15%)
+// $navbar-inverse-link-hover-color: #fff
+// $navbar-inverse-link-hover-bg: transparent
+// $navbar-inverse-link-active-color: $navbar-inverse-link-hover-color
+// $navbar-inverse-link-active-bg: darken($navbar-inverse-bg, 10%)
+// $navbar-inverse-link-disabled-color: #444
+// $navbar-inverse-link-disabled-bg: transparent
+
+// Inverted navbar brand label
+// $navbar-inverse-brand-color: $navbar-inverse-link-color
+// $navbar-inverse-brand-hover-color: #fff
+// $navbar-inverse-brand-hover-bg: transparent
+
+// Inverted navbar toggle
+// $navbar-inverse-toggle-hover-bg: #333
+// $navbar-inverse-toggle-icon-bar-bg: #fff
+// $navbar-inverse-toggle-border-color: #333
+
+
+//== Navs
+//
+//##
+
+//=== Shared nav styles
+// $nav-link-padding: 10px 15px
+// $nav-link-hover-bg: $gray-lighter
+
+// $nav-disabled-link-color: $gray-light
+// $nav-disabled-link-hover-color: $gray-light
+
+//== Tabs
+// $nav-tabs-border-color: #ddd
+
+// $nav-tabs-link-hover-border-color: $gray-lighter
+
+// $nav-tabs-active-link-hover-bg: $body-bg
+// $nav-tabs-active-link-hover-color: $gray
+// $nav-tabs-active-link-hover-border-color: #ddd
+
+// $nav-tabs-justified-link-border-color: #ddd
+// $nav-tabs-justified-active-link-border-color: $body-bg
+
+//== Pills
+// $nav-pills-border-radius: $border-radius-base
+// $nav-pills-active-link-hover-bg: $component-active-bg
+// $nav-pills-active-link-hover-color: $component-active-color
+
+
+//== Pagination
+//
+//##
+
+$pagination-color: #fff;
+$pagination-bg: $brand-success;
+$pagination-border: transparent;
+
+$pagination-hover-color: #fff;
+$pagination-hover-bg: darken($brand-success, 15%);
+$pagination-hover-border: transparent;
+
+$pagination-active-color: #fff;
+$pagination-active-bg: darken($brand-success, 15%);
+$pagination-active-border: transparent;
+
+$pagination-disabled-color: #b4bcc2;
+$pagination-disabled-bg: lighten($brand-success, 15%);
+$pagination-disabled-border: transparent;
+
+
+//== Pager
+//
+//##
+
+// $pager-bg: $pagination-bg
+// $pager-border: $pagination-border
+// $pager-border-radius: 15px
+
+// $pager-hover-bg: $pagination-hover-bg
+
+// $pager-active-bg: $pagination-active-bg
+// $pager-active-color: $pagination-active-color
+
+// $pager-disabled-color: $pagination-disabled-color
+
+
+//== Jumbotron
+//
+//##
+
+// $jumbotron-padding: 30px
+// $jumbotron-color: inherit
+// $jumbotron-bg: $gray-lighter
+// $jumbotron-heading-color: inherit
+// $jumbotron-font-size: ceil(($font-size-base * 1.5))
+
+
+//== Form states and alerts
+//
+//## Define colors for form feedback states and, by default, alerts.
+
+
+$state-success-text: #fff;
+$state-success-bg: $brand-success;
+$state-success-border: $brand-success;
+
+$state-info-text: #fff;
+$state-info-bg: $brand-info;
+$state-info-border: $brand-info;
+
+$state-warning-text: #fff;
+$state-warning-bg: $brand-warning;
+$state-warning-border: $brand-warning;
+
+$state-danger-text: #fff;
+$state-danger-bg: $brand-danger;
+$state-danger-border: $brand-danger;
+
+
+//== Tooltips
+//
+//##
+
+//** Tooltip max width
+// $tooltip-max-width: 200px
+//** Tooltip text color
+// $tooltip-color: #fff
+//** Tooltip background color
+// $tooltip-bg: #000
+// $tooltip-opacity: .9
+
+//** Tooltip arrow width
+// $tooltip-arrow-width: 5px
+//** Tooltip arrow color
+// $tooltip-arrow-color: $tooltip-bg
+
+
+//== Popovers
+//
+//##
+
+//** Popover body background color
+// $popover-bg: #fff
+//** Popover maximum width
+// $popover-max-width: 276px
+//** Popover border color
+// $popover-border-color: rgba(0,0,0,.2)
+//** Popover fallback border color
+// $popover-fallback-border-color: #ccc
+
+//** Popover title background color
+// $popover-title-bg: darken($popover-bg, 3%)
+
+//** Popover arrow width
+// $popover-arrow-width: 10px
+//** Popover arrow color
+// $popover-arrow-color: $popover-bg
+
+//** Popover outer arrow width
+// $popover-arrow-outer-width: ($popover-arrow-width + 1)
+//** Popover outer arrow color
+// $popover-arrow-outer-color: fade_in($popover-border-color, 0.05)
+//** Popover outer arrow fallback color
+// $popover-arrow-outer-fallback-color: darken($popover-fallback-border-color, 20%)
+
+
+//== Labels
+//
+//##
+
+//** Default label background color
+// $label-default-bg: $gray-light
+//** Primary label background color
+// $label-primary-bg: $brand-primary
+//** Success label background color
+// $label-success-bg: $brand-success
+//** Info label background color
+// $label-info-bg: $brand-info
+//** Warning label background color
+// $label-warning-bg: $brand-warning
+//** Danger label background color
+// $label-danger-bg: $brand-danger
+
+//** Default label text color
+// $label-color: #fff
+//** Default text color of a linked label
+// $label-link-hover-color: #fff
+
+
+//== Modals
+//
+//##
+
+//** Padding applied to the modal body
+// $modal-inner-padding: 15px
+
+//** Padding applied to the modal title
+// $modal-title-padding: 15px
+//** Modal title line-height
+// $modal-title-line-height: $line-height-base
+
+//** Background color of modal content area
+// $modal-content-bg: #fff
+//** Modal content border color
+// $modal-content-border-color: rgba(0,0,0,.2)
+//** Modal content border color **for IE8**
+// $modal-content-fallback-border-color: #999
+
+//** Modal backdrop background color
+// $modal-backdrop-bg: #000
+//** Modal backdrop opacity
+// $modal-backdrop-opacity: .5
+//** Modal header border color
+// $modal-header-border-color: #e5e5e5
+//** Modal footer border color
+// $modal-footer-border-color: $modal-header-border-color
+
+// $modal-lg: 900px
+// $modal-md: 600px
+// $modal-sm: 300px
+
+
+//== Alerts
+//
+//## Define alert colors, border radius, and padding.
+
+// $alert-padding: 15px
+$alert-border-radius: 0;
+// $alert-link-font-weight: bold
+
+// $alert-success-bg: $state-success-bg
+// $alert-success-text: $state-success-text
+// $alert-success-border: $state-success-border
+
+// $alert-info-bg: $state-info-bg
+// $alert-info-text: $state-info-text
+// $alert-info-border: $state-info-border
+
+// $alert-warning-bg: $state-warning-bg
+// $alert-warning-text: $state-warning-text
+// $alert-warning-border: $state-warning-border
+
+// $alert-danger-bg: $state-danger-bg
+// $alert-danger-text: $state-danger-text
+// $alert-danger-border: $state-danger-border
+
+
+//== Progress bars
+//
+//##
+
+//** Background color of the whole progress component
+// $progress-bg: #f5f5f5
+//** Progress bar text color
+// $progress-bar-color: #fff
+//** Variable for setting rounded corners on progress bar.
+// $progress-border-radius: $border-radius-base
+
+//** Default progress bar color
+// $progress-bar-bg: $brand-primary
+//** Success progress bar color
+// $progress-bar-success-bg: $brand-success
+//** Warning progress bar color
+// $progress-bar-warning-bg: $brand-warning
+//** Danger progress bar color
+// $progress-bar-danger-bg: $brand-danger
+//** Info progress bar color
+// $progress-bar-info-bg: $brand-info
+
+
+//== List group
+//
+//##
+
+//** Background color on `.list-group-item`
+// $list-group-bg: #fff
+//** `.list-group-item` border color
+// $list-group-border: #ddd
+//** List group border radius
+// $list-group-border-radius: $border-radius-base
+
+//** Background color of single list items on hover
+// $list-group-hover-bg: #f5f5f5
+//** Text color of active list items
+// $list-group-active-color: $component-active-color
+//** Background color of active list items
+// $list-group-active-bg: $component-active-bg
+//** Border color of active list elements
+// $list-group-active-border: $list-group-active-bg
+//** Text color for content within active list items
+// $list-group-active-text-color: lighten($list-group-active-bg, 40%)
+
+//** Text color of disabled list items
+// $list-group-disabled-color: $gray-light
+//** Background color of disabled list items
+// $list-group-disabled-bg: $gray-lighter
+//** Text color for content within disabled list items
+// $list-group-disabled-text-color: $list-group-disabled-color
+
+// $list-group-link-color: #555
+// $list-group-link-hover-color: $list-group-link-color
+// $list-group-link-heading-color: #333
+
+
+//== Panels
+//
+//##
+
+// $panel-bg: #fff
+// $panel-body-padding: 15px
+// $panel-heading-padding: 10px 15px
+// $panel-footer-padding: $panel-heading-padding
+$panel-border-radius: 0;
+
+//** Border color for elements within panels
+// $panel-inner-border: #ddd
+// $panel-footer-bg: #f5f5f5
+
+$panel-default-text: $text-color;
+// $panel-default-border: #ddd
+// $panel-default-heading-bg: #f5f5f5
+
+// $panel-primary-text: #fff
+// $panel-primary-border: $brand-primary
+// $panel-primary-heading-bg: $brand-primary
+
+// $panel-success-text: $state-success-text
+// $panel-success-border: $state-success-border
+// $panel-success-heading-bg: $state-success-bg
+
+// $panel-info-text: $state-info-text
+// $panel-info-border: $state-info-border
+// $panel-info-heading-bg: $state-info-bg
+
+// $panel-warning-text: $state-warning-text
+// $panel-warning-border: $state-warning-border
+// $panel-warning-heading-bg: $state-warning-bg
+
+// $panel-danger-text: $state-danger-text
+// $panel-danger-border: $state-danger-border
+// $panel-danger-heading-bg: $state-danger-bg
+
+
+//== Thumbnails
+//
+//##
+
+//** Padding around the thumbnail image
+// $thumbnail-padding: 4px
+//** Thumbnail background color
+// $thumbnail-bg: $body-bg
+//** Thumbnail border color
+// $thumbnail-border: #ddd
+//** Thumbnail border radius
+// $thumbnail-border-radius: $border-radius-base
+
+//** Custom text color for thumbnail captions
+// $thumbnail-caption-color: $text-color
+//** Padding around the thumbnail caption
+// $thumbnail-caption-padding: 9px
+
+
+//== Wells
+//
+//##
+
+$well-bg: $gray-lighter;
+$well-border: transparent;
+
+
+//== Badges
+//
+//##
+
+// $badge-color: #fff
+//** Linked badge text color on hover
+// $badge-link-hover-color: #fff
+// $badge-bg: $gray-light
+
+//** Badge text color in active nav link
+// $badge-active-color: $link-color
+//** Badge background color in active nav link
+// $badge-active-bg: #fff
+
+// $badge-font-weight: bold
+// $badge-line-height: 1
+// $badge-border-radius: 10px
+
+
+//== Breadcrumbs
+//
+//##
+
+// $breadcrumb-padding-vertical: 8px
+// $breadcrumb-padding-horizontal: 15px
+//** Breadcrumb background color
+// $breadcrumb-bg: #f5f5f5
+//** Breadcrumb text color
+// $breadcrumb-color: #ccc
+//** Text color of current page in the breadcrumb
+// $breadcrumb-active-color: $gray-light
+//** Textual separator for between breadcrumb elements
+// $breadcrumb-separator: "/"
+
+
+//== Carousel
+//
+//##
+
+// $carousel-text-shadow: 0 1px 2px rgba(0,0,0,.6)
+
+// $carousel-control-color: #fff
+// $carousel-control-width: 15%
+// $carousel-control-opacity: .5
+// $carousel-control-font-size: 20px
+
+// $carousel-indicator-active-bg: #fff
+// $carousel-indicator-border-color: #fff
+
+// $carousel-caption-color: #fff
+
+
+//== Close
+//
+//##
+
+// $close-font-weight: bold
+// $close-color: #000
+// $close-text-shadow: 0 1px 0 #fff
+
+
+//== Code
+//
+//##
+
+$code-color: #c7254e;
+$code-bg: #f9f2f4;
+
+$kbd-color: #fff;
+$kbd-bg: #333;
+
+$pre-bg: $gray-lighter;
+$pre-color: $text-color;
+$pre-border-color: #ccc;
+// $pre-scrollable-max-height: 340px
+
+
+//== Type
+//
+//##
+
+//** Horizontal offset for forms and lists.
+// $component-offset-horizontal: 180px
+//** Text muted color
+// $text-muted: $gray-light
+//** Abbreviations and acronyms border color
+// $abbr-border-color: $gray-light
+//** Headings small color
+$headings-small-color: $gray-dark;
+//** Blockquote small color
+// $blockquote-small-color: $gray-light
+//** Blockquote font size
+// $blockquote-font-size: ($font-size-base * 1.25)
+//** Blockquote border color
+// $blockquote-border-color: $gray-lighter
+//** Page header border color
+// $page-header-border-color: $gray-lighter
+//** Width of horizontal description list titles
+// $dl-horizontal-offset: $component-offset-horizontal
+//** Horizontal line color.
+// $hr-border: $gray-lighter
diff --git a/app/assets/stylesheets/main/layout.scss b/app/assets/stylesheets/base/layout.scss
index 1085e68b7d4..62c11b06368 100644
--- a/app/assets/stylesheets/main/layout.scss
+++ b/app/assets/stylesheets/base/layout.scss
@@ -4,7 +4,7 @@ html {
&.touch .tooltip { display: none !important; }
body {
- padding-top: 47px;
+ padding-top: 46px;
}
}
diff --git a/app/assets/stylesheets/main/mixins.scss b/app/assets/stylesheets/base/mixins.scss
index e54482d14c3..ccba65e3fd5 100644
--- a/app/assets/stylesheets/main/mixins.scss
+++ b/app/assets/stylesheets/base/mixins.scss
@@ -121,14 +121,6 @@
}
}
-@mixin page-title {
- color: #333;
- line-height: 1.5;
- font-weight: normal;
- margin-top: 0px;
- margin-bottom: 10px;
-}
-
@mixin str-truncated($max_width: 82%) {
display: inline-block;
overflow: hidden;
@@ -137,14 +129,3 @@
white-space: nowrap;
max-width: $max_width;
}
-
-@mixin panel-colored {
- border: 1px solid #EEE;
- background: $box_bg;
- @include box-shadow(0 1px 1px rgba(0, 0, 0, 0.09));
-
- .panel-heading {
- font-weight: bold;
- background-color: $box_bg;
- }
-}
diff --git a/app/assets/stylesheets/base/variables.scss b/app/assets/stylesheets/base/variables.scss
new file mode 100644
index 00000000000..54af78ee082
--- /dev/null
+++ b/app/assets/stylesheets/base/variables.scss
@@ -0,0 +1,37 @@
+$style_color: #474D57;
+$hover: #FFF3EB;
+$box_bg: #F9F9F9;
+$gl-link-color: #446e9b;
+$nprogress-color: #c0392b;
+$gl-font-size: 14px;
+$list-font-size: 15px;
+$sidebar_width: 230px;
+$avatar_radius: 50%;
+$code_font_size: 13px;
+$code_line_height: 1.5;
+
+/*
+ * State colors:
+ */
+$gl-success: #019875;
+$gl-danger: #d9534f;
+$gl-primary: #446e9b;
+$gl-info: #029ACF;
+$gl-warning: #EB9532;
+
+$gl-primary: #2C3E50;
+$gl-success: #18BC9C;
+$gl-info: #3498DB;
+$gl-warning: #F39C12;
+$gl-danger: #E74C3C;
+/*
+ * Commit Diff Colors
+ */
+$added: #63c363;
+$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;
diff --git a/app/assets/stylesheets/generic/buttons.scss b/app/assets/stylesheets/generic/buttons.scss
index 3b360275065..0224484d82b 100644
--- a/app/assets/stylesheets/generic/buttons.scss
+++ b/app/assets/stylesheets/generic/buttons.scss
@@ -1,127 +1,15 @@
.btn {
- display: inline-block;
- margin-bottom: 0;
- font-weight: normal;
- text-align: center;
- vertical-align: middle;
- cursor: pointer;
- background-image: none;
- border: $btn-border;
- white-space: nowrap;
- padding: 6px 12px;
- font-size: 13px;
- line-height: 18px;
- border-radius: 4px;
- -webkit-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- -o-user-select: none;
- user-select: none;
- color: #444444;
- background-color: #fff;
- text-shadow: none;
-
- &.hover,
- &:hover {
- color: #444444;
- text-decoration: none;
- background-color: #ebebeb;
- border-color: #adadad;
- }
-
- &.focus,
- &:focus {
- color: #444444;
- text-decoration: none;
- outline: thin dotted #333;
- outline: 5px auto -webkit-focus-ring-color;
- outline-offset: -2px;
- }
-
- &.active,
- &:active {
- outline: 0;
- background-image: none;
- -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
- box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
- }
-
- &.disabled,
- &[disabled] {
- cursor: not-allowed;
- pointer-events: none;
- opacity: 0.65;
- filter: alpha(opacity=65);
- -webkit-box-shadow: none;
- box-shadow: none;
- }
-
- &.btn-primary {
- color: #ffffff;
- background-color: $bg_primary;
- border-color: $border_primary;
-
- &.hover,
- &:hover,
- &.disabled,
- &[disabled] {
- color: #ffffff;
- }
- }
-
- &.btn-success {
- color: #ffffff;
- background-color: $bg_success;
- border-color: $border_success;
-
-
- &.hover,
- &:hover,
- &.disabled,
- &[disabled] {
- color: #ffffff;
- }
- }
-
- &.btn-danger {
- color: #ffffff;
- background-color: $bg_danger;
- border-color: $border_danger;
-
-
- &.hover,
- &:hover,
- &.disabled,
- &[disabled] {
- color: #ffffff;
- }
- }
-
- &.btn-warning {
- color: #ffffff;
- background-color: $bg_warning;
- border-color: $border_warning;
-
-
- &.hover,
- &:hover,
- &.disabled,
- &[disabled] {
- color: #ffffff;
- }
- }
+ @extend .btn-default;
&.btn-new {
@extend .btn-success;
}
&.btn-create {
- @extend .wide;
@extend .btn-success;
}
&.btn-save {
- @extend .wide;
@extend .btn-primary;
}
@@ -133,11 +21,6 @@
float: right;
}
- &.wide {
- padding-left: 20px;
- padding-right: 20px;
- }
-
&.btn-small {
padding: 2px 10px;
font-size: 12px;
@@ -151,16 +34,16 @@
}
&.btn-close {
- color: $bg_danger;
- border-color: $border_danger;
+ color: $gl-danger;
+ border-color: $gl-danger;
&:hover {
color: #B94A48;
}
}
&.btn-reopen {
- color: $bg_success;
- border-color: $border_success;
+ color: $gl-success;
+ border-color: $gl-success;
&:hover {
color: #468847;
}
@@ -174,9 +57,12 @@
}
}
- &.btn-lg {
- font-size: 15px;
- line-height: 1.4;
+ &.btn-save {
+ @extend .btn-primary;
+ }
+
+ &.btn-new, &.btn-create {
+ @extend .btn-success;
}
}
diff --git a/app/assets/stylesheets/generic/common.scss b/app/assets/stylesheets/generic/common.scss
index 3db821fdf76..af8e90eb1a9 100644
--- a/app/assets/stylesheets/generic/common.scss
+++ b/app/assets/stylesheets/generic/common.scss
@@ -24,7 +24,7 @@
.slead {
color: #666;
- font-size: 14px;
+ font-size: 15px;
margin-bottom: 12px;
font-weight: normal;
line-height: 24px;
@@ -61,7 +61,7 @@ pre {
.dropdown-menu > li > a:hover,
.dropdown-menu > li > a:focus {
- background: $bg_primary;
+ background: $gl-primary;
color: #FFF
}
@@ -71,7 +71,7 @@ pre {
/** FLASH message **/
.author_link {
- color: $link_color;
+ color: $gl-link-color;
}
.help li { color:$style_color; }
@@ -306,20 +306,8 @@ table {
width: 100%;
}
-.broadcast-message {
- padding: 10px;
- text-align: center;
- background: #555;
- color: #BBB;
-}
-
-.broadcast-message-preview {
- @extend .broadcast-message;
- margin-bottom: 20px;
-}
-
.btn-sign-in {
- margin-top: 7px;
+ margin-top: 5px;
text-shadow: none;
}
@@ -337,8 +325,11 @@ table {
overflow-x: auto;
}
-.footer-links a {
- margin-right: 15px;
+.footer-links {
+ margin-bottom: 20px;
+ a {
+ margin-right: 15px;
+ }
}
.search_box {
diff --git a/app/assets/stylesheets/generic/forms.scss b/app/assets/stylesheets/generic/forms.scss
index c8982cdc00d..19bc11086e9 100644
--- a/app/assets/stylesheets/generic/forms.scss
+++ b/app/assets/stylesheets/generic/forms.scss
@@ -29,7 +29,7 @@ fieldset legend {
padding: 17px 20px 18px;
margin-top: 18px;
margin-bottom: 18px;
- background-color: whitesmoke;
+ background-color: #ecf0f1;
border-top: 1px solid #e5e5e5;
}
diff --git a/app/assets/stylesheets/generic/gfm.scss b/app/assets/stylesheets/generic/gfm.scss
index 1427b6a5ae4..8fac5e534fa 100644
--- a/app/assets/stylesheets/generic/gfm.scss
+++ b/app/assets/stylesheets/generic/gfm.scss
@@ -3,7 +3,7 @@
*/
.issue-form, .merge-request-form, .wiki-form {
.description {
- height: 20em;
+ height: 16em;
border-top-left-radius: 0;
}
}
diff --git a/app/assets/stylesheets/generic/issue_box.scss b/app/assets/stylesheets/generic/issue_box.scss
index 2563ab516e2..9558f241b7c 100644
--- a/app/assets/stylesheets/generic/issue_box.scss
+++ b/app/assets/stylesheets/generic/issue_box.scss
@@ -11,17 +11,17 @@
margin-right: 5px;
&.issue-box-closed {
- background-color: $bg_danger;
+ background-color: $gl-danger;
color: #FFF;
}
&.issue-box-merged {
- background-color: $bg_primary;
+ background-color: $gl-primary;
color: #FFF;
}
&.issue-box-open {
- background-color: $bg_success;
+ background-color: $gl-success;
color: #FFF;
}
diff --git a/app/assets/stylesheets/generic/jquery.scss b/app/assets/stylesheets/generic/jquery.scss
index bfbbc7d25e3..871b808bad4 100644
--- a/app/assets/stylesheets/generic/jquery.scss
+++ b/app/assets/stylesheets/generic/jquery.scss
@@ -41,8 +41,8 @@
}
.ui-state-active {
- border: 1px solid $bg_primary;
- background: $bg_primary;
+ border: 1px solid $gl-primary;
+ background: $gl-primary;
color: #FFF;
}
diff --git a/app/assets/stylesheets/generic/markdown_area.scss b/app/assets/stylesheets/generic/markdown_area.scss
index 5a87cc6c612..eb39b6bb7e9 100644
--- a/app/assets/stylesheets/generic/markdown_area.scss
+++ b/app/assets/stylesheets/generic/markdown_area.scss
@@ -57,7 +57,6 @@
border: 1px solid #ddd;
min-height: 100px;
padding: 5px;
- font-size: 14px;
box-shadow: none;
}
@@ -77,3 +76,12 @@
}
}
}
+
+.markdown-area {
+ background: #FFF;
+ border: 1px solid #ddd;
+ min-height: 100px;
+ padding: 5px;
+ box-shadow: none;
+ width: 100%;
+}
diff --git a/app/assets/stylesheets/generic/mobile.scss b/app/assets/stylesheets/generic/mobile.scss
index b3727c33672..1b0e056216f 100644
--- a/app/assets/stylesheets/generic/mobile.scss
+++ b/app/assets/stylesheets/generic/mobile.scss
@@ -43,8 +43,10 @@
}
}
- .page-title .new-issue-link {
- display: none;
+ .page-title {
+ .note_created_ago, .new-issue-link {
+ display: none;
+ }
}
.issue_edited_ago, .note_edited_ago {
diff --git a/app/assets/stylesheets/sections/nav_sidebar.scss b/app/assets/stylesheets/generic/nav_sidebar.scss
index 335f1379662..c14f12284da 100644
--- a/app/assets/stylesheets/sections/nav_sidebar.scss
+++ b/app/assets/stylesheets/generic/nav_sidebar.scss
@@ -147,19 +147,27 @@
.collapse-nav a {
left: 0px;
- padding: 5px 23px 3px 22px;
+ padding: 7px 23px 3px 22px;
}
}
}
.collapse-nav a {
position: fixed;
- top: 47px;
- padding: 5px 13px 3px 13px;
+ top: 46px;
+ padding: 5px 13px 5px 13px;
left: 197px;
+ font-size: 13px;
background: #EEE;
color: black;
- border: 1px solid rgba(0,0,0,0.035);
+ border-left: 1px solid rgba(0,0,0,0.035);
+ border-right: 1px solid rgba(0,0,0,0.035);
+}
+
+.collapse-nav a:hover {
+ text-decoration: none;
+ color: #333;
+ background: #eaeaea;
}
@media (max-width: $screen-md-max) {
diff --git a/app/assets/stylesheets/generic/selects.scss b/app/assets/stylesheets/generic/selects.scss
index d85e80a512b..af0ecb192d6 100644
--- a/app/assets/stylesheets/generic/selects.scss
+++ b/app/assets/stylesheets/generic/selects.scss
@@ -3,9 +3,8 @@
.select2-choice {
background: #FFF;
border-color: #BBB;
- padding: 6px 12px;
- font-size: 13px;
- line-height: 18px;
+ padding: 6px 14px;
+ line-height: 1.42857143;
height: auto;
.select2-arrow {
@@ -20,7 +19,7 @@
}
.select2-container-multi .select2-choices .select2-search-field input {
- padding: 6px 12px;
+ padding: 8px 14px;
font-size: 13px;
line-height: 18px;
height: auto;
@@ -42,7 +41,7 @@
.select2-results {
max-height: 350px;
.select2-highlighted {
- background: $bg_primary;
+ background: $gl-primary;
}
}
}
diff --git a/app/assets/stylesheets/generic/typography.scss b/app/assets/stylesheets/generic/typography.scss
index c547ebb3aaf..4d940ee6b29 100644
--- a/app/assets/stylesheets/generic/typography.scss
+++ b/app/assets/stylesheets/generic/typography.scss
@@ -2,28 +2,12 @@
* Headers
*
*/
-h1.page-title {
- @include page-title;
- font-size: 28px;
-}
-
-h2.page-title {
- @include page-title;
- font-size: 24px;
-}
-
-h3.page-title {
- @include page-title;
- font-size: 22px;
-}
-
-h4.page-title {
+.page-title {
margin-top: 0px;
-}
-
-h6 {
- color: #888;
- text-transform: uppercase;
+ color: #333;
+ line-height: 1.5;
+ font-weight: normal;
+ margin-bottom: 5px;
}
/** CODE **/
@@ -36,52 +20,6 @@ pre {
}
}
-/**
- * Links
- *
- */
-a {
- outline: none;
- color: $link_color;
- &:hover {
- text-decoration: underline;
- color: $link_hover_color;
- }
-
- &:focus {
- text-decoration: underline;
- }
-
- &.darken {
- color: $style_color;
- }
-
- &.lined {
- text-decoration: underline;
- &:hover { text-decoration: underline; }
- }
-
- &.gray {
- color: gray;
- }
-
- &.supp_diff_link {
- text-align: center;
- padding: 20px 0;
- background: #f1f1f1;
- width: 100%;
- float: left;
- }
-
- &.neib {
- margin-right: 15px;
- }
-}
-
-a:focus {
- outline: none;
-}
-
.monospace {
font-family: $monospace_font;
}
diff --git a/app/assets/stylesheets/highlight/dark.scss b/app/assets/stylesheets/highlight/dark.scss
index fcd4d47bace..c8cb18ec35f 100644
--- a/app/assets/stylesheets/highlight/dark.scss
+++ b/app/assets/stylesheets/highlight/dark.scss
@@ -1,6 +1,10 @@
/* https://github.com/MozMorris/tomorrow-pygments */
+pre.code.highlight.dark,
.code.dark {
+ background-color: #1d1f21;
+ color: #c5c8c6;
+
pre.code,
.line-numbers,
.line-numbers a {
@@ -13,8 +17,8 @@
}
// highlight line via anchor
- pre.hll {
- background-color: #fff !important;
+ pre .hll {
+ background-color: #557 !important;
}
.hll { background-color: #373b41 }
diff --git a/app/assets/stylesheets/highlight/monokai.scss b/app/assets/stylesheets/highlight/monokai.scss
index bcd2e716657..001e8b31020 100644
--- a/app/assets/stylesheets/highlight/monokai.scss
+++ b/app/assets/stylesheets/highlight/monokai.scss
@@ -1,6 +1,10 @@
/* https://github.com/richleland/pygments-css/blob/master/monokai.css */
+pre.code.monokai,
.code.monokai {
+ background: #272822;
+ color: #f8f8f2;
+
pre.highlight,
.line-numbers,
.line-numbers a {
@@ -13,7 +17,7 @@
}
// highlight line via anchor
- pre.hll {
+ pre .hll {
background-color: #49483e !important;
}
diff --git a/app/assets/stylesheets/highlight/solarized_dark.scss b/app/assets/stylesheets/highlight/solarized_dark.scss
index 4a6b759bd2c..f5b827e7c02 100644
--- a/app/assets/stylesheets/highlight/solarized_dark.scss
+++ b/app/assets/stylesheets/highlight/solarized_dark.scss
@@ -1,6 +1,10 @@
/* https://gist.github.com/qguv/7936275 */
+pre.code.highlight.solarized-dark,
.code.solarized-dark {
+ background-color: #002b36;
+ color: #93a1a1;
+
pre.code,
.line-numbers,
.line-numbers a {
@@ -13,8 +17,8 @@
}
// highlight line via anchor
- pre.hll {
- background-color: #073642 !important;
+ pre .hll {
+ background-color: #174652 !important;
}
/* Solarized Dark
diff --git a/app/assets/stylesheets/highlight/solarized_light.scss b/app/assets/stylesheets/highlight/solarized_light.scss
index 7254f4d7ac1..6b44c00c305 100644
--- a/app/assets/stylesheets/highlight/solarized_light.scss
+++ b/app/assets/stylesheets/highlight/solarized_light.scss
@@ -1,6 +1,10 @@
/* https://gist.github.com/qguv/7936275 */
+pre.code.highlight.solarized-light,
.code.solarized-light {
+ background-color: #fdf6e3;
+ color: #586e75;
+
pre.code,
.line-numbers,
.line-numbers a {
@@ -13,8 +17,8 @@
}
// highlight line via anchor
- pre.hll {
- background-color: #eee8d5 !important;
+ pre .hll {
+ background-color: #ddd8c5 !important;
}
/* Solarized Light
diff --git a/app/assets/stylesheets/highlight/white.scss b/app/assets/stylesheets/highlight/white.scss
index 4d6f5dfd91e..a52ffc971d1 100644
--- a/app/assets/stylesheets/highlight/white.scss
+++ b/app/assets/stylesheets/highlight/white.scss
@@ -1,6 +1,10 @@
/* https://github.com/aahan/pygments-github-style */
+pre.code.highlight.white,
.code.white {
+ background-color: #fff;
+ color: #333;
+
pre.highlight,
.line-numbers,
.line-numbers a {
@@ -13,7 +17,7 @@
}
// highlight line via anchor
- pre.hll {
+ pre .hll {
background-color: #f8eec7 !important;
}
diff --git a/app/assets/stylesheets/main/fonts.scss b/app/assets/stylesheets/main/fonts.scss
deleted file mode 100644
index f945aaca848..00000000000
--- a/app/assets/stylesheets/main/fonts.scss
+++ /dev/null
@@ -1,3 +0,0 @@
-/** Typo **/
-$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;
diff --git a/app/assets/stylesheets/main/variables.scss b/app/assets/stylesheets/main/variables.scss
deleted file mode 100644
index acbf5be94a3..00000000000
--- a/app/assets/stylesheets/main/variables.scss
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * General Colors
- */
-$style_color: #474D57;
-$hover: #FFF3EB;
-$box_bg: #F9F9F9;
-
-/*
- * Link colors
- */
-$link_color: #446e9b;
-$link_hover_color: darken($link-color, 10%);
-
-$btn-border: 1px solid #ccc;
-
-/*
- * Success colors (green)
- */
-$border_success: #019875;
-$bg_success: #019875;
-
-/*
- * Danger colors (red)
- */
-$border_danger: #d43f3a;
-$bg_danger: #d9534f;
-
-/*
- * Primary colors (blue)
- */
-$border_primary: #446e9b;
-$bg_primary: #446e9b;
-
-/*
- * Warning colors (yellow)
- */
-$bg_warning: #EB9532;
-$border_warning: #EB9532;
-
-/**
- * Commit Diff Colors
- */
-$added: #63c363;
-$deleted: #f77;
-
-/**
- * NProgress customize
- */
-$nprogress-color: #c0392b;
-
-/**
- * Font sizes
- */
-$list-font-size: 15px;
-
-/**
- * Sidebar navigation width
- */
-$sidebar_width: 230px;
-
-$avatar_radius: 50%;
-$code_font_size: 13px;
-$code_line_height: 1.5;
diff --git a/app/assets/stylesheets/sections/admin.scss b/app/assets/stylesheets/pages/admin.scss
index a51deee7970..144852e7874 100644
--- a/app/assets/stylesheets/sections/admin.scss
+++ b/app/assets/stylesheets/pages/admin.scss
@@ -50,3 +50,14 @@
line-height: 2;
}
}
+
+.broadcast-message {
+ @extend .alert-warning;
+ padding: 10px;
+ text-align: center;
+}
+
+.broadcast-message-preview {
+ @extend .broadcast-message;
+ margin-bottom: 20px;
+}
diff --git a/app/assets/stylesheets/sections/commit.scss b/app/assets/stylesheets/pages/commit.scss
index 0e2d9571a45..f46d6542c03 100644
--- a/app/assets/stylesheets/sections/commit.scss
+++ b/app/assets/stylesheets/pages/commit.scss
@@ -45,15 +45,6 @@
}
}
-.commit-committer-link,
-.commit-author-link {
- font-size: 13px;
- color: #555;
- &:hover {
- color: #999;
- }
-}
-
.commit-box {
margin: 10px 0;
border-top: 1px solid #ddd;
diff --git a/app/assets/stylesheets/sections/commits.scss b/app/assets/stylesheets/pages/commits.scss
index 683aca73593..e167d044e47 100644
--- a/app/assets/stylesheets/sections/commits.scss
+++ b/app/assets/stylesheets/pages/commits.scss
@@ -100,6 +100,7 @@ li.commit {
.commit-row-info {
color: #777;
line-height: 24px;
+ font-size: 13px;
a {
color: #777;
diff --git a/app/assets/stylesheets/sections/dashboard.scss b/app/assets/stylesheets/pages/dashboard.scss
index d8fd83d44b7..5a543a852c2 100644
--- a/app/assets/stylesheets/sections/dashboard.scss
+++ b/app/assets/stylesheets/pages/dashboard.scss
@@ -23,28 +23,6 @@
}
}
-.dash-sidebar-tabs {
- margin-bottom: 2px;
- border: none;
- margin: 0 !important;
-
- li {
- &.active {
- a {
- background-color: #EEE;
- border-bottom: 1px solid #EEE !important;
- &:hover {
- background: #eee;
- }
- }
- }
-
- a {
- border-color: #DDD !important;
- }
- }
-}
-
.project-row, .group-row {
padding: 0 !important;
font-size: 14px;
@@ -111,17 +89,8 @@
}
.dash-new-project {
- background: $bg_success;
- border: 1px solid $border_success;
-
- a {
- color: #FFF;
- }
-}
-
-.dash-new-group {
- background: $bg_success;
- border: 1px solid $border_success;
+ background: $gl-success;
+ border: 1px solid $gl-success;
a {
color: #FFF;
diff --git a/app/assets/stylesheets/sections/diff.scss b/app/assets/stylesheets/pages/diff.scss
index 54311a68852..54311a68852 100644
--- a/app/assets/stylesheets/sections/diff.scss
+++ b/app/assets/stylesheets/pages/diff.scss
diff --git a/app/assets/stylesheets/sections/editor.scss b/app/assets/stylesheets/pages/editor.scss
index 88aa256e56e..88aa256e56e 100644
--- a/app/assets/stylesheets/sections/editor.scss
+++ b/app/assets/stylesheets/pages/editor.scss
diff --git a/app/assets/stylesheets/sections/errors.scss b/app/assets/stylesheets/pages/errors.scss
index 32d2d7b1dbf..32d2d7b1dbf 100644
--- a/app/assets/stylesheets/sections/errors.scss
+++ b/app/assets/stylesheets/pages/errors.scss
diff --git a/app/assets/stylesheets/sections/events.scss b/app/assets/stylesheets/pages/events.scss
index a477359dc88..3e9e36e477e 100644
--- a/app/assets/stylesheets/sections/events.scss
+++ b/app/assets/stylesheets/pages/events.scss
@@ -46,7 +46,6 @@
border-bottom: 1px solid #eee;
.event-title {
@include str-truncated(72%);
- color: #333;
font-weight: 500;
font-size: 14px;
.author_name {
@@ -54,6 +53,7 @@
}
}
.event-body {
+ font-size: 13px;
margin-left: 35px;
margin-right: 80px;
color: #777;
@@ -185,11 +185,10 @@
}
.event_filter {
-
li a {
+ font-size: 13px;
padding: 5px 10px;
background: rgba(0,0,0,0.045);
margin-left: 4px;
}
-
}
diff --git a/app/assets/stylesheets/sections/explore.scss b/app/assets/stylesheets/pages/explore.scss
index 9b92128624c..9b92128624c 100644
--- a/app/assets/stylesheets/sections/explore.scss
+++ b/app/assets/stylesheets/pages/explore.scss
diff --git a/app/assets/stylesheets/sections/graph.scss b/app/assets/stylesheets/pages/graph.scss
index 3d878d1e528..3d878d1e528 100644
--- a/app/assets/stylesheets/sections/graph.scss
+++ b/app/assets/stylesheets/pages/graph.scss
diff --git a/app/assets/stylesheets/sections/groups.scss b/app/assets/stylesheets/pages/groups.scss
index e49fe1a9dd6..e49fe1a9dd6 100644
--- a/app/assets/stylesheets/sections/groups.scss
+++ b/app/assets/stylesheets/pages/groups.scss
diff --git a/app/assets/stylesheets/sections/header.scss b/app/assets/stylesheets/pages/header.scss
index 26b4d04106e..26b4d04106e 100644
--- a/app/assets/stylesheets/sections/header.scss
+++ b/app/assets/stylesheets/pages/header.scss
diff --git a/app/assets/stylesheets/sections/help.scss b/app/assets/stylesheets/pages/help.scss
index 07c62f98c36..6da7a2511a2 100644
--- a/app/assets/stylesheets/sections/help.scss
+++ b/app/assets/stylesheets/pages/help.scss
@@ -12,7 +12,6 @@
color: #888;
a {
- font-size: 14px;
margin-right: 3px;
}
}
@@ -29,7 +28,6 @@
th {
padding-top: 15px;
- font-size: 14px;
line-height: 1.5;
color: #333;
text-align: left
diff --git a/app/assets/stylesheets/sections/import.scss b/app/assets/stylesheets/pages/import.scss
index 3df4bb84bd2..3df4bb84bd2 100644
--- a/app/assets/stylesheets/sections/import.scss
+++ b/app/assets/stylesheets/pages/import.scss
diff --git a/app/assets/stylesheets/sections/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index d8d12338859..d8d12338859 100644
--- a/app/assets/stylesheets/sections/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
diff --git a/app/assets/stylesheets/sections/issues.scss b/app/assets/stylesheets/pages/issues.scss
index b909725bff5..4ea34cc1dac 100644
--- a/app/assets/stylesheets/sections/issues.scss
+++ b/app/assets/stylesheets/pages/issues.scss
@@ -11,6 +11,7 @@
.issue-info {
color: #999;
+ font-size: 13px;
}
.issue-check {
@@ -40,7 +41,7 @@
}
.check-all-holder {
- height: 32px;
+ height: 36px;
float: left;
margin-right: 12px;
padding: 6px 15px;
diff --git a/app/assets/stylesheets/sections/labels.scss b/app/assets/stylesheets/pages/labels.scss
index d1590e42fcb..d1590e42fcb 100644
--- a/app/assets/stylesheets/sections/labels.scss
+++ b/app/assets/stylesheets/pages/labels.scss
diff --git a/app/assets/stylesheets/sections/login.scss b/app/assets/stylesheets/pages/login.scss
index d366300511e..d366300511e 100644
--- a/app/assets/stylesheets/sections/login.scss
+++ b/app/assets/stylesheets/pages/login.scss
diff --git a/app/assets/stylesheets/sections/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index 0d2d8b0173e..9bd34b7376f 100644
--- a/app/assets/stylesheets/sections/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -96,6 +96,7 @@
.merge-request-info {
color: #999;
+ font-size: 13px;
.merge-request-labels {
display: inline-block;
@@ -136,8 +137,8 @@
background-color: #F5F5F5;
&.ci-success {
- color: $bg_success;
- border-color: $border_success;
+ color: $gl-success;
+ border-color: $gl-success;
background-color: #F1FAF1;
}
@@ -148,20 +149,20 @@
}
&.ci-running {
- color: $bg_warning;
- border-color: $border_warning;
+ color: $gl-warning;
+ border-color: $gl-warning;
background-color: #FAF5F1;
}
&.ci-failed {
- color: $bg_danger;
- border-color: $border_danger;
+ color: $gl-danger;
+ border-color: $gl-danger;
background-color: #FAF1F1;
}
&.ci-error {
- color: $bg_danger;
- border-color: $border_danger;
+ color: $gl-danger;
+ border-color: $gl-danger;
background-color: #FAF1F1;
}
}
diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss
new file mode 100644
index 00000000000..15e3948e402
--- /dev/null
+++ b/app/assets/stylesheets/pages/milestone.scss
@@ -0,0 +1,9 @@
+.issues-sortable-list .str-truncated {
+ max-width: 90%;
+}
+
+li.milestone {
+ h4 {
+ font-weight: bold;
+ }
+}
diff --git a/app/assets/stylesheets/sections/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index a0522030785..a0522030785 100644
--- a/app/assets/stylesheets/sections/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
diff --git a/app/assets/stylesheets/sections/notes.scss b/app/assets/stylesheets/pages/notes.scss
index 40adc8b3ba7..384ff6d740c 100644
--- a/app/assets/stylesheets/sections/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -38,13 +38,11 @@ ul.notes {
.author {
color: #333;
font-weight: bold;
- font-size: 14px;
&:hover {
- color: $link_color;
+ color: $gl-link-color;
}
}
.author-username {
- font-size: 14px;
}
}
@@ -57,9 +55,6 @@ ul.notes {
.note {
display: block;
position:relative;
- .attachment {
- font-size: 14px;
- }
.note-body {
overflow: auto;
.note-text {
@@ -70,7 +65,7 @@ ul.notes {
a[href*="/uploads/"] {
&:before {
margin-right: 4px;
-
+
font: normal normal normal 14px/1 FontAwesome;
font-size: inherit;
text-rendering: auto;
@@ -153,7 +148,6 @@ ul.notes {
@extend .cgray;
&:hover {
- color: $link_hover_color;
&.danger { @extend .cred; }
}
}
@@ -181,10 +175,11 @@ ul.notes {
background: #FFF;
padding: 4px;
font-size: 16px;
- color: $link_color;
+ color: $gl-link-color;
margin-left: -60px;
position: absolute;
z-index: 10;
+ width: 32px;
transition: all 0.2s ease;
@@ -193,8 +188,9 @@ ul.notes {
filter: alpha(opacity=0);
&:hover {
- font-size: 24px;
- background: $bg_primary;
+ width: 38px;
+ font-size: 20px;
+ background: $gl-info;
color: #FFF;
@include show-add-diff-note;
}
diff --git a/app/assets/stylesheets/sections/notifications.scss b/app/assets/stylesheets/pages/notifications.scss
index f11c5dff4ab..cc273f55222 100644
--- a/app/assets/stylesheets/sections/notifications.scss
+++ b/app/assets/stylesheets/pages/notifications.scss
@@ -10,13 +10,13 @@
}
.ns-part {
- color: $bg_primary;
+ color: $gl-primary;
}
.ns-watch {
- color: $bg_success;
+ color: $gl-success;
}
.ns-mute {
- color: $bg_danger;
+ color: $gl-danger;
}
diff --git a/app/assets/stylesheets/sections/profile.scss b/app/assets/stylesheets/pages/profile.scss
index 0ab62b7ae49..81afe05162f 100644
--- a/app/assets/stylesheets/sections/profile.scss
+++ b/app/assets/stylesheets/pages/profile.scss
@@ -80,6 +80,10 @@
&.violet {
background: #548;
}
+
+ &.blue {
+ background: #2980b9;
+ }
}
}
}
diff --git a/app/assets/stylesheets/sections/projects.scss b/app/assets/stylesheets/pages/projects.scss
index 8bad9b139f4..bfd05973d75 100644
--- a/app/assets/stylesheets/sections/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -99,25 +99,6 @@
margin-right: 45px;
}
- .btn,
- .form-control {
- border: 1px solid #E1E1E1;
- box-shadow: none;
- padding: 6px 9px;
- }
-
- .btn {
- background: none;
- color: $link_color;
-
- &.active {
- background-color: #f5f5f5;
- border: 1px solid rgba(0,0,0,0.195);
- color: #333;
- font-weight: bold;
- }
- }
-
.form-control {
cursor: auto;
@extend .monospace;
@@ -267,15 +248,15 @@ ul.nav.nav-projects-tabs {
}
.vs-public {
- color: $bg_primary;
+ color: $gl-primary;
}
.vs-internal {
- color: $bg_warning;
+ color: $gl-warning;
}
.vs-private {
- color: $bg_success;
+ color: $gl-success;
}
.breadcrumb.repo-breadcrumb {
diff --git a/app/assets/stylesheets/sections/search.scss b/app/assets/stylesheets/pages/search.scss
index bdaa17ac339..bdaa17ac339 100644
--- a/app/assets/stylesheets/sections/search.scss
+++ b/app/assets/stylesheets/pages/search.scss
diff --git a/app/assets/stylesheets/sections/snippets.scss b/app/assets/stylesheets/pages/snippets.scss
index d79591d9915..d79591d9915 100644
--- a/app/assets/stylesheets/sections/snippets.scss
+++ b/app/assets/stylesheets/pages/snippets.scss
diff --git a/app/assets/stylesheets/sections/stat_graph.scss b/app/assets/stylesheets/pages/stat_graph.scss
index b9be47e7700..b9be47e7700 100644
--- a/app/assets/stylesheets/sections/stat_graph.scss
+++ b/app/assets/stylesheets/pages/stat_graph.scss
diff --git a/app/assets/stylesheets/sections/themes.scss b/app/assets/stylesheets/pages/themes.scss
index e69de29bb2d..e69de29bb2d 100644
--- a/app/assets/stylesheets/sections/themes.scss
+++ b/app/assets/stylesheets/pages/themes.scss
diff --git a/app/assets/stylesheets/sections/tree.scss b/app/assets/stylesheets/pages/tree.scss
index 60a1c00b04b..ce02cdb1652 100644
--- a/app/assets/stylesheets/sections/tree.scss
+++ b/app/assets/stylesheets/pages/tree.scss
@@ -39,14 +39,9 @@
.tree-item-file-name {
max-width: 320px;
vertical-align: middle;
- a {
- &:hover {
- color: $link_hover_color;
- }
- }
- i {
- color: $bg_primary;
+ i, a {
+ color: $gl-link-color;
}
img {
@@ -66,13 +61,18 @@
.tree_author {
padding-right: 8px;
+
+ .commit-author-name {
+ color: gray;
+ }
}
.tree_commit {
color: gray;
.tree-commit-link {
- color: #444;
+ color: gray;
+
&:hover {
text-decoration: underline;
}
diff --git a/app/assets/stylesheets/pages/ui_dev_kit.scss b/app/assets/stylesheets/pages/ui_dev_kit.scss
new file mode 100644
index 00000000000..277afa1db9e
--- /dev/null
+++ b/app/assets/stylesheets/pages/ui_dev_kit.scss
@@ -0,0 +1,9 @@
+.gitlab-ui-dev-kit {
+ > h2 {
+ font-size: 27px;
+ border-bottom: 1px solid #CCC;
+ color: #666;
+ margin: 30px 0;
+ font-weight: bold;
+ }
+}
diff --git a/app/assets/stylesheets/sections/votes.scss b/app/assets/stylesheets/pages/votes.scss
index ba0a519dca6..ba0a519dca6 100644
--- a/app/assets/stylesheets/sections/votes.scss
+++ b/app/assets/stylesheets/pages/votes.scss
diff --git a/app/assets/stylesheets/sections/wiki.scss b/app/assets/stylesheets/pages/wiki.scss
index dfaeba41cf6..dfaeba41cf6 100644
--- a/app/assets/stylesheets/sections/wiki.scss
+++ b/app/assets/stylesheets/pages/wiki.scss
diff --git a/app/assets/stylesheets/sections/markdown_area.scss b/app/assets/stylesheets/sections/markdown_area.scss
deleted file mode 100644
index 8ee8eaa4ee7..00000000000
--- a/app/assets/stylesheets/sections/markdown_area.scss
+++ /dev/null
@@ -1,9 +0,0 @@
-.markdown-area {
- background: #FFF;
- border: 1px solid #ddd;
- min-height: 100px;
- padding: 5px;
- font-size: 14px;
- box-shadow: none;
- width: 100%;
-}
diff --git a/app/assets/stylesheets/sections/milestone.scss b/app/assets/stylesheets/sections/milestone.scss
deleted file mode 100644
index d20391e38fd..00000000000
--- a/app/assets/stylesheets/sections/milestone.scss
+++ /dev/null
@@ -1,3 +0,0 @@
-.issues-sortable-list .str-truncated {
- max-width: 70%;
-}
diff --git a/app/assets/stylesheets/themes/ui_blue.scss b/app/assets/stylesheets/themes/ui_blue.scss
new file mode 100644
index 00000000000..cb7980b5a07
--- /dev/null
+++ b/app/assets/stylesheets/themes/ui_blue.scss
@@ -0,0 +1,6 @@
+/**
+ * Modern GitLab UI theme
+ */
+.ui_blue {
+ @include dark-theme(#BECDE9, #2980b9, #1970a9, #096099);
+}
diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb
index 65dc027c8eb..e338abeac4c 100644
--- a/app/controllers/admin/groups_controller.rb
+++ b/app/controllers/admin/groups_controller.rb
@@ -5,12 +5,12 @@ class Admin::GroupsController < Admin::ApplicationController
@groups = Group.all
@groups = @groups.sort(@sort = params[:sort])
@groups = @groups.search(params[:name]) if params[:name].present?
- @groups = @groups.page(params[:page]).per(20)
+ @groups = @groups.page(params[:page]).per(PER_PAGE)
end
def show
- @members = @group.members.order("access_level DESC").page(params[:members_page]).per(30)
- @projects = @group.projects.page(params[:projects_page]).per(30)
+ @members = @group.members.order("access_level DESC").page(params[:members_page]).per(PER_PAGE)
+ @projects = @group.projects.page(params[:projects_page]).per(PER_PAGE)
end
def new
diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb
index 2b1fc862b7f..5176a8399ae 100644
--- a/app/controllers/admin/projects_controller.rb
+++ b/app/controllers/admin/projects_controller.rb
@@ -11,15 +11,15 @@ class Admin::ProjectsController < Admin::ApplicationController
@projects = @projects.abandoned if params[:abandoned].present?
@projects = @projects.search(params[:name]) if params[:name].present?
@projects = @projects.sort(@sort = params[:sort])
- @projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page]).per(20)
+ @projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page]).per(PER_PAGE)
end
def show
if @group
- @group_members = @group.members.order("access_level DESC").page(params[:group_members_page]).per(30)
+ @group_members = @group.members.order("access_level DESC").page(params[:group_members_page]).per(PER_PAGE)
end
- @project_members = @project.project_members.page(params[:project_members_page]).per(30)
+ @project_members = @project.project_members.page(params[:project_members_page]).per(PER_PAGE)
end
def transfer
diff --git a/app/controllers/admin/services_controller.rb b/app/controllers/admin/services_controller.rb
index e80cabd6e18..44a3f1379d8 100644
--- a/app/controllers/admin/services_controller.rb
+++ b/app/controllers/admin/services_controller.rb
@@ -45,7 +45,8 @@ class Admin::ServicesController < Admin::ApplicationController
:room, :recipients, :project_url, :webhook,
:user_key, :device, :priority, :sound, :bamboo_url, :username, :password,
:build_key, :server, :teamcity_url, :build_type,
- :description, :issues_url, :new_issue_url, :restrict_to_branch
+ :description, :issues_url, :new_issue_url, :restrict_to_branch,
+ :send_from_committer_email, :disable_diffs
])
end
end
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index ecedb31a7f8..693970e5349 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -24,7 +24,7 @@ class Admin::UsersController < Admin::ApplicationController
def block
if user.block
- redirect_to :back, alert: "Successfully blocked"
+ redirect_to :back, notice: "Successfully blocked"
else
redirect_to :back, alert: "Error occurred. User was not blocked"
end
@@ -32,7 +32,7 @@ class Admin::UsersController < Admin::ApplicationController
def unblock
if user.activate
- redirect_to :back, alert: "Successfully unblocked"
+ redirect_to :back, notice: "Successfully unblocked"
else
redirect_to :back, alert: "Error occurred. User was not unblocked"
end
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index df1a588313e..e284f31f7ee 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -4,6 +4,8 @@ class ApplicationController < ActionController::Base
include Gitlab::CurrentSettings
include GitlabRoutingHelper
+ PER_PAGE = 20
+
before_filter :authenticate_user_from_token!
before_filter :authenticate_user!
before_filter :reject_blocked!
diff --git a/app/controllers/profiles/groups_controller.rb b/app/controllers/dashboard/groups_controller.rb
index ce9dd50df67..b827639978c 100644
--- a/app/controllers/profiles/groups_controller.rb
+++ b/app/controllers/dashboard/groups_controller.rb
@@ -1,15 +1,13 @@
-class Profiles::GroupsController < ApplicationController
- layout "profile"
-
+class Dashboard::GroupsController < ApplicationController
def index
- @user_groups = current_user.group_members.page(params[:page]).per(20)
+ @user_groups = current_user.group_members.page(params[:page]).per(PER_PAGE)
end
def leave
@users_group = group.group_members.where(user_id: current_user.id).first
if can?(current_user, :destroy, @users_group)
@users_group.destroy
- redirect_to(profile_groups_path, info: "You left #{group.name} group.")
+ redirect_to(dashboard_groups_path, info: "You left #{group.name} group.")
else
return render_403
end
diff --git a/app/controllers/dashboard/milestones_controller.rb b/app/controllers/dashboard/milestones_controller.rb
index 386e283f3a0..cb51792df16 100644
--- a/app/controllers/dashboard/milestones_controller.rb
+++ b/app/controllers/dashboard/milestones_controller.rb
@@ -8,7 +8,7 @@ class Dashboard::MilestonesController < ApplicationController
else state('active')
end
@dashboard_milestones = Milestones::GroupService.new(project_milestones).execute
- @dashboard_milestones = Kaminari.paginate_array(@dashboard_milestones).page(params[:page]).per(30)
+ @dashboard_milestones = Kaminari.paginate_array(@dashboard_milestones).page(params[:page]).per(PER_PAGE)
end
def show
diff --git a/app/controllers/dashboard/projects_controller.rb b/app/controllers/dashboard/projects_controller.rb
new file mode 100644
index 00000000000..56e6fcc41ca
--- /dev/null
+++ b/app/controllers/dashboard/projects_controller.rb
@@ -0,0 +1,27 @@
+class Dashboard::ProjectsController < ApplicationController
+ before_filter :event_filter
+
+ def starred
+ @projects = current_user.starred_projects
+ @projects = @projects.includes(:namespace, :forked_from_project, :tags)
+ @projects = @projects.sort(@sort = params[:sort])
+ @groups = []
+
+ respond_to do |format|
+ format.html
+
+ format.json do
+ load_events
+ pager_json("events/_events", @events.count)
+ end
+ end
+ end
+
+ private
+
+ def load_events
+ @events = Event.in_projects(@projects.pluck(:id))
+ @events = @event_filter.apply_filter(@events).with_associations
+ @events = @events.limit(20).offset(params[:offset] || 0)
+ end
+end
diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb
index 4930029e165..9bd853ed5c7 100644
--- a/app/controllers/dashboard_controller.rb
+++ b/app/controllers/dashboard_controller.rb
@@ -5,19 +5,9 @@ class DashboardController < ApplicationController
before_filter :event_filter, only: :show
def show
- # Fetch only 30 projects.
- # If user needs more - point to Dashboard#projects page
- @projects_limit = 30
-
- @groups = current_user.authorized_groups.order_name_asc
- @has_authorized_projects = @projects.count > 0
- @projects_count = @projects.count
- @projects = @projects.includes(:namespace).limit(@projects_limit)
-
+ @projects = @projects.includes(:namespace)
@last_push = current_user.recent_push
- @publicish_project_count = Project.publicish(current_user).count
-
respond_to do |format|
format.html
@@ -33,38 +23,15 @@ class DashboardController < ApplicationController
end
end
- def projects
- @projects = case params[:scope]
- when 'personal' then
- current_user.namespace.projects
- when 'joined' then
- current_user.authorized_projects.joined(current_user)
- when 'owned' then
- current_user.owned_projects
- else
- current_user.authorized_projects
- end
-
- @projects = @projects.where(namespace_id: Group.find_by(name: params[:group])) if params[:group].present?
- @projects = @projects.where(visibility_level: params[:visibility_level]) if params[:visibility_level].present?
- @projects = @projects.includes(:namespace, :forked_from_project, :tags)
- @projects = @projects.tagged_with(params[:tag]) if params[:tag].present?
- @projects = @projects.sort(@sort = params[:sort])
- @projects = @projects.page(params[:page]).per(30)
-
- @tags = current_user.authorized_projects.tags_on(:tags)
- @groups = current_user.authorized_groups
- end
-
def merge_requests
@merge_requests = get_merge_requests_collection
- @merge_requests = @merge_requests.page(params[:page]).per(20)
+ @merge_requests = @merge_requests.page(params[:page]).per(PER_PAGE)
@merge_requests = @merge_requests.preload(:author, :target_project)
end
def issues
@issues = get_issues_collection
- @issues = @issues.page(params[:page]).per(20)
+ @issues = @issues.page(params[:page]).per(PER_PAGE)
@issues = @issues.preload(:author, :project)
respond_to do |format|
diff --git a/app/controllers/explore/groups_controller.rb b/app/controllers/explore/groups_controller.rb
index ada7031fea4..c51a4a211a6 100644
--- a/app/controllers/explore/groups_controller.rb
+++ b/app/controllers/explore/groups_controller.rb
@@ -8,6 +8,6 @@ class Explore::GroupsController < ApplicationController
@groups = GroupsFinder.new.execute(current_user)
@groups = @groups.search(params[:search]) if params[:search].present?
@groups = @groups.sort(@sort = params[:sort])
- @groups = @groups.page(params[:page]).per(20)
+ @groups = @groups.page(params[:page]).per(PER_PAGE)
end
end
diff --git a/app/controllers/explore/projects_controller.rb b/app/controllers/explore/projects_controller.rb
index 0e5891ae807..b295f295bb1 100644
--- a/app/controllers/explore/projects_controller.rb
+++ b/app/controllers/explore/projects_controller.rb
@@ -6,19 +6,22 @@ class Explore::ProjectsController < ApplicationController
def index
@projects = ProjectsFinder.new.execute(current_user)
+ @tags = @projects.tags_on(:tags)
+ @projects = @projects.tagged_with(params[:tag]) if params[:tag].present?
+ @projects = @projects.where(visibility_level: params[:visibility_level]) if params[:visibility_level].present?
@projects = @projects.search(params[:search]) if params[:search].present?
@projects = @projects.sort(@sort = params[:sort])
- @projects = @projects.includes(:namespace).page(params[:page]).per(20)
+ @projects = @projects.includes(:namespace).page(params[:page]).per(PER_PAGE)
end
def trending
@trending_projects = TrendingProjectsFinder.new.execute(current_user)
- @trending_projects = @trending_projects.page(params[:page]).per(10)
+ @trending_projects = @trending_projects.page(params[:page]).per(PER_PAGE)
end
def starred
@starred_projects = ProjectsFinder.new.execute(current_user)
@starred_projects = @starred_projects.reorder('star_count DESC')
- @starred_projects = @starred_projects.page(params[:page]).per(10)
+ @starred_projects = @starred_projects.page(params[:page]).per(PER_PAGE)
end
end
diff --git a/app/controllers/groups/application_controller.rb b/app/controllers/groups/application_controller.rb
new file mode 100644
index 00000000000..7f27f2bb734
--- /dev/null
+++ b/app/controllers/groups/application_controller.rb
@@ -0,0 +1,10 @@
+class Groups::ApplicationController < ApplicationController
+
+ private
+
+ def authorize_admin_group!
+ unless can?(current_user, :manage_group, group)
+ return render_404
+ end
+ end
+end
diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb
index ca88d033878..b083cf5d8c5 100644
--- a/app/controllers/groups/group_members_controller.rb
+++ b/app/controllers/groups/group_members_controller.rb
@@ -1,4 +1,4 @@
-class Groups::GroupMembersController < ApplicationController
+class Groups::GroupMembersController < Groups::ApplicationController
before_filter :group
# Authorize
@@ -37,12 +37,6 @@ class Groups::GroupMembersController < ApplicationController
@group ||= Group.find_by(path: params[:group_id])
end
- def authorize_admin_group!
- unless can?(current_user, :manage_group, group)
- return render_404
- end
- end
-
def member_params
params.require(:group_member).permit(:access_level, :user_id)
end
diff --git a/app/controllers/groups/milestones_controller.rb b/app/controllers/groups/milestones_controller.rb
index 6802e529b54..c46b8fff88f 100644
--- a/app/controllers/groups/milestones_controller.rb
+++ b/app/controllers/groups/milestones_controller.rb
@@ -10,7 +10,7 @@ class Groups::MilestonesController < ApplicationController
else state('active')
end
@group_milestones = Milestones::GroupService.new(project_milestones).execute
- @group_milestones = Kaminari.paginate_array(@group_milestones).page(params[:page]).per(30)
+ @group_milestones = Kaminari.paginate_array(@group_milestones).page(params[:page]).per(PER_PAGE)
end
def show
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index d011523c94f..7e336803fbb 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -1,4 +1,4 @@
-class GroupsController < ApplicationController
+class GroupsController < Groups::ApplicationController
skip_before_filter :authenticate_user!, only: [:show, :issues, :members, :merge_requests]
respond_to :html
before_filter :group, except: [:new, :create]
@@ -52,13 +52,13 @@ class GroupsController < ApplicationController
def merge_requests
@merge_requests = get_merge_requests_collection
- @merge_requests = @merge_requests.page(params[:page]).per(20)
+ @merge_requests = @merge_requests.page(params[:page]).per(PER_PAGE)
@merge_requests = @merge_requests.preload(:author, :target_project)
end
def issues
@issues = get_issues_collection
- @issues = @issues.page(params[:page]).per(20)
+ @issues = @issues.page(params[:page]).per(PER_PAGE)
@issues = @issues.preload(:author, :project)
respond_to do |format|
@@ -132,12 +132,6 @@ class GroupsController < ApplicationController
end
end
- def authorize_admin_group!
- unless can?(current_user, :manage_group, group)
- return render_404
- end
- end
-
def set_title
@title = 'New Group'
end
diff --git a/app/controllers/help_controller.rb b/app/controllers/help_controller.rb
index fc498559d6b..c4d620d87b1 100644
--- a/app/controllers/help_controller.rb
+++ b/app/controllers/help_controller.rb
@@ -15,4 +15,7 @@ class HelpController < ApplicationController
def shortcuts
end
+
+ def ui
+ end
end
diff --git a/app/controllers/import/base_controller.rb b/app/controllers/import/base_controller.rb
index 7dc0cac8d4c..edb8bd4160b 100644
--- a/app/controllers/import/base_controller.rb
+++ b/app/controllers/import/base_controller.rb
@@ -3,19 +3,17 @@ class Import::BaseController < ApplicationController
private
def get_or_create_namespace
- existing_namespace = Namespace.find_by_path_or_name(@target_namespace)
-
- if existing_namespace
- if existing_namespace.owner == current_user
- namespace = existing_namespace
- else
+ begin
+ namespace = Group.create!(name: @target_namespace, path: @target_namespace, owner: current_user)
+ namespace.add_owner(current_user)
+ rescue ActiveRecord::RecordNotUnique, ActiveRecord::RecordInvalid
+ namespace = Namespace.find_by_path_or_name(@target_namespace)
+ unless namespace.owner == current_user
@already_been_taken = true
return false
end
- else
- namespace = Group.create(name: @target_namespace, path: @target_namespace, owner: current_user)
- namespace.add_owner(current_user)
- namespace
end
+
+ namespace
end
end
diff --git a/app/controllers/import/github_controller.rb b/app/controllers/import/github_controller.rb
index dc7668ee6fd..8650b6464dc 100644
--- a/app/controllers/import/github_controller.rb
+++ b/app/controllers/import/github_controller.rb
@@ -14,7 +14,7 @@ class Import::GithubController < Import::BaseController
def status
@repos = client.repos
client.orgs.each do |org|
- @repos += client.repos(org.login)
+ @repos += client.org_repos(org.login)
end
@already_added_projects = current_user.created_projects.where(import_type: "github")
diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb
index a7863aba756..1b9a86ee42c 100644
--- a/app/controllers/profiles_controller.rb
+++ b/app/controllers/profiles_controller.rb
@@ -43,7 +43,7 @@ class ProfilesController < ApplicationController
end
def history
- @events = current_user.recent_events.page(params[:page]).per(20)
+ @events = current_user.recent_events.page(params[:page]).per(PER_PAGE)
end
def update_username
diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb
index 690501f3060..f049e96e61d 100644
--- a/app/controllers/projects/branches_controller.rb
+++ b/app/controllers/projects/branches_controller.rb
@@ -8,7 +8,7 @@ class Projects::BranchesController < Projects::ApplicationController
def index
@sort = params[:sort] || 'name'
@branches = @repository.branches_sorted_by(@sort)
- @branches = Kaminari.paginate_array(@branches).page(params[:page]).per(30)
+ @branches = Kaminari.paginate_array(@branches).page(params[:page]).per(PER_PAGE)
end
def recent
diff --git a/app/controllers/projects/imports_controller.rb b/app/controllers/projects/imports_controller.rb
index 79d9910ce87..b64491b4666 100644
--- a/app/controllers/projects/imports_controller.rb
+++ b/app/controllers/projects/imports_controller.rb
@@ -37,7 +37,7 @@ class Projects::ImportsController < Projects::ApplicationController
private
def require_no_repo
- if @project.repository_exists?
+ if @project.repository_exists? && !@project.import_in_progress?
redirect_to(namespace_project_path(@project.namespace, @project)) and return
end
end
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index 6a2af08a199..4266bcaef16 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -20,7 +20,7 @@ class Projects::IssuesController < Projects::ApplicationController
terms = params['issue_search']
@issues = get_issues_collection
@issues = @issues.full_search(terms) if terms.present?
- @issues = @issues.page(params[:page]).per(20)
+ @issues = @issues.page(params[:page]).per(PER_PAGE)
respond_to do |format|
format.html
@@ -93,7 +93,7 @@ class Projects::IssuesController < Projects::ApplicationController
end
def bulk_update
- result = Issues::BulkUpdateService.new(project, current_user, params).execute
+ result = Issues::BulkUpdateService.new(project, current_user, bulk_update_params).execute
redirect_to :back, notice: "#{result[:count]} issues updated"
end
@@ -141,4 +141,13 @@ class Projects::IssuesController < Projects::ApplicationController
:milestone_id, :state_event, :task_num, label_ids: []
)
end
+
+ def bulk_update_params
+ params.require(:update).permit(
+ :issues_ids,
+ :assignee_id,
+ :milestone_id,
+ :state_event
+ )
+ end
end
diff --git a/app/controllers/projects/labels_controller.rb b/app/controllers/projects/labels_controller.rb
index 5e31fce4b0e..207a01ed3b0 100644
--- a/app/controllers/projects/labels_controller.rb
+++ b/app/controllers/projects/labels_controller.rb
@@ -7,7 +7,7 @@ class Projects::LabelsController < Projects::ApplicationController
respond_to :js, :html
def index
- @labels = @project.labels.page(params[:page]).per(20)
+ @labels = @project.labels.page(params[:page]).per(PER_PAGE)
end
def new
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 26d4c51773f..93d79d81661 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -17,8 +17,19 @@ class Projects::MergeRequestsController < Projects::ApplicationController
before_filter :authorize_modify_merge_request!, only: [:close, :edit, :update, :sort]
def index
+ terms = params['issue_search']
@merge_requests = get_merge_requests_collection
- @merge_requests = @merge_requests.page(params[:page]).per(20)
+ @merge_requests = @merge_requests.full_search(terms) if terms.present?
+ @merge_requests = @merge_requests.page(params[:page]).per(PER_PAGE)
+
+ respond_to do |format|
+ format.html
+ format.json do
+ render json: {
+ html: view_to_html_string("projects/merge_requests/_merge_requests")
+ }
+ end
+ end
end
def show
@@ -78,10 +89,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request = MergeRequests::CreateService.new(project, current_user, merge_request_params).execute
if @merge_request.valid?
- redirect_to(
- merge_request_path(@merge_request),
- notice: 'Merge request was successfully created.'
- )
+ redirect_to(merge_request_path(@merge_request))
else
@source_project = @merge_request.source_project
@target_project = @merge_request.target_project
@@ -97,8 +105,13 @@ class Projects::MergeRequestsController < Projects::ApplicationController
format.js
format.html do
redirect_to([@merge_request.target_project.namespace.becomes(Namespace),
- @merge_request.target_project, @merge_request],
- notice: 'Merge request was successfully updated.')
+ @merge_request.target_project, @merge_request])
+ end
+ format.json do
+ render json: {
+ saved: @merge_request.valid?,
+ assignee_avatar_url: @merge_request.assignee.try(:avatar_url)
+ }
end
end
else
diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb
index afdb560e73c..b49b549547a 100644
--- a/app/controllers/projects/milestones_controller.rb
+++ b/app/controllers/projects/milestones_controller.rb
@@ -18,7 +18,7 @@ class Projects::MilestonesController < Projects::ApplicationController
end
@milestones = @milestones.includes(:project)
- @milestones = @milestones.page(params[:page]).per(20)
+ @milestones = @milestones.page(params[:page]).per(PER_PAGE)
end
def new
diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb
index 087579de106..570447c746c 100644
--- a/app/controllers/projects/services_controller.rb
+++ b/app/controllers/projects/services_controller.rb
@@ -52,7 +52,8 @@ class Projects::ServicesController < Projects::ApplicationController
:build_key, :server, :teamcity_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
+ :push_events, :issues_events, :merge_requests_events, :tag_push_events,
+ :note_events, :send_from_committer_email, :disable_diffs
)
end
end
diff --git a/app/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb
index 08c7ce3f37d..c4f27a6d989 100644
--- a/app/controllers/projects/tags_controller.rb
+++ b/app/controllers/projects/tags_controller.rb
@@ -7,7 +7,7 @@ class Projects::TagsController < Projects::ApplicationController
def index
sorted = VersionSorter.rsort(@repository.tag_names)
- @tags = Kaminari.paginate_array(sorted).page(params[:page]).per(30)
+ @tags = Kaminari.paginate_array(sorted).page(params[:page]).per(PER_PAGE)
end
def create
@@ -27,7 +27,7 @@ class Projects::TagsController < Projects::ApplicationController
tag = @repository.find_tag(params[:id])
if tag && @repository.rm_tag(tag.name)
- EventCreateService.new.push_ref(@project, current_user, tag, 'rm', 'refs/tags')
+ EventCreateService.new.push_ref(@project, current_user, tag, 'rm', Gitlab::Git::TAG_REF_PREFIX)
end
respond_to do |format|
diff --git a/app/controllers/projects/wikis_controller.rb b/app/controllers/projects/wikis_controller.rb
index 3392fbca91e..643167947b9 100644
--- a/app/controllers/projects/wikis_controller.rb
+++ b/app/controllers/projects/wikis_controller.rb
@@ -7,7 +7,7 @@ class Projects::WikisController < Projects::ApplicationController
before_filter :load_project_wiki
def pages
- @wiki_pages = Kaminari.paginate_array(@project_wiki.pages).page(params[:page]).per(30)
+ @wiki_pages = Kaminari.paginate_array(@project_wiki.pages).page(params[:page]).per(PER_PAGE)
end
def show
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 82b8a1cc13a..0f28794b736 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -105,7 +105,7 @@ class ProjectsController < ApplicationController
if request.referer.include?('/admin')
redirect_to admin_namespaces_projects_path
else
- redirect_to projects_dashboard_path
+ redirect_to dashboard_path
end
end
end
@@ -176,11 +176,11 @@ class ProjectsController < ApplicationController
end
def autocomplete_emojis
- Rails.cache.fetch("autocomplete-emoji-#{Emoji::VERSION}") do
- Emoji.names.map do |e|
+ Rails.cache.fetch("autocomplete-emoji-#{Gemojione::VERSION}") do
+ Emoji.emojis.map do |name, emoji|
{
- name: e,
- path: view_context.image_url("emoji/#{e}.png")
+ name: name,
+ path: view_context.image_url("emoji/#{emoji["unicode"]}.png")
}
end
end
diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb
index 6ac048e4b83..ae501362dc2 100644
--- a/app/controllers/snippets_controller.rb
+++ b/app/controllers/snippets_controller.rb
@@ -16,7 +16,7 @@ class SnippetsController < ApplicationController
layout :determine_layout
def index
- @snippets = SnippetsFinder.new.execute(current_user, filter: :all).page(params[:page]).per(20)
+ @snippets = SnippetsFinder.new.execute(current_user, filter: :all).page(params[:page]).per(PER_PAGE)
end
def user_index
@@ -28,7 +28,7 @@ class SnippetsController < ApplicationController
filter: :by_user,
user: @user,
scope: params[:scope] }).
- page(params[:page]).per(20)
+ page(params[:page]).per(PER_PAGE)
if @user == current_user
render 'current_user_index'
diff --git a/app/controllers/uploads_controller.rb b/app/controllers/uploads_controller.rb
index 810ac9f34bd..c5f3da54ea2 100644
--- a/app/controllers/uploads_controller.rb
+++ b/app/controllers/uploads_controller.rb
@@ -1,24 +1,15 @@
class UploadsController < ApplicationController
- skip_before_filter :authenticate_user!, :reject_blocked!
- before_filter :authorize_access
+ skip_before_filter :authenticate_user!
+ before_filter :find_model, :authorize_access!
def show
- unless upload_model && upload_mount
- return not_found!
- end
-
- model = upload_model.find(params[:id])
- uploader = model.send(upload_mount)
-
- if model.respond_to?(:project) && !can?(current_user, :read_project, model.project)
- return not_found!
- end
+ uploader = @model.send(upload_mount)
unless uploader.file_storage?
return redirect_to uploader.url
end
- unless uploader.file.exists?
+ unless uploader.file && uploader.file.exists?
return not_found!
end
@@ -28,9 +19,34 @@ class UploadsController < ApplicationController
private
- def authorize_access
- unless params[:mounted_as] == 'avatar'
- authenticate_user! && reject_blocked!
+ def find_model
+ unless upload_model && upload_mount
+ return not_found!
+ end
+
+ @model = upload_model.find(params[:id])
+ end
+
+ def authorize_access!
+ authorized =
+ case @model
+ when Project
+ can?(current_user, :read_project, @model)
+ when Group
+ can?(current_user, :read_group, @model)
+ when Note
+ can?(current_user, :read_project, @model.project)
+ else
+ # No authentication required for user avatars.
+ true
+ end
+
+ return if authorized
+
+ if current_user
+ not_found!
+ else
+ authenticate_user!
end
end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index a81e41819b7..8ed6d59c20d 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -86,15 +86,6 @@ module ApplicationHelper
end
end
- def group_icon(group_path)
- group = Group.find_by(path: group_path)
- if group && group.avatar.present?
- group.avatar.url
- else
- image_path('no_group_avatar.png')
- end
- end
-
def avatar_icon(user_email = '', size = nil)
user = User.find_by(email: user_email)
diff --git a/app/helpers/dashboard_helper.rb b/app/helpers/dashboard_helper.rb
index 4dae96644c8..c25b54eadc6 100644
--- a/app/helpers/dashboard_helper.rb
+++ b/app/helpers/dashboard_helper.rb
@@ -1,20 +1,4 @@
module DashboardHelper
- def projects_dashboard_filter_path(options={})
- exist_opts = {
- sort: params[:sort],
- scope: params[:scope],
- group: params[:group],
- tag: params[:tag],
- visibility_level: params[:visibility_level],
- }
-
- options = exist_opts.merge(options)
-
- path = request.path
- path << "?#{options.to_param}"
- path
- end
-
def assigned_issues_dashboard_path
issues_dashboard_path(assignee_id: current_user.id)
end
diff --git a/app/helpers/emails_helper.rb b/app/helpers/emails_helper.rb
index 92cc9c426b8..08476f8516e 100644
--- a/app/helpers/emails_helper.rb
+++ b/app/helpers/emails_helper.rb
@@ -1,3 +1,6 @@
+require 'html/pipeline'
+require 'html/pipeline/gitlab'
+
module EmailsHelper
# Google Actions
@@ -39,4 +42,26 @@ module EmailsHelper
lexer = Rugments::Lexers::Diff.new
raw formatter.format(lexer.lex(diffcontent))
end
+
+ def replace_image_links_with_base64(text, project)
+ # Used pipelines in GitLab:
+ # GitlabEmailImageFilter - replaces images that have been uploaded as attachments with inline images in emails.
+ #
+ # see https://gitlab.com/gitlab-org/html-pipeline-gitlab for more filters
+ filters = [
+ HTML::Pipeline::Gitlab::GitlabEmailImageFilter
+ ]
+
+ context = {
+ base_url: File.join(Gitlab.config.gitlab.url, project.path_with_namespace, 'uploads'),
+ upload_path: File.join(Rails.root, 'public', 'uploads', project.path_with_namespace),
+ }
+
+ pipeline = HTML::Pipeline::Gitlab.new(filters).pipeline
+
+ result = pipeline.call(text, context)
+ text = result[:output].to_html(save_with: 0)
+
+ text.html_safe
+ end
end
diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb
index d38b546e1b2..779cebc0136 100644
--- a/app/helpers/events_helper.rb
+++ b/app/helpers/events_helper.rb
@@ -166,7 +166,7 @@ module EventsHelper
def event_note(text)
text = first_line_in_markdown(text, 150)
- sanitize(text, tags: %w(a img b pre code p))
+ sanitize(text, tags: %w(a img b pre code p span))
end
def event_commit_title(message)
diff --git a/app/helpers/explore_helper.rb b/app/helpers/explore_helper.rb
new file mode 100644
index 00000000000..7616fe6bad8
--- /dev/null
+++ b/app/helpers/explore_helper.rb
@@ -0,0 +1,17 @@
+module ExploreHelper
+ def explore_projects_filter_path(options={})
+ exist_opts = {
+ sort: params[:sort],
+ scope: params[:scope],
+ group: params[:group],
+ tag: params[:tag],
+ visibility_level: params[:visibility_level],
+ }
+
+ options = exist_opts.merge(options)
+
+ path = request.path
+ path << "?#{options.to_param}"
+ path
+ end
+end
diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb
index ab30f498c01..f8e104b0827 100644
--- a/app/helpers/gitlab_markdown_helper.rb
+++ b/app/helpers/gitlab_markdown_helper.rb
@@ -31,7 +31,9 @@ module GitlabMarkdownHelper
def markdown(text, options={})
unless (@markdown and options == @options)
@options = options
- gitlab_renderer = Redcarpet::Render::GitlabHTML.new(self, {
+ gitlab_renderer = Redcarpet::Render::GitlabHTML.new(self,
+ user_color_scheme_class,
+ {
# see https://github.com/vmg/redcarpet#darling-i-packed-you-a-couple-renderers-for-lunch-
filter_html: true,
with_toc_data: true,
@@ -119,7 +121,7 @@ module GitlabMarkdownHelper
end
def ignored_protocols
- ["http://","https://", "ftp://", "mailto:"]
+ ["http://","https://", "ftp://", "mailto:", "smb://"]
end
def rebuild_path(file_path)
diff --git a/app/helpers/gitlab_routing_helper.rb b/app/helpers/gitlab_routing_helper.rb
index ac37f909ce9..8518a47a3a0 100644
--- a/app/helpers/gitlab_routing_helper.rb
+++ b/app/helpers/gitlab_routing_helper.rb
@@ -44,4 +44,8 @@ module GitlabRoutingHelper
def merge_request_url(entity, *args)
namespace_project_merge_request_url(entity.project.namespace, entity.project, entity, *args)
end
+
+ def snippet_url(entity, *args)
+ namespace_project_snippet_url(entity.project.namespace, entity.project, entity, *args)
+ end
end
diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb
index 03fd461a462..2d0d0b494f6 100644
--- a/app/helpers/groups_helper.rb
+++ b/app/helpers/groups_helper.rb
@@ -40,4 +40,16 @@ module GroupsHelper
false
end
end
+
+ def group_icon(group)
+ if group.is_a?(String)
+ group = Group.find_by(path: group)
+ end
+
+ if group && group.avatar.present?
+ group.avatar.url
+ else
+ image_path('no_group_avatar.png')
+ end
+ end
end
diff --git a/app/helpers/milestones_helper.rb b/app/helpers/milestones_helper.rb
index 3383b1ae5be..59fdc0d49cc 100644
--- a/app/helpers/milestones_helper.rb
+++ b/app/helpers/milestones_helper.rb
@@ -8,4 +8,15 @@ module MilestonesHelper
dashboard_milestones_path(opts)
end
end
+
+ def milestone_progress_bar(milestone)
+ options = {
+ class: 'progress-bar progress-bar-success',
+ style: "width: #{milestone.percent_complete}%;"
+ }
+
+ content_tag :div, class: 'progress' do
+ content_tag :div, nil, options
+ end
+ end
end
diff --git a/app/helpers/namespaces_helper.rb b/app/helpers/namespaces_helper.rb
index 2bcfde62830..b3132a1f3ba 100644
--- a/app/helpers/namespaces_helper.rb
+++ b/app/helpers/namespaces_helper.rb
@@ -28,7 +28,7 @@ module NamespacesHelper
def namespace_icon(namespace, size = 40)
if namespace.kind_of?(Group)
- group_icon(namespace.path)
+ group_icon(namespace)
else
avatar_icon(namespace.owner.email, size)
end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index a5d7372bbe5..2225b110651 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -265,4 +265,14 @@ module ProjectsHelper
"success"
end
end
+
+ def service_field_value(type, value)
+ return value unless type == 'password'
+
+ if value.present?
+ "***********"
+ else
+ nil
+ end
+ end
end
diff --git a/app/mailers/emails/projects.rb b/app/mailers/emails/projects.rb
index 4bc40b35f2d..b55129de292 100644
--- a/app/mailers/emails/projects.rb
+++ b/app/mailers/emails/projects.rb
@@ -16,28 +16,38 @@ module Emails
subject: subject("Project was moved"))
end
- def repository_push_email(project_id, recipient, author_id, branch, compare)
+ def repository_push_email(project_id, recipient, author_id, branch, compare, reverse_compare = false, send_from_committer_email = false, disable_diffs = false)
@project = Project.find(project_id)
@author = User.find(author_id)
+ @reverse_compare = reverse_compare
@compare = compare
@commits = Commit.decorate(compare.commits)
@diffs = compare.diffs
- @branch = branch
+ @branch = Gitlab::Git.ref_name(branch)
+ @disable_diffs = disable_diffs
+
+ @subject = "[#{@project.path_with_namespace}][#{@branch}] "
+
if @commits.length > 1
@target_url = namespace_project_compare_url(@project.namespace,
@project,
- from: @commits.first,
- to: @commits.last)
- @subject = "#{@commits.length} new commits pushed to repository"
+ from: Commit.new(@compare.base),
+ to: Commit.new(@compare.head))
+ @subject << "Deleted " if @reverse_compare
+ @subject << "#{@commits.length} commits: #{@commits.first.title}"
else
@target_url = namespace_project_commit_url(@project.namespace,
@project, @commits.first)
- @subject = @commits.first.title
+
+ @subject << "Deleted 1 commit: " if @reverse_compare
+ @subject << @commits.first.title
end
- mail(from: sender(author_id),
+ @disable_footer = true
+
+ mail(from: sender(author_id, send_from_committer_email),
to: recipient,
- subject: subject(@subject))
+ subject: @subject)
end
end
end
diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb
index 46ead62f75f..ee27879cf40 100644
--- a/app/mailers/notify.rb
+++ b/app/mailers/notify.rb
@@ -34,21 +34,41 @@ class Notify < ActionMailer::Base
)
end
+ # Splits "gitlab.corp.company.com" up into "gitlab.corp.company.com",
+ # "corp.company.com" and "company.com".
+ # Respects set tld length so "company.co.uk" won't match "somethingelse.uk"
+ def self.allowed_email_domains
+ domain_parts = Gitlab.config.gitlab.host.split(".")
+ allowed_domains = []
+ begin
+ allowed_domains << domain_parts.join(".")
+ domain_parts.shift
+ end while domain_parts.length > ActionDispatch::Http::URL.tld_length
+
+ allowed_domains
+ end
+
private
# The default email address to send emails from
def default_sender_address
address = Mail::Address.new(Gitlab.config.gitlab.email_from)
- address.display_name = "GitLab"
+ address.display_name = Gitlab.config.gitlab.email_display_name
address
end
# Return an email address that displays the name of the sender.
# Only the displayed name changes; the actual email address is always the same.
- def sender(sender_id)
+ def sender(sender_id, send_from_user_email = false)
if sender = User.find(sender_id)
address = default_sender_address
address.display_name = sender.name
+
+ sender_domain = sender.email.split("@").last
+ if send_from_user_email && self.class.allowed_email_domains.include?(sender_domain)
+ address.address = sender.email
+ end
+
address.format
end
end
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index f1d918e5457..588668b3d1e 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -2,17 +2,17 @@
#
# Table name: application_settings
#
-# id :integer not null, primary key
-# default_projects_limit :integer
-# default_branch_protection :integer
-# signup_enabled :boolean
-# signin_enabled :boolean
-# gravatar_enabled :boolean
-# twitter_sharing_enabled :boolean
-# sign_in_text :text
-# created_at :datetime
-# updated_at :datetime
-# home_page_url :string(255)
+# id :integer not null, primary key
+# default_projects_limit :integer
+# signup_enabled :boolean
+# signin_enabled :boolean
+# gravatar_enabled :boolean
+# sign_in_text :text
+# created_at :datetime
+# updated_at :datetime
+# home_page_url :string(255)
+# default_branch_protection :integer default(2)
+# twitter_sharing_enabled :boolean default(TRUE)
#
class ApplicationSetting < ActiveRecord::Base
diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb
index 50be458bf24..74900d4675d 100644
--- a/app/models/concerns/mentionable.rb
+++ b/app/models/concerns/mentionable.rb
@@ -99,5 +99,4 @@ module Mentionable
preexisting = references(p, original)
create_cross_references!(p, a, preexisting)
end
-
end
diff --git a/app/models/event.rb b/app/models/event.rb
index 5579ab1dbb0..8d20d7ef252 100644
--- a/app/models/event.rb
+++ b/app/models/event.rb
@@ -190,19 +190,19 @@ class Event < ActiveRecord::Base
end
def tag?
- data[:ref]["refs/tags"]
+ Gitlab::Git.tag_ref?(data[:ref])
end
def branch?
- data[:ref]["refs/heads"]
+ Gitlab::Git.branch_ref?(data[:ref])
end
def new_ref?
- commit_from =~ /^00000/
+ Gitlab::Git.blank_ref?(commit_from)
end
def rm_ref?
- commit_to =~ /^00000/
+ Gitlab::Git.blank_ref?(commit_to)
end
def md_ref?
@@ -226,11 +226,11 @@ class Event < ActiveRecord::Base
end
def branch_name
- @branch_name ||= data[:ref].gsub("refs/heads/", "")
+ @branch_name ||= Gitlab::Git.ref_name(data[:ref])
end
def tag_name
- @tag_name ||= data[:ref].gsub("refs/tags/", "")
+ @tag_name ||= Gitlab::Git.ref_name(data[:ref])
end
# Max 20 commits from push DESC
diff --git a/app/models/identity.rb b/app/models/identity.rb
index b2c3792d1ce..440fcd0d052 100644
--- a/app/models/identity.rb
+++ b/app/models/identity.rb
@@ -6,6 +6,8 @@
# extern_uid :string(255)
# provider :string(255)
# user_id :integer
+# created_at :datetime
+# updated_at :datetime
#
class Identity < ActiveRecord::Base
diff --git a/app/models/note.rb b/app/models/note.rb
index e6c258ffbe9..9ca3e4d7e97 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -151,18 +151,41 @@ class Note < ActiveRecord::Base
)
end
- def create_new_commits_note(noteable, project, author, commits)
- commits_text = ActionController::Base.helpers.pluralize(commits.size, 'new commit')
+ def create_new_commits_note(merge_request, project, author, new_commits, existing_commits = [])
+ total_count = new_commits.length + existing_commits.length
+ commits_text = ActionController::Base.helpers.pluralize(total_count, 'commit')
body = "Added #{commits_text}:\n\n"
- commits.each do |commit|
+ if existing_commits.length > 0
+ commit_ids =
+ if existing_commits.length == 1
+ existing_commits.first.short_id
+ else
+ "#{existing_commits.first.short_id}..#{existing_commits.last.short_id}"
+ end
+
+ commits_text = ActionController::Base.helpers.pluralize(existing_commits.length, 'commit')
+
+ branch =
+ if merge_request.for_fork?
+ "#{merge_request.target_project_namespace}:#{merge_request.target_branch}"
+ else
+ merge_request.target_branch
+ end
+
+ message = "* #{commit_ids} - _#{commits_text} from branch `#{branch}`_"
+ body << message
+ body << "\n"
+ end
+
+ new_commits.each do |commit|
message = "* #{commit.short_id} - #{commit.title}"
body << message
body << "\n"
end
create(
- noteable: noteable,
+ noteable: merge_request,
project: project,
author: author,
note: body,
@@ -308,6 +331,10 @@ class Note < ActiveRecord::Base
end
end
+ def hook_attrs
+ attributes
+ end
+
def set_diff
# First lets find notes with same diff
# before iterating over all mr diffs
@@ -466,6 +493,10 @@ class Note < ActiveRecord::Base
for_merge_request? && for_diff_line?
end
+ def for_project_snippet?
+ noteable_type == "Snippet"
+ end
+
# override to return commits, which are not active record
def noteable
if for_commit?
diff --git a/app/models/project_services/asana_service.rb b/app/models/project_services/asana_service.rb
index 8ad1ad6267a..d52214cdd69 100644
--- a/app/models/project_services/asana_service.rb
+++ b/app/models/project_services/asana_service.rb
@@ -2,21 +2,21 @@
#
# 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)
+# 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 'asana'
class AsanaService < Service
@@ -77,7 +77,7 @@ automatically inspected. Leave blank to include all branches.'
end
user = data[:user_name]
- branch = data[:ref].gsub('refs/heads/', '')
+ branch = Gitlab::Git.ref_name(data[:ref])
branch_restriction = restrict_to_branch.to_s
diff --git a/app/models/project_services/assembla_service.rb b/app/models/project_services/assembla_service.rb
index 02aa7c972e7..fb7e0c0fb0d 100644
--- a/app/models/project_services/assembla_service.rb
+++ b/app/models/project_services/assembla_service.rb
@@ -2,19 +2,20 @@
#
# 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)
+# 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 AssemblaService < Service
diff --git a/app/models/project_services/bamboo_service.rb b/app/models/project_services/bamboo_service.rb
index 6c6d74c615e..0100f1e4a10 100644
--- a/app/models/project_services/bamboo_service.rb
+++ b/app/models/project_services/bamboo_service.rb
@@ -2,19 +2,20 @@
#
# 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)
+# 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 BambooService < CiService
diff --git a/app/models/project_services/buildbox_service.rb b/app/models/project_services/buildbox_service.rb
index 96428c91711..270863c1576 100644
--- a/app/models/project_services/buildbox_service.rb
+++ b/app/models/project_services/buildbox_service.rb
@@ -2,20 +2,22 @@
#
# 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)
+# 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 "addressable/uri"
class BuildboxService < CiService
diff --git a/app/models/project_services/campfire_service.rb b/app/models/project_services/campfire_service.rb
index 2f86fbe7a03..e591afdda64 100644
--- a/app/models/project_services/campfire_service.rb
+++ b/app/models/project_services/campfire_service.rb
@@ -2,19 +2,20 @@
#
# 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)
+# 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 CampfireService < Service
@@ -63,7 +64,7 @@ class CampfireService < Service
end
def build_message(push)
- ref = push[:ref].gsub("refs/heads/", "")
+ ref = Gitlab::Git.ref_name(push[:ref])
before = push[:before]
after = push[:after]
@@ -71,9 +72,9 @@ class CampfireService < Service
message << "[#{project.name_with_namespace}] "
message << "#{push[:user_name]} "
- if before.include?('000000')
+ if Gitlab::Git.blank_ref?(before)
message << "pushed new branch #{ref} \n"
- elsif after.include?('000000')
+ elsif Gitlab::Git.blank_ref?(after)
message << "removed branch #{ref} \n"
else
message << "pushed #{push[:total_commits_count]} commits to #{ref}. "
diff --git a/app/models/project_services/ci_service.rb b/app/models/project_services/ci_service.rb
index 646783c8733..c6f6b4952c9 100644
--- a/app/models/project_services/ci_service.rb
+++ b/app/models/project_services/ci_service.rb
@@ -2,19 +2,19 @@
#
# 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
-# issues_events :boolean
-# merge_requests_events :boolean
-# tag_push_events :boolean
+# 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)
#
# Base class for CI services
diff --git a/app/models/project_services/custom_issue_tracker_service.rb b/app/models/project_services/custom_issue_tracker_service.rb
index b29d1c86881..8d25f627870 100644
--- a/app/models/project_services/custom_issue_tracker_service.rb
+++ b/app/models/project_services/custom_issue_tracker_service.rb
@@ -2,15 +2,19 @@
#
# 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)
+# 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)
#
class CustomIssueTrackerService < IssueTrackerService
diff --git a/app/models/project_services/emails_on_push_service.rb b/app/models/project_services/emails_on_push_service.rb
index 21041e08a20..acb5e7f1af5 100644
--- a/app/models/project_services/emails_on_push_service.rb
+++ b/app/models/project_services/emails_on_push_service.rb
@@ -2,22 +2,24 @@
#
# 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
-# issues_events :boolean
-# merge_requests_events :boolean
-# tag_push_events :boolean
+# 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)
#
class EmailsOnPushService < Service
+ prop_accessor :send_from_committer_email
+ prop_accessor :disable_diffs
prop_accessor :recipients
validates :recipients, presence: true, if: :activated?
@@ -37,14 +39,27 @@ class EmailsOnPushService < Service
%w(push)
end
- def execute(data)
- return unless supported_events.include?(data[:object_kind])
+ def execute(push_data)
+ return unless supported_events.include?(push_data[:object_kind])
- EmailsOnPushWorker.perform_async(project_id, recipients, data)
+ EmailsOnPushWorker.perform_async(project_id, recipients, push_data, send_from_committer_email?, disable_diffs?)
+ end
+
+ def send_from_committer_email?
+ self.send_from_committer_email == "1"
+ end
+
+ def disable_diffs?
+ self.disable_diffs == "1"
end
def fields
+ domains = Notify.allowed_email_domains.map { |domain| "user@#{domain}" }.join(", ")
[
+ { type: 'checkbox', name: 'send_from_committer_email', title: "Send from committer",
+ help: "Send notifications from the committer's email address if the domain is part of the domain GitLab is running on (e.g. #{domains})." },
+ { type: 'checkbox', name: 'disable_diffs', title: "Disable code diffs",
+ help: "Don't include possibly sensitive code diffs in notification body." },
{ type: 'textarea', name: 'recipients', placeholder: 'Emails separated by whitespace' },
]
end
diff --git a/app/models/project_services/flowdock_service.rb b/app/models/project_services/flowdock_service.rb
index 443dca72a8c..99e361dd6ed 100644
--- a/app/models/project_services/flowdock_service.rb
+++ b/app/models/project_services/flowdock_service.rb
@@ -2,19 +2,19 @@
#
# 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
-# issues_events :boolean
-# merge_requests_events :boolean
-# tag_push_events :boolean
+# 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)
#
require "flowdock-git-hook"
diff --git a/app/models/project_services/gemnasium_service.rb b/app/models/project_services/gemnasium_service.rb
index 41eedc215d5..4e75bdfc953 100644
--- a/app/models/project_services/gemnasium_service.rb
+++ b/app/models/project_services/gemnasium_service.rb
@@ -2,19 +2,19 @@
#
# 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
-# issues_events :boolean
-# merge_requests_events :boolean
-# tag_push_events :boolean
+# 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)
#
require "gemnasium/gitlab_service"
diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb
index 02bf305f8f2..d81623625c9 100644
--- a/app/models/project_services/gitlab_ci_service.rb
+++ b/app/models/project_services/gitlab_ci_service.rb
@@ -2,19 +2,19 @@
#
# 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
-# issues_events :boolean
-# merge_requests_events :boolean
-# tag_push_events :boolean
+# 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)
#
class GitlabCiService < CiService
diff --git a/app/models/project_services/gitlab_issue_tracker_service.rb b/app/models/project_services/gitlab_issue_tracker_service.rb
index 00f8d430fd5..84346350a6c 100644
--- a/app/models/project_services/gitlab_issue_tracker_service.rb
+++ b/app/models/project_services/gitlab_issue_tracker_service.rb
@@ -2,19 +2,20 @@
#
# 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)
+# 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 GitlabIssueTrackerService < IssueTrackerService
diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb
index b85863d2f0d..d264a56ebdf 100644
--- a/app/models/project_services/hipchat_service.rb
+++ b/app/models/project_services/hipchat_service.rb
@@ -2,19 +2,19 @@
#
# 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
-# issues_events :boolean
-# merge_requests_events :boolean
-# tag_push_events :boolean
+# 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)
#
class HipchatService < Service
@@ -45,7 +45,7 @@ class HipchatService < Service
end
def supported_events
- %w(push)
+ %w(push issue merge_request note tag_push)
end
def execute(data)
@@ -62,22 +62,39 @@ class HipchatService < Service
@gate ||= HipChat::Client.new(token, options)
end
- def create_message(push)
- ref = push[:ref].gsub("refs/heads/", "")
+ def create_message(data)
+ object_kind = data[:object_kind]
+
+ message = \
+ case object_kind
+ when "push", "tag_push"
+ create_push_message(data)
+ when "issue"
+ create_issue_message(data) unless is_update?(data)
+ when "merge_request"
+ create_merge_request_message(data) unless is_update?(data)
+ when "note"
+ create_note_message(data)
+ end
+ end
+
+ def create_push_message(push)
+ ref_type = Gitlab::Git.tag_ref?(push[:ref]) ? 'tag' : 'branch'
+ ref = Gitlab::Git.ref_name(push[:ref])
+
before = push[:before]
after = push[:after]
message = ""
message << "#{push[:user_name]} "
- if before.include?('000000')
- message << "pushed new branch <a href=\""\
- "#{project.web_url}/commits/#{URI.escape(ref)}\">#{ref}</a>"\
- " to <a href=\"#{project.web_url}\">"\
- "#{project.name_with_namespace.gsub!(/\s/, "")}</a>\n"
- elsif after.include?('000000')
- message << "removed branch #{ref} from <a href=\"#{project.web_url}\">#{project.name_with_namespace.gsub!(/\s/,'')}</a> \n"
+ if Gitlab::Git.blank_ref?(before)
+ message << "pushed new #{ref_type} <a href=\""\
+ "#{project_url}/commits/#{URI.escape(ref)}\">#{ref}</a>"\
+ " to #{project_link}\n"
+ elsif Gitlab::Git.blank_ref?(after)
+ message << "removed #{ref_type} <b>#{ref}</b> from <a href=\"#{project.web_url}\">#{project_name}</a> \n"
else
- message << "pushed to branch <a href=\""\
+ message << "pushed to #{ref_type} <a href=\""\
"#{project.web_url}/commits/#{URI.escape(ref)}\">#{ref}</a> "
message << "of <a href=\"#{project.web_url}\">#{project.name_with_namespace.gsub!(/\s/,'')}</a> "
message << "(<a href=\"#{project.web_url}/compare/#{before}...#{after}\">Compare changes</a>)"
@@ -93,4 +110,129 @@ class HipchatService < Service
message
end
+
+ def format_body(body)
+ if body
+ body = body.truncate(200, separator: ' ', omission: '...')
+ end
+
+ "<pre>#{body}</pre>"
+ end
+
+ def create_issue_message(data)
+ user_name = data[:user][:name]
+
+ obj_attr = data[:object_attributes]
+ obj_attr = HashWithIndifferentAccess.new(obj_attr)
+ title = obj_attr[:title]
+ state = obj_attr[:state]
+ issue_iid = obj_attr[:iid]
+ issue_url = obj_attr[:url]
+ description = obj_attr[:description]
+
+ issue_link = "<a href=\"#{issue_url}\">issue ##{issue_iid}</a>"
+ message = "#{user_name} #{state} #{issue_link} in #{project_link}: <b>#{title}</b>"
+
+ if description
+ description = format_body(description)
+ message << description
+ end
+
+ message
+ end
+
+ def create_merge_request_message(data)
+ user_name = data[:user][:name]
+
+ obj_attr = data[:object_attributes]
+ obj_attr = HashWithIndifferentAccess.new(obj_attr)
+ merge_request_id = obj_attr[:iid]
+ source_branch = obj_attr[:source_branch]
+ target_branch = obj_attr[:target_branch]
+ state = obj_attr[:state]
+ description = obj_attr[:description]
+ title = obj_attr[:title]
+
+ merge_request_url = "#{project_url}/merge_requests/#{merge_request_id}"
+ merge_request_link = "<a href=\"#{merge_request_url}\">merge request ##{merge_request_id}</a>"
+ message = "#{user_name} #{state} #{merge_request_link} in " \
+ "#{project_link}: <b>#{title}</b>"
+
+ if description
+ description = format_body(description)
+ message << description
+ end
+
+ message
+ end
+
+ def format_title(title)
+ "<b>" + title.lines.first.chomp + "</b>"
+ end
+
+ def create_note_message(data)
+ data = HashWithIndifferentAccess.new(data)
+ user_name = data[:user][:name]
+
+ repo_attr = HashWithIndifferentAccess.new(data[:repository])
+
+ obj_attr = HashWithIndifferentAccess.new(data[:object_attributes])
+ note = obj_attr[:note]
+ note_url = obj_attr[:url]
+ noteable_type = obj_attr[:noteable_type]
+
+ case noteable_type
+ when "Commit"
+ commit_attr = HashWithIndifferentAccess.new(data[:commit])
+ subject_desc = commit_attr[:id]
+ subject_desc = Commit.truncate_sha(subject_desc)
+ subject_type = "commit"
+ title = format_title(commit_attr[:message])
+ when "Issue"
+ subj_attr = HashWithIndifferentAccess.new(data[:issue])
+ subject_id = subj_attr[:iid]
+ subject_desc = "##{subject_id}"
+ subject_type = "issue"
+ title = format_title(subj_attr[:title])
+ when "MergeRequest"
+ subj_attr = HashWithIndifferentAccess.new(data[:merge_request])
+ subject_id = subj_attr[:iid]
+ subject_desc = "##{subject_id}"
+ subject_type = "merge request"
+ title = format_title(subj_attr[:title])
+ when "Snippet"
+ subj_attr = HashWithIndifferentAccess.new(data[:snippet])
+ subject_id = subj_attr[:id]
+ subject_desc = "##{subject_id}"
+ subject_type = "snippet"
+ title = format_title(subj_attr[:title])
+ end
+
+ subject_html = "<a href=\"#{note_url}\">#{subject_type} #{subject_desc}</a>"
+ message = "#{user_name} commented on #{subject_html} in #{project_link}: "
+ message << title
+
+ if note
+ note = format_body(note)
+ message << note
+ end
+
+ message
+ end
+
+ def project_name
+ project.name_with_namespace.gsub(/\s/, '')
+ end
+
+ def project_url
+ project.web_url
+ end
+
+ def project_link
+ "<a href=\"#{project_url}\">#{project_name}</a>"
+ end
+
+ def is_update?(data)
+ data[:object_attributes][:action] == 'update'
+ end
end
diff --git a/app/models/project_services/irker_service.rb b/app/models/project_services/irker_service.rb
index 14ea5360aa5..2bddb7b881c 100644
--- a/app/models/project_services/irker_service.rb
+++ b/app/models/project_services/irker_service.rb
@@ -2,15 +2,20 @@
#
# 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)
+# 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)
+#
require 'uri'
diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb
index bfc65b5379f..8e90c44d103 100644
--- a/app/models/project_services/issue_tracker_service.rb
+++ b/app/models/project_services/issue_tracker_service.rb
@@ -2,19 +2,20 @@
#
# 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)
+# 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 IssueTrackerService < Service
@@ -29,18 +30,6 @@ class IssueTrackerService < Service
false
end
- def project_url
- # implement inside child
- end
-
- def issues_url
- # implement inside child
- end
-
- def new_issue_url
- # implement inside child
- end
-
def issue_url(iid)
self.issues_url.gsub(':id', iid.to_s)
end
@@ -59,9 +48,9 @@ class IssueTrackerService < Service
if enabled_in_gitlab_config
self.properties = {
title: issues_tracker['title'],
- project_url: set_project_url,
- issues_url: issues_tracker['issues_url'],
- new_issue_url: issues_tracker['new_issue_url']
+ project_url: add_issues_tracker_id(issues_tracker['project_url']),
+ issues_url: add_issues_tracker_id(issues_tracker['issues_url']),
+ new_issue_url: add_issues_tracker_id(issues_tracker['new_issue_url'])
}
else
self.properties = {}
@@ -110,15 +99,15 @@ class IssueTrackerService < Service
Gitlab.config.issues_tracker[to_param]
end
- def set_project_url
+ def add_issues_tracker_id(url)
if self.project
id = self.project.issues_tracker_id
if id
- issues_tracker['project_url'].gsub(":issues_tracker_id", id)
+ url = url.gsub(":issues_tracker_id", id)
end
end
- issues_tracker['project_url']
+ url
end
end
diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb
index 20611eeb60c..fcd9dc2f336 100644
--- a/app/models/project_services/jira_service.rb
+++ b/app/models/project_services/jira_service.rb
@@ -2,19 +2,20 @@
#
# 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)
+# 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 JiraService < IssueTrackerService
diff --git a/app/models/project_services/pivotaltracker_service.rb b/app/models/project_services/pivotaltracker_service.rb
index a2fa9788f10..ade9ee97873 100644
--- a/app/models/project_services/pivotaltracker_service.rb
+++ b/app/models/project_services/pivotaltracker_service.rb
@@ -2,19 +2,20 @@
#
# 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)
+# 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 PivotaltrackerService < Service
diff --git a/app/models/project_services/pushover_service.rb b/app/models/project_services/pushover_service.rb
index 586d9e94a95..53edf522e9a 100644
--- a/app/models/project_services/pushover_service.rb
+++ b/app/models/project_services/pushover_service.rb
@@ -2,19 +2,20 @@
#
# 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)
+# 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 PushoverService < Service
@@ -87,13 +88,13 @@ class PushoverService < Service
def execute(data)
return unless supported_events.include?(data[:object_kind])
- ref = data[:ref].gsub('refs/heads/', '')
+ ref = Gitlab::Git.ref_name(data[:ref])
before = data[:before]
after = data[:after]
- if before.include?('000000')
+ if Gitlab::Git.blank_ref?(before)
message = "#{data[:user_name]} pushed new branch \"#{ref}\"."
- elsif after.include?('000000')
+ elsif Gitlab::Git.blank_ref?(after)
message = "#{data[:user_name]} deleted branch \"#{ref}\"."
else
message = "#{data[:user_name]} push to branch \"#{ref}\"."
diff --git a/app/models/project_services/redmine_service.rb b/app/models/project_services/redmine_service.rb
index f96eae2daa1..dd9ba97ee1f 100644
--- a/app/models/project_services/redmine_service.rb
+++ b/app/models/project_services/redmine_service.rb
@@ -2,19 +2,20 @@
#
# 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)
+# 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 RedmineService < IssueTrackerService
diff --git a/app/models/project_services/slack_service.rb b/app/models/project_services/slack_service.rb
index 64d6f4327b8..36d9874edd3 100644
--- a/app/models/project_services/slack_service.rb
+++ b/app/models/project_services/slack_service.rb
@@ -2,19 +2,20 @@
#
# 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)
+# 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 SlackService < Service
@@ -43,7 +44,7 @@ class SlackService < Service
end
def supported_events
- %w(push issue merge_request)
+ %w(push issue merge_request note tag_push)
end
def execute(data)
@@ -63,12 +64,14 @@ class SlackService < Service
message = \
case object_kind
- when "push"
+ when "push", "tag_push"
PushMessage.new(data)
when "issue"
IssueMessage.new(data) unless is_update?(data)
when "merge_request"
MergeMessage.new(data) unless is_update?(data)
+ when "note"
+ NoteMessage.new(data)
end
opt = {}
@@ -99,3 +102,4 @@ end
require "slack_service/issue_message"
require "slack_service/push_message"
require "slack_service/merge_message"
+require "slack_service/note_message"
diff --git a/app/models/project_services/slack_service/issue_message.rb b/app/models/project_services/slack_service/issue_message.rb
index e2fed0bb1b9..5af24a80609 100644
--- a/app/models/project_services/slack_service/issue_message.rb
+++ b/app/models/project_services/slack_service/issue_message.rb
@@ -1,6 +1,6 @@
class SlackService
class IssueMessage < BaseMessage
- attr_reader :username
+ attr_reader :user_name
attr_reader :title
attr_reader :project_name
attr_reader :project_url
@@ -11,7 +11,7 @@ class SlackService
attr_reader :description
def initialize(params)
- @username = params[:user][:username]
+ @user_name = params[:user][:name]
@project_name = params[:project_name]
@project_url = params[:project_url]
@@ -34,7 +34,7 @@ class SlackService
private
def message
- "#{username} #{state} issue #{issue_link} in #{project_link}: #{title}"
+ "#{user_name} #{state} #{issue_link} in #{project_link}: *#{title}*"
end
def opened_issue?
@@ -50,7 +50,7 @@ class SlackService
end
def issue_link
- "[##{issue_iid}](#{issue_url})"
+ "[issue ##{issue_iid}](#{issue_url})"
end
end
end
diff --git a/app/models/project_services/slack_service/merge_message.rb b/app/models/project_services/slack_service/merge_message.rb
index 4dcce1d15a0..e792c258f73 100644
--- a/app/models/project_services/slack_service/merge_message.rb
+++ b/app/models/project_services/slack_service/merge_message.rb
@@ -1,15 +1,16 @@
class SlackService
class MergeMessage < BaseMessage
- attr_reader :username
+ attr_reader :user_name
attr_reader :project_name
attr_reader :project_url
attr_reader :merge_request_id
attr_reader :source_branch
attr_reader :target_branch
attr_reader :state
+ attr_reader :title
def initialize(params)
- @username = params[:user][:username]
+ @user_name = params[:user][:name]
@project_name = params[:project_name]
@project_url = params[:project_url]
@@ -19,6 +20,7 @@ class SlackService
@source_branch = obj_attr[:source_branch]
@target_branch = obj_attr[:target_branch]
@state = obj_attr[:state]
+ @title = format_title(obj_attr[:title])
end
def pretext
@@ -31,6 +33,10 @@ class SlackService
private
+ def format_title(title)
+ '*' + title.lines.first.chomp + '*'
+ end
+
def message
merge_request_message
end
@@ -40,11 +46,11 @@ class SlackService
end
def merge_request_message
- "#{username} #{state} merge request #{merge_request_link} in #{project_link}"
+ "#{user_name} #{state} #{merge_request_link} in #{project_link}: #{title}"
end
def merge_request_link
- "[##{merge_request_id}](#{merge_request_url})"
+ "[merge request ##{merge_request_id}](#{merge_request_url})"
end
def merge_request_url
diff --git a/app/models/project_services/slack_service/note_message.rb b/app/models/project_services/slack_service/note_message.rb
new file mode 100644
index 00000000000..074478b292d
--- /dev/null
+++ b/app/models/project_services/slack_service/note_message.rb
@@ -0,0 +1,82 @@
+class SlackService
+ class NoteMessage < BaseMessage
+ attr_reader :message
+ attr_reader :user_name
+ attr_reader :project_name
+ attr_reader :project_link
+ attr_reader :note
+ attr_reader :note_url
+ attr_reader :title
+
+ def initialize(params)
+ params = HashWithIndifferentAccess.new(params)
+ @user_name = params[:user][:name]
+ @project_name = params[:project_name]
+ @project_url = params[:project_url]
+
+ obj_attr = params[:object_attributes]
+ obj_attr = HashWithIndifferentAccess.new(obj_attr)
+ @note = obj_attr[:note]
+ @note_url = obj_attr[:url]
+ noteable_type = obj_attr[:noteable_type]
+
+ case noteable_type
+ when "Commit"
+ create_commit_note(HashWithIndifferentAccess.new(params[:commit]))
+ when "Issue"
+ create_issue_note(HashWithIndifferentAccess.new(params[:issue]))
+ when "MergeRequest"
+ create_merge_note(HashWithIndifferentAccess.new(params[:merge_request]))
+ when "Snippet"
+ create_snippet_note(HashWithIndifferentAccess.new(params[:snippet]))
+ end
+ end
+
+ def attachments
+ description_message
+ end
+
+ private
+
+ def format_title(title)
+ title.lines.first.chomp
+ end
+
+ def create_commit_note(commit)
+ commit_sha = commit[:id]
+ commit_sha = Commit.truncate_sha(commit_sha)
+ commit_link = "[commit #{commit_sha}](#{@note_url})"
+ title = format_title(commit[:message])
+ @message = "#{@user_name} commented on #{commit_link} in #{project_link}: *#{title}*"
+ end
+
+ def create_issue_note(issue)
+ issue_iid = issue[:iid]
+ note_link = "[issue ##{issue_iid}](#{@note_url})"
+ title = format_title(issue[:title])
+ @message = "#{@user_name} commented on #{note_link} in #{project_link}: *#{title}*"
+ end
+
+ def create_merge_note(merge_request)
+ merge_request_id = merge_request[:iid]
+ merge_request_link = "[merge request ##{merge_request_id}](#{@note_url})"
+ title = format_title(merge_request[:title])
+ @message = "#{@user_name} commented on #{merge_request_link} in #{project_link}: *#{title}*"
+ end
+
+ def create_snippet_note(snippet)
+ snippet_id = snippet[:id]
+ snippet_link = "[snippet ##{snippet_id}](#{@note_url})"
+ title = format_title(snippet[:title])
+ @message = "#{@user_name} commented on #{snippet_link} in #{project_link}: *#{title}*"
+ end
+
+ def description_message
+ [{ text: format(@note), color: attachment_color }]
+ end
+
+ def project_link
+ "[#{@project_name}](#{@project_url})"
+ end
+ end
+end
diff --git a/app/models/project_services/slack_service/push_message.rb b/app/models/project_services/slack_service/push_message.rb
index 2e566bc317b..b26f3e9ddce 100644
--- a/app/models/project_services/slack_service/push_message.rb
+++ b/app/models/project_services/slack_service/push_message.rb
@@ -6,7 +6,8 @@ class SlackService
attr_reader :project_name
attr_reader :project_url
attr_reader :ref
- attr_reader :username
+ attr_reader :ref_type
+ attr_reader :user_name
def initialize(params)
@after = params[:after]
@@ -14,8 +15,9 @@ class SlackService
@commits = params.fetch(:commits, [])
@project_name = params[:project_name]
@project_url = params[:project_url]
- @ref = params[:ref].gsub('refs/heads/', '')
- @username = params[:user_name]
+ @ref_type = Gitlab::Git.tag_ref?(params[:ref]) ? 'tag' : 'branch'
+ @ref = Gitlab::Git.ref_name(params[:ref])
+ @user_name = params[:user_name]
end
def pretext
@@ -45,15 +47,15 @@ class SlackService
end
def new_branch_message
- "#{username} pushed new branch #{branch_link} to #{project_link}"
+ "#{user_name} pushed new #{ref_type} #{branch_link} to #{project_link}"
end
def removed_branch_message
- "#{username} removed branch #{ref} from #{project_link}"
+ "#{user_name} removed #{ref_type} #{ref} from #{project_link}"
end
def push_message
- "#{username} pushed to branch #{branch_link} of #{project_link} (#{compare_link})"
+ "#{user_name} pushed to #{ref_type} #{branch_link} of #{project_link} (#{compare_link})"
end
def commit_messages
@@ -74,11 +76,11 @@ class SlackService
end
def new_branch?
- before.include?('000000')
+ Gitlab::Git.blank_ref?(before)
end
def removed_branch?
- after.include?('000000')
+ Gitlab::Git.blank_ref?(after)
end
def branch_url
diff --git a/app/models/project_services/teamcity_service.rb b/app/models/project_services/teamcity_service.rb
index 686e6225a2e..7403e19da9a 100644
--- a/app/models/project_services/teamcity_service.rb
+++ b/app/models/project_services/teamcity_service.rb
@@ -2,19 +2,20 @@
#
# 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)
+# 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 TeamcityService < CiService
@@ -131,7 +132,7 @@ class TeamcityService < CiService
password: password,
}
- branch = data[:ref].gsub('refs/heads/', '')
+ branch = Gitlab::Git.ref_name(data[:ref])
self.class.post("#{teamcity_url}/httpAuth/app/rest/buildQueue",
body: "<build branchName=\"#{branch}\">"\
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 5b52739df2b..6117db418a7 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -146,7 +146,8 @@ class Repository
end
def timestamps_by_user_log(user)
- args = %W(git log --author=#{user.email} --since=#{(Date.today - 1.year).to_s} --branches --pretty=format:%cd --date=short)
+ author_emails = '(' + user.all_emails.map{ |e| Regexp.escape(e) }.join('|') + ')'
+ args = %W(git log -E --author=#{author_emails} --since=#{(Date.today - 1.year).to_s} --branches --pretty=format:%cd --date=short)
dates = Gitlab::Popen.popen(args, path_to_repo).first.split("\n")
if dates.present?
diff --git a/app/models/service.rb b/app/models/service.rb
index 98bd40ae95e..33734e97c55 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -2,19 +2,19 @@
#
# 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
-# issues_events :boolean
-# merge_requests_events :boolean
-# tag_push_events :boolean
+# 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)
#
# To add new service you should build a class inherited from Service
@@ -28,6 +28,7 @@ class Service < ActiveRecord::Base
default_value_for :issues_events, true
default_value_for :merge_requests_events, true
default_value_for :tag_push_events, true
+ default_value_for :note_events, true
after_initialize :initialize_properties
@@ -42,6 +43,7 @@ class Service < ActiveRecord::Base
scope :tag_push_hooks, -> { where(tag_push_events: true, active: true) }
scope :issue_hooks, -> { where(issues_events: true, active: true) }
scope :merge_request_hooks, -> { where(merge_requests_events: true, active: true) }
+ scope :note_hooks, -> { where(note_events: true, active: true) }
def activated?
active
diff --git a/app/models/snippet.rb b/app/models/snippet.rb
index 82c1ab94446..3fb2ec1d66c 100644
--- a/app/models/snippet.rb
+++ b/app/models/snippet.rb
@@ -59,6 +59,10 @@ class Snippet < ActiveRecord::Base
content
end
+ def hook_attrs
+ attributes
+ end
+
def size
0
end
diff --git a/app/models/user.rb b/app/models/user.rb
index 55768a351e3..0d40ac8309e 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -2,51 +2,53 @@
#
# 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
-# 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)
-# hide_no_password :boolean default(FALSE)
-# website_url :string(255) default(""), not null
-# last_credential_check_at :datetime
-# github_access_token :string(255)
-# notification_email :string(255)
-# password_automatically_set :boolean default(FALSE)
-# bitbucket_access_token :string(255)
+# 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)
#
require 'carrierwave/orm/activerecord'
@@ -152,24 +154,6 @@ class User < ActiveRecord::Base
delegate :path, to: :namespace, allow_nil: true, prefix: true
state_machine :state, initial: :active do
- after_transition any => :blocked do |user, transition|
- # Remove user from all projects and
- user.project_members.find_each do |membership|
- # skip owned resources
- next if membership.project.owner == user
-
- return false unless membership.destroy
- end
-
- # Remove user from all groups
- user.group_members.find_each do |membership|
- # skip owned resources
- next if membership.group.last_owner?(user)
-
- return false unless membership.destroy
- end
- end
-
event :block do
transition active: :blocked
end
@@ -624,7 +608,7 @@ class User < ActiveRecord::Base
def contributed_projects_ids
Event.where(author_id: self).
where("created_at > ?", Time.now - 1.year).
- where("action = :pushed OR (target_type = 'MergeRequest' AND action = :created)",
+ where("action = :pushed OR (target_type = 'MergeRequest' AND action = :created)",
pushed: Event::PUSHED, created: Event::CREATED).
reorder(project_id: :desc).
select(:project_id).
diff --git a/app/services/create_tag_service.rb b/app/services/create_tag_service.rb
index 8cd65724cb9..dfc5677c9d4 100644
--- a/app/services/create_tag_service.rb
+++ b/app/services/create_tag_service.rb
@@ -21,7 +21,7 @@ class CreateTagService < BaseService
new_tag = repository.find_tag(tag_name)
if new_tag
- EventCreateService.new.push_ref(project, current_user, new_tag, 'add', 'refs/tags')
+ EventCreateService.new.push_ref(project, current_user, new_tag, 'add', Gitlab::Git::TAG_REF_PREFIX)
push_data = create_push_data(project, current_user, new_tag)
project.execute_hooks(push_data.dup, :tag_push_hooks)
@@ -41,7 +41,7 @@ class CreateTagService < BaseService
def create_push_data(project, user, tag)
data = Gitlab::PushDataBuilder.
- build(project, user, Gitlab::Git::BLANK_SHA, tag.target, 'refs/tags/' + tag.name, [])
+ build(project, user, Gitlab::Git::BLANK_SHA, tag.target, "#{Gitlab::Git::TAG_REF_PREFIX}#{tag.name}", [])
data[:object_kind] = "tag_push"
data
end
diff --git a/app/services/event_create_service.rb b/app/services/event_create_service.rb
index ba9547b9242..dc52d6d89df 100644
--- a/app/services/event_create_service.rb
+++ b/app/services/event_create_service.rb
@@ -62,19 +62,19 @@ class EventCreateService
create_event(project, current_user, Event::CREATED)
end
- def push_ref(project, current_user, ref, action = 'add', prefix = 'refs/heads')
+ def push_ref(project, current_user, ref, action = 'add', prefix = Gitlab::Git::BRANCH_REF_PREFIX)
commit = project.repository.commit(ref.target)
if action.to_s == 'add'
- before = '00000000'
+ before = Gitlab::Git::BLANK_SHA
after = commit.id
else
before = commit.id
- after = '00000000'
+ after = Gitlab::Git::BLANK_SHA
end
data = {
- ref: "#{prefix}/#{ref.name}",
+ ref: "#{prefix}#{ref.name}",
before: before,
after: after
}
diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb
index 13def127763..bfabfd7ade3 100644
--- a/app/services/git_push_service.rb
+++ b/app/services/git_push_service.rb
@@ -42,8 +42,10 @@ class GitPushService
# as a heuristic. This may include more commits than are actually pushed, but
# that shouldn't matter because we check for existing cross-references later.
@push_commits = project.repository.commits_between(project.default_branch, newrev)
+
+ # don't process commits for the initial push to the default branch
+ process_commit_messages(ref)
end
- process_commit_messages(ref)
elsif push_to_existing_branch?(ref, oldrev)
# Collect data for this git push
@push_commits = project.repository.commits_between(oldrev, newrev)
@@ -105,30 +107,24 @@ class GitPushService
end
def push_to_existing_branch?(ref, oldrev)
- ref_parts = ref.split('/')
-
# Return if this is not a push to a branch (e.g. new commits)
- ref_parts[1].include?('heads') && oldrev != Gitlab::Git::BLANK_SHA
+ Gitlab::Git.branch_ref?(ref) && oldrev != Gitlab::Git::BLANK_SHA
end
def push_to_new_branch?(ref, oldrev)
- ref_parts = ref.split('/')
-
- ref_parts[1].include?('heads') && oldrev == Gitlab::Git::BLANK_SHA
+ Gitlab::Git.branch_ref?(ref) && Gitlab::Git.blank_ref?(oldrev)
end
def push_remove_branch?(ref, newrev)
- ref_parts = ref.split('/')
-
- ref_parts[1].include?('heads') && newrev == Gitlab::Git::BLANK_SHA
+ Gitlab::Git.branch_ref?(ref) && Gitlab::Git.blank_ref?(newrev)
end
def push_to_branch?(ref)
- ref.include?('refs/heads')
+ Gitlab::Git.branch_ref?(ref)
end
def is_default_branch?(ref)
- ref == "refs/heads/#{project.default_branch}"
+ Gitlab::Git.branch_ref?(ref) && Gitlab::Git.ref_name(ref) == project.default_branch
end
def commit_user(commit)
diff --git a/app/services/issues/bulk_update_service.rb b/app/services/issues/bulk_update_service.rb
index f72a346af6f..c7cd20b6b60 100644
--- a/app/services/issues/bulk_update_service.rb
+++ b/app/services/issues/bulk_update_service.rb
@@ -1,38 +1,23 @@
module Issues
class BulkUpdateService < BaseService
def execute
- update_data = params[:update]
+ issues_ids = params.delete(:issues_ids).split(",")
+ issue_params = params
- issues_ids = update_data[:issues_ids].split(",")
- milestone_id = update_data[:milestone_id]
- assignee_id = update_data[:assignee_id]
- status = update_data[:status]
-
- new_state = nil
-
- if status.present?
- if status == 'closed'
- new_state = :close
- else
- new_state = :reopen
- end
- end
-
- opts = {}
- opts[:milestone_id] = milestone_id if milestone_id.present?
- opts[:assignee_id] = assignee_id if assignee_id.present?
+ issue_params.delete(:state_event) unless issue_params[:state_event].present?
+ issue_params.delete(:milestone_id) unless issue_params[:milestone_id].present?
+ issue_params.delete(:assignee_id) unless issue_params[:assignee_id].present?
issues = Issue.where(id: issues_ids)
- issues = issues.select { |issue| can?(current_user, :modify_issue, issue) }
-
issues.each do |issue|
- issue.update_attributes(opts)
- issue.send new_state if new_state
+ next unless can?(current_user, :modify_issue, issue)
+
+ Issues::UpdateService.new(issue.project, current_user, issue_params).execute(issue)
end
{
- count: issues.count,
- success: !issues.count.zero?
+ count: issues.count,
+ success: !issues.count.zero?
}
end
end
diff --git a/app/services/merge_requests/refresh_service.rb b/app/services/merge_requests/refresh_service.rb
index 96761bec99f..cab8a1e880e 100644
--- a/app/services/merge_requests/refresh_service.rb
+++ b/app/services/merge_requests/refresh_service.rb
@@ -1,10 +1,10 @@
module MergeRequests
class RefreshService < MergeRequests::BaseService
def execute(oldrev, newrev, ref)
- return true unless ref =~ /heads/
+ return true unless Gitlab::Git.branch_ref?(ref)
@oldrev, @newrev = oldrev, newrev
- @branch_name = ref.gsub("refs/heads/", "")
+ @branch_name = Gitlab::Git.ref_name(ref)
@fork_merge_requests = @project.fork_merge_requests.opened
@commits = @project.repository.commits_between(oldrev, newrev)
@@ -82,8 +82,14 @@ module MergeRequests
merge_requests = filter_merge_requests(merge_requests)
merge_requests.each do |merge_request|
+ mr_commit_ids = Set.new(merge_request.commits.map(&:id))
+
+ new_commits, existing_commits = @commits.partition do |commit|
+ mr_commit_ids.include?(commit.id)
+ end
+
Note.create_new_commits_note(merge_request, merge_request.project,
- @current_user, @commits)
+ @current_user, new_commits, existing_commits)
end
end
diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb
index f64006a4edc..e969061f229 100644
--- a/app/services/notes/create_service.rb
+++ b/app/services/notes/create_service.rb
@@ -17,10 +17,22 @@ module Notes
note.references.each do |mentioned|
Note.create_cross_reference_note(mentioned, note.noteable, note.author, note.project)
end
+
+ execute_hooks(note)
end
end
note
end
+
+ def hook_data(note)
+ Gitlab::NoteDataBuilder.build(note, current_user)
+ end
+
+ def execute_hooks(note)
+ note_data = hook_data(note)
+ # TODO: Support Webhooks
+ note.project.execute_services(note_data, :note_hooks)
+ end
end
end
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index 2fc63b9f4b7..0063b7ce40c 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -268,6 +268,7 @@ class NotificationService
# Also remove duplications and nil recipients
def reject_muted_users(users, project = nil)
users = users.to_a.compact.uniq
+ users = users.reject(&:blocked?)
users.reject do |user|
next user.notification.disabled? unless project
diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb
index 46f6e91e808..c5d0b08845b 100644
--- a/app/services/system_hooks_service.rb
+++ b/app/services/system_hooks_service.rb
@@ -41,7 +41,7 @@ class SystemHooksService
path_with_namespace: model.path_with_namespace,
project_id: model.id,
owner_name: owner.name,
- owner_email: owner.respond_to?(:email) ? owner.email : nil,
+ owner_email: owner.respond_to?(:email) ? owner.email : "",
project_visibility: Project.visibility_levels.key(model.visibility_level_field).downcase
})
when User
diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml
index f528d69f431..520f327f4e7 100644
--- a/app/views/admin/application_settings/_form.html.haml
+++ b/app/views/admin/application_settings/_form.html.haml
@@ -8,39 +8,47 @@
%fieldset
%legend Features
.form-group
- = f.label :signup_enabled, class: 'control-label'
- .col-sm-10
- = f.check_box :signup_enabled, class: 'checkbox'
- .form-group
- = f.label :signin_enabled, class: 'control-label'
- .col-sm-10
- = f.check_box :signin_enabled, class: 'checkbox'
- .form-group
- = f.label :gravatar_enabled, class: 'control-label'
- .col-sm-10
- = f.check_box :gravatar_enabled, class: 'checkbox'
- .form-group
- = f.label :twitter_sharing_enabled, "Twitter enabled", class: 'control-label'
- .col-sm-10
- = f.check_box :twitter_sharing_enabled, class: 'checkbox'
- %span.help-block Show users button to share their newly created public or internal projects on twitter
+ .col-sm-offset-2.col-sm-10
+ .checkbox
+ = f.label :signup_enabled do
+ = f.check_box :signup_enabled
+ Signin enabled
+ .form-group
+ .col-sm-offset-2.col-sm-10
+ .checkbox
+ = f.label :signin_enabled do
+ = f.check_box :signin_enabled
+ Signup enabled
+ .form-group
+ .col-sm-offset-2.col-sm-10
+ .checkbox
+ = f.label :gravatar_enabled do
+ = f.check_box :gravatar_enabled
+ Gravatar enabled
+ .form-group
+ .col-sm-offset-2.col-sm-10
+ .checkbox
+ = f.label :twitter_sharing_enabled do
+ = f.check_box :twitter_sharing_enabled, :'aria-describedby' => 'twitter_help_block'
+ %strong Twitter enabled
+ %span.help-block#twitter_help_block Show users a button to share their newly created public or internal projects on twitter
%fieldset
%legend Misc
.form-group
- = f.label :default_projects_limit, class: 'control-label'
+ = f.label :default_projects_limit, class: 'control-label col-sm-2'
.col-sm-10
= f.number_field :default_projects_limit, class: 'form-control'
.form-group
- = f.label :default_branch_protection, class: 'control-label'
+ = f.label :default_branch_protection, class: 'control-label col-sm-2'
.col-sm-10
= f.select :default_branch_protection, options_for_select(Gitlab::Access.protection_options, @application_setting.default_branch_protection), {}, class: 'form-control'
.form-group
- = f.label :home_page_url, class: 'control-label'
+ = f.label :home_page_url, class: 'control-label col-sm-2'
.col-sm-10
- = f.text_field :home_page_url, class: 'form-control', placeholder: 'http://company.example.com'
- %span.help-block We will redirect non-logged in users to this page
+ = f.text_field :home_page_url, class: 'form-control', placeholder: 'http://company.example.com', :'aria-describedby' => 'home_help_block'
+ %span.help-block#home_help_block We will redirect non-logged in users to this page
.form-group
- = f.label :sign_in_text, class: 'control-label'
+ = f.label :sign_in_text, class: 'control-label col-sm-2'
.col-sm-10
= f.text_area :sign_in_text, class: 'form-control', rows: 4
.help-block Markdown enabled
diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml
index 931b0c5c107..d1c586328a2 100644
--- a/app/views/admin/dashboard/index.html.haml
+++ b/app/views/admin/dashboard/index.html.haml
@@ -32,9 +32,9 @@
%span.light.pull-right
= Milestone.count
%p
- Users who signed in during last 30 days
+ Active Users
%span.light.pull-right
- = User.where("current_sign_in_at > ?", 30.days.ago).count
+ = User.active.count
.col-md-4
%h4
Features
diff --git a/app/views/admin/groups/_form.html.haml b/app/views/admin/groups/_form.html.haml
index 86a73200609..9e7751830a4 100644
--- a/app/views/admin/groups/_form.html.haml
+++ b/app/views/admin/groups/_form.html.haml
@@ -14,7 +14,7 @@
.form-group
.col-sm-2
.col-sm-10
- .bs-callout.bs-callout-info
+ .alert.alert-info
= render 'shared/group_tips'
.form-actions
= f.submit 'Create group', class: "btn btn-create"
diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml
index bb7f1972925..3040faa722b 100644
--- a/app/views/admin/groups/show.html.haml
+++ b/app/views/admin/groups/show.html.haml
@@ -12,7 +12,7 @@
Group info:
%ul.well-list
%li
- = image_tag group_icon(@group.path), class: "avatar s60"
+ = image_tag group_icon(@group), class: "avatar s60"
%li
%span.light Name:
%strong= @group.name
diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml
index b984188eb9d..3a1e61d5d8d 100644
--- a/app/views/admin/projects/index.html.haml
+++ b/app/views/admin/projects/index.html.haml
@@ -44,7 +44,7 @@
Projects (#{@projects.total_count})
.panel-head-actions
.dropdown.inline
- %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
+ %button.dropdown-toggle.btn.btn-sm{type: 'button', 'data-toggle' => 'dropdown'}
%span.light sort:
- if @sort.present?
= sort_options_hash[@sort]
@@ -63,7 +63,7 @@
= sort_title_oldest_updated
= link_to admin_namespaces_projects_path(sort: sort_value_largest_repo) do
= sort_title_largest_repo
- = link_to 'New Project', new_project_path, class: "btn btn-new"
+ = link_to 'New Project', new_project_path, class: "btn btn-sm btn-success"
%ul.well-list
- @projects.each do |project|
%li
diff --git a/app/views/admin/services/_form.html.haml b/app/views/admin/services/_form.html.haml
index 62f4001ca66..291e48efc12 100644
--- a/app/views/admin/services/_form.html.haml
+++ b/app/views/admin/services/_form.html.haml
@@ -10,7 +10,7 @@
- @service.errors.full_messages.each do |msg|
%p= msg
- if @service.help.present?
- .bs-callout
+ .alert.alert-info
= preserve do
= markdown @service.help
@@ -53,14 +53,16 @@
- @service.fields.each do |field|
- name = field[:name]
+ - title = field[:title] || name.humanize
- value = @service.send(name) unless field[:type] == 'password'
- type = field[:type]
- placeholder = field[:placeholder]
- choices = field[:choices]
- default_choice = field[:default_choice]
+ - help = field[:help]
.form-group
- = f.label name, class: "control-label"
+ = f.label name, title, class: "control-label"
.col-sm-10
- if type == 'text'
= f.text_field name, class: "form-control", placeholder: placeholder
@@ -72,6 +74,8 @@
= f.select name, options_for_select(choices, value ? value : default_choice), {}, { class: "form-control" }
- elsif type == 'password'
= f.password_field name, class: 'form-control'
+ - if help
+ %span.help-block= help
.form-actions
= f.submit 'Save', class: 'btn btn-save'
diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml
index 4a4f0549ada..35e9fd5154f 100644
--- a/app/views/admin/users/index.html.haml
+++ b/app/views/admin/users/index.html.haml
@@ -35,7 +35,7 @@
Users (#{@users.total_count})
.panel-head-actions
.dropdown.inline
- %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
+ %a.dropdown-toggle.btn.btn-sm{href: '#', "data-toggle" => "dropdown"}
%span.light sort:
- if @sort.present?
= sort_options_hash[@sort]
@@ -59,7 +59,7 @@
= link_to admin_users_path(sort: sort_value_oldest_updated) do
= sort_title_oldest_updated
- = link_to 'New User', new_admin_user_path, class: "btn btn-new"
+ = link_to 'New User', new_admin_user_path, class: "btn btn-new btn-sm"
%ul.well-list
- @users.each do |user|
%li
diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml
index 90267897503..90c9f8c2f9b 100644
--- a/app/views/admin/users/show.html.haml
+++ b/app/views/admin/users/show.html.haml
@@ -108,45 +108,49 @@
.col-md-6
- unless @user == current_user
- if @user.blocked?
- .alert.alert-info
- %h4 This user is blocked
- %p Blocking user has the following effects:
- %ul
- %li User will not be able to login
- %li User will not be able to access git repositories
- %li User will be removed from joined projects and groups
- %li Personal projects will be left
- %li Owned groups will be left
- %br
- = link_to 'Unblock user', unblock_admin_user_path(@user), method: :put, class: "btn btn-new", data: { confirm: 'Are you sure?' }
+ .panel.panel-info
+ .panel-heading
+ This user is blocked
+ .panel-body
+ %p Blocking user has the following effects:
+ %ul
+ %li User will not be able to login
+ %li User will not be able to access git repositories
+ %li Personal projects will be left
+ %li Owned groups will be left
+ %br
+ = link_to 'Unblock user', unblock_admin_user_path(@user), method: :put, class: "btn btn-info", data: { confirm: 'Are you sure?' }
- else
- .alert.alert-warning
- %h4 Block this user
- %p Blocking user has the following effects:
+ .panel.panel-warning
+ .panel-heading
+ Block this user
+ .panel-body
+ %p Blocking user has the following effects:
+ %ul
+ %li User will not be able to login
+ %li User will not be able to access git repositories
+ %li User will be removed from joined projects and groups
+ %li Personal projects will be left
+ %li Owned groups will be left
+ %br
+ = link_to 'Block user', block_admin_user_path(@user), data: { confirm: 'USER WILL BE BLOCKED! Are you sure?' }, method: :put, class: "btn btn-warning"
+
+ .panel.panel-danger
+ .panel-heading
+ Remove user
+ .panel-body
+ %p Deleting a user has the following effects:
%ul
- %li User will not be able to login
- %li User will not be able to access git repositories
- %li User will be removed from joined projects and groups
- %li Personal projects will be left
- %li Owned groups will be left
+ %li All user content like authored issues, snippets, comments will be removed
+ - rp = @user.personal_projects.count
+ - unless rp.zero?
+ %li #{pluralize rp, 'personal project'} will be removed and cannot be restored
+ - if @user.solo_owned_groups.present?
+ %li
+ Next groups with all content will be removed:
+ %strong #{@user.solo_owned_groups.map(&:name).join(', ')}
%br
- = link_to 'Block user', block_admin_user_path(@user), data: { confirm: 'USER WILL BE BLOCKED! Are you sure?' }, method: :put, class: "btn btn-remove"
-
- .alert.alert-danger
- %h4
- Remove user
- %p Deleting a user has the following effects:
- %ul
- %li All user content like authored issues, snippets, comments will be removed
- - rp = @user.personal_projects.count
- - unless rp.zero?
- %li #{pluralize rp, 'personal project'} will be removed and cannot be restored
- - if @user.solo_owned_groups.present?
- %li
- Next groups with all content will be removed:
- %strong #{@user.solo_owned_groups.map(&:name).join(', ')}
- %br
- = link_to 'Remove user', [:admin, @user], data: { confirm: "USER #{@user.name} WILL BE REMOVED! Are you sure?" }, method: :delete, class: "btn btn-remove"
+ = link_to 'Remove user', [:admin, @user], data: { confirm: "USER #{@user.name} WILL BE REMOVED! Are you sure?" }, method: :delete, class: "btn btn-remove"
#profile.tab-pane
.row
diff --git a/app/views/dashboard/_groups.html.haml b/app/views/dashboard/_groups.html.haml
deleted file mode 100644
index e3df43d8892..00000000000
--- a/app/views/dashboard/_groups.html.haml
+++ /dev/null
@@ -1,21 +0,0 @@
-.panel.panel-default
- .panel-heading.clearfix
- .input-group
- = search_field_tag :filter_group, nil, placeholder: 'Filter by name', class: 'dash-filter form-control'
- - if current_user.can_create_group?
- .input-group-addon.dash-new-group
- = link_to new_group_path, class: "" do
- %strong New group
- %ul.well-list.dash-list
- - groups.each do |group|
- %li.group-row
- = link_to group_path(id: group.path), class: dom_class(group) do
- .dash-project-avatar
- = image_tag group_icon(group.path), class: "avatar s40"
- %span.group-name.filter-title
- = truncate(group.name, length: 35)
- %span.arrow
- %i.fa.fa-angle-right
- - if groups.blank?
- %li
- .nothing-here-block You have no groups yet.
diff --git a/app/views/dashboard/_project.html.haml b/app/views/dashboard/_project.html.haml
deleted file mode 100644
index fa9179cb249..00000000000
--- a/app/views/dashboard/_project.html.haml
+++ /dev/null
@@ -1,14 +0,0 @@
-= link_to project_path(project), class: dom_class(project) do
- .dash-project-avatar
- = project_icon(project, alt: '', class: 'avatar project-avatar s40')
- .dash-project-access-icon
- = visibility_level_icon(project.visibility_level)
- %span.str-truncated
- %span.namespace-name
- - if project.namespace
- = project.namespace.human_name
- \/
- %span.project-name.filter-title
- = project.name
- %span.arrow
- %i.fa.fa-angle-right
diff --git a/app/views/dashboard/_projects.html.haml b/app/views/dashboard/_projects.html.haml
index 0596738342f..3634b2bfd7b 100644
--- a/app/views/dashboard/_projects.html.haml
+++ b/app/views/dashboard/_projects.html.haml
@@ -1,25 +1,10 @@
.panel.panel-default
.panel-heading.clearfix
.input-group
- = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'dash-filter form-control'
+ = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control'
- if current_user.can_create_project?
.input-group-addon.dash-new-project
= link_to new_project_path do
%strong New project
- %ul.well-list.dash-list
- - projects.each do |project|
- %li.project-row
- = render "project", project: project
-
- - 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.
- .pull-right
- = link_to projects_dashboard_path do
- Show all
- %i.fa.fa-angle-right
+ = render 'shared/projects_list', projects: @projects, projects_limit: 20
diff --git a/app/views/dashboard/_projects_filter.html.haml b/app/views/dashboard/_projects_filter.html.haml
deleted file mode 100644
index d87ca861aed..00000000000
--- a/app/views/dashboard/_projects_filter.html.haml
+++ /dev/null
@@ -1,100 +0,0 @@
-.dash-projects-filters.append-bottom-20
- .append-right-20
- %ul.nav.nav-tabs
- = nav_tab :scope, nil do
- = link_to projects_dashboard_filter_path(scope: nil) do
- All
- = nav_tab :scope, 'personal' do
- = link_to projects_dashboard_filter_path(scope: 'personal') do
- Personal
- = nav_tab :scope, 'joined' do
- = link_to projects_dashboard_filter_path(scope: 'joined') do
- Joined
- = nav_tab :scope, 'owned' do
- = link_to projects_dashboard_filter_path(scope: 'owned') do
- Owned
-
- .dropdown.inline.append-right-10
- %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
- %i.fa.fa-globe
- %span.light Visibility:
- - if params[:visibility_level].present?
- = visibility_level_label(params[:visibility_level].to_i)
- - else
- Any
- %b.caret
- %ul.dropdown-menu
- %li
- = link_to projects_dashboard_filter_path(visibility_level: nil) do
- Any
- - Gitlab::VisibilityLevel.values.each do |level|
- %li{ class: (level.to_s == params[:visibility_level]) ? 'active' : 'light' }
- = link_to projects_dashboard_filter_path(visibility_level: level) do
- = visibility_level_icon(level)
- = visibility_level_label(level)
-
- - if @groups.present?
- .dropdown.inline.append-right-10
- %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
- %i.fa.fa-group
- %span.light Group:
- - if params[:group].present?
- = Group.find_by(name: params[:group]).name
- - else
- Any
- %b.caret
- %ul.dropdown-menu
- %li
- = link_to projects_dashboard_filter_path(group: nil) do
- Any
- - @groups.each do |group|
- %li{ class: (group.name == params[:group]) ? 'active' : 'light' }
- = link_to projects_dashboard_filter_path(group: group.name) do
- = group.name
- %small.pull-right
- = group.projects.count
-
-
-
- - if @tags.present?
- .dropdown.inline.append-right-10
- %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
- %i.fa.fa-tags
- %span.light Tags:
- - if params[:tag].present?
- = params[:tag]
- - else
- Any
- %b.caret
- %ul.dropdown-menu
- %li
- = link_to projects_dashboard_filter_path(tag: nil) do
- Any
-
- - @tags.each do |tag|
- %li{ class: (tag.name == params[:tag]) ? 'active' : 'light' }
- = link_to projects_dashboard_filter_path(tag: tag.name) do
- %i.fa.fa-tag
- = tag.name
-
- .pull-right
- .dropdown.inline
- %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
- %span.light sort:
- - if @sort.present?
- = sort_options_hash[@sort]
- - else
- = sort_title_recently_created
- %b.caret
- %ul.dropdown-menu
- %li
- = link_to projects_dashboard_filter_path(sort: sort_value_recently_created) do
- = sort_title_recently_created
- = link_to projects_dashboard_filter_path(sort: sort_value_oldest_created) do
- = sort_title_oldest_created
- = link_to projects_dashboard_filter_path(sort: sort_value_recently_updated) do
- = sort_title_recently_updated
- = link_to projects_dashboard_filter_path(sort: sort_value_oldest_updated) do
- = sort_title_oldest_updated
- = link_to projects_dashboard_filter_path(sort: sort_value_name) do
- = sort_title_name
diff --git a/app/views/dashboard/_sidebar.html.haml b/app/views/dashboard/_sidebar.html.haml
index a980f495427..78f695be916 100644
--- a/app/views/dashboard/_sidebar.html.haml
+++ b/app/views/dashboard/_sidebar.html.haml
@@ -1,18 +1,3 @@
-%ul.nav.nav-tabs.dash-sidebar-tabs
- %li.active
- = link_to '#projects', 'data-toggle' => 'tab', id: 'sidebar-projects-tab' do
- Projects
- %span.badge= @projects_count
- %li
- = link_to '#groups', 'data-toggle' => 'tab', id: 'sidebar-groups-tab' do
- Groups
- %span.badge= @groups.count
-
-.tab-content
- .tab-pane.active#projects
- = render "projects", projects: @projects
- .tab-pane#groups
- = render "groups", groups: @groups
-
+= render "dashboard/projects", projects: @projects
.prepend-top-20
= render 'shared/promo'
diff --git a/app/views/dashboard/_zero_authorized_projects.html.haml b/app/views/dashboard/_zero_authorized_projects.html.haml
index 6e76f95b34e..4e7d6639727 100644
--- a/app/views/dashboard/_zero_authorized_projects.html.haml
+++ b/app/views/dashboard/_zero_authorized_projects.html.haml
@@ -1,3 +1,4 @@
+- publicish_project_count = Project.publicish(current_user).count
%h3.page-title Welcome to GitLab!
%p.light Self hosted Git management application.
%hr
@@ -35,7 +36,7 @@
%i.fa.fa-plus
New Group
--if @publicish_project_count > 0
+-if publicish_project_count > 0
%hr
%div
.dashboard-intro-icon
@@ -43,7 +44,7 @@
.dashboard-intro-text
%p.slead
There are
- %strong= @publicish_project_count
+ %strong= publicish_project_count
public projects on this server.
%br
Public projects are an easy way to allow everyone to have read-only access.
diff --git a/app/views/profiles/groups/index.html.haml b/app/views/dashboard/groups/index.html.haml
index daf76636ff2..f7df5352512 100644
--- a/app/views/profiles/groups/index.html.haml
+++ b/app/views/dashboard/groups/index.html.haml
@@ -23,10 +23,11 @@
Settings
- if can?(current_user, :destroy, user_group)
- = link_to leave_profile_group_path(group), data: { confirm: leave_group_message(group.name) }, method: :delete, class: "btn-small btn btn-grouped", title: 'Remove user from group' do
+ = link_to leave_dashboard_group_path(group), data: { confirm: leave_group_message(group.name) }, method: :delete, class: "btn-small btn btn-grouped", title: 'Remove user from group' do
%i.fa.fa-sign-out
Leave
+ = image_tag group_icon(group), class: "avatar s40 avatar-tile"
= link_to group, class: 'group-name' do
%strong= group.name
diff --git a/app/views/dashboard/milestones/index.html.haml b/app/views/dashboard/milestones/index.html.haml
index 65fc5898518..caf3b685864 100644
--- a/app/views/dashboard/milestones/index.html.haml
+++ b/app/views/dashboard/milestones/index.html.haml
@@ -28,8 +28,7 @@
= pluralize milestone.merge_requests_count, 'Merge Request'
&nbsp;
%span.light #{milestone.percent_complete}% complete
- .progress.progress-info
- .progress-bar{style: "width: #{milestone.percent_complete}%;"}
+ = milestone_progress_bar(milestone)
%div
%br
- milestone.milestones.each do |milestone|
diff --git a/app/views/dashboard/milestones/show.html.haml b/app/views/dashboard/milestones/show.html.haml
index a45a52001be..57cce9ab749 100644
--- a/app/views/dashboard/milestones/show.html.haml
+++ b/app/views/dashboard/milestones/show.html.haml
@@ -39,8 +39,7 @@
#{@dashboard_milestone.closed_items_count} closed
&ndash;
#{@dashboard_milestone.open_items_count} open
- .progress.progress-info
- .progress-bar{style: "width: #{@dashboard_milestone.percent_complete}%;"}
+ = milestone_progress_bar(@dashboard_milestone)
%ul.nav.nav-tabs
%li.active
diff --git a/app/views/dashboard/projects.html.haml b/app/views/dashboard/projects.html.haml
deleted file mode 100644
index 03d4b3d8bbb..00000000000
--- a/app/views/dashboard/projects.html.haml
+++ /dev/null
@@ -1,60 +0,0 @@
-%h3.page-title
- My Projects
-
- = link_to new_project_path, class: "btn btn-new pull-right" do
- %i.fa.fa-plus
- New Project
-
-%p.light
- All projects you have access to are listed here. Public projects are not included here unless you are a member
-%hr
-.side-filters
- = render "projects_filter"
-.dash-projects
- %ul.bordered-list.my-projects.top-list
- - @projects.each do |project|
- %li.my-project-row
- %h4.project-title
- .pull-left
- = project_icon(project, alt: '', class: 'avatar project-avatar s60')
- .project-access-icon
- = visibility_level_icon(project.visibility_level)
- = link_to project_path(project), class: dom_class(project) do
- %strong= project.name_with_namespace
-
- - if project.forked_from_project
- &nbsp;
- %small
- %i.fa.fa-code-fork
- Forked from:
- = link_to project.forked_from_project.name_with_namespace, namespace_project_path(project.namespace, project.forked_from_project)
-
- - if current_user.can_leave_project?(project)
- .pull-right
- = link_to leave_namespace_project_team_members_path(project.namespace, project), data: { confirm: "Leave project?"}, method: :delete, remote: true, class: "btn-tiny btn remove-row", title: 'Leave project' do
- %i.fa.fa-sign-out
- Leave
-
- .project-info
- .pull-right
- - if project.archived?
- %span.label
- %i.fa.fa-archive
- Archived
- - project.tags.each do |tag|
- %span.label.label-info
- %i.fa.fa-tag
- = tag.name
- - if project.description.present?
- %p= truncate project.description, length: 100
- .last-activity
- %span.light Last activity:
- %span.date= project_last_activity(project)
-
-
- - if @projects.blank?
- %li
- .nothing-here-block There are no projects here.
- .bottom
- = paginate @projects, theme: "gitlab"
-
diff --git a/app/views/dashboard/projects/starred.html.haml b/app/views/dashboard/projects/starred.html.haml
new file mode 100644
index 00000000000..94de6092563
--- /dev/null
+++ b/app/views/dashboard/projects/starred.html.haml
@@ -0,0 +1,23 @@
+- if @projects.any?
+ .dashboard.row
+ %section.activities.col-md-8
+ = render 'dashboard/activities'
+ %aside.col-md-4
+ .panel.panel-default
+ .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?
+ .input-group-addon.dash-new-project
+ = link_to new_project_path do
+ %strong New project
+
+ = render 'shared/projects_list', projects: @projects,
+ projects_limit: 20, stars: true, avatar: false
+
+ = link_to '#aside', class: 'show-aside' do
+ %i.fa.fa-angle-left
+
+- else
+ %h3 You dont 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 f973f4829a0..fa8946011b7 100644
--- a/app/views/dashboard/show.html.haml
+++ b/app/views/dashboard/show.html.haml
@@ -1,4 +1,4 @@
-- if @has_authorized_projects
+- if @projects.any?
.dashboard.row
%section.activities.col-md-8
= render 'activities'
diff --git a/app/views/events/_events.html.haml b/app/views/events/_events.html.haml
index 3d62d478869..68c19df092d 100644
--- a/app/views/events/_events.html.haml
+++ b/app/views/events/_events.html.haml
@@ -1 +1 @@
-= render @events
+= render partial: 'events/event', collection: @events
diff --git a/app/views/explore/projects/_filter.html.haml b/app/views/explore/projects/_filter.html.haml
new file mode 100644
index 00000000000..b3963a9d901
--- /dev/null
+++ b/app/views/explore/projects/_filter.html.haml
@@ -0,0 +1,67 @@
+.pull-left
+ = form_tag explore_projects_filter_path, method: :get, class: 'form-inline form-tiny' do |f|
+ .form-group
+ = search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input input-mn-300", id: "projects_search"
+ .form-group
+ = button_tag 'Search', class: "btn btn-primary wide"
+
+.pull-right.hidden-sm.hidden-xs
+ - if current_user
+ .dropdown.inline.append-right-10
+ %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
+ %i.fa.fa-globe
+ %span.light Visibility:
+ - if params[:visibility_level].present?
+ = visibility_level_label(params[:visibility_level].to_i)
+ - else
+ Any
+ %b.caret
+ %ul.dropdown-menu
+ %li
+ = link_to explore_projects_filter_path(visibility_level: nil) do
+ Any
+ - Gitlab::VisibilityLevel.values.each do |level|
+ %li{ class: (level.to_s == params[:visibility_level]) ? 'active' : 'light' }
+ = link_to explore_projects_filter_path(visibility_level: level) do
+ = visibility_level_icon(level)
+ = visibility_level_label(level)
+
+ - if @tags.present?
+ .dropdown.inline.append-right-10
+ %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
+ %i.fa.fa-tags
+ %span.light Tags:
+ - if params[:tag].present?
+ = params[:tag]
+ - else
+ Any
+ %b.caret
+ %ul.dropdown-menu
+ %li
+ = link_to explore_projects_filter_path(tag: nil) do
+ Any
+
+ - @tags.each do |tag|
+ %li{ class: (tag.name == params[:tag]) ? 'active' : 'light' }
+ = link_to explore_projects_filter_path(tag: tag.name) do
+ %i.fa.fa-tag
+ = tag.name
+
+ .dropdown.inline
+ %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
+ %span.light sort:
+ - if @sort.present?
+ = sort_options_hash[@sort]
+ - else
+ = sort_title_recently_created
+ %b.caret
+ %ul.dropdown-menu
+ %li
+ = link_to explore_projects_filter_path(sort: sort_value_recently_created) do
+ = sort_title_recently_created
+ = link_to explore_projects_filter_path(sort: sort_value_oldest_created) do
+ = sort_title_oldest_created
+ = link_to explore_projects_filter_path(sort: sort_value_recently_updated) do
+ = sort_title_recently_updated
+ = link_to explore_projects_filter_path(sort: sort_value_oldest_updated) do
+ = sort_title_oldest_updated
diff --git a/app/views/explore/projects/index.html.haml b/app/views/explore/projects/index.html.haml
index cb93b300d6a..5086b58cd03 100644
--- a/app/views/explore/projects/index.html.haml
+++ b/app/views/explore/projects/index.html.haml
@@ -1,30 +1,5 @@
.clearfix
- .pull-left
- = form_tag explore_projects_path, method: :get, class: 'form-inline form-tiny' do |f|
- .form-group
- = search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input input-mn-300", id: "projects_search"
- .form-group
- = button_tag 'Search', class: "btn btn-primary wide"
-
- .pull-right
- .dropdown.inline
- %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
- %span.light sort:
- - if @sort.present?
- = sort_options_hash[@sort]
- - else
- = sort_title_recently_created
- %b.caret
- %ul.dropdown-menu
- %li
- = link_to explore_projects_path(sort: sort_value_recently_created) do
- = sort_title_recently_created
- = link_to explore_projects_path(sort: sort_value_oldest_created) do
- = sort_title_oldest_created
- = link_to explore_projects_path(sort: sort_value_recently_updated) do
- = sort_title_recently_updated
- = link_to explore_projects_path(sort: sort_value_oldest_updated) do
- = sort_title_oldest_updated
+ = render 'filter'
%hr
.public-projects
diff --git a/app/views/groups/_projects.html.haml b/app/views/groups/_projects.html.haml
index b505760fa8f..6f53e125c47 100644
--- a/app/views/groups/_projects.html.haml
+++ b/app/views/groups/_projects.html.haml
@@ -1,23 +1,10 @@
.panel.panel-default
- .panel-heading
- Projects (#{projects.count})
- - if can? current_user, :create_projects, @group
- .panel-head-actions
- = link_to new_project_path(namespace_id: @group.id), class: "btn btn-new" do
- %i.fa.fa-plus
- New project
- %ul.well-list
- - if projects.blank?
- .nothing-here-block This group has no projects yet
- - projects.each do |project|
- %li.project-row
- = link_to project_path(project), class: dom_class(project) do
- .dash-project-avatar
- = project_icon(project, alt: '', class: 'avatar s40')
- .dash-project-access-icon
- = visibility_level_icon(project.visibility_level)
- %span.str-truncated
- %span.project-name
- = project.name
- %span.arrow
- %i.fa.fa-angle-right
+ .panel-heading.clearfix
+ .input-group
+ = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control'
+ - if can? current_user, :create_projects, @group
+ .input-group-addon.dash-new-project
+ = link_to new_project_path(namespace_id: @group.id) do
+ %strong New project
+
+ = render 'shared/projects_list', projects: @projects, projects_limit: 20
diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml
index c4eb00e8925..838290e4aca 100644
--- a/app/views/groups/edit.html.haml
+++ b/app/views/groups/edit.html.haml
@@ -12,7 +12,7 @@
.form-group
.col-sm-2
.col-sm-10
- = image_tag group_icon(@group.to_param), alt: '', class: 'avatar group-avatar s160'
+ = image_tag group_icon(@group), alt: '', class: 'avatar group-avatar s160'
%p.light
- if @group.avatar?
You can change your group avatar here
diff --git a/app/views/groups/group_members/_group_member.html.haml b/app/views/groups/group_members/_group_member.html.haml
index 21029c3a07d..6267006f63f 100644
--- a/app/views/groups/group_members/_group_member.html.haml
+++ b/app/views/groups/group_members/_group_member.html.haml
@@ -8,6 +8,9 @@
%span.cgray= user.username
- if user == current_user
%span.label.label-success It's you
+ - if user.blocked?
+ %label.label.label-danger
+ %strong Blocked
- if show_roles
%span.pull-right
@@ -19,7 +22,7 @@
%i.fa.fa-pencil-square-o
- if can?(current_user, :destroy, member)
- if current_user == member.user
- = link_to leave_profile_group_path(@group), data: { confirm: leave_group_message(@group.name)}, method: :delete, class: "btn-tiny btn btn-remove", title: 'Remove user from group' do
+ = link_to leave_dashboard_group_path(@group), data: { confirm: leave_group_message(@group.name)}, method: :delete, class: "btn-tiny btn btn-remove", title: 'Remove user from group' do
%i.fa.fa-minus.fa-inverse
- else
= link_to group_group_member_path(@group, member), data: { confirm: remove_user_from_group_message(@group, user) }, method: :delete, remote: true, class: "btn-tiny btn btn-remove", title: 'Remove user from group' do
diff --git a/app/views/groups/milestones/index.html.haml b/app/views/groups/milestones/index.html.haml
index fcbcb309aa7..9febaab04a7 100644
--- a/app/views/groups/milestones/index.html.haml
+++ b/app/views/groups/milestones/index.html.haml
@@ -36,8 +36,7 @@
= pluralize milestone.merge_requests_count, 'Merge Request'
&nbsp;
%span.light #{milestone.percent_complete}% complete
- .progress.progress-info
- .progress-bar{style: "width: #{milestone.percent_complete}%;"}
+ = milestone_progress_bar(milestone)
%div
%br
- milestone.milestones.each do |milestone|
diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml
index e3606d167ad..dd2d84499ba 100644
--- a/app/views/groups/milestones/show.html.haml
+++ b/app/views/groups/milestones/show.html.haml
@@ -45,8 +45,7 @@
#{@group_milestone.closed_items_count} closed
&ndash;
#{@group_milestone.open_items_count} open
- .progress.progress-info
- .progress-bar{style: "width: #{@group_milestone.percent_complete}%;"}
+ = milestone_progress_bar(@group_milestone)
%ul.nav.nav-tabs
%li.active
diff --git a/app/views/groups/projects.html.haml b/app/views/groups/projects.html.haml
index 8c829654fb0..c95347b3a55 100644
--- a/app/views/groups/projects.html.haml
+++ b/app/views/groups/projects.html.haml
@@ -4,7 +4,7 @@
projects:
- if can? current_user, :manage_group, @group
.panel-head-actions
- = link_to new_project_path(namespace_id: @group.id), class: "btn btn-new" do
+ = link_to new_project_path(namespace_id: @group.id), class: "btn btn-sm btn-success" do
%i.fa.fa-plus
New Project
%ul.well-list
diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml
index a453889f744..25efe973d4f 100644
--- a/app/views/groups/show.html.haml
+++ b/app/views/groups/show.html.haml
@@ -1,6 +1,6 @@
.dashboard
%div
- = image_tag group_icon(@group.path), class: "avatar group-avatar s90"
+ = image_tag group_icon(@group), class: "avatar group-avatar s90"
.clearfix
%h2
= @group.name
diff --git a/app/views/help/ui.html.haml b/app/views/help/ui.html.haml
new file mode 100644
index 00000000000..58de5b7c869
--- /dev/null
+++ b/app/views/help/ui.html.haml
@@ -0,0 +1,208 @@
+- lorem = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed fermentum nisi sapien, non consequat lectus aliquam ultrices. Suspendisse sodales est euismod nunc condimentum, a consectetur diam ornare."
+
+.gitlab-ui-dev-kit
+ %h1 GitLab UI development kit
+ %p.light
+ Use page inspector in your browser to check element classes and structure
+ of examples below.
+ %hr
+ %ul
+ %li
+ = link_to 'Blocks', '#blocks'
+ %li
+ = link_to 'Lists', '#lists'
+ %li
+ = link_to 'Tables', '#tables'
+ %li
+ = link_to 'Buttons', '#buttons'
+ %li
+ = link_to 'Panels', '#panels'
+ %li
+ = link_to 'Alerts', '#alerts'
+ %li
+ = link_to 'Forms', '#forms'
+ %li
+ = link_to 'Markdown', '#markdown'
+
+ %h2#blocks Blocks
+
+ %h3
+ %code .well
+
+
+ .well
+ %h4 Something
+ = lorem
+
+
+ %h2#lists Lists
+
+ %h3
+ %code .well-list
+ %ul.well-list
+ %li
+ One item
+ %li
+ One item
+ %li
+ One item
+
+ %h3
+ %code .panel .well-list
+
+ .panel.panel-default
+ .panel-heading My list
+ %ul.well-list
+ %li
+ One item
+ %li
+ One item
+ %li
+ One item
+
+ %h3
+ %code .bordered-list
+ %ul.bordered-list
+ %li
+ One item
+ %li
+ One item
+ %li
+ One item
+
+
+
+ %h2#tables Tables
+
+ .example
+ %table.table
+ %thead
+ %tr
+ %th #
+ %th First Name
+ %th Last Name
+ %th Username
+ %tbody
+ %tr
+ %td 1
+ %td Mark
+ %td Otto
+ %td @mdo
+ %tr
+ %td 2
+ %td Jacob
+ %td Thornton
+ %td @fat
+ %tr
+ %td 3
+ %td Larry
+ %td the Bird
+ %td @twitter
+
+
+ %h2#buttons Buttons
+
+ .example
+ %button.btn.btn-default{:type => "button"} Default
+ %button.btn.btn-primary{:type => "button"} Primary
+ %button.btn.btn-success{:type => "button"} Success
+ %button.btn.btn-info{:type => "button"} Info
+ %button.btn.btn-warning{:type => "button"} Warning
+ %button.btn.btn-danger{:type => "button"} Danger
+ %button.btn.btn-link{:type => "button"} Link
+
+ %h2#panels Panels
+
+ .row
+ .col-md-6
+ .panel.panel-success
+ .panel-heading Success
+ .panel-body
+ = lorem
+ .panel.panel-primary
+ .panel-heading Primary
+ .panel-body
+ = lorem
+ .panel.panel-info
+ .panel-heading Info
+ .panel-body
+ = lorem
+ .col-md-6
+ .panel.panel-warning
+ .panel-heading Warning
+ .panel-body
+ = lorem
+ .panel.panel-danger
+ .panel-heading Danger
+ .panel-body
+ = lorem
+
+ %h2#alert Alerts
+
+ .row
+ .col-md-6
+ .alert.alert-success
+ = lorem
+ .alert.alert-primary
+ = lorem
+ .alert.alert-info
+ = lorem
+ .col-md-6
+ .alert.alert-warning
+ = lorem
+ .alert.alert-danger
+ = lorem
+
+ %h2#forms Forms
+
+ %h3
+ %code form.horizontal-form
+
+ %form.form-horizontal
+ .form-group
+ %label.col-sm-2.control-label{:for => "inputEmail3"} Email
+ .col-sm-10
+ %input#inputEmail3.form-control{:placeholder => "Email", :type => "email"}/
+ .form-group
+ %label.col-sm-2.control-label{:for => "inputPassword3"} Password
+ .col-sm-10
+ %input#inputPassword3.form-control{:placeholder => "Password", :type => "password"}/
+ .form-group
+ .col-sm-offset-2.col-sm-10
+ .checkbox
+ %label
+ %input{:type => "checkbox"}/
+ Remember me
+ .form-group
+ .col-sm-offset-2.col-sm-10
+ %button.btn.btn-default{:type => "submit"} Sign in
+
+ %h3
+ %code form
+
+ %form
+ .form-group
+ %label{:for => "exampleInputEmail1"} Email address
+ %input#exampleInputEmail1.form-control{:placeholder => "Enter email", :type => "email"}/
+ .form-group
+ %label{:for => "exampleInputPassword1"} Password
+ %input#exampleInputPassword1.form-control{:placeholder => "Password", :type => "password"}/
+ .checkbox
+ %label
+ %input{:type => "checkbox"}/
+ Remember me
+ %button.btn.btn-default{:type => "submit"} Sign in
+
+ %h2#markdown Markdown
+ %h3
+ %code .md or .wiki and others
+
+ Markdown rendering has a bit different css and presented in next UI elements:
+
+ %ul
+ %li comment
+ %li issue, merge request description
+ %li wiki page
+ %li help page
+
+ You can check how markdown rendered at #{link_to 'Markdown help page', help_page_path("markdown", "markdown")}.
diff --git a/app/views/import/bitbucket/status.html.haml b/app/views/import/bitbucket/status.html.haml
index bcbbaadf3e0..9da3c920c62 100644
--- a/app/views/import/bitbucket/status.html.haml
+++ b/app/views/import/bitbucket/status.html.haml
@@ -23,7 +23,7 @@
%strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
%td.job-status
- if project.import_status == 'finished'
- %span.cgreen
+ %span
%i.fa.fa-check
done
- elsif project.import_status == 'started'
diff --git a/app/views/import/github/status.html.haml b/app/views/import/github/status.html.haml
index 883090a3026..9c4d91013ec 100644
--- a/app/views/import/github/status.html.haml
+++ b/app/views/import/github/status.html.haml
@@ -23,7 +23,7 @@
%strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
%td.job-status
- if project.import_status == 'finished'
- %span.cgreen
+ %span
%i.fa.fa-check
done
- elsif project.import_status == 'started'
diff --git a/app/views/import/gitlab/status.html.haml b/app/views/import/gitlab/status.html.haml
index 41ac073eae1..e809643d8d4 100644
--- a/app/views/import/gitlab/status.html.haml
+++ b/app/views/import/gitlab/status.html.haml
@@ -23,7 +23,7 @@
%strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
%td.job-status
- if project.import_status == 'finished'
- %span.cgreen
+ %span
%i.fa.fa-check
done
- elsif project.import_status == 'started'
diff --git a/app/views/import/gitorious/status.html.haml b/app/views/import/gitorious/status.html.haml
index ebe24747a05..645241a6c69 100644
--- a/app/views/import/gitorious/status.html.haml
+++ b/app/views/import/gitorious/status.html.haml
@@ -23,7 +23,7 @@
%strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
%td.job-status
- if project.import_status == 'finished'
- %span.cgreen
+ %span
%i.fa.fa-check
done
- elsif project.import_status == 'started'
diff --git a/app/views/layouts/_public_head_panel.html.haml b/app/views/layouts/_public_head_panel.html.haml
index bd6bb3c720d..3d6d2bfc00a 100644
--- a/app/views/layouts/_public_head_panel.html.haml
+++ b/app/views/layouts/_public_head_panel.html.haml
@@ -12,7 +12,7 @@
- unless current_controller?('sessions')
.pull-right.hidden-xs
- = link_to "Sign in", new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in btn-new'
+ = link_to "Sign in", new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in btn-new append-right-10'
.navbar-collapse.collapse
%ul.nav.navbar-nav
diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml
index 304744ba251..e4f630c6a18 100644
--- a/app/views/layouts/nav/_dashboard.html.haml
+++ b/app/views/layouts/nav/_dashboard.html.haml
@@ -3,12 +3,17 @@
= link_to root_path, title: 'Home', class: 'shortcuts-activity' do
%i.fa.fa-dashboard
%span
- Activity
- = nav_link(path: 'dashboard#projects') do
- = link_to projects_dashboard_path, title: 'Projects', class: 'shortcuts-projects' do
- %i.fa.fa-cube
+ Your Projects
+ = nav_link(path: 'projects#starred') do
+ = link_to starred_dashboard_projects_path, title: 'Starred Projects' do
+ %i.fa.fa-star
%span
- Projects
+ Starred Projects
+ = nav_link(controller: :groups) do
+ = link_to dashboard_groups_path, title: 'Groups' do
+ %i.fa.fa-group
+ %span
+ Groups
= nav_link(controller: :milestones) do
= link_to dashboard_milestones_path, title: 'Milestones' do
%i.fa.fa-clock-o
@@ -31,4 +36,3 @@
%i.fa.fa-question-circle
%span
Help
-
diff --git a/app/views/layouts/nav/_profile.html.haml b/app/views/layouts/nav/_profile.html.haml
index 0914d2a167a..d88e862829d 100644
--- a/app/views/layouts/nav/_profile.html.haml
+++ b/app/views/layouts/nav/_profile.html.haml
@@ -43,11 +43,6 @@
%i.fa.fa-image
%span
Design
- = nav_link(controller: :groups) do
- = link_to profile_groups_path, title: 'Groups' do
- %i.fa.fa-group
- %span
- Groups
= nav_link(path: 'profiles#history') do
= link_to history_profile_path, title: 'History' do
%i.fa.fa-history
diff --git a/app/views/layouts/notify.html.haml b/app/views/layouts/notify.html.haml
index 8cca80e5248..7eec93abdf6 100644
--- a/app/views/layouts/notify.html.haml
+++ b/app/views/layouts/notify.html.haml
@@ -16,6 +16,18 @@
font-size:small;
color:#777
}
+ pre.commit-message {
+ white-space: pre-wrap;
+ }
+ .file-stats a {
+ text-decoration: none;
+ }
+ .file-stats .new-file {
+ color: #090;
+ }
+ .file-stats .deleted-file {
+ color: #B00;
+ }
#{add_email_highlight_css}
%body
%div.content
@@ -27,5 +39,5 @@
- if @target_url
#{link_to "View it on GitLab", @target_url}
= email_action @target_url
- - if @project
+ - if @project && !@disable_footer
You're receiving this notification because you are a member of the #{link_to_unless @target_url, @project.name_with_namespace, namespace_project_url(@project.namespace, @project)} project team.
diff --git a/app/views/notify/_note_message.html.haml b/app/views/notify/_note_message.html.haml
index 5272dfa0ede..778a78acf56 100644
--- a/app/views/notify/_note_message.html.haml
+++ b/app/views/notify/_note_message.html.haml
@@ -1,2 +1,2 @@
%div
- = markdown(@note.note)
+ = replace_image_links_with_base64(markdown(@note.note), @note.project)
diff --git a/app/views/notify/new_issue_email.html.haml b/app/views/notify/new_issue_email.html.haml
index f2f8eee18c4..03cbee94608 100644
--- a/app/views/notify/new_issue_email.html.haml
+++ b/app/views/notify/new_issue_email.html.haml
@@ -1,5 +1,5 @@
-if @issue.description
- = markdown(@issue.description)
+ = replace_image_links_with_base64(markdown(@issue.description), @issue.project)
- if @issue.assignee_id.present?
%p
diff --git a/app/views/notify/new_merge_request_email.html.haml b/app/views/notify/new_merge_request_email.html.haml
index f02d5111b22..729a7bb505d 100644
--- a/app/views/notify/new_merge_request_email.html.haml
+++ b/app/views/notify/new_merge_request_email.html.haml
@@ -6,4 +6,4 @@
Assignee: #{@merge_request.author_name} &rarr; #{@merge_request.assignee_name}
-if @merge_request.description
- = markdown(@merge_request.description)
+ = replace_image_links_with_base64(markdown(@merge_request.description), @merge_request.project)
diff --git a/app/views/notify/repository_push_email.html.haml b/app/views/notify/repository_push_email.html.haml
index a45d1dedcd1..039b92df2be 100644
--- a/app/views/notify/repository_push_email.html.haml
+++ b/app/views/notify/repository_push_email.html.haml
@@ -1,6 +1,12 @@
%h3 #{@author.name} pushed to #{@branch} at #{link_to @project.name_with_namespace, namespace_project_url(@project.namespace, @project)}
-%h4 Commits:
+- if @reverse_compare
+ %p
+ %strong WARNING:
+ The push did not contain any new commits, but force pushed to delete the commits and changes below.
+
+%h4
+ = @reverse_compare ? "Deleted commits:" : "Commits:"
%ul
- @commits.each do |commit|
@@ -9,22 +15,52 @@
%div
%span by #{commit.author_name}
%i at #{commit.committed_date.strftime("%Y-%m-%dT%H:%M:%SZ")}
- %pre #{commit.safe_message}
+ %pre.commit-message
+ = commit.safe_message
+
+%h4 #{pluralize @diffs.count, "changed file"}:
+
+%ul
+ - @diffs.each_with_index do |diff, i|
+ %li.file-stats
+ %a{href: "#{@target_url if @disable_diffs}#diff-#{i}" }
+ - if diff.deleted_file
+ %span.deleted-file
+ &minus;
+ = diff.old_path
+ - elsif diff.renamed_file
+ = diff.old_path
+ &rarr;
+ = diff.new_path
+ - elsif diff.new_file
+ %span.new-file
+ &plus;
+ = diff.new_path
+ - else
+ = diff.new_path
-%h4 Changes:
-- @diffs.each do |diff|
- %li
- %strong
- - if diff.old_path == diff.new_path
- = diff.new_path
- - elsif diff.new_path && diff.old_path
- #{diff.old_path} &rarr; #{diff.new_path}
- - else
- = diff.new_path || diff.old_path
- %hr
- %pre
- = color_email_diff(diff.diff)
- %br
+- unless @disable_diffs
+ %h4 Changes:
+ - @diffs.each_with_index do |diff, i|
+ %li{id: "diff-#{i}"}
+ %a{href: @target_url + "#diff-#{i}"}
+ - if diff.deleted_file
+ %strong
+ = diff.old_path
+ deleted
+ - elsif diff.renamed_file
+ %strong
+ = diff.old_path
+ &rarr;
+ %strong
+ = diff.new_path
+ - else
+ %strong
+ = diff.new_path
+ %hr
+ %pre
+ = color_email_diff(diff.diff)
+ %br
- if @compare.timeout
%h5 Huge diff. To prevent performance issues changes are hidden
diff --git a/app/views/notify/repository_push_email.text.haml b/app/views/notify/repository_push_email.text.haml
index fa355cb5269..8d67a42234e 100644
--- a/app/views/notify/repository_push_email.text.haml
+++ b/app/views/notify/repository_push_email.text.haml
@@ -1,25 +1,47 @@
-#{@author.name} pushed to #{@branch} at #{link_to @project.name_with_namespace, namespace_project_url(@project.namespace, @project)}
-
+#{@author.name} pushed to #{@branch} at #{@project.name_with_namespace}
\
-Commits:
+\
+- if @reverse_compare
+ WARNING: The push did not contain any new commits, but force pushed to delete the commits and changes below.
+ \
+ \
+= @reverse_compare ? "Deleted commits:" : "Commits:"
- @commits.each do |commit|
- #{link_to commit.short_id, namespace_project_commit_url(@project.namespace, @project, commit)} by #{commit.author_name}
+ #{commit.short_id} by #{commit.author_name} at #{commit.committed_date.strftime("%Y-%m-%dT%H:%M:%SZ")}
#{commit.safe_message}
\- - - - -
\
\
-Changes:
+#{pluralize @diffs.count, "changed file"}:
+\
- @diffs.each do |diff|
- \
- \=====================================
- - if diff.old_path == diff.new_path
- = diff.new_path
- - elsif diff.new_path && diff.old_path
- #{diff.old_path} &rarr; #{diff.new_path}
+ - if diff.deleted_file
+ \- − #{diff.old_path}
+ - elsif diff.renamed_file
+ \- #{diff.old_path} → #{diff.new_path}
+ - elsif diff.new_file
+ \- + #{diff.new_path}
- else
- = diff.new_path || diff.old_path
- \=====================================
- != diff.diff
-\
+ \- #{diff.new_path}
+- unless @disable_diffs
+ \
+ \
+ Changes:
+ - @diffs.each do |diff|
+ \
+ \=====================================
+ - if diff.deleted_file
+ #{diff.old_path} deleted
+ - elsif diff.renamed_file
+ #{diff.old_path} → #{diff.new_path}
+ - else
+ = diff.new_path
+ \=====================================
+ != diff.diff
- if @compare.timeout
+ \
+ \
Huge diff. To prevent performance issues it was hidden
+\
+\
+View it on GitLab: #{@target_url}
diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml
index f124637c07b..6bafcb56551 100644
--- a/app/views/profiles/accounts/show.html.haml
+++ b/app/views/profiles/accounts/show.html.haml
@@ -57,7 +57,7 @@
%p.light
= user_url(@user)
%div
- = f.submit 'Save username', class: "btn btn-save"
+ = f.submit 'Save username', class: "btn btn-warning"
- if show_profile_remove_tab?
%fieldset.remove-account
diff --git a/app/views/profiles/design.html.haml b/app/views/profiles/design.html.haml
index 8d09595fd4f..cc00d08d03b 100644
--- a/app/views/profiles/design.html.haml
+++ b/app/views/profiles/design.html.haml
@@ -33,6 +33,11 @@
.prev.violet
= f.radio_button :theme_id, 5
Violet
+
+ = label_tag do
+ .prev.blue
+ = f.radio_button :theme_id, 6
+ Blue
%br
.clearfix
diff --git a/app/views/profiles/notifications/show.html.haml b/app/views/profiles/notifications/show.html.haml
index e3cd323927e..6cf5c81c19e 100644
--- a/app/views/profiles/notifications/show.html.haml
+++ b/app/views/profiles/notifications/show.html.haml
@@ -50,7 +50,7 @@
%p You will receive all notifications from projects in which you participate
.form-actions
- = f.submit 'Save changes', class: "btn btn-save"
+ = f.submit 'Save changes', class: "btn btn-create"
.clearfix
%hr
diff --git a/app/views/profiles/passwords/edit.html.haml b/app/views/profiles/passwords/edit.html.haml
index 3b1ebbfaf59..4b04b113e89 100644
--- a/app/views/profiles/passwords/edit.html.haml
+++ b/app/views/profiles/passwords/edit.html.haml
@@ -35,4 +35,4 @@
.col-sm-10
= f.password_field :password_confirmation, required: true, class: 'form-control'
.form-actions
- = f.submit 'Save password', class: "btn btn-save"
+ = f.submit 'Save password', class: "btn btn-create"
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index b2808c46c00..1a7bc353bf3 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -56,7 +56,7 @@
.form-group
= f.label :bio, class: "control-label"
.col-sm-10
- = f.text_area :bio, rows: 6, class: "form-control", maxlength: 250
+ = f.text_area :bio, rows: 4, class: "form-control", maxlength: 250
%span.help-block Tell us about yourself in fewer than 250 characters.
.col-md-5
@@ -89,10 +89,12 @@
= link_to 'Remove avatar', profile_avatar_path, data: { confirm: "Avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-small remove-avatar"
- if @user.public_profile?
- .bs-callout.bs-callout-info
+ .alert.alert-info
%h4 Public profile
%p Your profile is publicly visible because you joined public project(s)
- .form-actions
- = f.submit 'Save changes', class: "btn btn-save"
+ .row
+ .col-md-7
+ .col-sm-2
+ = f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/projects/_bitbucket_import_modal.html.haml b/app/views/projects/_bitbucket_import_modal.html.haml
index 5c52f91927d..07d4d602769 100644
--- a/app/views/projects/_bitbucket_import_modal.html.haml
+++ b/app/views/projects/_bitbucket_import_modal.html.haml
@@ -10,4 +10,4 @@
you need to
- else
your GitLab administrator needs to
- == #{link_to 'setup OAuth integration', 'https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/integration/butbucket.md'}. \ No newline at end of file
+ == #{link_to 'setup OAuth integration', 'https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/integration/bitbucket.md'}.
diff --git a/app/views/projects/_issuable_form.html.haml b/app/views/projects/_issuable_form.html.haml
index bfacab5e48e..a7cd129b631 100644
--- a/app/views/projects/_issuable_form.html.haml
+++ b/app/views/projects/_issuable_form.html.haml
@@ -50,6 +50,7 @@
= f.select(:milestone_id, milestone_options(issuable),
{ include_blank: 'Select milestone' }, { class: 'select2' })
- else
+ .prepend-top-10
%span.light No open milestones available.
&nbsp;
- if can? current_user, :admin_milestone, issuable.project
@@ -63,6 +64,7 @@
= f.collection_select :label_ids, issuable.project.labels.all, :id, :name,
{ selected: issuable.label_ids }, multiple: true, class: 'select2'
- else
+ .prepend-top-10
%span.light No labels yet.
&nbsp;
- if can? current_user, :admin_label, issuable.project
diff --git a/app/views/projects/blob/_blob.html.haml b/app/views/projects/blob/_blob.html.haml
index 64cc3fad6cf..9ff61f3887f 100644
--- a/app/views/projects/blob/_blob.html.haml
+++ b/app/views/projects/blob/_blob.html.haml
@@ -15,7 +15,7 @@
- else
= link_to title, '#'
-%ul.blob-commit-info.bs-callout.bs-callout-info.hidden-xs
+%ul.blob-commit-info.well.hidden-xs
- blob_commit = @repository.last_commit_for_path(@commit.id, blob.path)
= render blob_commit, project: @project
diff --git a/app/views/projects/diffs/_stats.html.haml b/app/views/projects/diffs/_stats.html.haml
index 20e51d18da5..9b5eb84a86d 100644
--- a/app/views/projects/diffs/_stats.html.haml
+++ b/app/views/projects/diffs/_stats.html.haml
@@ -26,7 +26,7 @@
%a{href: "#diff-#{i}"}
%i.fa.fa-minus
= diff.old_path
- \->
+ &rarr;
= diff.new_path
- elsif diff.new_file
%span.new-file
diff --git a/app/views/projects/diffs/_warning.html.haml b/app/views/projects/diffs/_warning.html.haml
index c9a6b3ebd9e..af1f342afbd 100644
--- a/app/views/projects/diffs/_warning.html.haml
+++ b/app/views/projects/diffs/_warning.html.haml
@@ -1,4 +1,4 @@
-.bs-callout.bs-callout-warning
+.alert.alert-warning
%h4
Too many changes.
.pull-right
diff --git a/app/views/projects/imports/new.html.haml b/app/views/projects/imports/new.html.haml
index 097374e1128..f1248ac2af5 100644
--- a/app/views/projects/imports/new.html.haml
+++ b/app/views/projects/imports/new.html.haml
@@ -12,7 +12,7 @@
%span Import existing git repo
.col-sm-10
= f.text_field :import_url, class: 'form-control', placeholder: 'https://github.com/randx/six.git'
- .bs-callout.bs-callout-info
+ .alert.alert-info
This URL must be publicly accessible or you can add a username and password like this: https://username:password@gitlab.com/company/project.git.
%br
The import will time out after 4 minutes. For big repositories, use a clone/push combination.
diff --git a/app/views/projects/issues/index.html.haml b/app/views/projects/issues/index.html.haml
index 7defc8787a9..2cb94d10b6f 100644
--- a/app/views/projects/issues/index.html.haml
+++ b/app/views/projects/issues/index.html.haml
@@ -6,14 +6,7 @@
= link_to namespace_project_issues_path(@project.namespace, @project, :atom, { private_token: current_user.private_token }), class: 'btn append-right-10' do
%i.fa.fa-rss
- = form_tag namespace_project_issues_path(@project.namespace, @project), method: :get, id: "issue_search_form", class: 'pull-left issue-search-form' do
- .append-right-10.hidden-xs.hidden-sm
- = search_field_tag :issue_search, params[:issue_search], { placeholder: 'Filter by title or description', class: 'form-control issue_search search-text-input input-mn-300' }
- = hidden_field_tag :state, params['state']
- = hidden_field_tag :scope, params['scope']
- = hidden_field_tag :assignee_id, params['assignee_id']
- = hidden_field_tag :milestone_id, params['milestone_id']
- = hidden_field_tag :label_id, params['label_id']
+ = render 'shared/issuable_search_form', path: namespace_project_issues_path(@project.namespace, @project)
- if can? current_user, :write_issue, @project
= link_to new_namespace_project_issue_path(@project.namespace, @project, issue: { assignee_id: params[:assignee_id], milestone_id: params[:milestone_id]}), class: "btn btn-new pull-left", title: "New Issue", id: "new_issue_link" do
@@ -25,11 +18,11 @@
.clearfix
.issues_bulk_update.hide
= form_tag bulk_update_namespace_project_issues_path(@project.namespace, @project), method: :post do
- = select_tag('update[status]', options_for_select([['Open', 'open'], ['Closed', 'closed']]), prompt: "Status")
+ = select_tag('update[state_event]', options_for_select([['Open', 'reopen'], ['Closed', 'close']]), prompt: "Status")
= project_users_select_tag('update[assignee_id]', placeholder: 'Assignee')
= select_tag('update[milestone_id]', bulk_update_milestone_options, prompt: "Milestone")
= hidden_field_tag 'update[issues_ids]', []
- = hidden_field_tag :status, params[:status]
+ = hidden_field_tag :state_event, params[:state_event]
= button_tag "Update issues", class: "btn update_selected_issues btn-save"
.issues-holder
diff --git a/app/views/projects/labels/_form.html.haml b/app/views/projects/labels/_form.html.haml
index 95912536e42..2305fce112e 100644
--- a/app/views/projects/labels/_form.html.haml
+++ b/app/views/projects/labels/_form.html.haml
@@ -2,7 +2,7 @@
-if @label.errors.any?
.row
.col-sm-10.col-sm-offset-2
- .bs-callout.bs-callout-danger
+ .alert.alert-danger
- @label.errors.full_messages.each do |msg|
%span= msg
%br
diff --git a/app/views/projects/merge_requests/_merge_requests.html.haml b/app/views/projects/merge_requests/_merge_requests.html.haml
new file mode 100644
index 00000000000..b8a0ca9a42f
--- /dev/null
+++ b/app/views/projects/merge_requests/_merge_requests.html.haml
@@ -0,0 +1,13 @@
+.panel.panel-default
+ %ul.well-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
+ %span.cgray.pull-right #{@merge_requests.total_count} merge requests for this filter
+
+ = paginate @merge_requests, theme: "gitlab"
+
diff --git a/app/views/projects/merge_requests/_new_submit.html.haml b/app/views/projects/merge_requests/_new_submit.html.haml
index 73eccfa556e..bf80afe8785 100644
--- a/app/views/projects/merge_requests/_new_submit.html.haml
+++ b/app/views/projects/merge_requests/_new_submit.html.haml
@@ -99,11 +99,11 @@
- if @diffs.present?
= render "projects/diffs/diffs", diffs: @diffs, project: @project
- elsif @commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE
- .bs-callout.bs-callout-danger
+ .alert.alert-danger
%h4 This comparison includes more than #{MergeRequestDiff::COMMITS_SAFE_SIZE} commits.
%p To preserve performance the line changes are not shown.
- else
- .bs-callout.bs-callout-danger
+ .alert.alert-danger
%h4 This comparison includes a huge diff.
%p To preserve performance the line changes are not shown.
diff --git a/app/views/projects/merge_requests/index.html.haml b/app/views/projects/merge_requests/index.html.haml
index e3b9a28033b..d7992bdd19e 100644
--- a/app/views/projects/merge_requests/index.html.haml
+++ b/app/views/projects/merge_requests/index.html.haml
@@ -1,22 +1,11 @@
-.merge-requests-holder
- .append-bottom-10
- .pull-right
- - if can? current_user, :write_merge_request, @project
- = link_to new_namespace_project_merge_request_path(@project.namespace, @project), class: "btn btn-new pull-left", title: "New Merge Request" do
- %i.fa.fa-plus
- New Merge Request
- = render 'shared/issuable_filter'
- .panel.panel-default
- %ul.well-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
- %span.cgray.pull-right #{@merge_requests.total_count} merge requests for this filter
-
- = paginate @merge_requests, theme: "gitlab"
+.append-bottom-10
+ .pull-right
+ = render 'shared/issuable_search_form', path: namespace_project_merge_requests_path(@project.namespace, @project)
-:javascript
- $(merge_requestsPage);
+ - if can? current_user, :write_merge_request, @project
+ = link_to new_namespace_project_merge_request_path(@project.namespace, @project), class: "btn btn-new pull-left", title: "New Merge Request" do
+ %i.fa.fa-plus
+ New Merge Request
+ = render 'shared/issuable_filter'
+.merge-requests-holder
+ = render 'merge_requests'
diff --git a/app/views/projects/merge_requests/show/_diffs.html.haml b/app/views/projects/merge_requests/show/_diffs.html.haml
index cfef1d5e4cc..786b5f39063 100644
--- a/app/views/projects/merge_requests/show/_diffs.html.haml
+++ b/app/views/projects/merge_requests/show/_diffs.html.haml
@@ -3,7 +3,7 @@
- elsif @merge_request_diff.empty?
.nothing-here-block Nothing to merge from #{@merge_request.source_branch} into #{@merge_request.target_branch}
- else
- .bs-callout.bs-callout-warning
+ .alert.alert-warning
%h4
Changes view for this comparison is extremely large.
%p
diff --git a/app/views/projects/milestones/_merge_request.html.haml b/app/views/projects/milestones/_merge_request.html.haml
index 46f2df1b183..42fbd0cd2ca 100644
--- a/app/views/projects/milestones/_merge_request.html.haml
+++ b/app/views/projects/milestones/_merge_request.html.haml
@@ -3,3 +3,6 @@
= link_to [@project.namespace.becomes(Namespace), @project, merge_request] do
%span.cgray ##{merge_request.iid}
= link_to_gfm merge_request.title, [@project.namespace.becomes(Namespace), @project, merge_request], title: merge_request.title
+ .pull-right.assignee-icon
+ - if merge_request.assignee
+ = image_tag avatar_icon(merge_request.assignee.email, 16), class: "avatar s16"
diff --git a/app/views/projects/milestones/_milestone.html.haml b/app/views/projects/milestones/_milestone.html.haml
index d32b2ba271f..dcf56541db8 100644
--- a/app/views/projects/milestones/_milestone.html.haml
+++ b/app/views/projects/milestones/_milestone.html.haml
@@ -23,5 +23,4 @@
= pluralize milestone.merge_requests.count, 'Merge Request'
&nbsp;
%span.light #{milestone.percent_complete}% complete
- .progress.progress-info
- .progress-bar{style: "width: #{milestone.percent_complete}%;"}
+ = milestone_progress_bar(milestone)
diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml
index fea96f37011..110d8967342 100644
--- a/app/views/projects/milestones/show.html.haml
+++ b/app/views/projects/milestones/show.html.haml
@@ -43,8 +43,7 @@
&nbsp;
%span.light #{@milestone.percent_complete}% complete
%span.pull-right= @milestone.expires_at
- .progress.progress-info
- .progress-bar{style: "width: #{@milestone.percent_complete}%;"}
+ = milestone_progress_bar(@milestone)
%ul.nav.nav-tabs
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index 025c4fd5506..00b912742b2 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -34,7 +34,7 @@
%span Import existing git repo
.col-sm-10
= f.text_field :import_url, class: 'form-control', placeholder: 'https://github.com/randx/six.git'
- .bs-callout.bs-callout-info
+ .alert.alert-info.prepend-top-10
This URL must be publicly accessible or you can add a username and password like this: https://username:password@gitlab.com/company/project.git.
%br
The import will time out after 4 minutes. For big repositories, use a clone/push combination.
@@ -65,7 +65,7 @@
%i.fa.fa-bitbucket
Import projects from Bitbucket
= render 'bitbucket_import_modal'
-
+
- unless request.host == 'gitlab.com'
.project-import.form-group
.col-sm-2
diff --git a/app/views/projects/protected_branches/index.html.haml b/app/views/projects/protected_branches/index.html.haml
index dc20e96732e..cfe28084170 100644
--- a/app/views/projects/protected_branches/index.html.haml
+++ b/app/views/projects/protected_branches/index.html.haml
@@ -2,7 +2,7 @@
%p.light Keep stable branches secure and force developers to use Merge Requests
%hr
-.bs-callout.bs-callout-info
+.alert.alert-info
%p Protected branches are designed to
%ul
%li prevent pushes from everybody except #{link_to "masters", help_page_path("permissions", "permissions"), class: "vlink"}
diff --git a/app/views/projects/services/_form.html.haml b/app/views/projects/services/_form.html.haml
index 55ac85c32b9..3492dd5babd 100644
--- a/app/views/projects/services/_form.html.haml
+++ b/app/views/projects/services/_form.html.haml
@@ -18,7 +18,7 @@
%li= msg
- if @service.help.present?
- .bs-callout
+ .alert.alert-info
= preserve do
= markdown @service.help
@@ -47,6 +47,14 @@
%strong Tag push events
%p.light
This url will be triggered when a new tag is pushed to the repository
+ - if @service.supported_events.include?("note")
+ %div
+ = f.check_box :note_events, class: 'pull-left'
+ .prepend-left-20
+ = f.label :note_events, class: 'list-label' do
+ %strong Comments
+ %p.light
+ This url will be triggered when someone adds a comment
- if @service.supported_events.include?("issue")
%div
= f.check_box :issues_events, class: 'pull-left'
@@ -66,14 +74,16 @@
- @service.fields.each do |field|
- name = field[:name]
- - value = @service.send(name) unless field[:type] == 'password'
+ - title = field[:title] || name.humanize
+ - value = service_field_value(field[:type], @service.send(name))
- type = field[:type]
- placeholder = field[:placeholder]
- choices = field[:choices]
- default_choice = field[:default_choice]
+ - help = field[:help]
.form-group
- = f.label name, class: "control-label"
+ = f.label name, title, class: "control-label"
.col-sm-10
- if type == 'text'
= f.text_field name, class: "form-control", placeholder: placeholder
@@ -84,7 +94,9 @@
- elsif type == 'select'
= f.select name, options_for_select(choices, value ? value : default_choice), {}, { class: "form-control" }
- elsif type == 'password'
- = f.password_field name, class: 'form-control'
+ = f.password_field name, placeholder: value, class: 'form-control'
+ - if help
+ %span.help-block= help
.form-actions
= f.submit 'Save', class: 'btn btn-save'
diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml
index 787cfd9304f..74b07395650 100644
--- a/app/views/projects/show.html.haml
+++ b/app/views/projects/show.html.haml
@@ -40,7 +40,7 @@
%p Repository is read-only
- if @project.forked_from_project
- .alert.alert-success
+ .well
%i.fa.fa-code-fork.project-fork-icon
Forked from:
%br
diff --git a/app/views/projects/team_members/_team_member.html.haml b/app/views/projects/team_members/_team_member.html.haml
index 61c50af31bf..eb815447407 100644
--- a/app/views/projects/team_members/_team_member.html.haml
+++ b/app/views/projects/team_members/_team_member.html.haml
@@ -12,6 +12,7 @@
= image_tag avatar_icon(user.email, 32), class: "avatar s32"
%p
%strong= user.name
+ - if user.blocked?
+ %label.label.label-danger
+ %strong Blocked
%span.cgray= user.username
-
-
diff --git a/app/views/shared/_group_form.html.haml b/app/views/shared/_group_form.html.haml
index 5875f71bac2..b34dd53e3b5 100644
--- a/app/views/shared/_group_form.html.haml
+++ b/app/views/shared/_group_form.html.haml
@@ -15,7 +15,7 @@
= f.text_field :path, placeholder: 'open-source', class: 'form-control',
autofocus: local_assigns[:autofocus] || false
- if @group.persisted?
- .bs-callout.bs-callout-danger
+ .alert.alert-danger
%ul
%li Changing group path can have unintended side effects.
%li Renaming group path will rename directory for all related projects
diff --git a/app/views/shared/_issuable_search_form.html.haml b/app/views/shared/_issuable_search_form.html.haml
new file mode 100644
index 00000000000..639d203dcd6
--- /dev/null
+++ b/app/views/shared/_issuable_search_form.html.haml
@@ -0,0 +1,9 @@
+= form_tag(path, method: :get, id: "issue_search_form", class: 'pull-left issue-search-form') do
+ .append-right-10.hidden-xs.hidden-sm
+ = search_field_tag :issue_search, params[:issue_search], { placeholder: 'Filter by title or description', class: 'form-control issue_search search-text-input input-mn-300' }
+ = hidden_field_tag :state, params['state']
+ = hidden_field_tag :scope, params['scope']
+ = hidden_field_tag :assignee_id, params['assignee_id']
+ = hidden_field_tag :author_id, params['author_id']
+ = hidden_field_tag :milestone_id, params['milestone_id']
+ = hidden_field_tag :label_id, params['label_id']
diff --git a/app/views/shared/_no_ssh.html.haml b/app/views/shared/_no_ssh.html.haml
index 1a2946baccb..089179e677a 100644
--- a/app/views/shared/_no_ssh.html.haml
+++ b/app/views/shared/_no_ssh.html.haml
@@ -1,8 +1,8 @@
- if cookies[:hide_no_ssh_message].blank? && !current_user.hide_no_ssh_key && current_user.require_ssh_key?
.no-ssh-key-message.alert.alert-warning.hidden-xs
- You won't be able to pull or push project code via SSH until you #{link_to 'add an SSH key', new_profile_key_path} to your profile
+ You won't be able to pull or push project code via SSH until you #{link_to 'add an SSH key', new_profile_key_path, class: 'alert-link'} to your profile
.pull-right
- = link_to "Don't show again", profile_path(user: {hide_no_ssh_key: true}), method: :put
+ = link_to "Don't show again", profile_path(user: {hide_no_ssh_key: true}), method: :put, class: 'alert-link'
|
- = link_to 'Remind later', '#', class: 'hide-no-ssh-message'
+ = link_to 'Remind later', '#', class: 'hide-no-ssh-message alert-link'
diff --git a/app/views/shared/_project.html.haml b/app/views/shared/_project.html.haml
new file mode 100644
index 00000000000..8746970c239
--- /dev/null
+++ b/app/views/shared/_project.html.haml
@@ -0,0 +1,21 @@
+= cache [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')
+ .dash-project-access-icon
+ = visibility_level_icon(project.visibility_level)
+ %span.str-truncated
+ %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
+ - else
+ %span.arrow
+ %i.fa.fa-angle-right
diff --git a/app/views/shared/_projects_list.html.haml b/app/views/shared/_projects_list.html.haml
new file mode 100644
index 00000000000..4c58092af44
--- /dev/null
+++ b/app/views/shared/_projects_list.html.haml
@@ -0,0 +1,17 @@
+- 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/users/_groups.html.haml b/app/views/users/_groups.html.haml
index cb84570a6d5..f360fbb3d5d 100644
--- a/app/views/users/_groups.html.haml
+++ b/app/views/users/_groups.html.haml
@@ -1,4 +1,4 @@
.clearfix
- groups.each do |group|
= link_to group, class: 'profile-groups-avatars inline', title: group.name do
- = image_tag group_icon(group.path), class: 'avatar group-avatar s40'
+ = image_tag group_icon(group), class: 'avatar group-avatar s40'
diff --git a/app/views/users/_profile.html.haml b/app/views/users/_profile.html.haml
index 3b44959baad..0a70b738071 100644
--- a/app/views/users/_profile.html.haml
+++ b/app/views/users/_profile.html.haml
@@ -12,7 +12,7 @@
- unless user.linkedin.blank?
%li
%span.light LinkedIn:
- %strong= user.linkedin
+ %strong= link_to user.linkedin, "http://www.linkedin.com/in/#{user.linkedin}"
- unless user.twitter.blank?
%li
%span.light Twitter:
diff --git a/app/views/users/_projects.html.haml b/app/views/users/_projects.html.haml
index c925a48f550..6c7779be30e 100644
--- a/app/views/users/_projects.html.haml
+++ b/app/views/users/_projects.html.haml
@@ -1,21 +1,13 @@
- if @contributed_projects.present?
.panel.panel-default
.panel-heading Projects contributed to
- %ul.well-list
- - @contributed_projects.sort_by(&:star_count).reverse.each do |project|
- %li
- = link_to_project project
- %span.pull-right.light
- %i.fa.fa-star
- = project.star_count
+ = render 'shared/projects_list',
+ projects: @contributed_projects.sort_by(&:star_count).reverse,
+ projects_limit: 5, stars: true, avatar: false
- if @projects.present?
.panel.panel-default
.panel-heading Personal projects
- %ul.well-list
- - @projects.sort_by(&:star_count).reverse.each do |project|
- %li
- = link_to_project project
- %span.pull-right.light
- %i.fa.fa-star
- = project.star_count
+ = render 'shared/projects_list',
+ projects: @projects.sort_by(&:star_count).reverse,
+ projects_limit: 10, stars: true, avatar: false
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index 5e82d5780cf..abd6b229782 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -1,5 +1,7 @@
.row
- .col-md-8
+ = link_to '#aside', class: 'show-aside' do
+ %i.fa.fa-angle-left
+ %section.col-md-8
%h3.page-title
= image_tag avatar_icon(@user.email, 90), class: "avatar avatar-tile s90", alt: ''
= @user.name
@@ -19,10 +21,11 @@
= render 'groups', groups: @groups
%hr
- .user-calendar
- %h4.center.light
- %i.fa.fa-spinner.fa-spin
- %hr
+ .hidden-xs
+ .user-calendar
+ %h4.center.light
+ %i.fa.fa-spinner.fa-spin
+ %hr
%h4
User Activity
@@ -33,7 +36,7 @@
%i.fa.fa-rss
= render @events
- .col-md-4
+ %aside.col-md-4
= render 'profile', user: @user
= render 'projects'
diff --git a/app/workers/emails_on_push_worker.rb b/app/workers/emails_on_push_worker.rb
index e3f6f3a6aef..e59ca81defe 100644
--- a/app/workers/emails_on_push_worker.rb
+++ b/app/workers/emails_on_push_worker.rb
@@ -1,25 +1,41 @@
class EmailsOnPushWorker
include Sidekiq::Worker
- def perform(project_id, recipients, push_data)
+ def perform(project_id, recipients, push_data, send_from_committer_email = false, disable_diffs = false)
project = Project.find(project_id)
before_sha = push_data["before"]
after_sha = push_data["after"]
branch = push_data["ref"]
author_id = push_data["user_id"]
- if before_sha =~ /^000000/ || after_sha =~ /^000000/
+ if Gitlab::Git.blank_ref?(before_sha) || Gitlab::Git.blank_ref?(after_sha)
# skip if new branch was pushed or branch was removed
return true
end
compare = Gitlab::Git::Compare.new(project.repository.raw_repository, before_sha, after_sha)
- # Do not send emails if git compare failed
- return false unless compare && compare.commits.present?
+ return false if compare.same
+
+ if compare.commits.empty?
+ compare = Gitlab::Git::Compare.new(project.repository.raw_repository, after_sha, before_sha)
+
+ reverse_compare = true
+
+ return false if compare.commits.empty?
+ end
recipients.split(" ").each do |recipient|
- Notify.repository_push_email(project_id, recipient, author_id, branch, compare).deliver
+ Notify.repository_push_email(
+ project_id,
+ recipient,
+ author_id,
+ branch,
+ compare,
+ reverse_compare,
+ send_from_committer_email,
+ disable_diffs
+ ).deliver
end
ensure
compare = nil
diff --git a/app/workers/irker_worker.rb b/app/workers/irker_worker.rb
index 613bae351d8..e1a99d9cad8 100644
--- a/app/workers/irker_worker.rb
+++ b/app/workers/irker_worker.rb
@@ -57,9 +57,9 @@ class IrkerWorker
end
def send_branch_updates(push_data, project, repo_name, committer, branch)
- if push_data['before'] =~ /^000000/
+ if push_data['before'] == Gitlab::Git::BLANK_SHA
send_new_branch project, repo_name, committer, branch
- elsif push_data['after'] =~ /^000000/
+ elsif push_data['after'] == Gitlab::Git::BLANK_SHA
send_del_branch repo_name, committer, branch
end
end
@@ -83,7 +83,7 @@ class IrkerWorker
return if push_data['total_commits_count'] == 0
# Next message is for number of commit pushed, if any
- if push_data['before'] =~ /^000000/
+ if push_data['before'] == Gitlab::Git::BLANK_SHA
# Tweak on push_data["before"] in order to have a nice compare URL
push_data['before'] = before_on_new_branch push_data, project
end
diff --git a/app/workers/post_receive.rb b/app/workers/post_receive.rb
index 1406cba2db3..ecc6c8e53a3 100644
--- a/app/workers/post_receive.rb
+++ b/app/workers/post_receive.rb
@@ -33,7 +33,7 @@ class PostReceive
return false
end
- if tag?(ref)
+ if Gitlab::Git.tag_ref?(ref)
GitTagPushService.new.execute(project, @user, oldrev, newrev, ref)
else
GitPushService.new.execute(project, @user, oldrev, newrev, ref)
@@ -44,10 +44,4 @@ class PostReceive
def log(message)
Gitlab::GitLogger.error("POST-RECEIVE: #{message}")
end
-
- private
-
- def tag?(ref)
- !!(/refs\/tags\/(.*)/.match(ref))
- end
end
diff --git a/config/application.rb b/config/application.rb
index bd4578848c5..fa399533e52 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -50,6 +50,8 @@ module Gitlab
# Version of your assets, change this if you want to expire all your assets
config.assets.version = '1.0'
+ config.action_view.sanitized_allowed_protocols = %w(smb)
+
# Relative url support
# Uncomment and customize the last line to run in a non-root path
# WARNING: We recommend creating a FQDN to host GitLab in a root path instead of this.
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index 6dff07cf9df..75d9e65aefe 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -43,6 +43,7 @@ production: &base
# email_enabled: true
# Email address used in the "From" field in mails sent by GitLab
email_from: example@example.com
+ email_display_name: GitLab
# Email server smtp settings are in config/initializers/smtp_settings.rb.sample
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 6a8bbb80b9c..70af7a829c4 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -102,6 +102,7 @@ Settings.gitlab['relative_url_root'] ||= ENV['RAILS_RELATIVE_URL_ROOT'] || ''
Settings.gitlab['protocol'] ||= Settings.gitlab.https ? "https" : "http"
Settings.gitlab['email_enabled'] ||= true if Settings.gitlab['email_enabled'].nil?
Settings.gitlab['email_from'] ||= "gitlab@#{Settings.gitlab.host}"
+Settings.gitlab['email_display_name'] ||= "GitLab"
Settings.gitlab['url'] ||= Settings.send(:build_gitlab_url)
Settings.gitlab['user'] ||= 'git'
Settings.gitlab['user_home'] ||= begin
diff --git a/config/initializers/smtp_settings.rb.sample b/config/initializers/smtp_settings.rb.sample
index e00923e7e0c..f0fe2fdfa43 100644
--- a/config/initializers/smtp_settings.rb.sample
+++ b/config/initializers/smtp_settings.rb.sample
@@ -3,6 +3,9 @@
# 2. Edit settings inside this file
# 3. Restart GitLab instance
#
+# For full list of options and their values see http://api.rubyonrails.org/classes/ActionMailer/Base.html
+#
+
if Rails.env.production?
Gitlab::Application.config.action_mailer.delivery_method = :smtp
@@ -14,6 +17,6 @@ if Rails.env.production?
domain: "gitlab.company.com",
authentication: :login,
enable_starttls_auto: true,
- openssl_verify_mode: 'none'
+ openssl_verify_mode: 'peer' # See ActionMailer documentation for other possible options
}
end
diff --git a/config/initializers/timeout.rb b/config/initializers/timeout.rb
new file mode 100644
index 00000000000..bc88595cf26
--- /dev/null
+++ b/config/initializers/timeout.rb
@@ -0,0 +1,8 @@
+# Slowpoke extends Rack::Timeout to gracefully kill Unicorn workers so they can clean up state.
+Slowpoke.timeout = 60
+
+# The `Rack::Timeout` middleware kills requests after 60 seconds (as set above).
+# We're replacing it with our `Gitlab::Middleware::Timeout` that does the same,
+# except ignoring Git-over-HTTP requests, letting those take as long as they need.
+
+Rails.application.config.middleware.swap(Rack::Timeout, Gitlab::Middleware::Timeout)
diff --git a/config/routes.rb b/config/routes.rb
index 5348c86ea9d..889995e92a6 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -7,9 +7,8 @@ Gitlab::Application.routes.draw do
authorized_applications: 'oauth/authorized_applications',
authorizations: 'oauth/authorizations'
end
- #
+
# Search
- #
get 'search' => 'search#show'
get 'search/autocomplete' => 'search#autocomplete', as: :search_autocomplete
@@ -33,13 +32,11 @@ Gitlab::Application.routes.draw do
receive_pack: Gitlab.config.gitlab_shell.receive_pack
}), at: '/', constraints: lambda { |request| /[-\/\w\.]+\.git\//.match(request.path_info) }, via: [:get, :post]
- #
# Help
- #
-
get 'help' => 'help#index'
get 'help/:category/:file' => 'help#show', as: :help_page
get 'help/shortcuts'
+ get 'help/ui' => 'help#ui'
#
# Global snippets
@@ -73,7 +70,7 @@ Gitlab::Application.routes.draw do
get :callback
get :jobs
end
-
+
resource :gitorious, only: [:create, :new], controller: :gitorious do
get :status
get :callback
@@ -196,11 +193,6 @@ Gitlab::Application.routes.draw do
end
resources :keys
resources :emails, only: [:index, :create, :destroy]
- resources :groups, only: [:index] do
- member do
- delete :leave
- end
- end
resource :avatar, only: [:destroy]
end
end
@@ -216,13 +208,24 @@ Gitlab::Application.routes.draw do
#
resource :dashboard, controller: 'dashboard', only: [:show] do
member do
- get :projects
get :issues
get :merge_requests
end
scope module: :dashboard do
resources :milestones, only: [:index, :show]
+
+ resources :groups, only: [:index] do
+ member do
+ delete :leave
+ end
+ end
+
+ resources :projects, only: [] do
+ collection do
+ get :starred
+ end
+ end
end
end
diff --git a/config/unicorn.rb.example b/config/unicorn.rb.example
index d8b4f5c7c32..29253b71f49 100644
--- a/config/unicorn.rb.example
+++ b/config/unicorn.rb.example
@@ -35,22 +35,10 @@ working_directory "/home/git/gitlab" # available in 0.94.0+
listen "/home/git/gitlab/tmp/sockets/gitlab.socket", :backlog => 1024
listen "127.0.0.1:8080", :tcp_nopush => true
-# nuke workers after 30 seconds instead of 60 seconds (the default)
-#
-# NOTICE: git push over http depends on this value.
-# If you want be able to push huge amount of data to git repository over http
-# you will have to increase this value too.
-#
-# Example of output if you try to push 1GB repo to GitLab over http.
-# -> git push http://gitlab.... master
-#
-# error: RPC failed; result=18, HTTP code = 200
-# fatal: The remote end hung up unexpectedly
-# fatal: The remote end hung up unexpectedly
-#
-# For more information see http://stackoverflow.com/a/21682112/752049
-#
-timeout 60
+# Kill workers after 1 hour.
+# A shorter timeout of 60 seconds is enforced by rack-timeout for web requests.
+# Git-over-HTTP only has the below timeout since large pulls/pushes can take a long time.
+timeout 60 * 60
# feel free to point this anywhere accessible on the filesystem
pid "/home/git/gitlab/tmp/pids/unicorn.pid"
diff --git a/db/fixtures/development/05_users.rb b/db/fixtures/development/05_users.rb
index b697f58d4ef..24952a1f661 100644
--- a/db/fixtures/development/05_users.rb
+++ b/db/fixtures/development/05_users.rb
@@ -1,30 +1,31 @@
Gitlab::Seeder.quiet do
(2..20).each do |i|
begin
- User.seed(:id, [{
- id: i,
+ User.create!(
username: Faker::Internet.user_name,
name: Faker::Name.name,
email: Faker::Internet.email,
- confirmed_at: DateTime.now
- }])
+ confirmed_at: DateTime.now,
+ password: '12345678'
+ )
+
print '.'
- rescue ActiveRecord::RecordNotSaved
+ rescue ActiveRecord::RecordInvalid
print 'F'
end
end
(1..5).each do |i|
begin
- User.seed do |s|
- s.username = "user#{i}"
- s.name = "User #{i}"
- s.email = "user#{i}@example.com"
- s.confirmed_at = DateTime.now
- s.password = '12345678'
- end
+ User.create!(
+ username: "user#{i}",
+ name: "User #{i}",
+ email: "user#{i}@example.com",
+ confirmed_at: DateTime.now,
+ password: '12345678'
+ )
print '.'
- rescue ActiveRecord::RecordNotSaved
+ rescue ActiveRecord::RecordInvalid
print 'F'
end
end
diff --git a/db/migrate/20150225065047_add_note_events_to_services.rb b/db/migrate/20150225065047_add_note_events_to_services.rb
new file mode 100644
index 00000000000..d54ba9e482f
--- /dev/null
+++ b/db/migrate/20150225065047_add_note_events_to_services.rb
@@ -0,0 +1,5 @@
+class AddNoteEventsToServices < ActiveRecord::Migration
+ def change
+ add_column :services, :note_events, :boolean, default: true, null: false
+ end
+end
diff --git a/db/migrate/20150306023106_fix_namespace_duplication.rb b/db/migrate/20150306023106_fix_namespace_duplication.rb
new file mode 100644
index 00000000000..334e5574559
--- /dev/null
+++ b/db/migrate/20150306023106_fix_namespace_duplication.rb
@@ -0,0 +1,21 @@
+class FixNamespaceDuplication < ActiveRecord::Migration
+ def up
+ #fixes path duplication
+ select_all('SELECT MAX(id) max, COUNT(id) cnt, path FROM namespaces GROUP BY path HAVING COUNT(id) > 1').each do |nms|
+ bad_nms_ids = select_all("SELECT id FROM namespaces WHERE path = '#{nms['path']}' AND id <> #{nms['max']}").map{|x| x["id"]}
+ execute("UPDATE projects SET namespace_id = #{nms["max"]} WHERE namespace_id IN(#{bad_nms_ids.join(', ')})")
+ execute("DELETE FROM namespaces WHERE id IN(#{bad_nms_ids.join(', ')})")
+ end
+
+ #fixes name duplication
+ select_all('SELECT MAX(id) max, COUNT(id) cnt, name FROM namespaces GROUP BY name HAVING COUNT(id) > 1').each do |nms|
+ bad_nms_ids = select_all("SELECT id FROM namespaces WHERE name = '#{nms['name']}' AND id <> #{nms['max']}").map{|x| x["id"]}
+ execute("UPDATE projects SET namespace_id = #{nms["max"]} WHERE namespace_id IN(#{bad_nms_ids.join(', ')})")
+ execute("DELETE FROM namespaces WHERE id IN(#{bad_nms_ids.join(', ')})")
+ end
+ end
+
+ def down
+ # not implemented
+ end
+end
diff --git a/db/migrate/20150306023112_add_unique_index_to_namespace.rb b/db/migrate/20150306023112_add_unique_index_to_namespace.rb
new file mode 100644
index 00000000000..6472138e3ef
--- /dev/null
+++ b/db/migrate/20150306023112_add_unique_index_to_namespace.rb
@@ -0,0 +1,9 @@
+class AddUniqueIndexToNamespace < ActiveRecord::Migration
+ def change
+ remove_index :namespaces, column: :name if index_exists?(:namespaces, :name)
+ remove_index :namespaces, column: :path if index_exists?(:namespaces, :path)
+
+ add_index :namespaces, :name, unique: true
+ add_index :namespaces, :path, unique: true
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 1a9b512e159..3afbc082b70 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: 20150223022001) do
+ActiveRecord::Schema.define(version: 20150306023112) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -242,9 +242,9 @@ ActiveRecord::Schema.define(version: 20150223022001) do
end
add_index "namespaces", ["created_at", "id"], name: "index_namespaces_on_created_at_and_id", using: :btree
- add_index "namespaces", ["name"], name: "index_namespaces_on_name", using: :btree
+ add_index "namespaces", ["name"], name: "index_namespaces_on_name", unique: true, using: :btree
add_index "namespaces", ["owner_id"], name: "index_namespaces_on_owner_id", using: :btree
- add_index "namespaces", ["path"], name: "index_namespaces_on_path", using: :btree
+ add_index "namespaces", ["path"], name: "index_namespaces_on_path", unique: true, using: :btree
add_index "namespaces", ["type"], name: "index_namespaces_on_type", using: :btree
create_table "notes", force: true do |t|
@@ -334,12 +334,12 @@ ActiveRecord::Schema.define(version: 20150223022001) do
t.string "import_url"
t.integer "visibility_level", default: 0, null: false
t.boolean "archived", default: false, null: false
- t.string "avatar"
t.string "import_status"
t.float "repository_size", default: 0.0
t.integer "star_count", default: 0, null: false
t.string "import_type"
t.string "import_source"
+ t.string "avatar"
end
add_index "projects", ["created_at", "id"], name: "index_projects_on_created_at_and_id", using: :btree
@@ -371,6 +371,7 @@ ActiveRecord::Schema.define(version: 20150223022001) do
t.boolean "issues_events", default: true
t.boolean "merge_requests_events", default: true
t.boolean "tag_push_events", default: true
+ t.boolean "note_events", default: true, null: false
end
add_index "services", ["created_at", "id"], name: "index_services_on_created_at_and_id", using: :btree
diff --git a/doc/development/README.md b/doc/development/README.md
index c31e5d7ae97..d5d264be19d 100644
--- a/doc/development/README.md
+++ b/doc/development/README.md
@@ -5,3 +5,4 @@
- [Rake tasks](rake_tasks.md) for development
- [CI setup](ci_setup.md) for testing GitLab
- [Sidekiq debugging](sidekiq_debugging.md)
+- [UI guide](ui_guide.md) for building GitLab with existing css styles and elements
diff --git a/doc/development/ui_guide.md b/doc/development/ui_guide.md
new file mode 100644
index 00000000000..2f01defc11d
--- /dev/null
+++ b/doc/development/ui_guide.md
@@ -0,0 +1,12 @@
+# UI Guide for building GitLab
+
+## Best practices for creating new pages in GitLab
+
+TODO: write some best practices when develop GitLab features.
+
+## GitLab UI development kit
+
+We created a page inside GitLab where you can check commonly used html and css elements.
+
+When you run GitLab instance locally - just visit http://localhost:3000/help/ui page to see UI examples
+you can use during GitLab development.
diff --git a/doc/integration/github.md b/doc/integration/github.md
index a9f1bc31bb4..b64501c2aaa 100644
--- a/doc/integration/github.md
+++ b/doc/integration/github.md
@@ -53,7 +53,7 @@ GitHub will generate an application ID and secret key for you to use.
"app_id" => "YOUR_APP_ID",
"app_secret" => "YOUR_APP_SECRET",
"url" => "https://github.com/",
- "args" => { "scope" => "user:email" } }
+ "args" => { "scope" => "user:email" }
}
]
```
@@ -76,4 +76,4 @@ GitHub will generate an application ID and secret key for you to use.
On the sign in page there should now be a GitHub icon below the regular sign in form.
Click the icon to begin the authentication process. GitHub will ask the user to sign in and authorize the GitLab application.
-If everything goes well the user will be returned to GitLab and will be signed in. \ No newline at end of file
+If everything goes well the user will be returned to GitLab and will be signed in.
diff --git a/doc/integration/gitlab.md b/doc/integration/gitlab.md
index 49ffaa62af8..216f1f11a9b 100644
--- a/doc/integration/gitlab.md
+++ b/doc/integration/gitlab.md
@@ -58,7 +58,7 @@ GitLab.com will generate an application ID and secret key for you to use.
"name" => "gitlab",
"app_id" => "YOUR_APP_ID",
"app_secret" => "YOUR_APP_SECRET",
- "args" => { "scope" => "api" } }
+ "args" => { "scope" => "api" }
}
]
```
@@ -81,4 +81,4 @@ GitLab.com will generate an application ID and secret key for you to use.
On the sign in page there should now be a GitLab.com icon below the regular sign in form.
Click the icon to begin the authentication process. GitLab.com will ask the user to sign in and authorize the GitLab application.
-If everything goes well the user will be returned to your GitLab instance and will be signed in. \ No newline at end of file
+If everything goes well the user will be returned to your GitLab instance and will be signed in.
diff --git a/doc/integration/google.md b/doc/integration/google.md
index d7b741ece69..e1c14c7c948 100644
--- a/doc/integration/google.md
+++ b/doc/integration/google.md
@@ -55,7 +55,7 @@ To enable the Google OAuth2 OmniAuth provider you must register your application
"name" => "google_oauth2",
"app_id" => "YOUR_APP_ID",
"app_secret" => "YOUR_APP_SECRET",
- "args" => { "access_type" => "offline", "approval_prompt" => '' } }
+ "args" => { "access_type" => "offline", "approval_prompt" => '' }
}
]
```
diff --git a/doc/markdown/markdown.md b/doc/markdown/markdown.md
index 1096ea9656c..52779103775 100644
--- a/doc/markdown/markdown.md
+++ b/doc/markdown/markdown.md
@@ -140,25 +140,25 @@ But let's throw in a <b>tag</b>.
## Emoji
- Sometimes you want to be a :ninja: and add some :glowing_star: to your :speech_balloon:. Well we have a gift for you:
+ Sometimes you want to :monkey: around a bit and add some :star2: to your :speech_balloon:. Well we have a gift for you:
- :high_voltage_sign: You can use emoji anywhere GFM is supported. :victory_hand:
+ :zap: You can use emoji anywhere GFM is supported. :v:
- You can use it to point out a :bug: or warn about :speak_no_evil_monkey: patches. And if someone improves your really :snail: code, send them some :cake:. People will :heart: you for that.
+ You can use it to point out a :bug: or warn about :speak_no_evil: patches. And if someone improves your really :snail: code, send them some :birthday:. People will :heart: you for that.
- If you are new to this, don't be :fearful_face:. You can easily join the emoji :family:. All you need to do is to look up on the supported codes.
+ If you are new to this, don't be :fearful:. You can easily join the emoji :family:. All you need to do is to look up on the supported codes.
- Consult the [Emoji Cheat Sheet](https://s3.amazonaws.com/emoji-cheatsheet/cheat_sheet.pdf) for a list of all supported emoji codes. :thumbsup:
+ Consult the [Emoji Cheat Sheet](http://emoji.codes) for a list of all supported emoji codes. :thumbsup:
-Sometimes you want to be a :ninja: and add some :glowing_star: to your :speech_balloon:. Well we have a gift for you:
+Sometimes you want to :monkey: around a bit and add some :star2: to your :speech_balloon:. Well we have a gift for you:
-:high_voltage_sign: You can use emoji anywhere GFM is supported. :victory_hand:
+:zap: You can use emoji anywhere GFM is supported. :v:
-You can use it to point out a :bug: or warn about :speak_no_evil_monkey: patches. And if someone improves your really :snail: code, send them some :cake:. People will :heart: you for that.
+You can use it to point out a :bug: or warn about :speak_no_evil: patches. And if someone improves your really :snail: code, send them some :birthday:. People will :heart: you for that.
-If you are new to this, don't be :fearful_face:. You can easily join the emoji :family:. All you need to do is to look up on the supported codes.
+If you are new to this, don't be :fearful:. You can easily join the emoji :family:. All you need to do is to look up on the supported codes.
-Consult the [Emoji Cheat Sheet](https://s3.amazonaws.com/emoji-cheatsheet/cheat_sheet.pdf) for a list of all supported emoji codes. :thumbsup:
+Consult the [Emoji Cheat Sheet](http://emoji.codes) for a list of all supported emoji codes. :thumbsup:
## Special GitLab References
diff --git a/doc/release/monthly.md b/doc/release/monthly.md
index dd44c1eb860..ec96be27f3f 100644
--- a/doc/release/monthly.md
+++ b/doc/release/monthly.md
@@ -51,6 +51,7 @@ Xth: (5 working days before the 22nd)
Xth: (4 working days before the 22nd)
- [ ] Update GitLab.com with rc1 (#LINK) (https://dev.gitlab.org/cookbooks/chef-repo/blob/master/doc/administration.md#deploy-the-package)
+- [ ] Update ci.gitLab.com with rc1 (#LINK) (https://dev.gitlab.org/cookbooks/chef-repo/blob/master/doc/administration.md#deploy-the-package)
- [ ] Create regression issues (CE, CI) (#LINK)
- [ ] Tweet about rc1 (#LINK)
@@ -68,6 +69,7 @@ Xth: (1 working day before the 22nd)
- [ ] Create CE, EE, CI stable versions (#LINK)
- [ ] Create Omnibus tags and build packages
- [ ] Update GitLab.com with the stable version (#LINK)
+- [ ] Update ci.gitLab.com with the stable version (#LINK)
22nd:
@@ -200,3 +202,7 @@ Consider creating a post on Hacker News.
## Update GitLab.com with the stable version
- Deploy the package (should not need downtime because of the small difference with RC1)
+
+## Release new AMIs
+
+[Follow this guide](https://dev.gitlab.org/gitlab/AMI/blob/master/README.md)
diff --git a/doc/release/patch.md b/doc/release/patch.md
index 80afa19b6c5..68156ae9c0e 100644
--- a/doc/release/patch.md
+++ b/doc/release/patch.md
@@ -51,6 +51,8 @@ CE=false be rake release['x.x.x']
1. [Build new packages with the latest version](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/release.md)
1. Apply the patch to GitLab.com and the private GitLab development server
+1. Apply the patch to ci.gitLab.com and the private GitLab CI development server
1. Create and publish a blog post, see [patch release blog template](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/doc/patch_release_blog_template.md)
1. Send tweets about the release from `@gitlab`, tweet should include the most important feature that the release is addressing and link to the blog post
1. Note in the 'GitLab X.X regressions' issue that the patch was published (CE only)
+1. [Create new AMIs](https://dev.gitlab.org/gitlab/AMI/blob/master/README.md)
diff --git a/doc/release/security.md b/doc/release/security.md
index b67e0f37a04..60bcfbb6da5 100644
--- a/doc/release/security.md
+++ b/doc/release/security.md
@@ -18,11 +18,13 @@ Please report suspected security vulnerabilities in private to <support@gitlab.c
1. Do the steps from [patch release document](doc/release/patch.md), starting with "Create an issue on private GitLab development server"
1. The MR with the security fix should get a 'security' label and be assigned to the release manager
1. Build the package for GitLab.com and do a deploy
+1. Build the package for ci.gitLab.com and do a deploy
+1. [Create new AMIs](https://dev.gitlab.org/gitlab/AMI/blob/master/README.md)
1. Create feature branches for the blog post on GitLab.com and link them from the code branch
1. Merge and publish the blog posts
1. Send tweets about the release from `@gitlabhq`
1. Send out an email to [the community google mailing list](https://groups.google.com/forum/#!forum/gitlabhq)
-1. Post a signed copy of our complete announcement to [oss-security](http://www.openwall.com/lists/oss-security/) and request a CVE number
+1. Post a signed copy of our complete announcement to [oss-security](http://www.openwall.com/lists/oss-security/) and request a CVE number. CVE is only needed for bugs that allow someone to own the server (Remote Code Execution) or access to code of projects they are not a member of.
1. Add the security researcher to the [Security Researcher Acknowledgments list](http://about.gitlab.com/vulnerability-acknowledgements/)
1. Thank the security researcher in an email for their cooperation
1. Update the blog post and the CHANGELOG when we receive the CVE number
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 3584a754c62..4eb280f9554 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -11,7 +11,7 @@ RUN apt-get update -q \
# If the Omnibus package version below is outdated please contribute a merge request to update it.
# If you run GitLab Enterprise Edition point it to a location where you have downloaded it.
RUN TMP_FILE=$(mktemp); \
- wget -q -O $TMP_FILE https://downloads-packages.s3.amazonaws.com/ubuntu-14.04/gitlab_7.8.1-omnibus-1_amd64.deb \
+ wget -q -O $TMP_FILE https://downloads-packages.s3.amazonaws.com/ubuntu-14.04/gitlab_7.8.3-omnibus-1_amd64.deb \
&& dpkg -i $TMP_FILE \
&& rm -f $TMP_FILE
diff --git a/features/dashboard/archived_projects.feature b/features/dashboard/archived_projects.feature
index 3af93bc373c..69b3a776441 100644
--- a/features/dashboard/archived_projects.feature
+++ b/features/dashboard/archived_projects.feature
@@ -10,8 +10,3 @@ Feature: Dashboard Archived Projects
Scenario: I should see non-archived projects on dashboard
Then I should see "Shop" project link
And I should not see "Forum" project link
-
- Scenario: I should see all projects on projects page
- And I visit dashboard projects page
- Then I should see "Shop" project link
- And I should see "Forum" project link
diff --git a/features/profile/group.feature b/features/dashboard/group.feature
index e2fbfde77be..cf4b8d7283b 100644
--- a/features/profile/group.feature
+++ b/features/dashboard/group.feature
@@ -1,5 +1,5 @@
-@profile
-Feature: Profile Group
+@dashboard
+Feature: Dashboard Group
Background:
Given I sign in as "John Doe"
And "John Doe" is owner of group "Owned"
@@ -10,18 +10,18 @@ Feature: Profile Group
@javascript
Scenario: Owner should be able to leave from group if he is not the last owner
Given "Mary Jane" is owner of group "Owned"
- When I visit profile groups page
+ When I visit dashboard groups page
Then I should see group "Owned" in group list
Then I should see group "Guest" in group list
When I click on the "Leave" button for group "Owned"
- And I visit profile groups page
+ And I visit dashboard groups page
Then I should not see group "Owned" in group list
Then I should see group "Guest" in group list
@javascript
Scenario: Owner should not be able to leave from group if he is the last owner
Given "Mary Jane" is guest of group "Owned"
- When I visit profile groups page
+ When I visit dashboard groups page
Then I should see group "Owned" in group list
Then I should see group "Guest" in group list
Then I should not see the "Leave" button for group "Owned"
@@ -29,20 +29,28 @@ Feature: Profile Group
@javascript
Scenario: Guest should be able to leave from group
Given "Mary Jane" is guest of group "Guest"
- When I visit profile groups page
+ When I visit dashboard groups page
Then I should see group "Owned" in group list
Then I should see group "Guest" in group list
When I click on the "Leave" button for group "Guest"
- When I visit profile groups page
+ When I visit dashboard groups page
Then I should see group "Owned" in group list
Then I should not see group "Guest" in group list
@javascript
Scenario: Guest should be able to leave from group even if he is the only user in the group
- When I visit profile groups page
+ When I visit dashboard groups page
Then I should see group "Owned" in group list
Then I should see group "Guest" in group list
When I click on the "Leave" button for group "Guest"
- When I visit profile groups page
+ When I visit dashboard groups page
Then I should see group "Owned" in group list
Then I should not see group "Guest" in group list
+
+ Scenario: Create a group from dasboard
+ And I visit dashboard groups page
+ And I click new group link
+ And submit form with new group "Samurai" info
+ Then I should be redirected to group "Samurai" page
+ And I should see newly created group "Samurai"
+
diff --git a/features/dashboard/projects.feature b/features/dashboard/projects.feature
deleted file mode 100644
index bb4e84f0159..00000000000
--- a/features/dashboard/projects.feature
+++ /dev/null
@@ -1,9 +0,0 @@
-@dashboard
-Feature: Dashboard Projects
- Background:
- Given I sign in as a user
- And I own project "Shop"
- And I visit dashboard projects page
-
- Scenario: I should see projects list
- Then I should see projects list
diff --git a/features/dashboard/starred_projects.feature b/features/dashboard/starred_projects.feature
new file mode 100644
index 00000000000..9dfd2fbab9c
--- /dev/null
+++ b/features/dashboard/starred_projects.feature
@@ -0,0 +1,12 @@
+@dashboard
+Feature: Dashboard Starred Projects
+ Background:
+ Given I sign in as a user
+ And public project "Community"
+ And I starred project "Community"
+ And I own project "Shop"
+ And I visit dashboard starred projects page
+
+ Scenario: I should see projects list
+ Then I should see project "Community"
+ And I should not see project "Shop"
diff --git a/features/groups.feature b/features/groups.feature
index b5ff03db844..05546e0d6ef 100644
--- a/features/groups.feature
+++ b/features/groups.feature
@@ -10,14 +10,6 @@ Feature: Groups
Then I should see group "Owned" projects list
And I should see projects activity feed
- Scenario: Create a group from dasboard
- When I visit group "Owned" page
- And I visit dashboard page
- And I click new group link
- And submit form with new group "Samurai" info
- Then I should be redirected to group "Samurai" page
- And I should see newly created group "Samurai"
-
Scenario: I should see group "Owned" issues list
Given project from group "Owned" has issues assigned to me
When I visit group "Owned" issues page
diff --git a/features/project/archived.feature b/features/project/archived.feature
index 9aac29384ba..ad466f4f307 100644
--- a/features/project/archived.feature
+++ b/features/project/archived.feature
@@ -14,15 +14,6 @@ Feature: Project Archived
And I visit project "Forum" page
Then I should see "Archived"
- Scenario: I should not see archived on projects page with no archived projects
- And I visit dashboard projects page
- Then I should not see "Archived"
-
- Scenario: I should see archived on projects page with archived projects
- And project "Forum" is archived
- And I visit dashboard projects page
- Then I should see "Archived"
-
Scenario: I archive project
When project "Shop" has push event
And I visit project "Shop" page
diff --git a/features/project/merge_requests.feature b/features/project/merge_requests.feature
index 7c029f05d75..adad100e56c 100644
--- a/features/project/merge_requests.feature
+++ b/features/project/merge_requests.feature
@@ -218,3 +218,10 @@ Feature: Project Merge Requests
And I click link "Edit" for the merge request
And I preview a description text like "Bug fixed :smile:"
Then I should see the Markdown write tab
+
+ @javascript
+ Scenario: I search merge request
+ Given I click link "All"
+ When I fill in merge request search with "Fe"
+ Then I should see "Feature NS-03" in merge requests
+ And I should not see "Bug NS-04" in merge requests
diff --git a/features/steps/profile/group.rb b/features/steps/dashboard/group.rb
index 0a10e04e219..8384df2fb59 100644
--- a/features/steps/profile/group.rb
+++ b/features/steps/dashboard/group.rb
@@ -1,4 +1,4 @@
-class Spinach::Features::ProfileGroup < Spinach::FeatureSteps
+class Spinach::Features::DashboardGroup < Spinach::FeatureSteps
include SharedAuthentication
include SharedGroup
include SharedPaths
@@ -41,4 +41,23 @@ class Spinach::Features::ProfileGroup < Spinach::FeatureSteps
step 'I should not see group "Guest" in group list' do
page.should_not have_content("Guest")
end
+
+ step 'I click new group link' do
+ click_link "New Group"
+ end
+
+ step 'submit form with new group "Samurai" info' do
+ fill_in 'group_path', with: 'Samurai'
+ fill_in 'group_description', with: 'Tokugawa Shogunate'
+ click_button "Create group"
+ end
+
+ step 'I should be redirected to group "Samurai" page' do
+ current_path.should == group_path(Group.find_by(name: 'Samurai'))
+ end
+
+ step 'I should see newly created group "Samurai"' do
+ page.should have_content "Samurai"
+ page.should have_content "Tokugawa Shogunate"
+ end
end
diff --git a/features/steps/dashboard/projects.rb b/features/steps/dashboard/projects.rb
deleted file mode 100644
index 2a348163060..00000000000
--- a/features/steps/dashboard/projects.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-class Spinach::Features::DashboardProjects < Spinach::FeatureSteps
- include SharedAuthentication
- include SharedPaths
- include SharedProject
-
- step 'I should see projects list' do
- @user.authorized_projects.all.each do |project|
- page.should have_link project.name_with_namespace
- end
- end
-end
diff --git a/features/steps/dashboard/starred_projects.rb b/features/steps/dashboard/starred_projects.rb
new file mode 100644
index 00000000000..b9ad2f13e29
--- /dev/null
+++ b/features/steps/dashboard/starred_projects.rb
@@ -0,0 +1,15 @@
+class Spinach::Features::DashboardStarredProjects < Spinach::FeatureSteps
+ include SharedAuthentication
+ include SharedPaths
+ include SharedProject
+
+ step 'I starred project "Community"' do
+ current_user.toggle_star(Project.find_by(name: 'Community'))
+ end
+
+ step 'I should not see project "Shop"' do
+ within 'aside' do
+ page.should_not have_content('Shop')
+ end
+ end
+end
diff --git a/features/steps/groups.rb b/features/steps/groups.rb
index c3c34070e2e..91921f5e21c 100644
--- a/features/steps/groups.rb
+++ b/features/steps/groups.rb
@@ -72,25 +72,6 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
author: current_user
end
- When 'I click new group link' do
- click_link "New group"
- end
-
- step 'submit form with new group "Samurai" info' do
- fill_in 'group_path', with: 'Samurai'
- fill_in 'group_description', with: 'Tokugawa Shogunate'
- click_button "Create group"
- end
-
- step 'I should be redirected to group "Samurai" page' do
- current_path.should == group_path(Group.find_by(name: 'Samurai'))
- end
-
- step 'I should see newly created group "Samurai"' do
- page.should have_content "Samurai"
- page.should have_content "Tokugawa Shogunate"
- end
-
step 'I change group "Owned" name to "new-name"' do
fill_in 'group_name', with: 'new-name'
fill_in 'group_path', with: 'new-name'
diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb
index 263f2ef2438..b67b2e58caf 100644
--- a/features/steps/project/merge_requests.rb
+++ b/features/steps/project/merge_requests.rb
@@ -276,6 +276,10 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
end
end
+ step 'I fill in merge request search with "Fe"' do
+ fill_in 'issue_search', with: "Fe"
+ end
+
def merge_request
@merge_request ||= MergeRequest.find_by!(title: "Bug NS-05")
end
diff --git a/features/steps/shared/active_tab.rb b/features/steps/shared/active_tab.rb
index c229864bc83..9beb688bd16 100644
--- a/features/steps/shared/active_tab.rb
+++ b/features/steps/shared/active_tab.rb
@@ -26,7 +26,7 @@ module SharedActiveTab
end
step 'the active main tab should be Home' do
- ensure_active_main_tab('Activity')
+ ensure_active_main_tab('Your Projects')
end
step 'the active main tab should be Projects' do
diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb
index 835b644e6c7..bb6c336d7cd 100644
--- a/features/steps/shared/paths.rb
+++ b/features/steps/shared/paths.rb
@@ -87,6 +87,18 @@ module SharedPaths
visit help_path
end
+ step 'I visit dashboard groups page' do
+ visit dashboard_groups_path
+ end
+
+ step 'I should be redirected to the dashboard groups page' do
+ current_path.should == dashboard_groups_path
+ end
+
+ step 'I visit dashboard starred projects page' do
+ visit starred_dashboard_projects_path
+ end
+
# ----------------------------------------
# Profile
# ----------------------------------------
@@ -119,14 +131,6 @@ module SharedPaths
visit history_profile_path
end
- step 'I visit profile groups page' do
- visit profile_groups_path
- end
-
- step 'I should be redirected to the profile groups page' do
- current_path.should == profile_groups_path
- end
-
# ----------------------------------------
# Admin
# ----------------------------------------
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 228a719fbdf..ee678d84c84 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -83,7 +83,10 @@ module API
end
def authenticate_by_gitlab_shell_token!
- unauthorized! unless secret_token == params['secret_token'].try(:chomp)
+ input = params['secret_token'].try(:chomp)
+ unless Devise.secure_compare(secret_token, input)
+ unauthorized!
+ end
end
def authenticated_as_admin!
diff --git a/lib/gitlab/git.rb b/lib/gitlab/git.rb
index 4a712c6345f..0c350d7c675 100644
--- a/lib/gitlab/git.rb
+++ b/lib/gitlab/git.rb
@@ -1,9 +1,25 @@
module Gitlab
module Git
BLANK_SHA = '0' * 40
+ TAG_REF_PREFIX = "refs/tags/"
+ BRANCH_REF_PREFIX = "refs/heads/"
- def self.extract_ref_name(ref)
- ref.gsub(/\Arefs\/(tags|heads)\//, '')
+ class << self
+ def ref_name(ref)
+ ref.gsub(/\Arefs\/(tags|heads)\//, '')
+ end
+
+ def tag_ref?(ref)
+ ref.start_with?(TAG_REF_PREFIX)
+ end
+
+ def branch_ref?(ref)
+ ref.start_with?(BRANCH_REF_PREFIX)
+ end
+
+ def blank_ref?(ref)
+ ref == BLANK_SHA
+ end
end
end
end
diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb
index 9b31190a882..cb69e4b13d3 100644
--- a/lib/gitlab/git_access.rb
+++ b/lib/gitlab/git_access.rb
@@ -115,7 +115,7 @@ module Gitlab
# we dont allow force push to protected branch
if forced_push?(project, oldrev, newrev)
:force_push_code_to_protected_branches
- elsif newrev == Gitlab::Git::BLANK_SHA
+ elsif Gitlab::Git.blank_ref?(newrev)
# and we dont allow remove of protected branch
:remove_protected_branches
elsif project.developers_can_push_to_protected_branch?(branch_name)
@@ -135,8 +135,8 @@ module Gitlab
def branch_name(ref)
ref = ref.to_s
- if ref.start_with?('refs/heads')
- ref.sub(%r{\Arefs/heads/}, '')
+ if Gitlab::Git.branch_ref?(ref)
+ Gitlab::Git.ref_name(ref)
else
nil
end
@@ -144,8 +144,8 @@ module Gitlab
def tag_name(ref)
ref = ref.to_s
- if ref.start_with?('refs/tags')
- ref.sub(%r{\Arefs/tags/}, '')
+ if Gitlab::Git.tag_ref?(ref)
+ Gitlab::Git.ref_name(ref)
else
nil
end
diff --git a/lib/gitlab/ldap/access.rb b/lib/gitlab/ldap/access.rb
index 0c85acf7e69..6e30724e1f7 100644
--- a/lib/gitlab/ldap/access.rb
+++ b/lib/gitlab/ldap/access.rb
@@ -34,7 +34,14 @@ module Gitlab
def allowed?
if Gitlab::LDAP::Person.find_by_dn(user.ldap_identity.extern_uid, adapter)
return true unless ldap_config.active_directory
- !Gitlab::LDAP::Person.disabled_via_active_directory?(user.ldap_identity.extern_uid, adapter)
+
+ # Block user in GitLab if he/she was blocked in AD
+ if Gitlab::LDAP::Person.disabled_via_active_directory?(user.ldap_identity.extern_uid, adapter)
+ user.block unless user.blocked?
+ false
+ else
+ true
+ end
else
false
end
diff --git a/lib/gitlab/ldap/authentication.rb b/lib/gitlab/ldap/authentication.rb
index 8af2c74e959..649cf3194b8 100644
--- a/lib/gitlab/ldap/authentication.rb
+++ b/lib/gitlab/ldap/authentication.rb
@@ -50,7 +50,7 @@ module Gitlab
end
def user_filter(login)
- filter = Net::LDAP::Filter.eq(config.uid, login)
+ filter = Net::LDAP::Filter.equals(config.uid, login)
# Apply LDAP user filter if present
if config.user_filter.present?
diff --git a/lib/gitlab/ldap/person.rb b/lib/gitlab/ldap/person.rb
index 3e0b3e6cbf8..3c426179375 100644
--- a/lib/gitlab/ldap/person.rb
+++ b/lib/gitlab/ldap/person.rb
@@ -9,10 +9,12 @@ module Gitlab
attr_accessor :entry, :provider
def self.find_by_uid(uid, adapter)
+ uid = Net::LDAP::Filter.escape(uid)
adapter.user(adapter.config.uid, uid)
end
def self.find_by_dn(dn, adapter)
+ dn = Net::LDAP::Filter.escape(dn)
adapter.user('dn', dn)
end
diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb
index d85c2ee4f2d..2dfa18da482 100644
--- a/lib/gitlab/markdown.rb
+++ b/lib/gitlab/markdown.rb
@@ -14,6 +14,7 @@ module Gitlab
# * !123 for merge requests
# * $123 for snippets
# * 123456 for commits
+ # * 123456...7890123 for commit ranges (comparisons)
#
# It also parses Emoji codes to insert images. See
# http://www.emoji-cheat-sheet.com/ for a list of the supported icons.
@@ -133,13 +134,14 @@ module Gitlab
|#{PROJ_STR}?\#(?<issue>([a-zA-Z\-]+-)?\d+) # Issue ID
|#{PROJ_STR}?!(?<merge_request>\d+) # MR ID
|\$(?<snippet>\d+) # Snippet ID
+ |(#{PROJ_STR}@)?(?<commit_range>[\h]{6,40}\.{2,3}[\h]{6,40}) # Commit range
|(#{PROJ_STR}@)?(?<commit>[\h]{6,40}) # Commit ID
|(?<skip>gfm-extraction-[\h]{6,40}) # Skip gfm extractions. Otherwise will be parsed as commit
)
(?<suffix>\W)? # Suffix
}x.freeze
- TYPES = [:user, :issue, :label, :merge_request, :snippet, :commit].freeze
+ TYPES = [:user, :issue, :label, :merge_request, :snippet, :commit, :commit_range].freeze
def parse_references(text, project = @project)
# parse reference links
@@ -290,6 +292,30 @@ module Gitlab
end
end
+ def reference_commit_range(identifier, project = @project, prefix_text = nil)
+ from_id, to_id = identifier.split(/\.{2,3}/, 2)
+
+ inclusive = identifier !~ /\.{3}/
+ from_id << "^" if inclusive
+
+ if project.valid_repo? &&
+ from = project.repository.commit(from_id) &&
+ to = project.repository.commit(to_id)
+
+ options = html_options.merge(
+ title: "Commits #{from_id} through #{to_id}",
+ class: "gfm gfm-commit_range #{html_options[:class]}"
+ )
+ prefix_text = "#{prefix_text}@" if prefix_text
+
+ link_to(
+ "#{prefix_text}#{identifier}",
+ namespace_project_compare_url(project.namespace, project, from: from_id, to: to_id),
+ options
+ )
+ end
+ end
+
def reference_external_issue(identifier, project = @project,
prefix_text = nil)
url = url_for_issue(identifier, project)
diff --git a/lib/gitlab/middleware/timeout.rb b/lib/gitlab/middleware/timeout.rb
new file mode 100644
index 00000000000..015600392b9
--- /dev/null
+++ b/lib/gitlab/middleware/timeout.rb
@@ -0,0 +1,13 @@
+module Gitlab
+ module Middleware
+ class Timeout < Rack::Timeout
+ GRACK_REGEX = /[-\/\w\.]+\.git\//.freeze
+
+ def call(env)
+ return @app.call(env) if env['PATH_INFO'] =~ GRACK_REGEX
+
+ super
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/note_data_builder.rb b/lib/gitlab/note_data_builder.rb
new file mode 100644
index 00000000000..644dec45dca
--- /dev/null
+++ b/lib/gitlab/note_data_builder.rb
@@ -0,0 +1,77 @@
+module Gitlab
+ class NoteDataBuilder
+ class << self
+ # Produce a hash of post-receive data
+ #
+ # For all notes:
+ #
+ # data = {
+ # object_kind: "note",
+ # user: {
+ # name: String,
+ # username: String,
+ # avatar_url: String
+ # }
+ # project_id: Integer,
+ # repository: {
+ # name: String,
+ # url: String,
+ # description: String,
+ # homepage: String,
+ # }
+ # object_attributes: {
+ # <hook data for note>
+ # }
+ # <note-specific data>: {
+ # }
+ # note-specific data is a hash with one of the following keys and contains
+ # the hook data for that type.
+ # - commit
+ # - issue
+ # - merge_request
+ # - snippet
+ #
+ def build(note, user)
+ project = note.project
+ data = build_base_data(project, user, note)
+
+ if note.for_commit?
+ data[:commit] = build_data_for_commit(project, user, note)
+ elsif note.for_issue?
+ data[:issue] = note.noteable.hook_attrs
+ elsif note.for_merge_request?
+ data[:merge_request] = note.noteable.hook_attrs
+ elsif note.for_project_snippet?
+ data[:snippet] = note.noteable.hook_attrs
+ end
+
+ data
+ end
+
+ def build_base_data(project, user, note)
+ base_data = {
+ object_kind: "note",
+ user: user.hook_attrs,
+ project_id: project.id,
+ repository: {
+ name: project.name,
+ url: project.url_to_repo,
+ description: project.description,
+ homepage: project.web_url,
+ },
+ object_attributes: note.hook_attrs
+ }
+
+ base_data[:object_attributes][:url] =
+ Gitlab::UrlBuilder.new(:note).build(note.id)
+ base_data
+ end
+
+ def build_data_for_commit(project, user, note)
+ # commit_id is the SHA hash
+ commit = project.repository.commit(note.commit_id)
+ commit.hook_attrs(project)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/push_data_builder.rb b/lib/gitlab/push_data_builder.rb
index 5cefa67d3ab..0cc6b0ac694 100644
--- a/lib/gitlab/push_data_builder.rb
+++ b/lib/gitlab/push_data_builder.rb
@@ -58,6 +58,7 @@ module Gitlab
data[:commits] << commit.hook_attrs(project)
end
+ data[:commits] = "" if data[:commits].count == 0
data
end
@@ -65,12 +66,13 @@ module Gitlab
# existing project and commits to test web hooks
def build_sample(project, user)
commits = project.repository.commits(project.default_branch, nil, 3)
- build(project, user, commits.last.id, commits.first.id, "refs/heads/#{project.default_branch}", commits)
+ ref = "#{Gitlab::Git::BRANCH_REF_PREFIX}#{project.default_branch}"
+ build(project, user, commits.last.id, commits.first.id, ref, commits)
end
def checkout_sha(repository, newrev, ref)
- if newrev != Gitlab::Git::BLANK_SHA && ref.start_with?('refs/tags/')
- tag_name = Gitlab::Git.extract_ref_name(ref)
+ if newrev != Gitlab::Git::BLANK_SHA && Gitlab::Git.tag_ref?(ref)
+ tag_name = Gitlab::Git.ref_name(ref)
tag = repository.find_tag(tag_name)
if tag
diff --git a/lib/gitlab/reference_extractor.rb b/lib/gitlab/reference_extractor.rb
index 7e5c991a222..5b9772de168 100644
--- a/lib/gitlab/reference_extractor.rb
+++ b/lib/gitlab/reference_extractor.rb
@@ -1,13 +1,13 @@
module Gitlab
# Extract possible GFM references from an arbitrary String for further processing.
class ReferenceExtractor
- attr_accessor :users, :labels, :issues, :merge_requests, :snippets, :commits
+ attr_accessor :users, :labels, :issues, :merge_requests, :snippets, :commits, :commit_ranges
include Markdown
def initialize
- @users, @labels, @issues, @merge_requests, @snippets, @commits =
- [], [], [], [], [], []
+ @users, @labels, @issues, @merge_requests, @snippets, @commits, @commit_ranges =
+ [], [], [], [], [], [], []
end
def analyze(string, project)
@@ -60,6 +60,16 @@ module Gitlab
end.reject(&:nil?)
end
+ def commit_ranges_for(project = nil)
+ commit_ranges.map do |entry|
+ repo = entry[:project].repository if entry[:project]
+ if repo && should_lookup?(project, entry[:project])
+ from_id, to_id = entry[:id].split(/\.{2,3}/, 2)
+ [repo.commit(from_id), repo.commit(to_id)]
+ end
+ end.reject(&:nil?)
+ end
+
private
def reference_link(type, identifier, project, _)
diff --git a/lib/gitlab/theme.rb b/lib/gitlab/theme.rb
index a7c83a880f6..9799e54de5d 100644
--- a/lib/gitlab/theme.rb
+++ b/lib/gitlab/theme.rb
@@ -5,6 +5,7 @@ module Gitlab
MODERN = 3 unless const_defined?(:MODERN)
GRAY = 4 unless const_defined?(:GRAY)
COLOR = 5 unless const_defined?(:COLOR)
+ BLUE = 6 unless const_defined?(:BLUE)
def self.css_class_by_id(id)
themes = {
@@ -12,7 +13,8 @@ module Gitlab
MARS => "ui_mars",
MODERN => "ui_modern",
GRAY => "ui_gray",
- COLOR => "ui_color"
+ COLOR => "ui_color",
+ BLUE => "ui_blue"
}
id ||= Gitlab.config.gitlab.default_theme
diff --git a/lib/gitlab/url_builder.rb b/lib/gitlab/url_builder.rb
index ab7c8ad89f3..6830d15875a 100644
--- a/lib/gitlab/url_builder.rb
+++ b/lib/gitlab/url_builder.rb
@@ -13,6 +13,9 @@ module Gitlab
build_issue_url(id)
when :merge_request
build_merge_request_url(id)
+ when :note
+ build_note_url(id)
+
end
end
@@ -27,5 +30,31 @@ module Gitlab
merge_request = MergeRequest.find(id)
merge_request_url(merge_request, host: Gitlab.config.gitlab['url'])
end
+
+ def build_note_url(id)
+ note = Note.find(id)
+ if note.for_commit?
+ 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)
+ snippet_url(snippet,
+ host: Gitlab.config.gitlab['url'],
+ anchor: "note_#{note.id}")
+ end
+ end
end
end
diff --git a/lib/redcarpet/render/gitlab_html.rb b/lib/redcarpet/render/gitlab_html.rb
index 714261f815c..1cd3933e4b7 100644
--- a/lib/redcarpet/render/gitlab_html.rb
+++ b/lib/redcarpet/render/gitlab_html.rb
@@ -3,13 +3,20 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
attr_reader :template
alias_method :h, :template
- def initialize(template, options = {})
+ def initialize(template, color_scheme, options = {})
@template = template
+ @color_scheme = color_scheme
@project = @template.instance_variable_get("@project")
@options = options.dup
super options
end
+ def preprocess(full_document)
+ # Redcarpet doesn't allow SMB links when `safe_links_only` is enabled.
+ # FTP links are allowed, so we trick Redcarpet.
+ full_document.gsub("smb://", "ftp://smb:")
+ end
+
# If project has issue number 39, apostrophe will be linked in
# regular text to the issue as Redcarpet will convert apostrophe to
# #39;
@@ -34,7 +41,7 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
end
formatter = Rugments::Formatters::HTML.new(
- cssclass: "code highlight white #{lexer.tag}"
+ cssclass: "code highlight #{@color_scheme} #{lexer.tag}"
)
formatter.format(lexer.lex(code))
end
@@ -54,6 +61,8 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
end
def postprocess(full_document)
+ full_document.gsub!("ftp://smb:", "smb://")
+
full_document.gsub!("&rsquo;", "'")
unless @template.instance_variable_get("@project_wiki") || @project.nil?
full_document = h.create_relative_links(full_document)
diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake
index 43115915de1..976c4b5f22f 100644
--- a/lib/tasks/gitlab/check.rake
+++ b/lib/tasks/gitlab/check.rake
@@ -29,6 +29,7 @@ namespace :gitlab do
check_redis_version
check_ruby_version
check_git_version
+ check_active_users
finished_checking "GitLab"
end
@@ -781,6 +782,10 @@ namespace :gitlab do
end
end
+ def check_active_users
+ puts "Active users: #{User.active.count}"
+ end
+
def omnibus_gitlab?
Dir.pwd == '/opt/gitlab/embedded/service/gitlab-rails'
end
diff --git a/public/503.html b/public/503.html
new file mode 100644
index 00000000000..efdae0f512d
--- /dev/null
+++ b/public/503.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Page took too long to load (503)</title>
+ <link href="/static.css" media="screen" rel="stylesheet" type="text/css" />
+</head>
+<body>
+ <h1>503</h1>
+ <h3>Page took too long to load.</h3>
+ <hr/>
+ <p>Please contact your GitLab administrator if this problem persists.</p>
+</body>
+</html>
diff --git a/spec/controllers/import/github_controller_spec.rb b/spec/controllers/import/github_controller_spec.rb
index b8820413406..5b967bfcc0c 100644
--- a/spec/controllers/import/github_controller_spec.rb
+++ b/spec/controllers/import/github_controller_spec.rb
@@ -27,17 +27,20 @@ describe Import::GithubController do
describe "GET status" do
before do
@repo = OpenStruct.new(login: 'vim', full_name: 'asd/vim')
+ @org = OpenStruct.new(login: 'company')
+ @org_repo = OpenStruct.new(login: 'company', full_name: 'company/repo')
end
it "assigns variables" do
@project = create(:project, import_type: 'github', creator_id: user.id)
controller.stub_chain(:client, :repos).and_return([@repo])
- controller.stub_chain(:client, :orgs).and_return([])
+ controller.stub_chain(:client, :orgs).and_return([@org])
+ controller.stub_chain(:client, :org_repos).with(@org.login).and_return([@org_repo])
get :status
expect(assigns(:already_added_projects)).to eq([@project])
- expect(assigns(:repos)).to eq([@repo])
+ expect(assigns(:repos)).to eq([@repo, @org_repo])
end
it "does not show already added project" do
diff --git a/spec/controllers/uploads_controller_spec.rb b/spec/controllers/uploads_controller_spec.rb
new file mode 100644
index 00000000000..0f9780356b1
--- /dev/null
+++ b/spec/controllers/uploads_controller_spec.rb
@@ -0,0 +1,296 @@
+require 'spec_helper'
+
+describe UploadsController do
+ let!(:user) { create(:user, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) }
+
+ describe "GET show" do
+ context "when viewing a user avatar" do
+ context "when signed in" do
+ before do
+ sign_in(user)
+ end
+
+ context "when the user is blocked" do
+ before do
+ user.block
+ end
+
+ it "redirects to the sign in page" do
+ get :show, model: "user", mounted_as: "avatar", id: user.id, filename: "image.png"
+
+ expect(response).to redirect_to(new_user_session_path)
+ end
+ end
+
+ context "when the user isn't blocked" do
+ it "responds with status 200" do
+ get :show, model: "user", mounted_as: "avatar", id: user.id, filename: "image.png"
+
+ expect(response.status).to eq(200)
+ end
+ end
+ end
+
+ context "when not signed in" do
+ it "responds with status 200" do
+ get :show, model: "user", mounted_as: "avatar", id: user.id, filename: "image.png"
+
+ expect(response.status).to eq(200)
+ end
+ end
+ end
+
+ context "when viewing a project avatar" do
+ let!(:project) { create(:project, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) }
+
+ context "when the project is public" do
+ before do
+ project.update_attribute(:visibility_level, Project::PUBLIC)
+ end
+
+ context "when not signed in" do
+ it "responds with status 200" do
+ get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png"
+
+ expect(response.status).to eq(200)
+ end
+ end
+
+ context "when signed in" do
+ before do
+ sign_in(user)
+ end
+
+ it "responds with status 200" do
+ get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png"
+
+ expect(response.status).to eq(200)
+ end
+ end
+ end
+
+ context "when the project is private" do
+ before do
+ project.update_attribute(:visibility_level, Project::PRIVATE)
+ end
+
+ context "when not signed in" do
+ it "redirects to the sign in page" do
+ get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png"
+
+ expect(response).to redirect_to(new_user_session_path)
+ end
+ end
+
+ context "when signed in" do
+ before do
+ sign_in(user)
+ end
+
+ context "when the user has access to the project" do
+ before do
+ project.team << [user, :master]
+ end
+
+ context "when the user is blocked" do
+ before do
+ user.block
+ project.team << [user, :master]
+ end
+
+ it "redirects to the sign in page" do
+ get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png"
+
+ expect(response).to redirect_to(new_user_session_path)
+ end
+ end
+
+ context "when the user isn't blocked" do
+ it "responds with status 200" do
+ get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png"
+
+ expect(response.status).to eq(200)
+ end
+ end
+ end
+
+ context "when the user doesn't have access to the project" do
+ it "responds with status 404" do
+ get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png"
+
+ expect(response.status).to eq(404)
+ end
+ end
+ end
+ end
+ end
+
+ context "when viewing a group avatar" do
+ let!(:group) { create(:group, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) }
+ let!(:project) { create(:project, namespace: group) }
+
+ context "when the group has public projects" do
+ before do
+ project.update_attribute(:visibility_level, Project::PUBLIC)
+ end
+
+ context "when not signed in" do
+ it "responds with status 200" do
+ get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png"
+
+ expect(response.status).to eq(200)
+ end
+ end
+
+ context "when signed in" do
+ before do
+ sign_in(user)
+ end
+
+ it "responds with status 200" do
+ get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png"
+
+ expect(response.status).to eq(200)
+ end
+ end
+ end
+
+ context "when the project doesn't have public projects" do
+ context "when not signed in" do
+ it "redirects to the sign in page" do
+ get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png"
+
+ expect(response).to redirect_to(new_user_session_path)
+ end
+ end
+
+ context "when signed in" do
+ before do
+ sign_in(user)
+ end
+
+ context "when the user has access to the project" do
+ before do
+ project.team << [user, :master]
+ end
+
+ context "when the user is blocked" do
+ before do
+ user.block
+ project.team << [user, :master]
+ end
+
+ it "redirects to the sign in page" do
+ get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png"
+
+ expect(response).to redirect_to(new_user_session_path)
+ end
+ end
+
+ context "when the user isn't blocked" do
+ it "responds with status 200" do
+ get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png"
+
+ expect(response.status).to eq(200)
+ end
+ end
+ end
+
+ context "when the user doesn't have access to the project" do
+ it "responds with status 404" do
+ get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png"
+
+ expect(response.status).to eq(404)
+ end
+ end
+ end
+ end
+ end
+
+ context "when viewing a note attachment" do
+ let!(:note) { create(:note, :with_attachment) }
+ let(:project) { note.project }
+
+ context "when the project is public" do
+ before do
+ project.update_attribute(:visibility_level, Project::PUBLIC)
+ end
+
+ context "when not signed in" do
+ it "responds with status 200" do
+ get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png"
+
+ expect(response.status).to eq(200)
+ end
+ end
+
+ context "when signed in" do
+ before do
+ sign_in(user)
+ end
+
+ it "responds with status 200" do
+ get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png"
+
+ expect(response.status).to eq(200)
+ end
+ end
+ end
+
+ context "when the project is private" do
+ before do
+ project.update_attribute(:visibility_level, Project::PRIVATE)
+ end
+
+ context "when not signed in" do
+ it "redirects to the sign in page" do
+ get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png"
+
+ expect(response).to redirect_to(new_user_session_path)
+ end
+ end
+
+ context "when signed in" do
+ before do
+ sign_in(user)
+ end
+
+ context "when the user has access to the project" do
+ before do
+ project.team << [user, :master]
+ end
+
+ context "when the user is blocked" do
+ before do
+ user.block
+ project.team << [user, :master]
+ end
+
+ it "redirects to the sign in page" do
+ get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png"
+
+ expect(response).to redirect_to(new_user_session_path)
+ end
+ end
+
+ context "when the user isn't blocked" do
+ it "responds with status 200" do
+ get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png"
+
+ expect(response.status).to eq(200)
+ end
+ end
+ end
+
+ context "when the user doesn't have access to the project" do
+ it "responds with status 404" do
+ get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png"
+
+ expect(response.status).to eq(404)
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/factories/merge_requests.rb b/spec/factories/merge_requests.rb
index 6ce1d7446fe..77cd37c22d9 100644
--- a/spec/factories/merge_requests.rb
+++ b/spec/factories/merge_requests.rb
@@ -40,7 +40,7 @@ FactoryGirl.define do
source_branch "master"
target_branch "feature"
- merge_status :can_be_merged
+ merge_status "can_be_merged"
trait :with_diffs do
end
diff --git a/spec/factories/notes.rb b/spec/factories/notes.rb
index 83d0cc62dbf..f1c33461b55 100644
--- a/spec/factories/notes.rb
+++ b/spec/factories/notes.rb
@@ -30,6 +30,7 @@ FactoryGirl.define do
factory :note_on_issue, traits: [:on_issue], aliases: [:votable_note]
factory :note_on_merge_request, traits: [:on_merge_request]
factory :note_on_merge_request_diff, traits: [:on_merge_request, :on_diff]
+ factory :note_on_project_snippet, traits: [:on_project_snippet]
trait :on_commit do
project factory: :project
@@ -52,6 +53,11 @@ FactoryGirl.define do
noteable_type "Issue"
end
+ trait :on_project_snippet do
+ noteable_id 1
+ noteable_type "Snippet"
+ end
+
trait :with_attachment do
attachment { fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "`/png") }
end
diff --git a/spec/features/security/dashboard_access_spec.rb b/spec/features/security/dashboard_access_spec.rb
index d1f00a3dd82..67238e3ab76 100644
--- a/spec/features/security/dashboard_access_spec.rb
+++ b/spec/features/security/dashboard_access_spec.rb
@@ -25,8 +25,8 @@ describe "Dashboard access", feature: true do
it { is_expected.to be_denied_for :visitor }
end
- describe "GET /dashboard/projects" do
- subject { projects_dashboard_path }
+ describe "GET /dashboard/projects/starred" do
+ subject { starred_dashboard_projects_path }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for :user }
@@ -52,4 +52,12 @@ describe "Dashboard access", feature: true do
it { expect(new_group_path).to be_allowed_for :user }
it { expect(new_group_path).to be_denied_for :visitor }
end
+
+ describe "GET /profile/groups" do
+ subject { dashboard_groups_path }
+
+ it { is_expected.to be_allowed_for :admin }
+ it { is_expected.to be_allowed_for :user }
+ it { is_expected.to be_denied_for :visitor }
+ end
end
diff --git a/spec/features/security/profile_access_spec.rb b/spec/features/security/profile_access_spec.rb
index 5f254c42e58..2512a9c0e3d 100644
--- a/spec/features/security/profile_access_spec.rb
+++ b/spec/features/security/profile_access_spec.rb
@@ -1,76 +1,65 @@
require 'spec_helper'
-describe "Users Security", feature: true do
- describe "Project" do
- before do
- @u1 = create(:user)
- end
-
- describe "GET /login" do
- it { expect(new_user_session_path).not_to be_404_for :visitor }
- end
-
- describe "GET /profile/keys" do
- subject { profile_keys_path }
+describe "Profile access", feature: true do
+ before do
+ @u1 = create(:user)
+ end
- it { is_expected.to be_allowed_for @u1 }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_denied_for :visitor }
- end
+ describe "GET /login" do
+ it { expect(new_user_session_path).not_to be_404_for :visitor }
+ end
- describe "GET /profile" do
- subject { profile_path }
+ describe "GET /profile/keys" do
+ subject { profile_keys_path }
- it { is_expected.to be_allowed_for @u1 }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_denied_for :visitor }
- end
+ it { is_expected.to be_allowed_for @u1 }
+ it { is_expected.to be_allowed_for :admin }
+ it { is_expected.to be_allowed_for :user }
+ it { is_expected.to be_denied_for :visitor }
+ end
- describe "GET /profile/account" do
- subject { profile_account_path }
+ describe "GET /profile" do
+ subject { profile_path }
- it { is_expected.to be_allowed_for @u1 }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_denied_for :visitor }
- end
+ it { is_expected.to be_allowed_for @u1 }
+ it { is_expected.to be_allowed_for :admin }
+ it { is_expected.to be_allowed_for :user }
+ it { is_expected.to be_denied_for :visitor }
+ end
- describe "GET /profile/design" do
- subject { design_profile_path }
+ describe "GET /profile/account" do
+ subject { profile_account_path }
- it { is_expected.to be_allowed_for @u1 }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_denied_for :visitor }
- end
+ it { is_expected.to be_allowed_for @u1 }
+ it { is_expected.to be_allowed_for :admin }
+ it { is_expected.to be_allowed_for :user }
+ it { is_expected.to be_denied_for :visitor }
+ end
- describe "GET /profile/history" do
- subject { history_profile_path }
+ describe "GET /profile/design" do
+ subject { design_profile_path }
- it { is_expected.to be_allowed_for @u1 }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_denied_for :visitor }
- end
+ it { is_expected.to be_allowed_for @u1 }
+ it { is_expected.to be_allowed_for :admin }
+ it { is_expected.to be_allowed_for :user }
+ it { is_expected.to be_denied_for :visitor }
+ end
- describe "GET /profile/notifications" do
- subject { profile_notifications_path }
+ describe "GET /profile/history" do
+ subject { history_profile_path }
- it { is_expected.to be_allowed_for @u1 }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_denied_for :visitor }
- end
+ it { is_expected.to be_allowed_for @u1 }
+ it { is_expected.to be_allowed_for :admin }
+ it { is_expected.to be_allowed_for :user }
+ it { is_expected.to be_denied_for :visitor }
+ end
- describe "GET /profile/groups" do
- subject { profile_groups_path }
+ describe "GET /profile/notifications" do
+ subject { profile_notifications_path }
- it { is_expected.to be_allowed_for @u1 }
- it { is_expected.to be_allowed_for :admin }
- it { is_expected.to be_allowed_for :user }
- it { is_expected.to be_denied_for :visitor }
- end
+ it { is_expected.to be_allowed_for @u1 }
+ it { is_expected.to be_allowed_for :admin }
+ it { is_expected.to be_allowed_for :user }
+ it { is_expected.to be_denied_for :visitor }
end
end
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index 99ff8a32ea5..4c11709ed6e 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -39,24 +39,6 @@ describe ApplicationHelper do
end
end
- describe 'group_icon' do
- avatar_file_path = File.join(Rails.root, 'public', 'gitlab_logo.png')
-
- it 'should return an url for the avatar' do
- group = create(:group)
- group.avatar = File.open(avatar_file_path)
- group.save!
- expect(group_icon(group.path).to_s).
- to match("/uploads/group/avatar/#{ group.id }/gitlab_logo.png")
- end
-
- it 'should give default avatar_icon when no avatar is present' do
- group = create(:group)
- group.save!
- expect(group_icon(group.path)).to match('group_avatar.png')
- end
- end
-
describe 'project_icon' do
avatar_file_path = File.join(Rails.root, 'public', 'gitlab_logo.png')
diff --git a/spec/helpers/events_helper_spec.rb b/spec/helpers/events_helper_spec.rb
index c4a192ac1aa..b392371deb4 100644
--- a/spec/helpers/events_helper_spec.rb
+++ b/spec/helpers/events_helper_spec.rb
@@ -4,6 +4,8 @@ describe EventsHelper do
include ApplicationHelper
include GitlabMarkdownHelper
+ let(:current_user) { create(:user, email: "current@email.com") }
+
it 'should display one line of plain text without alteration' do
input = 'A short, plain note'
expect(event_note(input)).to match(input)
@@ -50,4 +52,14 @@ describe EventsHelper do
expect(event_note(input)).to match(link_url)
expect(event_note(input)).to match(expected_link_text)
end
+
+ it 'should preserve code color scheme' do
+ input = "```ruby\ndef test\n 'hello world'\nend\n```"
+ expected = '<pre class="code highlight white 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>\n" \
+ '</code></pre>'
+ expect(event_note(input)).to eq(expected)
+ end
end
diff --git a/spec/helpers/gitlab_markdown_helper_spec.rb b/spec/helpers/gitlab_markdown_helper_spec.rb
index 76fcf888a6a..fd80c615221 100644
--- a/spec/helpers/gitlab_markdown_helper_spec.rb
+++ b/spec/helpers/gitlab_markdown_helper_spec.rb
@@ -9,6 +9,7 @@ describe GitlabMarkdownHelper do
let(:user) { create(:user, username: 'gfm') }
let(:commit) { project.repository.commit }
+ let(:earlier_commit){ project.repository.commit("HEAD~2") }
let(:issue) { create(:issue, project: project) }
let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
let(:snippet) { create(:project_snippet, project: project) }
@@ -53,6 +54,53 @@ describe GitlabMarkdownHelper do
to have_selector('a.gfm.foo')
end
+ describe "referencing a commit range" do
+ let(:expected) { namespace_project_compare_path(project.namespace, project, from: earlier_commit.id, to: commit.id) }
+
+ it "should link using a full id" do
+ actual = "What happened in #{earlier_commit.id}...#{commit.id}"
+ expect(gfm(actual)).to match(expected)
+ end
+
+ it "should link using a short id" do
+ actual = "What happened in #{earlier_commit.short_id}...#{commit.short_id}"
+ expected = namespace_project_compare_path(project.namespace, project, from: earlier_commit.short_id, to: commit.short_id)
+ expect(gfm(actual)).to match(expected)
+ end
+
+ it "should link inclusively" do
+ actual = "What happened in #{earlier_commit.id}..#{commit.id}"
+ expected = namespace_project_compare_path(project.namespace, project, from: "#{earlier_commit.id}^", to: commit.id)
+ expect(gfm(actual)).to match(expected)
+ end
+
+ it "should link with adjacent text" do
+ actual = "(see #{earlier_commit.id}...#{commit.id})"
+ expect(gfm(actual)).to match(expected)
+ end
+
+ it "should keep whitespace intact" do
+ actual = "Changes #{earlier_commit.id}...#{commit.id} dramatically"
+ expected = /Changes <a.+>#{earlier_commit.id}...#{commit.id}<\/a> dramatically/
+ expect(gfm(actual)).to match(expected)
+ end
+
+ it "should not link with an invalid id" do
+ actual = expected = "What happened in #{earlier_commit.id.reverse}...#{commit.id.reverse}"
+ expect(gfm(actual)).to eq(expected)
+ end
+
+ it "should include a title attribute" do
+ actual = "What happened in #{earlier_commit.id}...#{commit.id}"
+ expect(gfm(actual)).to match(/title="Commits #{earlier_commit.id} through #{commit.id}"/)
+ end
+
+ it "should include standard gfm classes" do
+ actual = "What happened in #{earlier_commit.id}...#{commit.id}"
+ expect(gfm(actual)).to match(/class="\s?gfm gfm-commit_range\s?"/)
+ end
+ end
+
describe "referencing a commit" do
let(:expected) { namespace_project_commit_path(project.namespace, project, commit) }
@@ -616,19 +664,19 @@ describe GitlabMarkdownHelper do
it "should generate absolute urls for emoji" do
expect(markdown(':smile:')).to(
- include(%(src="#{Gitlab.config.gitlab.url}/assets/emoji/smile.png))
+ include(%(src="#{Gitlab.config.gitlab.url}/assets/emoji/#{Emoji.emoji_filename('smile')}.png))
)
end
it "should generate absolute urls for emoji if relative url is present" do
allow(Gitlab.config.gitlab).to receive(:url).and_return('http://localhost/gitlab/root')
- expect(markdown(":smile:")).to include("src=\"http://localhost/gitlab/root/assets/emoji/smile.png")
+ expect(markdown(":smile:")).to include("src=\"http://localhost/gitlab/root/assets/emoji/#{Emoji.emoji_filename('smile')}.png")
end
it "should generate absolute urls for emoji if asset_host is present" do
allow(Gitlab::Application.config).to receive(:asset_host).and_return("https://cdn.example.com")
ActionView::Base.any_instance.stub_chain(:config, :asset_host).and_return("https://cdn.example.com")
- expect(markdown(":smile:")).to include("src=\"https://cdn.example.com/assets/emoji/smile.png")
+ expect(markdown(":smile:")).to include("src=\"https://cdn.example.com/assets/emoji/#{Emoji.emoji_filename('smile')}.png")
end
diff --git a/spec/helpers/groups_helper.rb b/spec/helpers/groups_helper.rb
new file mode 100644
index 00000000000..3e99ab84ec9
--- /dev/null
+++ b/spec/helpers/groups_helper.rb
@@ -0,0 +1,21 @@
+require 'spec_helper'
+
+describe GroupsHelper do
+ describe 'group_icon' do
+ avatar_file_path = File.join(Rails.root, 'public', 'gitlab_logo.png')
+
+ it 'should return an url for the avatar' do
+ group = create(:group)
+ group.avatar = File.open(avatar_file_path)
+ group.save!
+ expect(group_icon(group.path).to_s).
+ to match("/uploads/group/avatar/#{ group.id }/gitlab_logo.png")
+ end
+
+ it 'should give default avatar_icon when no avatar is present' do
+ group = create(:group)
+ group.save!
+ expect(group_icon(group.path)).to match('group_avatar.png')
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ldap/access_spec.rb b/spec/lib/gitlab/ldap/access_spec.rb
index a2b05249147..39d46efcbc3 100644
--- a/spec/lib/gitlab/ldap/access_spec.rb
+++ b/spec/lib/gitlab/ldap/access_spec.rb
@@ -20,6 +20,11 @@ describe Gitlab::LDAP::Access do
before { Gitlab::LDAP::Person.stub(disabled_via_active_directory?: true) }
it { is_expected.to be_falsey }
+
+ it "should block user in GitLab" do
+ access.allowed?
+ user.should be_blocked
+ end
end
context 'and has no disabled flag in active diretory' do
@@ -38,4 +43,4 @@ describe Gitlab::LDAP::Access do
end
end
end
-end \ No newline at end of file
+end
diff --git a/spec/lib/gitlab/note_data_builder_spec.rb b/spec/lib/gitlab/note_data_builder_spec.rb
new file mode 100644
index 00000000000..448cd0c6880
--- /dev/null
+++ b/spec/lib/gitlab/note_data_builder_spec.rb
@@ -0,0 +1,73 @@
+require 'spec_helper'
+
+describe 'Gitlab::NoteDataBuilder' do
+ let(:project) { create(:project) }
+ let(:user) { create(:user) }
+ let(:data) { Gitlab::NoteDataBuilder.build(note, user) }
+ let(:note_url) { Gitlab::UrlBuilder.new(:note).build(note.id) }
+ let(:fixed_time) { Time.at(1425600000) } # Avoid time precision errors
+
+ before(:each) do
+ expect(data).to have_key(:object_attributes)
+ expect(data[:object_attributes]).to have_key(:url)
+ expect(data[:object_attributes][:url]).to eq(note_url)
+ expect(data[:object_kind]).to eq('note')
+ expect(data[:user]).to eq(user.hook_attrs)
+ end
+
+ describe 'When asking for a note on commit' do
+ let(:note) { create(:note_on_commit) }
+
+ it 'returns the note and commit-specific data' do
+ expect(data).to have_key(:commit)
+ end
+ end
+
+ describe 'When asking for a note on commit diff' do
+ let(:note) { create(:note_on_commit_diff) }
+
+ it 'returns the note and commit-specific data' do
+ expect(data).to have_key(:commit)
+ end
+ end
+
+ describe 'When asking for a note on issue' do
+ let(:issue) { create(:issue, created_at: fixed_time, updated_at: fixed_time) }
+ let(:note) { create(:note_on_issue, noteable_id: issue.id) }
+
+ it 'returns the note and issue-specific data' do
+ expect(data).to have_key(:issue)
+ expect(data[:issue]).to eq(issue.hook_attrs)
+ end
+ end
+
+ describe 'When asking for a note on merge request' do
+ let(:merge_request) { create(:merge_request, created_at: fixed_time, updated_at: fixed_time) }
+ let(:note) { create(:note_on_merge_request, noteable_id: merge_request.id) }
+
+ it 'returns the note and merge request data' do
+ expect(data).to have_key(:merge_request)
+ expect(data[:merge_request]).to eq(merge_request.hook_attrs)
+ end
+ end
+
+ describe 'When asking for a note on merge request diff' do
+ let(:merge_request) { create(:merge_request, created_at: fixed_time, updated_at: fixed_time) }
+ let(:note) { create(:note_on_merge_request_diff, noteable_id: merge_request.id) }
+
+ it 'returns the note and merge request diff data' do
+ expect(data).to have_key(:merge_request)
+ expect(data[:merge_request]).to eq(merge_request.hook_attrs)
+ end
+ end
+
+ describe 'When asking for a note on project snippet' do
+ let!(:snippet) { create(:project_snippet, created_at: fixed_time, updated_at: fixed_time) }
+ let!(:note) { create(:note_on_project_snippet, noteable_id: snippet.id) }
+
+ it 'returns the note and project snippet data' do
+ expect(data).to have_key(:snippet)
+ expect(data[:snippet]).to eq(snippet.hook_attrs)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/reference_extractor_spec.rb b/spec/lib/gitlab/reference_extractor_spec.rb
index 0847c31258c..034f8ee7c45 100644
--- a/spec/lib/gitlab/reference_extractor_spec.rb
+++ b/spec/lib/gitlab/reference_extractor_spec.rb
@@ -31,6 +31,11 @@ describe Gitlab::ReferenceExtractor do
expect(subject.commits).to eq([{ project: nil, id: '98cf0ae3' }])
end
+ it 'extracts commit ranges' do
+ subject.analyze('here you go, a commit range: 98cf0ae3...98cf0ae4', nil)
+ expect(subject.commit_ranges).to eq([{ project: nil, id: '98cf0ae3...98cf0ae4' }])
+ end
+
it 'extracts multiple references and preserves their order' do
subject.analyze('@me and @you both care about this', nil)
expect(subject.users).to eq([
@@ -100,5 +105,19 @@ describe Gitlab::ReferenceExtractor do
expect(extracted[0].sha).to eq(commit.sha)
expect(extracted[0].message).to eq(commit.message)
end
+
+ it 'accesses valid commit ranges' do
+ commit = project.repository.commit('master')
+ earlier_commit = project.repository.commit('master~2')
+
+ subject.analyze("this references commits #{earlier_commit.sha[0..6]}...#{commit.sha[0..6]}",
+ project)
+ extracted = subject.commit_ranges_for(project)
+ expect(extracted.size).to eq(1)
+ expect(extracted[0][0].sha).to eq(earlier_commit.sha)
+ expect(extracted[0][0].message).to eq(earlier_commit.message)
+ expect(extracted[0][1].sha).to eq(commit.sha)
+ expect(extracted[0][1].message).to eq(commit.message)
+ end
end
end
diff --git a/spec/lib/gitlab/url_builder_spec.rb b/spec/lib/gitlab/url_builder_spec.rb
index 94b2fd5508e..5153ed15af3 100644
--- a/spec/lib/gitlab/url_builder_spec.rb
+++ b/spec/lib/gitlab/url_builder_spec.rb
@@ -16,4 +16,62 @@ describe Gitlab::UrlBuilder do
expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}"
end
end
+
+ describe 'When asking for a note on commit' do
+ let(:note) { create(:note_on_commit) }
+ let(:url) { Gitlab::UrlBuilder.new(:note).build(note.id) }
+
+ it 'returns the note url' do
+ expect(url).to eq "#{Settings.gitlab['url']}/#{note.project.path_with_namespace}/commit/#{note.commit_id}#note_#{note.id}"
+ end
+ end
+
+ describe 'When asking for a note on commit diff' do
+ let(:note) { create(:note_on_commit_diff) }
+ let(:url) { Gitlab::UrlBuilder.new(:note).build(note.id) }
+
+ it 'returns the note url' do
+ expect(url).to eq "#{Settings.gitlab['url']}/#{note.project.path_with_namespace}/commit/#{note.commit_id}#note_#{note.id}"
+ end
+ end
+
+ describe 'When asking for a note on issue' do
+ let(:issue) { create(:issue) }
+ let(:note) { create(:note_on_issue, noteable_id: issue.id) }
+ let(:url) { Gitlab::UrlBuilder.new(:note).build(note.id) }
+
+ it 'returns the note url' do
+ expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.path_with_namespace}/issues/#{issue.iid}#note_#{note.id}"
+ end
+ end
+
+ describe 'When asking for a note on merge request' do
+ let(:merge_request) { create(:merge_request) }
+ let(:note) { create(:note_on_merge_request, noteable_id: merge_request.id) }
+ let(:url) { Gitlab::UrlBuilder.new(:note).build(note.id) }
+
+ it 'returns the note url' do
+ expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}#note_#{note.id}"
+ end
+ end
+
+ describe 'When asking for a note on merge request diff' do
+ let(:merge_request) { create(:merge_request) }
+ let(:note) { create(:note_on_merge_request_diff, noteable_id: merge_request.id) }
+ let(:url) { Gitlab::UrlBuilder.new(:note).build(note.id) }
+
+ it 'returns the note url' do
+ expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}#note_#{note.id}"
+ end
+ end
+
+ describe 'When asking for a note on project snippet' do
+ let(:snippet) { create(:project_snippet) }
+ let(:note) { create(:note_on_project_snippet, noteable_id: snippet.id) }
+ let(:url) { Gitlab::UrlBuilder.new(:note).build(note.id) }
+
+ it 'returns the note url' do
+ expect(url).to eq "#{Settings.gitlab['url']}/#{snippet.project.path_with_namespace}/snippets/#{note.noteable_id}#note_#{note.id}"
+ end
+ end
end
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index 3b09c618f2a..e3a3b542358 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -5,6 +5,7 @@ describe Notify do
include EmailSpec::Matchers
include RepoHelpers
+ let(:gitlab_sender_display_name) { Gitlab.config.gitlab.email_display_name }
let(:gitlab_sender) { Gitlab.config.gitlab.email_from }
let(:recipient) { create(:user, email: 'recipient@example.com') }
let(:project) { create(:project) }
@@ -23,7 +24,7 @@ describe Notify do
shared_examples 'an email sent from GitLab' do
it 'is sent from GitLab' do
sender = subject.header[:from].addrs[0]
- expect(sender.display_name).to eq('GitLab')
+ expect(sender.display_name).to eq(gitlab_sender_display_name)
expect(sender.address).to eq(gitlab_sender)
end
end
@@ -182,6 +183,13 @@ describe Notify do
context 'for issues' do
let(:issue) { create(:issue, author: current_user, assignee: assignee, project: project) }
let(:issue_with_description) { create(:issue, author: current_user, assignee: assignee, project: project, description: Faker::Lorem.sentence) }
+ let(:issue_with_image) do
+ create(:issue,
+ author: current_user,
+ assignee: assignee,
+ project: project,
+ description: "![test](#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/12345/test.jpg)")
+ end
describe 'that are new' do
subject { Notify.new_issue_email(issue.assignee_id, issue.id) }
@@ -206,6 +214,22 @@ describe Notify do
end
end
+ describe 'that contain images' do
+ let(:png) { File.read("#{Rails.root}/spec/fixtures/dk.png") }
+ let(:png_encoded) { Base64::encode64(png) }
+
+ before :each do
+ file_path = File.join(Rails.root, 'public', 'uploads', issue_with_image.project.path_with_namespace, '12345/test.jpg')
+ allow(File).to receive(:file?).with(file_path).and_return(true)
+ allow(File).to receive(:read).with(file_path).and_return(png)
+ end
+
+ subject { Notify.new_issue_email(issue_with_image.assignee_id, issue_with_image.id) }
+ it 'replaces attached images with inline images' do
+ is_expected.to have_body_text URI.encode(png_encoded)
+ end
+ end
+
describe 'that have been reassigned' do
subject { Notify.reassigned_issue_email(recipient.id, issue.id, previous_assignee.id, current_user) }
@@ -270,6 +294,14 @@ describe Notify do
let(:merge_author) { create(:user) }
let(:merge_request) { create(:merge_request, author: current_user, assignee: assignee, source_project: project, target_project: project) }
let(:merge_request_with_description) { create(:merge_request, author: current_user, assignee: assignee, source_project: project, target_project: project, description: Faker::Lorem.sentence) }
+ let(:merge_request_with_image) do
+ create(:merge_request,
+ author: current_user,
+ assignee: assignee,
+ source_project: project,
+ target_project: project,
+ description: "![test](#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/12345/test.jpg)")
+ end
describe 'that are new' do
subject { Notify.new_merge_request_email(merge_request.assignee_id, merge_request.id) }
@@ -306,6 +338,22 @@ describe Notify do
end
end
+ describe 'that are new and contain contain images in the description' do
+ let(:png) {File.read("#{Rails.root}/spec/fixtures/dk.png")}
+ let(:png_encoded) { Base64::encode64(png) }
+
+ before :each do
+ file_path = File.join(Rails.root, 'public', 'uploads', merge_request_with_image.project.path_with_namespace, '/12345/test.jpg')
+ allow(File).to receive(:file?).with(file_path).and_return(true)
+ allow(File).to receive(:read).with(file_path).and_return(png)
+ end
+
+ subject { Notify.new_merge_request_email(merge_request_with_image.assignee_id, merge_request_with_image.id) }
+ it 'replaces attached images with inline images' do
+ is_expected.to have_body_text URI.encode(png_encoded)
+ end
+ end
+
describe 'that are reassigned' do
subject { Notify.reassigned_merge_request_email(recipient.id, merge_request.id, previous_assignee.id, current_user.id) }
@@ -414,9 +462,12 @@ describe Notify do
describe 'project access changed' do
let(:project) { create(:project) }
let(:user) { create(:user) }
- let(:project_member) { create(:project_member,
- project: project,
- user: user) }
+ let(:project_member) do
+ create(:project_member,
+ project: project,
+ user: user)
+ end
+
subject { Notify.project_access_granted_email(project_member.id) }
it_behaves_like 'an email sent from GitLab'
@@ -456,6 +507,32 @@ describe Notify do
end
end
+ describe 'on a commit that contains an image' do
+ let(:commit) { project.repository.commit }
+ let(:note_with_image) do
+ create(:note,
+ project: project,
+ author: note_author,
+ note: "![test](#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/12345/test.jpg)")
+ end
+
+ let(:png) {File.read("#{Rails.root}/spec/fixtures/dk.png")}
+ let(:png_encoded) { Base64::encode64(png) }
+
+ before :each do
+ file_path = File.join(Rails.root, 'public', 'uploads', note_with_image.project.path_with_namespace, '12345/test.jpg')
+ allow(File).to receive(:file?).with(file_path).and_return(true)
+ allow(File).to receive(:read).with(file_path).and_return(png)
+ allow(Note).to receive(:find).with(note_with_image.id).and_return(note_with_image)
+ allow(note_with_image).to receive(:noteable).and_return(commit)
+ end
+
+ subject { Notify.note_commit_email(recipient.id, note_with_image.id) }
+ it 'replaces attached images with inline images' do
+ is_expected.to have_body_text URI.encode(png_encoded)
+ end
+ end
+
describe 'on a commit' do
let(:commit) { project.repository.commit }
@@ -568,9 +645,10 @@ describe Notify do
let(:user) { create(:user) }
let(:compare) { Gitlab::Git::Compare.new(project.repository.raw_repository, sample_image_commit.id, sample_commit.id) }
let(:commits) { Commit.decorate(compare.commits) }
- let(:diff_path) { namespace_project_compare_path(project.namespace, project, from: commits.first, to: commits.last) }
+ let(:diff_path) { namespace_project_compare_path(project.namespace, project, from: Commit.new(compare.base), to: Commit.new(compare.head)) }
+ let(:send_from_committer_email) { false }
- subject { Notify.repository_push_email(project.id, 'devs@company.name', user.id, 'master', compare) }
+ subject { Notify.repository_push_email(project.id, 'devs@company.name', user.id, 'master', compare, false, send_from_committer_email) }
it 'is sent as the author' do
sender = subject.header[:from].addrs[0]
@@ -583,7 +661,7 @@ describe Notify do
end
it 'has the correct subject' do
- is_expected.to have_subject /#{commits.length} new commits pushed to repository/
+ is_expected.to have_subject /\[#{project.path_with_namespace}\]\[master\] #{commits.length} commits:/
end
it 'includes commits list' do
@@ -597,6 +675,58 @@ describe Notify do
it 'contains a link to the diff' do
is_expected.to have_body_text /#{diff_path}/
end
+
+ it 'doesn not contain the misleading footer' do
+ is_expected.not_to have_body_text /you are a member of/
+ end
+
+ context "when set to send from committer email if domain matches" do
+
+ let(:send_from_committer_email) { true }
+
+ before do
+ allow(Gitlab.config.gitlab).to receive(:host).and_return("gitlab.corp.company.com")
+ end
+
+ context "when the committer email domain is within the GitLab domain" do
+
+ before do
+ user.update_attribute(:email, "user@company.com")
+ user.confirm!
+ end
+
+ it "is sent from the committer email" do
+ sender = subject.header[:from].addrs[0]
+ expect(sender.address).to eq(user.email)
+ end
+ end
+
+ context "when the committer email domain is not completely within the GitLab domain" do
+
+ before do
+ user.update_attribute(:email, "user@something.company.com")
+ user.confirm!
+ end
+
+ it "is sent from the default email" do
+ sender = subject.header[:from].addrs[0]
+ expect(sender.address).to eq(gitlab_sender)
+ end
+ end
+
+ context "when the committer email domain is outside the GitLab domain" do
+
+ before do
+ user.update_attribute(:email, "user@mpany.com")
+ user.confirm!
+ end
+
+ it "is sent from the default email" do
+ sender = subject.header[:from].addrs[0]
+ expect(sender.address).to eq(gitlab_sender)
+ end
+ end
+ end
end
describe 'email on push with a single commit' do
diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb
index cb43fdb7fc7..d1027f64d13 100644
--- a/spec/models/application_setting_spec.rb
+++ b/spec/models/application_setting_spec.rb
@@ -2,16 +2,17 @@
#
# Table name: application_settings
#
-# id :integer not null, primary key
-# default_projects_limit :integer
-# default_branch_protection :integer
-# signup_enabled :boolean
-# signin_enabled :boolean
-# gravatar_enabled :boolean
-# sign_in_text :text
-# created_at :datetime
-# updated_at :datetime
-# home_page_url :string(255)
+# id :integer not null, primary key
+# default_projects_limit :integer
+# signup_enabled :boolean
+# signin_enabled :boolean
+# gravatar_enabled :boolean
+# sign_in_text :text
+# created_at :datetime
+# updated_at :datetime
+# home_page_url :string(255)
+# default_branch_protection :integer default(2)
+# twitter_sharing_enabled :boolean default(TRUE)
#
require 'spec_helper'
diff --git a/spec/models/asana_service_spec.rb b/spec/models/asana_service_spec.rb
index 83e39f87f33..13c8d54a2af 100644
--- a/spec/models/asana_service_spec.rb
+++ b/spec/models/asana_service_spec.rb
@@ -2,14 +2,19 @@
#
# Table name: services
#
-# id :integer not null, primary key
-# type :string(255)
-# title :string(255)
-# project_id :integer not null
-# created_at :datetime
-# updated_at :datetime
-# active :boolean default(FALSE), not null
-# properties :text
+# 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)
#
require 'spec_helper'
diff --git a/spec/models/project_services/assembla_service_spec.rb b/spec/models/project_services/assembla_service_spec.rb
index cd34e006ebe..91730da1eec 100644
--- a/spec/models/project_services/assembla_service_spec.rb
+++ b/spec/models/project_services/assembla_service_spec.rb
@@ -5,15 +5,16 @@
# id :integer not null, primary key
# type :string(255)
# title :string(255)
-# project_id :integer not null
+# project_id :integer
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
-# push_events :boolean
-# issues_events :boolean
-# merge_requests_events :boolean
-# tag_push_events :boolean
+# 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)
#
require 'spec_helper'
diff --git a/spec/models/project_services/buildbox_service_spec.rb b/spec/models/project_services/buildbox_service_spec.rb
index c246e1c9d41..39d7df54cf0 100644
--- a/spec/models/project_services/buildbox_service_spec.rb
+++ b/spec/models/project_services/buildbox_service_spec.rb
@@ -5,15 +5,16 @@
# id :integer not null, primary key
# type :string(255)
# title :string(255)
-# project_id :integer not null
+# project_id :integer
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
-# push_events :boolean
-# issues_events :boolean
-# merge_requests_events :boolean
-# tag_push_events :boolean
+# 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)
#
require 'spec_helper'
diff --git a/spec/models/project_services/flowdock_service_spec.rb b/spec/models/project_services/flowdock_service_spec.rb
index 2ec167a7330..73f68301a34 100644
--- a/spec/models/project_services/flowdock_service_spec.rb
+++ b/spec/models/project_services/flowdock_service_spec.rb
@@ -5,15 +5,16 @@
# id :integer not null, primary key
# type :string(255)
# title :string(255)
-# project_id :integer not null
+# project_id :integer
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
-# push_events :boolean
-# issues_events :boolean
-# merge_requests_events :boolean
-# tag_push_events :boolean
+# 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)
#
require 'spec_helper'
diff --git a/spec/models/project_services/gemnasium_service_spec.rb b/spec/models/project_services/gemnasium_service_spec.rb
index 5f665fadfff..d44064bbe6a 100644
--- a/spec/models/project_services/gemnasium_service_spec.rb
+++ b/spec/models/project_services/gemnasium_service_spec.rb
@@ -5,15 +5,16 @@
# id :integer not null, primary key
# type :string(255)
# title :string(255)
-# project_id :integer not null
+# project_id :integer
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
-# push_events :boolean
-# issues_events :boolean
-# merge_requests_events :boolean
-# tag_push_events :boolean
+# 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)
#
require 'spec_helper'
diff --git a/spec/models/project_services/gitlab_ci_service_spec.rb b/spec/models/project_services/gitlab_ci_service_spec.rb
index fcb33b11732..8bfb19e524b 100644
--- a/spec/models/project_services/gitlab_ci_service_spec.rb
+++ b/spec/models/project_services/gitlab_ci_service_spec.rb
@@ -5,15 +5,16 @@
# id :integer not null, primary key
# type :string(255)
# title :string(255)
-# project_id :integer not null
+# project_id :integer
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
-# push_events :boolean
-# issues_events :boolean
-# merge_requests_events :boolean
-# tag_push_events :boolean
+# 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)
#
require 'spec_helper'
diff --git a/spec/models/project_services/gitlab_issue_tracker_service_spec.rb b/spec/models/project_services/gitlab_issue_tracker_service_spec.rb
index c474f4a2d95..959044dc727 100644
--- a/spec/models/project_services/gitlab_issue_tracker_service_spec.rb
+++ b/spec/models/project_services/gitlab_issue_tracker_service_spec.rb
@@ -2,16 +2,21 @@
#
# 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)
+# 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)
#
+
require 'spec_helper'
describe GitlabIssueTrackerService do
diff --git a/spec/models/project_services/hipchat_service_spec.rb b/spec/models/project_services/hipchat_service_spec.rb
new file mode 100644
index 00000000000..8ab847e6432
--- /dev/null
+++ b/spec/models/project_services/hipchat_service_spec.rb
@@ -0,0 +1,217 @@
+# == Schema Information
+#
+# Table name: services
+#
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# project_id :integer not null
+# created_at :datetime
+# updated_at :datetime
+# active :boolean default(FALSE), not null
+# properties :text
+# push_events :boolean default(TRUE)
+# issues_events :boolean default(TRUE)
+# merge_requests_events :boolean default(TRUE)
+# tag_push_events :boolean default(TRUE)
+#
+
+require 'spec_helper'
+
+describe HipchatService do
+ describe "Associations" do
+ it { is_expected.to belong_to :project }
+ it { is_expected.to have_one :service_hook }
+ end
+
+ describe "Execute" do
+ let(:hipchat) { HipchatService.new }
+ let(:user) { create(:user, username: 'username') }
+ let(:project) { create(:project, name: 'project') }
+ let(:api_url) { 'https://hipchat.example.com/v2/room/123456/notification?auth_token=verySecret' }
+ let(:project_name) { project.name_with_namespace.gsub(/\s/, '') }
+
+ before(:each) do
+ hipchat.stub(
+ project_id: project.id,
+ project: project,
+ room: 123456,
+ server: 'https://hipchat.example.com',
+ token: 'verySecret'
+ )
+ WebMock.stub_request(:post, api_url)
+ end
+
+ context 'push events' do
+ let(:push_sample_data) { Gitlab::PushDataBuilder.build_sample(project, user) }
+
+ it "should call Hipchat API for push events" do
+ hipchat.execute(push_sample_data)
+
+ expect(WebMock).to have_requested(:post, api_url).once
+ end
+
+ it "should create a push message" do
+ message = hipchat.send(:create_push_message, push_sample_data)
+
+ obj_attr = push_sample_data[:object_attributes]
+ branch = push_sample_data[:ref].gsub('refs/heads/', '')
+ expect(message).to include("#{user.name} pushed to branch " \
+ "<a href=\"#{project.web_url}/commits/#{branch}\">#{branch}</a> of " \
+ "<a href=\"#{project.web_url}\">#{project_name}</a>")
+ end
+ end
+
+ context 'tag_push events' do
+ let(:push_sample_data) { Gitlab::PushDataBuilder.build(project, user, Gitlab::Git::BLANK_SHA, '1' * 40, 'refs/tags/test', []) }
+
+ it "should call Hipchat API for tag push events" do
+ hipchat.execute(push_sample_data)
+
+ expect(WebMock).to have_requested(:post, api_url).once
+ end
+
+ it "should create a tag push message" do
+ message = hipchat.send(:create_push_message, push_sample_data)
+
+ obj_attr = push_sample_data[:object_attributes]
+ expect(message).to eq("#{user.name} pushed new tag " \
+ "<a href=\"#{project.web_url}/commits/test\">test</a> to " \
+ "<a href=\"#{project.web_url}\">#{project_name}</a>\n")
+ end
+ end
+
+ context 'issue events' do
+ let(:issue) { create(:issue, title: 'Awesome issue', description: 'please fix') }
+ let(:issue_service) { Issues::CreateService.new(project, user) }
+ let(:issues_sample_data) { issue_service.hook_data(issue, 'open') }
+
+ it "should call Hipchat API for issue events" do
+ hipchat.execute(issues_sample_data)
+
+ expect(WebMock).to have_requested(:post, api_url).once
+ end
+
+ it "should create an issue message" do
+ message = hipchat.send(:create_issue_message, issues_sample_data)
+
+ obj_attr = issues_sample_data[:object_attributes]
+ expect(message).to eq("#{user.name} opened " \
+ "<a href=\"#{obj_attr[:url]}\">issue ##{obj_attr["iid"]}</a> in " \
+ "<a href=\"#{project.web_url}\">#{project_name}</a>: " \
+ "<b>Awesome issue</b>" \
+ "<pre>please fix</pre>")
+ end
+ end
+
+ context 'merge request events' do
+ let(:merge_request) { create(:merge_request, description: 'please fix', title: 'Awesome merge request', target_project: project, source_project: project) }
+ let(:merge_service) { MergeRequests::CreateService.new(project, user) }
+ let(:merge_sample_data) { merge_service.hook_data(merge_request, 'open') }
+
+ it "should call Hipchat API for merge requests events" do
+ hipchat.execute(merge_sample_data)
+
+ expect(WebMock).to have_requested(:post, api_url).once
+ end
+
+ it "should create a merge request message" do
+ message = hipchat.send(:create_merge_request_message,
+ merge_sample_data)
+
+ obj_attr = merge_sample_data[:object_attributes]
+ expect(message).to eq("#{user.name} opened " \
+ "<a href=\"#{obj_attr[:url]}\">merge request ##{obj_attr["iid"]}</a> in " \
+ "<a href=\"#{project.web_url}\">#{project_name}</a>: " \
+ "<b>Awesome merge request</b>" \
+ "<pre>please fix</pre>")
+ end
+ end
+
+ context "Note events" do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, creator_id: user.id) }
+ let(:issue) { create(:issue, project: project) }
+ let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
+ let(:snippet) { create(:project_snippet, project: project) }
+ let(:commit_note) { create(:note_on_commit, author: user, project: project, commit_id: project.repository.commit.id, note: 'a comment on a commit') }
+ let(:merge_request_note) { create(:note_on_merge_request, noteable_id: merge_request.id, note: "merge request note") }
+ let(:issue_note) { create(:note_on_issue, noteable_id: issue.id, note: "issue note")}
+ let(:snippet_note) { create(:note_on_project_snippet, noteable_id: snippet.id, note: "snippet note") }
+
+ it "should call Hipchat API for commit comment events" do
+ data = Gitlab::NoteDataBuilder.build(commit_note, user)
+ hipchat.execute(data)
+
+ expect(WebMock).to have_requested(:post, api_url).once
+
+ message = hipchat.send(:create_message, data)
+
+ obj_attr = data[:object_attributes]
+ commit_id = Commit.truncate_sha(data[:commit][:id])
+ title = hipchat.send(:format_title, data[:commit][:message])
+
+ expect(message).to eq("#{user.name} commented on " \
+ "<a href=\"#{obj_attr[:url]}\">commit #{commit_id}</a> in " \
+ "<a href=\"#{project.web_url}\">#{project_name}</a>: " \
+ "#{title}" \
+ "<pre>a comment on a commit</pre>")
+ end
+
+ it "should call Hipchat API for merge request comment events" do
+ data = Gitlab::NoteDataBuilder.build(merge_request_note, user)
+ hipchat.execute(data)
+
+ expect(WebMock).to have_requested(:post, api_url).once
+
+ message = hipchat.send(:create_message, data)
+
+ obj_attr = data[:object_attributes]
+ merge_id = data[:merge_request]['iid']
+ title = data[:merge_request]['title']
+
+ expect(message).to eq("#{user.name} commented on " \
+ "<a href=\"#{obj_attr[:url]}\">merge request ##{merge_id}</a> in " \
+ "<a href=\"#{project.web_url}\">#{project_name}</a>: " \
+ "<b>#{title}</b>" \
+ "<pre>merge request note</pre>")
+ end
+
+ it "should call Hipchat API for issue comment events" do
+ data = Gitlab::NoteDataBuilder.build(issue_note, user)
+ hipchat.execute(data)
+
+ message = hipchat.send(:create_message, data)
+
+ obj_attr = data[:object_attributes]
+ issue_id = data[:issue]['iid']
+ title = data[:issue]['title']
+
+ expect(message).to eq("#{user.name} commented on " \
+ "<a href=\"#{obj_attr[:url]}\">issue ##{issue_id}</a> in " \
+ "<a href=\"#{project.web_url}\">#{project_name}</a>: " \
+ "<b>#{title}</b>" \
+ "<pre>issue note</pre>")
+ end
+
+ it "should call Hipchat API for snippet comment events" do
+ data = Gitlab::NoteDataBuilder.build(snippet_note, user)
+ hipchat.execute(data)
+
+ expect(WebMock).to have_requested(:post, api_url).once
+
+ message = hipchat.send(:create_message, data)
+
+ obj_attr = data[:object_attributes]
+ snippet_id = data[:snippet]['id']
+ title = data[:snippet]['title']
+
+ expect(message).to eq("#{user.name} commented on " \
+ "<a href=\"#{obj_attr[:url]}\">snippet ##{snippet_id}</a> in " \
+ "<a href=\"#{project.web_url}\">#{project_name}</a>: " \
+ "<b>#{title}</b>" \
+ "<pre>snippet note</pre>")
+ end
+ end
+ end
+end
diff --git a/spec/models/project_services/irker_service_spec.rb b/spec/models/project_services/irker_service_spec.rb
index bbd5245ad34..d55399bc360 100644
--- a/spec/models/project_services/irker_service_spec.rb
+++ b/spec/models/project_services/irker_service_spec.rb
@@ -2,14 +2,19 @@
#
# Table name: services
#
-# id :integer not null, primary key
-# type :string(255)
-# title :string(255)
-# project_id :integer not null
-# created_at :datetime
-# updated_at :datetime
-# active :boolean default(FALSE), not null
-# properties :text
+# 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)
#
require 'spec_helper'
diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb
index 6ef4d036c3f..355911e6377 100644
--- a/spec/models/project_services/jira_service_spec.rb
+++ b/spec/models/project_services/jira_service_spec.rb
@@ -2,14 +2,19 @@
#
# Table name: services
#
-# id :integer not null, primary key
-# type :string(255)
-# title :string(255)
-# project_id :integer not null
-# created_at :datetime
-# updated_at :datetime
-# active :boolean default(FALSE), not null
-# properties :text
+# 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)
#
require 'spec_helper'
diff --git a/spec/models/project_services/pushover_service_spec.rb b/spec/models/project_services/pushover_service_spec.rb
index bb2e72c3ac1..5a18fd09bfc 100644
--- a/spec/models/project_services/pushover_service_spec.rb
+++ b/spec/models/project_services/pushover_service_spec.rb
@@ -5,15 +5,16 @@
# id :integer not null, primary key
# type :string(255)
# title :string(255)
-# project_id :integer not null
+# project_id :integer
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
-# push_events :boolean
-# issues_events :boolean
-# merge_requests_events :boolean
-# tag_push_events :boolean
+# 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)
#
require 'spec_helper'
diff --git a/spec/models/project_services/slack_service/issue_message_spec.rb b/spec/models/project_services/slack_service/issue_message_spec.rb
index a23a7cc068e..8bca1fef44c 100644
--- a/spec/models/project_services/slack_service/issue_message_spec.rb
+++ b/spec/models/project_services/slack_service/issue_message_spec.rb
@@ -6,7 +6,8 @@ describe SlackService::IssueMessage do
let(:args) {
{
user: {
- username: 'username'
+ name: 'Test User',
+ username: 'Test User'
},
project_name: 'project_name',
project_url: 'somewhere.com',
@@ -29,8 +30,8 @@ describe SlackService::IssueMessage do
context 'open' do
it 'returns a message regarding opening of issues' do
expect(subject.pretext).to eq(
- 'username opened issue <url|#100> in <somewhere.com|project_name>: '\
- 'Issue title')
+ 'Test User opened <url|issue #100> in <somewhere.com|project_name>: '\
+ '*Issue title*')
expect(subject.attachments).to eq([
{
text: "issue description",
@@ -47,8 +48,8 @@ describe SlackService::IssueMessage do
end
it 'returns a message regarding closing of issues' do
expect(subject.pretext). to eq(
- 'username closed issue <url|#100> in <somewhere.com|project_name>: '\
- 'Issue title')
+ 'Test User closed <url|issue #100> in <somewhere.com|project_name>: '\
+ '*Issue title*')
expect(subject.attachments).to be_empty
end
end
diff --git a/spec/models/project_services/slack_service/merge_message_spec.rb b/spec/models/project_services/slack_service/merge_message_spec.rb
index 25d03cd8736..aeb408aa766 100644
--- a/spec/models/project_services/slack_service/merge_message_spec.rb
+++ b/spec/models/project_services/slack_service/merge_message_spec.rb
@@ -6,13 +6,14 @@ describe SlackService::MergeMessage do
let(:args) {
{
user: {
- username: 'username'
+ name: 'Test User',
+ username: 'Test User'
},
project_name: 'project_name',
project_url: 'somewhere.com',
object_attributes: {
- title: 'Issue title',
+ title: "Issue title\nSecond line",
id: 10,
iid: 100,
assignee_id: 1,
@@ -30,8 +31,8 @@ describe SlackService::MergeMessage do
context 'open' do
it 'returns a message regarding opening of merge requests' do
expect(subject.pretext).to eq(
- 'username opened merge request <somewhere.com/merge_requests/100|#100> '\
- 'in <somewhere.com|project_name>')
+ 'Test User opened <somewhere.com/merge_requests/100|merge request #100> '\
+ 'in <somewhere.com|project_name>: *Issue title*')
expect(subject.attachments).to be_empty
end
end
@@ -42,8 +43,8 @@ describe SlackService::MergeMessage do
end
it 'returns a message regarding closing of merge requests' do
expect(subject.pretext).to eq(
- 'username closed merge request <somewhere.com/merge_requests/100|#100> '\
- 'in <somewhere.com|project_name>')
+ 'Test User closed <somewhere.com/merge_requests/100|merge request #100> '\
+ 'in <somewhere.com|project_name>: *Issue title*')
expect(subject.attachments).to be_empty
end
end
diff --git a/spec/models/project_services/slack_service/note_message_spec.rb b/spec/models/project_services/slack_service/note_message_spec.rb
new file mode 100644
index 00000000000..21fb575480b
--- /dev/null
+++ b/spec/models/project_services/slack_service/note_message_spec.rb
@@ -0,0 +1,129 @@
+require 'spec_helper'
+
+describe SlackService::NoteMessage do
+ let(:color) { '#345' }
+
+ before do
+ @args = {
+ user: {
+ name: 'Test User',
+ username: 'username',
+ avatar_url: 'http://fakeavatar'
+ },
+ project_name: 'project_name',
+ project_url: 'somewhere.com',
+ repository: {
+ name: 'project_name',
+ url: 'somewhere.com',
+ },
+ object_attributes: {
+ id: 10,
+ note: 'comment on a commit',
+ url: 'url',
+ noteable_type: 'Commit'
+ }
+ }
+ end
+
+ context 'commit notes' do
+ before do
+ @args[:object_attributes][:note] = 'comment on a commit'
+ @args[:object_attributes][:noteable_type] = 'Commit'
+ @args[:commit] = {
+ id: '5f163b2b95e6f53cbd428f5f0b103702a52b9a23',
+ message: "Added a commit message\ndetails\n123\n"
+ }
+ end
+
+ it 'returns a message regarding notes on commits' do
+ message = SlackService::NoteMessage.new(@args)
+ expect(message.pretext).to eq("Test User commented on " \
+ "<url|commit 5f163b2b> in <somewhere.com|project_name>: " \
+ "*Added a commit message*")
+ expected_attachments = [
+ {
+ text: "comment on a commit",
+ color: color,
+ }
+ ]
+ expect(message.attachments).to eq(expected_attachments)
+ end
+ end
+
+ context 'merge request notes' do
+ before do
+ @args[:object_attributes][:note] = 'comment on a merge request'
+ @args[:object_attributes][:noteable_type] = 'MergeRequest'
+ @args[:merge_request] = {
+ id: 1,
+ iid: 30,
+ title: "merge request title\ndetails\n"
+ }
+ end
+ it 'returns a message regarding notes on a merge request' do
+ message = SlackService::NoteMessage.new(@args)
+ expect(message.pretext).to eq("Test User commented on " \
+ "<url|merge request #30> in <somewhere.com|project_name>: " \
+ "*merge request title*")
+ expected_attachments = [
+ {
+ text: "comment on a merge request",
+ color: color,
+ }
+ ]
+ expect(message.attachments).to eq(expected_attachments)
+ end
+ end
+
+ context 'issue notes' do
+ before do
+ @args[:object_attributes][:note] = 'comment on an issue'
+ @args[:object_attributes][:noteable_type] = 'Issue'
+ @args[:issue] = {
+ id: 1,
+ iid: 20,
+ title: "issue title\ndetails\n"
+ }
+ end
+
+ it 'returns a message regarding notes on an issue' do
+ message = SlackService::NoteMessage.new(@args)
+ expect(message.pretext).to eq(
+ "Test User commented on " \
+ "<url|issue #20> in <somewhere.com|project_name>: " \
+ "*issue title*")
+ expected_attachments = [
+ {
+ text: "comment on an issue",
+ color: color,
+ }
+ ]
+ expect(message.attachments).to eq(expected_attachments)
+ end
+ end
+
+ context 'project snippet notes' do
+ before do
+ @args[:object_attributes][:note] = 'comment on a snippet'
+ @args[:object_attributes][:noteable_type] = 'Snippet'
+ @args[:snippet] = {
+ id: 5,
+ title: "snippet title\ndetails\n"
+ }
+ end
+
+ it 'returns a message regarding notes on a project snippet' do
+ message = SlackService::NoteMessage.new(@args)
+ expect(message.pretext).to eq("Test User commented on " \
+ "<url|snippet #5> in <somewhere.com|project_name>: " \
+ "*snippet title*")
+ expected_attachments = [
+ {
+ text: "comment on a snippet",
+ color: color,
+ }
+ ]
+ expect(message.attachments).to eq(expected_attachments)
+ end
+ end
+end
diff --git a/spec/models/project_services/slack_service/push_message_spec.rb b/spec/models/project_services/slack_service/push_message_spec.rb
index ef0e7a6ee30..10963481a12 100644
--- a/spec/models/project_services/slack_service/push_message_spec.rb
+++ b/spec/models/project_services/slack_service/push_message_spec.rb
@@ -39,9 +39,29 @@ describe SlackService::PushMessage do
end
end
+ context 'tag push' do
+ let(:args) {
+ {
+ after: 'after',
+ before: Gitlab::Git::BLANK_SHA,
+ project_name: 'project_name',
+ ref: 'refs/tags/new_tag',
+ user_name: 'user_name',
+ project_url: 'url'
+ }
+ }
+
+ it 'returns a message regarding pushes' do
+ expect(subject.pretext).to eq('user_name pushed new tag ' \
+ '<url/commits/new_tag|new_tag> to ' \
+ '<url|project_name>')
+ expect(subject.attachments).to be_empty
+ end
+ end
+
context 'new branch' do
before do
- args[:before] = '000000'
+ args[:before] = Gitlab::Git::BLANK_SHA
end
it 'returns a message regarding a new branch' do
@@ -55,7 +75,7 @@ describe SlackService::PushMessage do
context 'removed branch' do
before do
- args[:after] = '000000'
+ args[:after] = Gitlab::Git::BLANK_SHA
end
it 'returns a message regarding a removed branch' do
diff --git a/spec/models/project_services/slack_service_spec.rb b/spec/models/project_services/slack_service_spec.rb
index 9024e53f0ff..c36506644b3 100644
--- a/spec/models/project_services/slack_service_spec.rb
+++ b/spec/models/project_services/slack_service_spec.rb
@@ -5,15 +5,16 @@
# id :integer not null, primary key
# type :string(255)
# title :string(255)
-# project_id :integer not null
+# project_id :integer
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
-# push_events :boolean
-# issues_events :boolean
-# merge_requests_events :boolean
-# tag_push_events :boolean
+# 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)
#
require 'spec_helper'
@@ -75,16 +76,16 @@ describe SlackService do
'open')
end
- it "should call Slack API for pull requests" do
+ it "should call Slack API for push events" do
slack.execute(push_sample_data)
- WebMock.should have_requested(:post, webhook_url).once
+ expect(WebMock).to have_requested(:post, webhook_url).once
end
it "should call Slack API for issue events" do
slack.execute(@issues_sample_data)
- WebMock.should have_requested(:post, webhook_url).once
+ expect(WebMock).to have_requested(:post, webhook_url).once
end
it "should call Slack API for merge requests events" do
@@ -96,10 +97,10 @@ describe SlackService do
it 'should use the username as an option for slack when configured' do
slack.stub(username: username)
expect(Slack::Notifier).to receive(:new).
- with(webhook_url, username: username).
- and_return(
- double(:slack_service).as_null_object
- )
+ with(webhook_url, username: username).
+ and_return(
+ double(:slack_service).as_null_object
+ )
slack.execute(push_sample_data)
end
@@ -113,4 +114,57 @@ describe SlackService do
slack.execute(push_sample_data)
end
end
+
+ describe "Note events" do
+ let(:slack) { SlackService.new }
+ let(:user) { create(:user) }
+ let(:project) { create(:project, creator_id: user.id) }
+ let(:issue) { create(:issue, project: project) }
+ let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
+ let(:snippet) { create(:project_snippet, project: project) }
+ let(:commit_note) { create(:note_on_commit, author: user, project: project, commit_id: project.repository.commit.id, note: 'a comment on a commit') }
+ let(:merge_request_note) { create(:note_on_merge_request, noteable_id: merge_request.id, note: "merge request note") }
+ let(:issue_note) { create(:note_on_issue, noteable_id: issue.id, note: "issue note")}
+ let(:snippet_note) { create(:note_on_project_snippet, noteable_id: snippet.id, note: "snippet note") }
+ let(:webhook_url) { 'https://hooks.slack.com/services/SVRWFV0VVAR97N/B02R25XN3/ZBqu7xMupaEEICInN685' }
+
+ before do
+ slack.stub(
+ project: project,
+ project_id: project.id,
+ service_hook: true,
+ webhook: webhook_url
+ )
+
+ WebMock.stub_request(:post, webhook_url)
+ end
+
+ it "should call Slack API for commit comment events" do
+ data = Gitlab::NoteDataBuilder.build(commit_note, user)
+ slack.execute(data)
+
+ expect(WebMock).to have_requested(:post, webhook_url).once
+ end
+
+ it "should call Slack API for merge request comment events" do
+ data = Gitlab::NoteDataBuilder.build(merge_request_note, user)
+ slack.execute(data)
+
+ expect(WebMock).to have_requested(:post, webhook_url).once
+ end
+
+ it "should call Slack API for issue comment events" do
+ data = Gitlab::NoteDataBuilder.build(issue_note, user)
+ slack.execute(data)
+
+ expect(WebMock).to have_requested(:post, webhook_url).once
+ end
+
+ it "should call Slack API for snippet comment events" do
+ data = Gitlab::NoteDataBuilder.build(snippet_note, user)
+ slack.execute(data)
+
+ expect(WebMock).to have_requested(:post, webhook_url).once
+ end
+ end
end
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index eeb0f3d9ee0..b3a38f6c5b9 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -18,4 +18,27 @@ describe Repository do
it { is_expected.to eq('c1acaa58bbcbc3eafe538cb8274ba387047b69f8') }
end
+
+ context :timestamps_by_user_log do
+ before do
+ Date.stub(:today).and_return(Date.new(2015, 03, 01))
+ end
+
+ describe 'single e-mail for user' do
+ let(:user) { create(:user, email: sample_commit.author_email) }
+
+ subject { repository.timestamps_by_user_log(user) }
+
+ it { is_expected.to eq(["2014-08-06", "2014-07-31", "2014-07-31"]) }
+ end
+
+ describe 'multiple emails for user' do
+ let(:email_alias) { create(:email, email: another_sample_commit.author_email) }
+ let(:user) { create(:user, email: sample_commit.author_email, emails: [email_alias]) }
+
+ subject { repository.timestamps_by_user_log(user) }
+
+ it { is_expected.to eq(["2015-01-10", "2014-08-06", "2014-07-31", "2014-07-31"]) }
+ end
+ end
end
diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb
index cc047a20dd2..735652aea78 100644
--- a/spec/models/service_spec.rb
+++ b/spec/models/service_spec.rb
@@ -2,19 +2,19 @@
#
# 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
-# issues_events :boolean
-# merge_requests_events :boolean
-# tag_push_events :boolean
+# 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)
#
require 'spec_helper'
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 29d0c24e87e..10e90cae143 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -2,47 +2,53 @@
#
# 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
-# 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
-# last_credential_check_at :datetime
-# github_access_token :string(255)
+# 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)
#
require 'spec_helper'
diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb
index e264072b573..1b1e3ca5f8b 100644
--- a/spec/services/git_push_service_spec.rb
+++ b/spec/services/git_push_service_spec.rb
@@ -197,15 +197,6 @@ describe GitPushService do
service.execute(project, user, @blankrev, @newrev, 'refs/heads/other')
end
-
- it "finds references in the first push to a default branch" do
- allow(project.repository).to receive(:commits_between).with(@blankrev, @newrev).and_return([])
- allow(project.repository).to receive(:commits).with(@newrev).and_return([commit])
-
- expect(Note).to receive(:create_cross_reference_note).with(issue, commit, commit_author, project)
-
- service.execute(project, user, @blankrev, @newrev, 'refs/heads/master')
- end
end
describe "closing issues from pushed commits" do
diff --git a/spec/services/issues/bulk_update_service_spec.rb b/spec/services/issues/bulk_update_service_spec.rb
index 504213e667f..a97c55011c9 100644
--- a/spec/services/issues/bulk_update_service_spec.rb
+++ b/spec/services/issues/bulk_update_service_spec.rb
@@ -21,10 +21,8 @@ describe Issues::BulkUpdateService do
create(:issue, project: @project)
end
@params = {
- update: {
- status: 'closed',
- issues_ids: @issues.map(&:id)
- }
+ state_event: 'close',
+ issues_ids: @issues.map(&:id)
}
end
@@ -46,10 +44,8 @@ describe Issues::BulkUpdateService do
create(:closed_issue, project: @project)
end
@params = {
- update: {
- status: 'reopen',
- issues_ids: @issues.map(&:id)
- }
+ state_event: 'reopen',
+ issues_ids: @issues.map(&:id)
}
end
@@ -69,10 +65,8 @@ describe Issues::BulkUpdateService do
before do
@new_assignee = create :user
@params = {
- update: {
- issues_ids: [issue.id],
- assignee_id: @new_assignee.id
- }
+ issues_ids: [issue.id],
+ assignee_id: @new_assignee.id
}
end
@@ -88,7 +82,7 @@ describe Issues::BulkUpdateService do
@project.issues.first.update_attribute(:assignee, @new_assignee)
expect(@project.issues.first.assignee).not_to be_nil
- @params[:update][:assignee_id] = -1
+ @params[:assignee_id] = -1
Issues::BulkUpdateService.new(@project, @user, @params).execute
expect(@project.issues.first.assignee).to be_nil
@@ -98,7 +92,7 @@ describe Issues::BulkUpdateService do
@project.issues.first.update_attribute(:assignee, @new_assignee)
expect(@project.issues.first.assignee).not_to be_nil
- @params[:update][:assignee_id] = ''
+ @params[:assignee_id] = ''
Issues::BulkUpdateService.new(@project, @user, @params).execute
expect(@project.issues.first.assignee).not_to be_nil
@@ -110,10 +104,8 @@ describe Issues::BulkUpdateService do
before do
@milestone = create :milestone
@params = {
- update: {
- issues_ids: [issue.id],
- milestone_id: @milestone.id
- }
+ issues_ids: [issue.id],
+ milestone_id: @milestone.id
}
end
diff --git a/spec/services/merge_requests/refresh_service_spec.rb b/spec/services/merge_requests/refresh_service_spec.rb
index 2830da87814..879df0c9c67 100644
--- a/spec/services/merge_requests/refresh_service_spec.rb
+++ b/spec/services/merge_requests/refresh_service_spec.rb
@@ -61,7 +61,7 @@ describe MergeRequests::RefreshService do
it { expect(@merge_request.notes).to be_empty }
it { expect(@merge_request).to be_open }
- it { expect(@fork_merge_request.notes.last.note).to include('new commit') }
+ it { expect(@fork_merge_request.notes.last.note).to include('Added 4 commits') }
it { expect(@fork_merge_request).to be_open }
end
diff --git a/spec/support/repo_helpers.rb b/spec/support/repo_helpers.rb
index 4c4775da692..aadf791bf3f 100644
--- a/spec/support/repo_helpers.rb
+++ b/spec/support/repo_helpers.rb
@@ -43,6 +43,25 @@ eos
)
end
+ def another_sample_commit
+ OpenStruct.new(
+ id: "e56497bb5f03a90a51293fc6d516788730953899",
+ parent_id: '4cd80ccab63c82b4bad16faa5193fbd2aa06df40',
+ author_full_name: "Sytse Sijbrandij",
+ author_email: "sytse@gitlab.com",
+ files_changed_count: 1,
+ message: <<eos
+Add directory structure for tree_helper spec
+
+This directory structure is needed for a testing the method flatten_tree(tree) in the TreeHelper module
+
+See [merge request #275](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/275#note_732774)
+
+See merge request !2
+eos
+ )
+ end
+
def sample_big_commit
OpenStruct.new(
id: "913c66a37b4a45b9769037c55c2d238bd0942d2e",