summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG42
-rw-r--r--GITLAB_SHELL_VERSION2
-rw-r--r--Gemfile11
-rw-r--r--Gemfile.lock38
-rw-r--r--app/assets/images/authbuttons/github_32.pngbin1902 -> 1822 bytes
-rw-r--r--app/assets/images/authbuttons/github_64.pngbin4444 -> 4196 bytes
-rw-r--r--app/assets/images/authbuttons/google_32.pngbin1611 -> 1501 bytes
-rw-r--r--app/assets/images/authbuttons/google_64.pngbin3437 -> 3169 bytes
-rw-r--r--app/assets/images/authbuttons/twitter_32.pngbin1417 -> 1311 bytes
-rw-r--r--app/assets/images/authbuttons/twitter_64.pngbin3328 -> 3054 bytes
-rw-r--r--app/assets/images/bg-header.pngbin210 -> 90 bytes
-rw-r--r--app/assets/images/bg_fallback.pngbin2976 -> 167 bytes
-rw-r--r--app/assets/images/brand_logo.pngbin32119 -> 27059 bytes
-rw-r--r--app/assets/images/chosen-sprite.pngbin396 -> 367 bytes
-rw-r--r--app/assets/images/dark-scheme-preview.pngbin5792 -> 3996 bytes
-rw-r--r--app/assets/images/diff_note_add.pngbin691 -> 418 bytes
-rw-r--r--app/assets/images/icon-link.pngbin1019 -> 726 bytes
-rw-r--r--app/assets/images/icon-search.pngbin331 -> 222 bytes
-rw-r--r--app/assets/images/icon_sprite.pngbin2782 -> 2636 bytes
-rw-r--r--app/assets/images/images.pngbin6644 -> 5849 bytes
-rw-r--r--app/assets/images/logo-black.pngbin2797 -> 2608 bytes
-rw-r--r--app/assets/images/logo-white.pngbin7501 -> 7331 bytes
-rw-r--r--app/assets/images/monokai-scheme-preview.pngbin5401 -> 3711 bytes
-rw-r--r--app/assets/images/move.pngbin260 -> 197 bytes
-rw-r--r--app/assets/images/no_avatar.pngbin704 -> 621 bytes
-rw-r--r--app/assets/images/no_group_avatar.pngbin4884 -> 942 bytes
-rw-r--r--app/assets/images/slider_handles.pngbin4122 -> 1377 bytes
-rw-r--r--app/assets/images/solarized-dark-scheme-preview.pngbin4993 -> 3195 bytes
-rw-r--r--app/assets/images/solarized-light-scheme-preview.pngbin4746 -> 3095 bytes
-rw-r--r--app/assets/images/switch_icon.pngbin1197 -> 231 bytes
-rw-r--r--app/assets/images/trans_bg.gifbin50 -> 49 bytes
-rw-r--r--app/assets/images/white-scheme-preview.pngbin5617 -> 3751 bytes
-rw-r--r--app/assets/javascripts/application.js.coffee25
-rw-r--r--app/assets/javascripts/blob/blob.js.coffee (renamed from app/assets/javascripts/blob.js.coffee)0
-rw-r--r--app/assets/javascripts/blob/edit_blob.js.coffee44
-rw-r--r--app/assets/javascripts/blob/new_blob.js.coffee21
-rw-r--r--app/assets/javascripts/calendar.js.coffee33
-rw-r--r--app/assets/javascripts/dispatcher.js.coffee1
-rw-r--r--app/assets/javascripts/project_avatar.js.coffee9
-rw-r--r--app/assets/javascripts/project_new.js.coffee14
-rw-r--r--app/assets/javascripts/project_show.js.coffee2
-rw-r--r--app/assets/stylesheets/application.scss1
-rw-r--r--app/assets/stylesheets/generic/avatar.scss29
-rw-r--r--app/assets/stylesheets/generic/buttons.scss5
-rw-r--r--app/assets/stylesheets/generic/calendar.scss95
-rw-r--r--app/assets/stylesheets/generic/highlight.scss8
-rw-r--r--app/assets/stylesheets/generic/timeline.scss6
-rw-r--r--app/assets/stylesheets/generic/typography.scss4
-rw-r--r--app/assets/stylesheets/gl_bootstrap.scss3
-rw-r--r--app/assets/stylesheets/main/mixins.scss2
-rw-r--r--app/assets/stylesheets/main/variables.scss8
-rw-r--r--app/assets/stylesheets/sections/commits.scss1
-rw-r--r--app/assets/stylesheets/sections/dashboard.scss21
-rw-r--r--app/assets/stylesheets/sections/diff.scss6
-rw-r--r--app/assets/stylesheets/sections/editor.scss22
-rw-r--r--app/assets/stylesheets/sections/header.scss7
-rw-r--r--app/assets/stylesheets/sections/issues.scss3
-rw-r--r--app/assets/stylesheets/sections/merge_requests.scss1
-rw-r--r--app/assets/stylesheets/sections/note_form.scss4
-rw-r--r--app/assets/stylesheets/sections/profile.scss9
-rw-r--r--app/assets/stylesheets/sections/projects.scss18
-rw-r--r--app/controllers/application_controller.rb4
-rw-r--r--app/controllers/github_imports_controller.rb6
-rw-r--r--app/controllers/passwords_controller.rb4
-rw-r--r--app/controllers/projects/avatars_controller.rb29
-rw-r--r--app/controllers/projects/base_tree_controller.rb7
-rw-r--r--app/controllers/projects/blame_controller.rb2
-rw-r--r--app/controllers/projects/blob_controller.rb98
-rw-r--r--app/controllers/projects/commit_controller.rb1
-rw-r--r--app/controllers/projects/commits_controller.rb2
-rw-r--r--app/controllers/projects/edit_tree_controller.rb60
-rw-r--r--app/controllers/projects/network_controller.rb2
-rw-r--r--app/controllers/projects/new_tree_controller.rb20
-rw-r--r--app/controllers/projects/protected_branches_controller.rb2
-rw-r--r--app/controllers/projects/raw_controller.rb2
-rw-r--r--app/controllers/projects/refs_controller.rb8
-rw-r--r--app/controllers/projects/services_controller.rb16
-rw-r--r--app/controllers/projects/tree_controller.rb9
-rw-r--r--app/controllers/projects_controller.rb41
-rw-r--r--app/controllers/users_controller.rb32
-rw-r--r--app/helpers/application_helper.rb69
-rw-r--r--app/helpers/blob_helper.rb38
-rw-r--r--app/helpers/commits_helper.rb22
-rw-r--r--app/helpers/diff_helper.rb15
-rw-r--r--app/helpers/events_helper.rb11
-rw-r--r--app/helpers/graph_helper.rb4
-rw-r--r--app/helpers/icons_helper.rb28
-rw-r--r--app/helpers/issues_helper.rb35
-rw-r--r--app/helpers/notes_helper.rb6
-rw-r--r--app/helpers/notifications_helper.rb8
-rw-r--r--app/helpers/projects_helper.rb28
-rw-r--r--app/helpers/submodule_helper.rb4
-rw-r--r--app/helpers/tags_helper.rb2
-rw-r--r--app/helpers/tree_helper.rb58
-rw-r--r--app/mailers/emails/merge_requests.rb2
-rw-r--r--app/mailers/notify.rb4
-rw-r--r--app/models/ability.rb26
-rw-r--r--app/models/application_setting.rb15
-rw-r--r--app/models/concerns/issuable.rb24
-rw-r--r--app/models/concerns/mentionable.rb2
-rw-r--r--app/models/group.rb15
-rw-r--r--app/models/identity.rb12
-rw-r--r--app/models/key.rb4
-rw-r--r--app/models/members/group_member.rb14
-rw-r--r--app/models/merge_request.rb8
-rw-r--r--app/models/network/graph.rb4
-rw-r--r--app/models/note.rb22
-rw-r--r--app/models/project.rb138
-rw-r--r--app/models/project_contributions.rb23
-rw-r--r--app/models/project_services/bamboo_service.rb14
-rw-r--r--app/models/project_services/custom_issue_tracker_service.rb52
-rw-r--r--app/models/project_services/gitlab_issue_tracker_service.rb39
-rw-r--r--app/models/project_services/issue_tracker_service.rb88
-rw-r--r--app/models/project_services/jira_service.rb38
-rw-r--r--app/models/project_services/redmine_service.rb38
-rw-r--r--app/models/project_services/teamcity_service.rb14
-rw-r--r--app/models/project_team.rb2
-rw-r--r--app/models/protected_branch.rb11
-rw-r--r--app/models/repository.rb94
-rw-r--r--app/models/service.rb10
-rw-r--r--app/models/user.rb11
-rw-r--r--app/services/files/create_service.rb19
-rw-r--r--app/services/issues/close_service.rb2
-rw-r--r--app/services/issues/update_service.rb2
-rw-r--r--app/services/merge_requests/auto_merge_service.rb2
-rw-r--r--app/services/merge_requests/close_service.rb2
-rw-r--r--app/services/merge_requests/merge_service.rb2
-rw-r--r--app/services/merge_requests/reopen_service.rb2
-rw-r--r--app/services/merge_requests/update_service.rb2
-rw-r--r--app/services/notification_service.rb46
-rw-r--r--app/services/oauth2/access_token_validation_service.rb4
-rw-r--r--app/services/projects/autocomplete_service.rb15
-rw-r--r--app/services/projects/create_service.rb2
-rw-r--r--app/services/projects/fork_service.rb9
-rw-r--r--app/services/system_hooks_service.rb23
-rw-r--r--app/views/admin/dashboard/index.html.haml2
-rw-r--r--app/views/dashboard/_project.html.haml2
-rw-r--r--app/views/dashboard/_projects.html.haml4
-rw-r--r--app/views/dashboard/projects.html.haml2
-rw-r--r--app/views/events/_event.html.haml3
-rw-r--r--app/views/groups/_new_group_member.html.haml6
-rw-r--r--app/views/groups/_projects.html.haml2
-rw-r--r--app/views/groups/_settings_nav.html.haml4
-rw-r--r--app/views/groups/show.html.haml2
-rw-r--r--app/views/layouts/_head_panel.html.haml2
-rw-r--r--app/views/layouts/_page.html.haml1
-rw-r--r--app/views/layouts/admin.html.haml1
-rw-r--r--app/views/layouts/application.html.haml1
-rw-r--r--app/views/layouts/group.html.haml1
-rw-r--r--app/views/layouts/nav/_admin.html.haml29
-rw-r--r--app/views/layouts/nav/_dashboard.html.haml8
-rw-r--r--app/views/layouts/nav/_group.html.haml10
-rw-r--r--app/views/layouts/nav/_profile.html.haml19
-rw-r--r--app/views/layouts/nav/_project.html.haml21
-rw-r--r--app/views/layouts/profile.html.haml1
-rw-r--r--app/views/layouts/project_settings.html.haml1
-rw-r--r--app/views/layouts/projects.html.haml1
-rw-r--r--app/views/layouts/public_group.html.haml1
-rw-r--r--app/views/layouts/public_projects.html.haml1
-rw-r--r--app/views/layouts/public_users.html.haml1
-rw-r--r--app/views/profiles/notifications/show.html.haml2
-rw-r--r--app/views/projects/_blob_editor.html.haml15
-rw-r--r--app/views/projects/_github_import_modal.html.haml22
-rw-r--r--app/views/projects/_home_panel.html.haml2
-rw-r--r--app/views/projects/_settings_nav.html.haml12
-rw-r--r--app/views/projects/blob/_editor.html.haml25
-rw-r--r--app/views/projects/blob/edit.html.haml24
-rw-r--r--app/views/projects/blob/new.html.haml12
-rw-r--r--app/views/projects/blob/preview.html.haml (renamed from app/views/projects/edit_tree/preview.html.haml)0
-rw-r--r--app/views/projects/branches/new.html.haml3
-rw-r--r--app/views/projects/commit/_commit_box.html.haml23
-rw-r--r--app/views/projects/diffs/_file.html.haml3
-rw-r--r--app/views/projects/edit.html.haml38
-rw-r--r--app/views/projects/edit_tree/show.html.haml57
-rw-r--r--app/views/projects/empty.html.haml14
-rw-r--r--app/views/projects/forks/new.html.haml3
-rw-r--r--app/views/projects/issues/_issues.html.haml2
-rw-r--r--app/views/projects/merge_requests/show/_mr_accept.html.haml11
-rw-r--r--app/views/projects/merge_requests/show/_mr_ci.html.haml6
-rw-r--r--app/views/projects/merge_requests/show/_remove_source_branch.html.haml2
-rw-r--r--app/views/projects/new.html.haml13
-rw-r--r--app/views/projects/new_tree/show.html.haml38
-rw-r--r--app/views/projects/notes/_note.html.haml26
-rw-r--r--app/views/projects/services/index.html.haml25
-rw-r--r--app/views/projects/show.html.haml4
-rw-r--r--app/views/projects/tags/new.html.haml3
-rw-r--r--app/views/projects/team_members/_form.html.haml7
-rw-r--r--app/views/projects/tree/_submodule_item.html.haml10
-rw-r--r--app/views/projects/tree/_tree.html.haml2
-rw-r--r--app/views/users/_groups.html.haml7
-rw-r--r--app/views/users/calendar.html.haml8
-rw-r--r--app/views/users/show.html.haml16
-rw-r--r--config/application.rb17
-rw-r--r--config/gitlab.yml.example8
-rw-r--r--config/initializers/1_settings.rb2
-rw-r--r--config/initializers/devise.rb3
-rw-r--r--config/initializers/doorkeeper.rb6
-rw-r--r--config/routes.rb62
-rw-r--r--db/migrate/20140125162722_add_avatar_to_projects.rb5
-rw-r--r--db/migrate/20150116234545_add_gitlab_access_token_to_user.rb5
-rw-r--r--db/schema.rb4
-rw-r--r--doc/README.md2
-rw-r--r--doc/api/groups.md22
-rw-r--r--doc/api/milestones.md13
-rw-r--r--doc/api/projects.md25
-rw-r--r--doc/api/users.md25
-rw-r--r--doc/development/shell_commands.md2
-rw-r--r--doc/integration/ldap.md7
-rw-r--r--doc/ssh/ssh.md19
-rw-r--r--doc/system_hooks/system_hooks.md63
-rw-r--r--doc/update/6.x-or-7.x-to-7.7.md3
-rw-r--r--doc/workflow/README.md2
-rw-r--r--doc/workflow/workflow.md2
-rw-r--r--docker/Dockerfile2
-rw-r--r--features/project/edit_issuetracker.feature18
-rw-r--r--features/project/project.feature13
-rw-r--r--features/project/source/browse_files.feature13
-rw-r--r--features/steps/project/issue_tracker.rb31
-rw-r--r--features/steps/project/merge_requests.rb4
-rw-r--r--features/steps/project/project.rb57
-rw-r--r--features/steps/project/source/browse_files.rb18
-rw-r--r--features/steps/shared/paths.rb4
-rw-r--r--features/steps/shared/project.rb4
-rw-r--r--lib/api/milestones.rb15
-rw-r--r--lib/api/projects.rb43
-rw-r--r--lib/api/repositories.rb37
-rw-r--r--lib/extracts_path.rb8
-rw-r--r--lib/gitlab/backend/grack_auth.rb14
-rw-r--r--lib/gitlab/closing_issue_extractor.rb19
-rw-r--r--lib/gitlab/commits_calendar.rb33
-rw-r--r--lib/gitlab/force_push_check.rb2
-rw-r--r--lib/gitlab/git_access.rb2
-rw-r--r--lib/gitlab/ldap/user.rb8
-rw-r--r--lib/gitlab/markdown.rb12
-rw-r--r--lib/gitlab/satellite/files/new_file_action.rb12
-rw-r--r--lib/repository_cache.rb21
-rw-r--r--lib/tasks/spinach.rake8
-rw-r--r--spec/controllers/users_controller_spec.rb27
-rw-r--r--spec/factories/merge_requests.rb1
-rw-r--r--spec/factories/projects.rb19
-rw-r--r--spec/features/atom/dashboard_spec.rb2
-rw-r--r--spec/helpers/application_helper_spec.rb126
-rw-r--r--spec/helpers/gitlab_markdown_helper_spec.rb12
-rw-r--r--spec/helpers/issues_helper_spec.rb21
-rw-r--r--spec/helpers/notifications_helper_spec.rb3
-rw-r--r--spec/helpers/projects_helper_spec.rb21
-rw-r--r--spec/lib/gitlab/closing_issue_extractor_spec.rb84
-rw-r--r--spec/lib/gitlab/ldap/user_spec.rb17
-rw-r--r--spec/lib/gitlab/reference_extractor_spec.rb1
-rw-r--r--spec/lib/repository_cache_spec.rb34
-rw-r--r--spec/lib/votes_spec.rb101
-rw-r--r--spec/models/application_setting_spec.rb15
-rw-r--r--spec/models/commit_spec.rb4
-rw-r--r--spec/models/hooks/project_hook_spec.rb (renamed from spec/models/project_hook_spec.rb)0
-rw-r--r--spec/models/hooks/service_hook_spec.rb (renamed from spec/models/service_hook_spec.rb)0
-rw-r--r--spec/models/hooks/system_hook_spec.rb (renamed from spec/models/system_hook_spec.rb)35
-rw-r--r--spec/models/hooks/web_hook_spec.rb (renamed from spec/models/web_hook_spec.rb)0
-rw-r--r--spec/models/members/group_member_spec.rb (renamed from spec/models/group_member_spec.rb)0
-rw-r--r--spec/models/members/project_member_spec.rb (renamed from spec/models/project_member_spec.rb)0
-rw-r--r--spec/models/members_spec.rb2
-rw-r--r--spec/models/merge_request_spec.rb1
-rw-r--r--spec/models/project_services/assembla_service_spec.rb (renamed from spec/models/assembla_service_spec.rb)0
-rw-r--r--spec/models/project_services/buildbox_service_spec.rb (renamed from spec/models/buildbox_service_spec.rb)0
-rw-r--r--spec/models/project_services/flowdock_service_spec.rb (renamed from spec/models/flowdock_service_spec.rb)0
-rw-r--r--spec/models/project_services/gemnasium_service_spec.rb (renamed from spec/models/gemnasium_service_spec.rb)0
-rw-r--r--spec/models/project_services/gitlab_ci_service_spec.rb (renamed from spec/models/gitlab_ci_service_spec.rb)0
-rw-r--r--spec/models/project_services/jira_service_spec.rb97
-rw-r--r--spec/models/project_services/pushover_service_spec.rb (renamed from spec/models/pushover_service_spec.rb)0
-rw-r--r--spec/models/project_services/slack_message_spec.rb (renamed from spec/models/slack_message_spec.rb)0
-rw-r--r--spec/models/project_services/slack_service_spec.rb (renamed from spec/models/slack_service_spec.rb)0
-rw-r--r--spec/models/project_spec.rb75
-rw-r--r--spec/models/protected_branch_spec.rb11
-rw-r--r--spec/models/user_spec.rb5
-rw-r--r--spec/requests/api/milestones_spec.rb53
-rw-r--r--spec/requests/api/projects_spec.rb415
-rw-r--r--spec/requests/api/repositories_spec.rb16
-rw-r--r--spec/routing/project_routing_spec.rb360
-rw-r--r--spec/services/issues/update_service_spec.rb1
-rw-r--r--spec/services/merge_requests/update_service_spec.rb2
-rw-r--r--spec/services/notification_service_spec.rb7
-rw-r--r--spec/services/system_hooks_service_spec.rb31
281 files changed, 3436 insertions, 1443 deletions
diff --git a/CHANGELOG b/CHANGELOG
index f1346885ab4..2db5beb0022 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -3,40 +3,43 @@ Note: The upcoming release contains empty lines to reduce the number of merge co
v 7.8.0
- Replace highlight.js with rouge-fork rugments (Stefan Tatschner)
- Make project search case insensitive (Hannes Rosenögger)
- -
+ - Include issue/mr participants in list of recipients for reassign/close/reopen emails
- Expose description in groups API
- -
- -
+ - Better UI for project services page
+ - Cleaner UI for web editor
- Add diff syntax highlighting in email-on-push service notifications (Hannes Rosenögger)
-
-
-
+ - Allow more variations for commit messages closing issues (Julien Bianchi and Hannes Rosenögger)
-
-
+ - Show tags in commit view (Hannes Rosenögger)
+ - Only count a user's vote once on a merge request or issue (Michael Clarke)
-
+ - Increate font size when browse source files and diffs
+ - Create new file in empty repository using GitLab UI
-
+ - Ability to clone project using oauth2 token
-
- -
- -
- -
- -
- -
- -
+ - Upgrade Sidekiq gem to version 3.3.0
+ - Stop git zombie creation during force push check
+ - Show success/error messages for test setting button in services
-
- Fix commits pagination
-
-
-
-
+ - Add a commit calendar to the user profile (Hannes Rosenögger)
-
-
-
+ - Fix long broadcast message cut-off on left sidebar (Visay Keo)
+ - Add Project Avatars (Steven Thonus and Hannes Rosenögger)
-
-
- -
- -
- -
- -
+ - Password reset token validity increased from 2 hours to 2 days since it is also send on account creation.
-
-
-
@@ -47,16 +50,27 @@ v 7.8.0
-
-
-
+ - Add a new API function that retrieves all issues assigned to a single milestone (Justin Whear and Hannes Rosenögger)
-
-
-
-
-
-
+ - API: Add support for editing an existing project (Mika Mäenpää and Hannes Rosenögger)
-
-
-
- -
+ - Added support for firing system hooks on group create/destroy and adding/removing users to group (Boyan Tabakov)
+
+v 7.7.2
+ - Update GitLab Shell to version 2.4.2 that fixes a bug when developers can push to protected branch
+ - Fix issue when LDAP user can't login with existing GitLab account
+
+v 7.7.1
+ - Improve mention autocomplete performance
+ - Show setup instructions for GitHub import if disabled
+ - Allow use http for OAuth applications
v 7.7.0
- Import from GitHub.com feature
diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION
index 005119baaa0..8e8299dcc06 100644
--- a/GITLAB_SHELL_VERSION
+++ b/GITLAB_SHELL_VERSION
@@ -1 +1 @@
-2.4.1
+2.4.2
diff --git a/Gemfile b/Gemfile
index cdbc2963d4f..be78831e1fc 100644
--- a/Gemfile
+++ b/Gemfile
@@ -118,7 +118,7 @@ gem "acts-as-taggable-on"
# Background jobs
gem 'slim'
gem 'sinatra', require: nil
-gem 'sidekiq', '2.17.8'
+gem 'sidekiq', '~> 3.3'
# HTTP requests
gem "httparty"
@@ -154,6 +154,9 @@ gem "slack-notifier", "~> 1.0.0"
# d3
gem "d3_rails", "~> 3.1.4"
+#cal-heatmap
+gem "cal-heatmap-rails", "~> 0.0.1"
+
# underscore-rails
gem "underscore-rails", "~> 1.4.4"
@@ -170,7 +173,7 @@ gem 'ace-rails-ap'
gem 'mousetrap-rails'
# Semantic UI Sass for Sidebar
-gem 'semantic-ui-sass', '~> 0.16.1.0'
+gem 'semantic-ui-sass', '~> 1.8.0'
gem "sass-rails", '~> 4.0.2'
gem "coffee-rails"
@@ -218,7 +221,7 @@ group :development, :test do
gem 'spinach-rails'
gem "rspec-rails"
gem "capybara", '~> 2.2.1'
- gem "pry"
+ gem "pry-rails"
gem "awesome_print"
gem "database_cleaner"
gem "launchy"
@@ -251,7 +254,7 @@ end
group :test do
gem "simplecov", require: false
- gem "shoulda-matchers", "~> 2.1.0"
+ gem "shoulda-matchers", "~> 2.7.0"
gem 'email_spec'
gem "webmock"
gem 'test_after_commit'
diff --git a/Gemfile.lock b/Gemfile.lock
index d9ba4e3c172..551f16722f2 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -52,6 +52,7 @@ GEM
sass (~> 3.2)
browser (0.7.2)
builder (3.2.2)
+ cal-heatmap-rails (0.0.1)
capybara (2.2.1)
mime-types (>= 1.16)
nokogiri (>= 1.3.3)
@@ -62,8 +63,8 @@ GEM
activemodel (>= 3.2.0)
activesupport (>= 3.2.0)
json (>= 1.7)
- celluloid (0.15.2)
- timers (~> 1.1.0)
+ celluloid (0.16.0)
+ timers (~> 4.0.0)
charlock_holmes (0.6.9.4)
cliver (0.3.2)
code_analyzer (0.4.3)
@@ -244,6 +245,7 @@ GEM
hike (1.2.3)
hipchat (1.4.0)
httparty
+ hitimes (1.2.2)
html-pipeline (1.11.0)
activesupport (>= 2)
nokogiri (~> 1.4)
@@ -258,7 +260,7 @@ GEM
multi_xml (>= 0.5.2)
httpauth (0.2.1)
httpclient (2.5.3.3)
- i18n (0.6.11)
+ i18n (0.7.0)
ice_nine (0.10.0)
jasmine (2.0.2)
jasmine-core (~> 2.0.0)
@@ -277,7 +279,7 @@ GEM
turbolinks
jquery-ui-rails (4.2.1)
railties (>= 3.2.16)
- json (1.8.1)
+ json (1.8.2)
jwt (0.1.13)
multi_json (>= 1.5)
kaminari (0.15.1)
@@ -364,6 +366,8 @@ GEM
coderay (~> 1.0)
method_source (~> 0.8)
slop (~> 3.4)
+ pry-rails (0.3.2)
+ pry (>= 0.9.10)
pyu-ruby-sasl (0.0.3.3)
quiet_assets (1.0.2)
railties (>= 3.1, < 5.0)
@@ -489,18 +493,18 @@ GEM
activesupport (>= 3.1, < 4.2)
select2-rails (3.5.2)
thor (~> 0.14)
- semantic-ui-sass (0.16.1.0)
+ semantic-ui-sass (1.8.0.0)
sass (~> 3.2)
settingslogic (2.0.9)
sexp_processor (4.4.0)
- shoulda-matchers (2.1.0)
+ shoulda-matchers (2.7.0)
activesupport (>= 3.0.0)
- sidekiq (2.17.8)
- celluloid (= 0.15.2)
- connection_pool (~> 2.0)
+ sidekiq (3.3.0)
+ celluloid (>= 0.16.0)
+ connection_pool (>= 2.0.0)
json
- redis (~> 3.1)
- redis-namespace (~> 1.3)
+ redis (>= 3.0.6)
+ redis-namespace (>= 1.3.1)
simple_oauth (0.1.9)
simplecov (0.9.0)
docile (~> 1.1.0)
@@ -555,7 +559,8 @@ GEM
thor (0.19.1)
thread_safe (0.3.4)
tilt (1.4.1)
- timers (1.1.0)
+ timers (4.0.1)
+ hitimes
timfel-krb5-auth (0.8)
tinder (1.9.3)
eventmachine (~> 1.0)
@@ -625,6 +630,7 @@ DEPENDENCIES
binding_of_caller
bootstrap-sass (~> 3.0)
browser
+ cal-heatmap-rails (~> 0.0.1)
capybara (~> 2.2.1)
carrierwave
coffee-rails
@@ -690,7 +696,7 @@ DEPENDENCIES
org-ruby (= 0.9.12)
pg
poltergeist (~> 1.5.1)
- pry
+ pry-rails
quiet_assets (~> 1.0.1)
rack-attack
rack-cors
@@ -713,10 +719,10 @@ DEPENDENCIES
sdoc
seed-fu
select2-rails
- semantic-ui-sass (~> 0.16.1.0)
+ semantic-ui-sass (~> 1.8.0)
settingslogic
- shoulda-matchers (~> 2.1.0)
- sidekiq (= 2.17.8)
+ shoulda-matchers (~> 2.7.0)
+ sidekiq (~> 3.3)
simplecov
sinatra
six
diff --git a/app/assets/images/authbuttons/github_32.png b/app/assets/images/authbuttons/github_32.png
index c56eef05eb9..0445b567bbc 100644
--- a/app/assets/images/authbuttons/github_32.png
+++ b/app/assets/images/authbuttons/github_32.png
Binary files differ
diff --git a/app/assets/images/authbuttons/github_64.png b/app/assets/images/authbuttons/github_64.png
index 39de55bc796..dc7c03d1005 100644
--- a/app/assets/images/authbuttons/github_64.png
+++ b/app/assets/images/authbuttons/github_64.png
Binary files differ
diff --git a/app/assets/images/authbuttons/google_32.png b/app/assets/images/authbuttons/google_32.png
index 6225cc9c2d7..b03c3ec5207 100644
--- a/app/assets/images/authbuttons/google_32.png
+++ b/app/assets/images/authbuttons/google_32.png
Binary files differ
diff --git a/app/assets/images/authbuttons/google_64.png b/app/assets/images/authbuttons/google_64.png
index 4d608f71008..94a0e089c6e 100644
--- a/app/assets/images/authbuttons/google_64.png
+++ b/app/assets/images/authbuttons/google_64.png
Binary files differ
diff --git a/app/assets/images/authbuttons/twitter_32.png b/app/assets/images/authbuttons/twitter_32.png
index 696eb02484d..a3d4964f40f 100644
--- a/app/assets/images/authbuttons/twitter_32.png
+++ b/app/assets/images/authbuttons/twitter_32.png
Binary files differ
diff --git a/app/assets/images/authbuttons/twitter_64.png b/app/assets/images/authbuttons/twitter_64.png
index 2893274766f..5c9f14cb077 100644
--- a/app/assets/images/authbuttons/twitter_64.png
+++ b/app/assets/images/authbuttons/twitter_64.png
Binary files differ
diff --git a/app/assets/images/bg-header.png b/app/assets/images/bg-header.png
index 9ecdaf4e2d5..639271c6faf 100644
--- a/app/assets/images/bg-header.png
+++ b/app/assets/images/bg-header.png
Binary files differ
diff --git a/app/assets/images/bg_fallback.png b/app/assets/images/bg_fallback.png
index d9066ad7d7b..e5fe659ba63 100644
--- a/app/assets/images/bg_fallback.png
+++ b/app/assets/images/bg_fallback.png
Binary files differ
diff --git a/app/assets/images/brand_logo.png b/app/assets/images/brand_logo.png
index 09b1689ca45..9c564bb6141 100644
--- a/app/assets/images/brand_logo.png
+++ b/app/assets/images/brand_logo.png
Binary files differ
diff --git a/app/assets/images/chosen-sprite.png b/app/assets/images/chosen-sprite.png
index d08e4b7e624..3d936b07d44 100644
--- a/app/assets/images/chosen-sprite.png
+++ b/app/assets/images/chosen-sprite.png
Binary files differ
diff --git a/app/assets/images/dark-scheme-preview.png b/app/assets/images/dark-scheme-preview.png
index 2d631a49fd3..2ef58e52549 100644
--- a/app/assets/images/dark-scheme-preview.png
+++ b/app/assets/images/dark-scheme-preview.png
Binary files differ
diff --git a/app/assets/images/diff_note_add.png b/app/assets/images/diff_note_add.png
index 8ec15b701fc..0084422e330 100644
--- a/app/assets/images/diff_note_add.png
+++ b/app/assets/images/diff_note_add.png
Binary files differ
diff --git a/app/assets/images/icon-link.png b/app/assets/images/icon-link.png
index 32ade0fe9a3..60021d5ac47 100644
--- a/app/assets/images/icon-link.png
+++ b/app/assets/images/icon-link.png
Binary files differ
diff --git a/app/assets/images/icon-search.png b/app/assets/images/icon-search.png
index 084b89e3a7c..3c1c146541d 100644
--- a/app/assets/images/icon-search.png
+++ b/app/assets/images/icon-search.png
Binary files differ
diff --git a/app/assets/images/icon_sprite.png b/app/assets/images/icon_sprite.png
index 9ad65fc443b..2e7a5023398 100644
--- a/app/assets/images/icon_sprite.png
+++ b/app/assets/images/icon_sprite.png
Binary files differ
diff --git a/app/assets/images/images.png b/app/assets/images/images.png
index da91f6b1f4c..ad146246caf 100644
--- a/app/assets/images/images.png
+++ b/app/assets/images/images.png
Binary files differ
diff --git a/app/assets/images/logo-black.png b/app/assets/images/logo-black.png
index 4a96572d570..49cdc16cacd 100644
--- a/app/assets/images/logo-black.png
+++ b/app/assets/images/logo-black.png
Binary files differ
diff --git a/app/assets/images/logo-white.png b/app/assets/images/logo-white.png
index bc2ef601a53..2299153caba 100644
--- a/app/assets/images/logo-white.png
+++ b/app/assets/images/logo-white.png
Binary files differ
diff --git a/app/assets/images/monokai-scheme-preview.png b/app/assets/images/monokai-scheme-preview.png
index 6791d1ee33d..fbb339c6a91 100644
--- a/app/assets/images/monokai-scheme-preview.png
+++ b/app/assets/images/monokai-scheme-preview.png
Binary files differ
diff --git a/app/assets/images/move.png b/app/assets/images/move.png
index 9d2d55ddf0b..6a0567f8f25 100644
--- a/app/assets/images/move.png
+++ b/app/assets/images/move.png
Binary files differ
diff --git a/app/assets/images/no_avatar.png b/app/assets/images/no_avatar.png
index dac3ab1bb89..8287acbce13 100644
--- a/app/assets/images/no_avatar.png
+++ b/app/assets/images/no_avatar.png
Binary files differ
diff --git a/app/assets/images/no_group_avatar.png b/app/assets/images/no_group_avatar.png
index a97d4515982..bfb31bb2184 100644
--- a/app/assets/images/no_group_avatar.png
+++ b/app/assets/images/no_group_avatar.png
Binary files differ
diff --git a/app/assets/images/slider_handles.png b/app/assets/images/slider_handles.png
index a6d477033fa..884378ec96a 100644
--- a/app/assets/images/slider_handles.png
+++ b/app/assets/images/slider_handles.png
Binary files differ
diff --git a/app/assets/images/solarized-dark-scheme-preview.png b/app/assets/images/solarized-dark-scheme-preview.png
index 8f904405310..7ed7336896b 100644
--- a/app/assets/images/solarized-dark-scheme-preview.png
+++ b/app/assets/images/solarized-dark-scheme-preview.png
Binary files differ
diff --git a/app/assets/images/solarized-light-scheme-preview.png b/app/assets/images/solarized-light-scheme-preview.png
index 7da5d2d2090..c50db75449b 100644
--- a/app/assets/images/solarized-light-scheme-preview.png
+++ b/app/assets/images/solarized-light-scheme-preview.png
Binary files differ
diff --git a/app/assets/images/switch_icon.png b/app/assets/images/switch_icon.png
index 6b8bde41bc9..c6b6c8d9521 100644
--- a/app/assets/images/switch_icon.png
+++ b/app/assets/images/switch_icon.png
Binary files differ
diff --git a/app/assets/images/trans_bg.gif b/app/assets/images/trans_bg.gif
index 5f6ed04a43c..1a1c9c15ec7 100644
--- a/app/assets/images/trans_bg.gif
+++ b/app/assets/images/trans_bg.gif
Binary files differ
diff --git a/app/assets/images/white-scheme-preview.png b/app/assets/images/white-scheme-preview.png
index d32b7485e1e..fc4c40b9227 100644
--- a/app/assets/images/white-scheme-preview.png
+++ b/app/assets/images/white-scheme-preview.png
Binary files differ
diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee
index 9b36a172ba6..4912c534b0e 100644
--- a/app/assets/javascripts/application.js.coffee
+++ b/app/assets/javascripts/application.js.coffee
@@ -39,6 +39,7 @@
#= require shortcuts_dashboard_navigation
#= require shortcuts_issueable
#= require shortcuts_network
+#= require cal-heatmap
#= require_tree .
window.slugify = (text) ->
@@ -74,24 +75,18 @@ window.disableButtonIfEmptyField = (field_selector, button_selector) ->
# Disable button if any input field with given selector is empty
window.disableButtonIfAnyEmptyField = (form, form_selector, button_selector) ->
closest_submit = form.find(button_selector)
- empty = false
- form.find('input').filter(form_selector).each ->
- empty = true if rstrip($(this).val()) is ""
-
- if empty
- closest_submit.disable()
- else
- closest_submit.enable()
-
- form.keyup ->
- empty = false
+ updateButtons = ->
+ filled = true
form.find('input').filter(form_selector).each ->
- empty = true if rstrip($(this).val()) is ""
+ filled = rstrip($(this).val()) != "" || !$(this).attr('required')
- if empty
- closest_submit.disable()
- else
+ if filled
closest_submit.enable()
+ else
+ closest_submit.disable()
+
+ updateButtons()
+ form.keyup(updateButtons)
window.sanitize = (str) ->
return str.replace(/<(?:.|\n)*?>/gm, '')
diff --git a/app/assets/javascripts/blob.js.coffee b/app/assets/javascripts/blob/blob.js.coffee
index a5f15f80c5c..a5f15f80c5c 100644
--- a/app/assets/javascripts/blob.js.coffee
+++ b/app/assets/javascripts/blob/blob.js.coffee
diff --git a/app/assets/javascripts/blob/edit_blob.js.coffee b/app/assets/javascripts/blob/edit_blob.js.coffee
new file mode 100644
index 00000000000..6914ca759f6
--- /dev/null
+++ b/app/assets/javascripts/blob/edit_blob.js.coffee
@@ -0,0 +1,44 @@
+class @EditBlob
+ constructor: (assets_path, mode)->
+ ace.config.set "modePath", assets_path + '/ace'
+ ace.config.loadModule "ace/ext/searchbox"
+ if mode
+ ace_mode = mode
+ editor = ace.edit("editor")
+ editor.focus()
+ @editor = editor
+
+ if ace_mode
+ editor.getSession().setMode "ace/mode/" + ace_mode
+
+ disableButtonIfEmptyField "#commit_message", ".js-commit-button"
+ $(".js-commit-button").click ->
+ $("#file-content").val editor.getValue()
+ $(".file-editor form").submit()
+ return
+
+ editModePanes = $(".js-edit-mode-pane")
+ editModeLinks = $(".js-edit-mode a")
+ editModeLinks.click (event) ->
+ event.preventDefault()
+ currentLink = $(this)
+ paneId = currentLink.attr("href")
+ currentPane = editModePanes.filter(paneId)
+ editModeLinks.parent().removeClass "active hover"
+ currentLink.parent().addClass "active hover"
+ editModePanes.hide()
+ if paneId is "#preview"
+ currentPane.fadeIn 200
+ $.post currentLink.data("preview-url"),
+ content: editor.getValue()
+ , (response) ->
+ currentPane.empty().append response
+ return
+
+ else
+ currentPane.fadeIn 200
+ editor.focus()
+ return
+
+ editor: ->
+ return @editor
diff --git a/app/assets/javascripts/blob/new_blob.js.coffee b/app/assets/javascripts/blob/new_blob.js.coffee
new file mode 100644
index 00000000000..a6e27116b40
--- /dev/null
+++ b/app/assets/javascripts/blob/new_blob.js.coffee
@@ -0,0 +1,21 @@
+class @NewBlob
+ constructor: (assets_path, mode)->
+ ace.config.set "modePath", assets_path + '/ace'
+ ace.config.loadModule "ace/ext/searchbox"
+ if mode
+ ace_mode = mode
+ editor = ace.edit("editor")
+ editor.focus()
+ @editor = editor
+
+ if ace_mode
+ editor.getSession().setMode "ace/mode/" + ace_mode
+
+ disableButtonIfEmptyField "#commit_message", ".js-commit-button"
+ $(".js-commit-button").click ->
+ $("#file-content").val editor.getValue()
+ $(".file-editor form").submit()
+ return
+
+ editor: ->
+ return @editor
diff --git a/app/assets/javascripts/calendar.js.coffee b/app/assets/javascripts/calendar.js.coffee
new file mode 100644
index 00000000000..70940e13858
--- /dev/null
+++ b/app/assets/javascripts/calendar.js.coffee
@@ -0,0 +1,33 @@
+class @calendar
+ options =
+ month: "short"
+ day: "numeric"
+ year: "numeric"
+
+ constructor: (timestamps, starting_year, starting_month) ->
+ cal = new CalHeatMap()
+ cal.init
+ itemName: ["commit"]
+ data: timestamps
+ start: new Date(starting_year, starting_month)
+ domainLabelFormat: "%b"
+ id: "cal-heatmap"
+ domain: "month"
+ subDomain: "day"
+ range: 12
+ tooltip: true
+ domainDynamicDimension: false
+ colLimit: 4
+ label:
+ position: "top"
+ domainMargin: 1
+ legend: [
+ 0
+ 1
+ 4
+ 7
+ ]
+ legendCellPadding: 3
+ onClick: (date, count) ->
+ return
+ return
diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee
index ef86c2781c9..1643ca941ff 100644
--- a/app/assets/javascripts/dispatcher.js.coffee
+++ b/app/assets/javascripts/dispatcher.js.coffee
@@ -96,6 +96,7 @@ class Dispatcher
new Profile()
when 'projects'
new Project()
+ new ProjectAvatar()
switch path[1]
when 'edit'
shortcut_handler = new ShortcutsNavigation()
diff --git a/app/assets/javascripts/project_avatar.js.coffee b/app/assets/javascripts/project_avatar.js.coffee
new file mode 100644
index 00000000000..8bec6e2ccca
--- /dev/null
+++ b/app/assets/javascripts/project_avatar.js.coffee
@@ -0,0 +1,9 @@
+class @ProjectAvatar
+ constructor: ->
+ $('.js-choose-project-avatar-button').bind 'click', ->
+ form = $(this).closest('form')
+ form.find('.js-project-avatar-input').click()
+ $('.js-project-avatar-input').bind 'change', ->
+ form = $(this).closest('form')
+ filename = $(this).val().replace(/^.*[\\\/]/, '')
+ form.find('.js-avatar-filename').text(filename)
diff --git a/app/assets/javascripts/project_new.js.coffee b/app/assets/javascripts/project_new.js.coffee
index f4a2ca813d2..836269c44f9 100644
--- a/app/assets/javascripts/project_new.js.coffee
+++ b/app/assets/javascripts/project_new.js.coffee
@@ -9,17 +9,3 @@ class @ProjectNew
initEvents: ->
disableButtonIfEmptyField '#project_name', '.project-submit'
-
- $('#project_issues_enabled').change ->
- if ($(this).is(':checked') == true)
- $('#project_issues_tracker').removeAttr('disabled')
- else
- $('#project_issues_tracker').attr('disabled', 'disabled')
-
- $('#project_issues_tracker').change()
-
- $('#project_issues_tracker').change ->
- if ($(this).val() == gon.default_issues_tracker || $(this).is(':disabled'))
- $('#project_issues_tracker_id').attr('disabled', 'disabled')
- else
- $('#project_issues_tracker_id').removeAttr('disabled')
diff --git a/app/assets/javascripts/project_show.js.coffee b/app/assets/javascripts/project_show.js.coffee
index 02a7d7b731d..d0eaaad92b8 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")
+ $.cookie "default_view", $(e.target).attr("href"), { expires: 30 }
defaultView = $.cookie("default_view")
if defaultView
diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss
index 3cf08782c3c..8f63a7fee64 100644
--- a/app/assets/stylesheets/application.scss
+++ b/app/assets/stylesheets/application.scss
@@ -8,6 +8,7 @@
*= require select2
*= require_self
*= require dropzone/basic
+ *= require cal-heatmap
*/
@import "main/*";
diff --git a/app/assets/stylesheets/generic/avatar.scss b/app/assets/stylesheets/generic/avatar.scss
index 4f038b977e2..b88cdd83937 100644
--- a/app/assets/stylesheets/generic/avatar.scss
+++ b/app/assets/stylesheets/generic/avatar.scss
@@ -2,15 +2,21 @@
float: left;
margin-right: 12px;
width: 40px;
- padding: 1px;
- @include border-radius(4px);
+ height: 40px;
+ padding: 0;
+ @include border-radius($avatar_radius);
&.avatar-inline {
float: none;
- margin-left: 3px;
+ margin-left: 4px;
+ margin-bottom: 2px;
- &.s16 { margin-right: 2px; }
- &.s24 { margin-right: 2px; }
+ &.s16 { margin-right: 4px; }
+ &.s24 { margin-right: 4px; }
+ }
+
+ &.avatar-tile {
+ @include border-radius(0px);
}
&.s16 { width: 16px; height: 16px; margin-right: 6px; }
@@ -21,3 +27,16 @@
&.s90 { width: 90px; height: 90px; margin-right: 15px; }
&.s160 { width: 160px; height: 160px; margin-right: 20px; }
}
+
+.identicon {
+ text-align: center;
+ vertical-align: top;
+
+ &.s16 { font-size: 12px; line-height: 1.33; }
+ &.s24 { font-size: 14px; line-height: 1.8; }
+ &.s26 { font-size: 20px; line-height: 1.33; }
+ &.s32 { font-size: 24px; line-height: 1.33; }
+ &.s60 { font-size: 45px; line-height: 1.33; }
+ &.s90 { font-size: 68px; line-height: 1.33; }
+ &.s160 { font-size: 120px; line-height: 1.33; }
+}
diff --git a/app/assets/stylesheets/generic/buttons.scss b/app/assets/stylesheets/generic/buttons.scss
index d098f1ecaa2..3b360275065 100644
--- a/app/assets/stylesheets/generic/buttons.scss
+++ b/app/assets/stylesheets/generic/buttons.scss
@@ -173,6 +173,11 @@
margin-right: 0px;
}
}
+
+ &.btn-lg {
+ font-size: 15px;
+ line-height: 1.4;
+ }
}
.btn-block {
diff --git a/app/assets/stylesheets/generic/calendar.scss b/app/assets/stylesheets/generic/calendar.scss
new file mode 100644
index 00000000000..9483b26164e
--- /dev/null
+++ b/app/assets/stylesheets/generic/calendar.scss
@@ -0,0 +1,95 @@
+.calendar_onclick_placeholder {
+ padding: 0 0 2px 0;
+}
+
+.calendar_commit_activity {
+ padding: 5px 0 0;
+}
+
+.calendar_onclick_second {
+ font-size: 14px;
+ display: block;
+}
+
+.calendar_onclick_hr {
+ padding: 0;
+ margin: 10px 0;
+}
+
+.calendar_commit_date {
+ color: #999;
+}
+
+.calendar_activity_summary {
+ font-size: 14px;
+}
+
+/**
+* This overwrites the default values of the cal-heatmap gem
+*/
+.calendar {
+ .qi {
+ background-color: #999;
+ fill: #fff;
+ }
+
+ .q1 {
+ background-color: #dae289;
+ fill: #ededed;
+ }
+
+ .q2 {
+ background-color: #cedb9c;
+ fill: #ACD5F2;
+ }
+
+ .q3 {
+ background-color: #b5cf6b;
+ fill: #7FA8D1;
+ }
+
+ .q4 {
+ background-color: #637939;
+ fill: #49729B;
+ }
+
+ .q5 {
+ background-color: #3b6427;
+ fill: #254E77;
+ }
+
+ .domain-background {
+ fill: none;
+ shape-rendering: crispedges;
+ }
+
+ .ch-tooltip {
+ position: absolute;
+ display: none;
+ margin-top: 22px;
+ margin-left: 1px;
+ font-size: 13px;
+ padding: 3px;
+ font-weight: 550;
+ background-color: #222;
+ span {
+ position: absolute;
+ width: 200px;
+ text-align: center;
+ visibility: hidden;
+ border-radius: 10px;
+ &:after {
+ content: '';
+ position: absolute;
+ top: 100%;
+ left: 50%;
+ margin-left: -8px;
+ width: 0;
+ height: 0;
+ border-top: 8px solid #000000;
+ border-right: 8px solid transparent;
+ border-left: 8px solid transparent;
+ }
+ }
+ }
+}
diff --git a/app/assets/stylesheets/generic/highlight.scss b/app/assets/stylesheets/generic/highlight.scss
index 83dc7ab491a..e1ca86af816 100644
--- a/app/assets/stylesheets/generic/highlight.scss
+++ b/app/assets/stylesheets/generic/highlight.scss
@@ -10,8 +10,8 @@
border: none;
border-radius: 0;
font-family: $monospace_font;
- font-size: 12px !important;
- line-height: 16px !important;
+ font-size: $code_font_size !important;
+ line-height: $code_line_height !important;
margin: 0;
overflow: auto;
overflow-y: hidden;
@@ -38,8 +38,8 @@
a {
font-family: $monospace_font;
display: block;
- font-size: 12px !important;
- line-height: 16px !important;
+ font-size: $code_font_size !important;
+ line-height: $code_line_height !important;
white-space: nowrap;
i {
diff --git a/app/assets/stylesheets/generic/timeline.scss b/app/assets/stylesheets/generic/timeline.scss
index 82ee41b71bd..f92a79f7a5f 100644
--- a/app/assets/stylesheets/generic/timeline.scss
+++ b/app/assets/stylesheets/generic/timeline.scss
@@ -42,7 +42,7 @@
background: #fff;
color: #737881;
float: left;
- @include border-radius(40px);
+ @include border-radius($avatar_radius);
@include box-shadow(0 0 0 3px #EEE);
overflow: hidden;
@@ -58,6 +58,10 @@
padding: 10px 15px;
margin-left: 60px;
+ img {
+ max-width: 100%;
+ }
+
&:after {
content: '';
display: block;
diff --git a/app/assets/stylesheets/generic/typography.scss b/app/assets/stylesheets/generic/typography.scss
index 385a627b4be..58243bc5ba2 100644
--- a/app/assets/stylesheets/generic/typography.scss
+++ b/app/assets/stylesheets/generic/typography.scss
@@ -128,3 +128,7 @@ a:focus {
textarea.js-gfm-input {
font-family: $monospace_font;
}
+
+.strikethrough {
+ text-decoration: line-through;
+} \ No newline at end of file
diff --git a/app/assets/stylesheets/gl_bootstrap.scss b/app/assets/stylesheets/gl_bootstrap.scss
index 2a68d922bb7..6efa56544a5 100644
--- a/app/assets/stylesheets/gl_bootstrap.scss
+++ b/app/assets/stylesheets/gl_bootstrap.scss
@@ -1,9 +1,6 @@
/*
* Twitter bootstrap with GitLab customizations/additions
*
- * Some unused bootstrap compontents like panels are not included.
- * Other components like tabs are modified to GitLab style.
- *
*/
$font-size-base: 13px !default;
diff --git a/app/assets/stylesheets/main/mixins.scss b/app/assets/stylesheets/main/mixins.scss
index 8435d1dae79..e54482d14c3 100644
--- a/app/assets/stylesheets/main/mixins.scss
+++ b/app/assets/stylesheets/main/mixins.scss
@@ -139,7 +139,7 @@
}
@mixin panel-colored {
- border: none;
+ border: 1px solid #EEE;
background: $box_bg;
@include box-shadow(0 1px 1px rgba(0, 0, 0, 0.09));
diff --git a/app/assets/stylesheets/main/variables.scss b/app/assets/stylesheets/main/variables.scss
index aded9cb549d..acbf5be94a3 100644
--- a/app/assets/stylesheets/main/variables.scss
+++ b/app/assets/stylesheets/main/variables.scss
@@ -2,7 +2,7 @@
* General Colors
*/
$style_color: #474D57;
-$hover: #FFECDB;
+$hover: #FFF3EB;
$box_bg: #F9F9F9;
/*
@@ -56,4 +56,8 @@ $list-font-size: 15px;
/**
* Sidebar navigation width
*/
-$sidebar_width: 240px;
+$sidebar_width: 230px;
+
+$avatar_radius: 50%;
+$code_font_size: 13px;
+$code_line_height: 1.5;
diff --git a/app/assets/stylesheets/sections/commits.scss b/app/assets/stylesheets/sections/commits.scss
index 2fd2dcba475..2e274d06c12 100644
--- a/app/assets/stylesheets/sections/commits.scss
+++ b/app/assets/stylesheets/sections/commits.scss
@@ -101,7 +101,6 @@
.commit-title {
margin: 0;
- font-size: 20px;
}
.commit-description {
diff --git a/app/assets/stylesheets/sections/dashboard.scss b/app/assets/stylesheets/sections/dashboard.scss
index e540f7ff940..90010781af0 100644
--- a/app/assets/stylesheets/sections/dashboard.scss
+++ b/app/assets/stylesheets/sections/dashboard.scss
@@ -75,6 +75,9 @@
}
}
}
+.project-avatar {
+ float: left;
+}
.project-description {
overflow: hidden;
@@ -92,8 +95,24 @@
}
}
+.dash-project-avatar {
+ float: left;
+}
.dash-project-access-icon {
float: left;
- margin-right: 3px;
+ margin-right: 5px;
width: 16px;
}
+
+.dash-new-project {
+ background: $bg_success;
+ border: 1px solid $border_success;
+
+ a {
+ color: #FFF;
+ }
+}
+
+.dash-list .str-truncated {
+ max-width: 72%;
+}
diff --git a/app/assets/stylesheets/sections/diff.scss b/app/assets/stylesheets/sections/diff.scss
index 758f15c8013..da50dbe4715 100644
--- a/app/assets/stylesheets/sections/diff.scss
+++ b/app/assets/stylesheets/sections/diff.scss
@@ -37,7 +37,7 @@
overflow-y: hidden;
background: #FFF;
color: #333;
- font-size: 12px;
+ font-size: $code_font_size;
.old {
span.idiff {
background-color: #F99;
@@ -64,8 +64,8 @@
margin: 0px;
padding: 0px;
td {
- line-height: 18px;
- font-size: 12px;
+ line-height: $code_line_height;
+ font-size: $code_font_size;
}
}
diff --git a/app/assets/stylesheets/sections/editor.scss b/app/assets/stylesheets/sections/editor.scss
index f62f46ee168..88aa256e56e 100644
--- a/app/assets/stylesheets/sections/editor.scss
+++ b/app/assets/stylesheets/sections/editor.scss
@@ -31,4 +31,26 @@
margin: 5px 8px 0 8px;
}
}
+
+ .file-title {
+ @extend .monospace;
+ font-size: 14px;
+ padding: 5px;
+ }
+
+ .editor-ref {
+ background: #f5f5f5;
+ padding: 11px 15px;
+ border-right: 1px solid #CCC;
+ display: inline-block;
+ margin: -5px -5px;
+ margin-right: 10px;
+ }
+
+ .editor-file-name {
+ .new-file-name {
+ display: inline-block;
+ width: 200px;
+ }
+ }
}
diff --git a/app/assets/stylesheets/sections/header.scss b/app/assets/stylesheets/sections/header.scss
index a5098b6da5b..e255cbcada8 100644
--- a/app/assets/stylesheets/sections/header.scss
+++ b/app/assets/stylesheets/sections/header.scss
@@ -138,9 +138,10 @@ header {
top: -1px;
padding-right: 0px !important;
img {
- width: 26px;
- height: 26px;
- @include border-radius(4px);
+ width: 50px;
+ height: 50px;
+ margin: -15px;
+ margin-left: 5px;
}
}
diff --git a/app/assets/stylesheets/sections/issues.scss b/app/assets/stylesheets/sections/issues.scss
index fbfd9c8cd9b..7a9d3334d96 100644
--- a/app/assets/stylesheets/sections/issues.scss
+++ b/app/assets/stylesheets/sections/issues.scss
@@ -163,8 +163,9 @@ form.edit-issue {
}
}
-.issue-title {
+h3.issue-title {
margin-top: 0;
+ font-size: 2em;
}
.context .select2-container {
diff --git a/app/assets/stylesheets/sections/merge_requests.scss b/app/assets/stylesheets/sections/merge_requests.scss
index 8bd32f41e2c..0e27c389387 100644
--- a/app/assets/stylesheets/sections/merge_requests.scss
+++ b/app/assets/stylesheets/sections/merge_requests.scss
@@ -122,6 +122,7 @@
background: $box_bg;
margin-bottom: 20px;
color: #666;
+ border: 1px solid #EEE;
@include box-shadow(0 1px 1px rgba(0, 0, 0, 0.09));
.ci_widget {
diff --git a/app/assets/stylesheets/sections/note_form.scss b/app/assets/stylesheets/sections/note_form.scss
index 26511d799f3..61a877a5e43 100644
--- a/app/assets/stylesheets/sections/note_form.scss
+++ b/app/assets/stylesheets/sections/note_form.scss
@@ -52,6 +52,10 @@
}
}
+ img {
+ max-width: 100%;
+ }
+
.note_text {
width: 100%;
}
diff --git a/app/assets/stylesheets/sections/profile.scss b/app/assets/stylesheets/sections/profile.scss
index 086875582f3..0ab62b7ae49 100644
--- a/app/assets/stylesheets/sections/profile.scss
+++ b/app/assets/stylesheets/sections/profile.scss
@@ -102,12 +102,3 @@
}
}
}
-
-.profile-groups-avatars {
- margin: 0 5px 10px 0;
-
- img {
- width: 50px;
- height: 50px;
- }
-}
diff --git a/app/assets/stylesheets/sections/projects.scss b/app/assets/stylesheets/sections/projects.scss
index 93c0c2bc518..0a7671e3feb 100644
--- a/app/assets/stylesheets/sections/projects.scss
+++ b/app/assets/stylesheets/sections/projects.scss
@@ -16,6 +16,8 @@
.project-home-panel {
margin-bottom: 15px;
+ position: relative;
+ padding-left: 85px;
&.empty-project {
border-bottom: 0px;
@@ -23,6 +25,22 @@
margin-bottom: 0px;
}
+ .project-identicon-holder {
+ position: absolute;
+ left: 0;
+
+ .avatar {
+ width: 70px;
+ height: 70px;
+ @include border-radius(0px);
+ }
+
+ .identicon {
+ font-size: 45px;
+ line-height: 1.6;
+ }
+ }
+
.project-home-dropdown {
margin-left: 10px;
float: right;
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 6da4f91c3f4..36e13706768 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -49,7 +49,7 @@ class ApplicationController < ActionController::Base
end
def authenticate_user!(*args)
- # If user is not signe-in and tries to access root_path - redirect him to landing page
+ # If user is not signed-in and tries to access root_path - redirect him to landing page
if current_application_settings.home_page_url.present?
if current_user.nil? && controller_name == 'dashboard' && action_name == 'show'
redirect_to current_application_settings.home_page_url and return
@@ -181,7 +181,7 @@ class ApplicationController < ActionController::Base
end
def add_gon_variables
- gon.default_issues_tracker = Project.issues_tracker.default_value
+ gon.default_issues_tracker = Project.new.default_issue_tracker.to_param
gon.api_version = API::API.version
gon.relative_url_root = Gitlab.config.gitlab.relative_url_root
gon.default_avatar_url = URI::join(Gitlab.config.gitlab.url, ActionController::Base.helpers.image_path('no_avatar.png')).to_s
diff --git a/app/controllers/github_imports_controller.rb b/app/controllers/github_imports_controller.rb
index ac5cfd8d45d..3c5448bc709 100644
--- a/app/controllers/github_imports_controller.rb
+++ b/app/controllers/github_imports_controller.rb
@@ -63,11 +63,11 @@ class GithubImportsController < ApplicationController
def github_auth
if current_user.github_access_token.blank?
- go_to_gihub_for_permissions
+ go_to_github_for_permissions
end
end
- def go_to_gihub_for_permissions
+ def go_to_github_for_permissions
redirect_to client.auth_code.authorize_url({
redirect_uri: callback_github_import_url,
scope: "repo, user, user:email"
@@ -75,6 +75,6 @@ class GithubImportsController < ApplicationController
end
def github_unauthorized
- go_to_gihub_for_permissions
+ go_to_github_for_permissions
end
end
diff --git a/app/controllers/passwords_controller.rb b/app/controllers/passwords_controller.rb
index 988ede3007b..dcbbe5baa4b 100644
--- a/app/controllers/passwords_controller.rb
+++ b/app/controllers/passwords_controller.rb
@@ -5,12 +5,12 @@ class PasswordsController < Devise::PasswordsController
resource_found = resource_class.find_by_email(email)
if resource_found && resource_found.ldap_user?
flash[:alert] = "Cannot reset password for LDAP user."
- respond_with({}, :location => after_sending_reset_password_instructions_path_for(resource_name)) and return
+ respond_with({}, location: after_sending_reset_password_instructions_path_for(resource_name)) and return
end
self.resource = resource_class.send_reset_password_instructions(resource_params)
if successfully_sent?(resource)
- respond_with({}, :location => after_sending_reset_password_instructions_path_for(resource_name))
+ respond_with({}, location: after_sending_reset_password_instructions_path_for(resource_name))
else
respond_with(resource)
end
diff --git a/app/controllers/projects/avatars_controller.rb b/app/controllers/projects/avatars_controller.rb
new file mode 100644
index 00000000000..a482b90880d
--- /dev/null
+++ b/app/controllers/projects/avatars_controller.rb
@@ -0,0 +1,29 @@
+class Projects::AvatarsController < Projects::ApplicationController
+ layout 'project'
+
+ before_filter :project
+
+ def show
+ @blob = @project.repository.blob_at_branch('master', @project.avatar_in_git)
+ if @blob
+ headers['X-Content-Type-Options'] = 'nosniff'
+ send_data(
+ @blob.data,
+ type: @blob.mime_type,
+ disposition: 'inline',
+ filename: @blob.name
+ )
+ else
+ not_found!
+ end
+ end
+
+ def destroy
+ @project.remove_avatar!
+
+ @project.save
+ @project.reset_events_cache
+
+ redirect_to edit_project_path(@project)
+ end
+end
diff --git a/app/controllers/projects/base_tree_controller.rb b/app/controllers/projects/base_tree_controller.rb
deleted file mode 100644
index a7b1b7b40e8..00000000000
--- a/app/controllers/projects/base_tree_controller.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-class Projects::BaseTreeController < Projects::ApplicationController
- include ExtractsPath
-
- before_filter :authorize_download_code!
- before_filter :require_non_empty_project
-end
-
diff --git a/app/controllers/projects/blame_controller.rb b/app/controllers/projects/blame_controller.rb
index 367d1295f34..106f21b83e6 100644
--- a/app/controllers/projects/blame_controller.rb
+++ b/app/controllers/projects/blame_controller.rb
@@ -2,7 +2,7 @@
class Projects::BlameController < Projects::ApplicationController
include ExtractsPath
- # Authorize
+ before_filter :assign_ref_vars
before_filter :authorize_download_code!
before_filter :require_non_empty_project
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index 2412800c493..b471d57f698 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -2,16 +2,70 @@
class Projects::BlobController < Projects::ApplicationController
include ExtractsPath
- # Authorize
+ # Raised when given an invalid file path
+ class InvalidPathError < StandardError; end
+
before_filter :authorize_download_code!
- before_filter :require_non_empty_project
+ before_filter :require_non_empty_project, except: [:new, :create]
before_filter :authorize_push_code!, only: [:destroy]
+ before_filter :assign_blob_vars
+ before_filter :commit, except: [:new, :create]
+ before_filter :blob, except: [:new, :create]
+ before_filter :from_merge_request, only: [:edit, :update]
+ before_filter :after_edit_path, only: [:edit, :update]
+ before_filter :require_branch_head, only: [:edit, :update]
+
+ def new
+ commit unless @repository.empty?
+ end
- before_filter :blob
+ def create
+ file_path = File.join(@path, File.basename(params[:file_name]))
+ result = Files::CreateService.new(@project, current_user, params, @ref, file_path).execute
+
+ if result[:status] == :success
+ flash[:notice] = "Your changes have been successfully committed"
+ redirect_to project_blob_path(@project, File.join(@ref, file_path))
+ else
+ flash[:alert] = result[:message]
+ render :new
+ end
+ end
def show
end
+ def edit
+ @last_commit = Gitlab::Git::Commit.last_for_path(@repository, @ref, @path).sha
+ end
+
+ def update
+ result = Files::UpdateService.
+ new(@project, current_user, params, @ref, @path).execute
+
+ if result[:status] == :success
+ flash[:notice] = "Your changes have been successfully committed"
+
+ if from_merge_request
+ from_merge_request.reload_code
+ end
+
+ redirect_to after_edit_path
+ else
+ flash[:alert] = result[:message]
+ render :edit
+ end
+ end
+
+ def preview
+ @content = params[:content]
+ diffy = Diffy::Diff.new(@blob.data, @content, diff: '-U 3',
+ include_diff_info: true)
+ @diff_lines = Gitlab::Diff::Parser.new.parse(diffy.diff.scan(/.*\n/))
+
+ render layout: false
+ end
+
def destroy
result = Files::DeleteService.new(@project, current_user, params, @ref, @path).execute
@@ -46,10 +100,44 @@ class Projects::BlobController < Projects::ApplicationController
if @blob
@blob
- elsif tree.entries.any?
- redirect_to project_tree_path(@project, File.join(@ref, @path)) and return
else
+ if tree = @repository.tree(@commit.id, @path)
+ if tree.entries.any?
+ redirect_to project_tree_path(@project, File.join(@ref, @path)) and return
+ end
+ end
+
return not_found!
end
end
+
+ def commit
+ @commit = @repository.commit(@ref)
+
+ return not_found! unless @commit
+ end
+
+ def assign_blob_vars
+ @id = params[:id]
+ @ref, @path = extract_ref(@id)
+
+
+ rescue InvalidPathError
+ not_found!
+ end
+
+ def after_edit_path
+ @after_edit_path ||=
+ if from_merge_request
+ diffs_project_merge_request_path(from_merge_request.target_project, from_merge_request) +
+ "#file-path-#{hexdigest(@path)}"
+ else
+ project_blob_path(@project, @id)
+ end
+ end
+
+ def from_merge_request
+ # If blob edit was initiated from merge request page
+ @from_merge_request ||= MergeRequest.find_by(id: params[:from_merge_request_id])
+ end
end
diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb
index dac858d8e16..470efbd2114 100644
--- a/app/controllers/projects/commit_controller.rb
+++ b/app/controllers/projects/commit_controller.rb
@@ -12,6 +12,7 @@ class Projects::CommitController < Projects::ApplicationController
@line_notes = @project.notes.for_commit_id(commit.id).inline
@branches = @project.repository.branch_names_contains(commit.id)
+ @tags = @project.repository.tag_names_contains(commit.id)
@diffs = @commit.diffs
@note = @project.build_commit_note(commit)
@notes_count = @project.notes.for_commit_id(commit.id).count
diff --git a/app/controllers/projects/commits_controller.rb b/app/controllers/projects/commits_controller.rb
index 9476b6c0284..0a85c36a758 100644
--- a/app/controllers/projects/commits_controller.rb
+++ b/app/controllers/projects/commits_controller.rb
@@ -3,7 +3,7 @@ require "base64"
class Projects::CommitsController < Projects::ApplicationController
include ExtractsPath
- # Authorize
+ before_filter :assign_ref_vars
before_filter :authorize_download_code!
before_filter :require_non_empty_project
diff --git a/app/controllers/projects/edit_tree_controller.rb b/app/controllers/projects/edit_tree_controller.rb
deleted file mode 100644
index 65661c80410..00000000000
--- a/app/controllers/projects/edit_tree_controller.rb
+++ /dev/null
@@ -1,60 +0,0 @@
-class Projects::EditTreeController < Projects::BaseTreeController
- before_filter :require_branch_head
- before_filter :blob
- before_filter :authorize_push_code!
- before_filter :from_merge_request
- before_filter :after_edit_path
-
- def show
- @last_commit = Gitlab::Git::Commit.last_for_path(@repository, @ref, @path).sha
- end
-
- def update
- result = Files::UpdateService.
- new(@project, current_user, params, @ref, @path).execute
-
- if result[:status] == :success
- flash[:notice] = "Your changes have been successfully committed"
-
- if from_merge_request
- from_merge_request.reload_code
- end
-
- redirect_to after_edit_path
- else
- flash[:alert] = result[:message]
- render :show
- end
- end
-
- def preview
- @content = params[:content]
-
- diffy = Diffy::Diff.new(@blob.data, @content, diff: '-U 3',
- include_diff_info: true)
- @diff_lines = Gitlab::Diff::Parser.new.parse(diffy.diff.scan(/.*\n/))
-
- render layout: false
- end
-
- private
-
- def blob
- @blob ||= @repository.blob_at(@commit.id, @path)
- end
-
- def after_edit_path
- @after_edit_path ||=
- if from_merge_request
- diffs_project_merge_request_path(from_merge_request.target_project, from_merge_request) +
- "#file-path-#{hexdigest(@path)}"
- else
- project_blob_path(@project, @id)
- end
- end
-
- def from_merge_request
- # If blob edit was initiated from merge request page
- @from_merge_request ||= MergeRequest.find_by(id: params[:from_merge_request_id])
- end
-end
diff --git a/app/controllers/projects/network_controller.rb b/app/controllers/projects/network_controller.rb
index ada1aed0df7..59f2a745367 100644
--- a/app/controllers/projects/network_controller.rb
+++ b/app/controllers/projects/network_controller.rb
@@ -2,7 +2,7 @@ class Projects::NetworkController < Projects::ApplicationController
include ExtractsPath
include ApplicationHelper
- # Authorize
+ before_filter :assign_ref_vars
before_filter :authorize_download_code!
before_filter :require_non_empty_project
diff --git a/app/controllers/projects/new_tree_controller.rb b/app/controllers/projects/new_tree_controller.rb
deleted file mode 100644
index ffba706b2f6..00000000000
--- a/app/controllers/projects/new_tree_controller.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-class Projects::NewTreeController < Projects::BaseTreeController
- before_filter :require_branch_head
- before_filter :authorize_push_code!
-
- def show
- end
-
- def update
- file_path = File.join(@path, File.basename(params[:file_name]))
- result = Files::CreateService.new(@project, current_user, params, @ref, file_path).execute
-
- if result[:status] == :success
- flash[:notice] = "Your changes have been successfully committed"
- redirect_to project_blob_path(@project, File.join(@ref, file_path))
- else
- flash[:alert] = result[:message]
- render :show
- end
- end
-end
diff --git a/app/controllers/projects/protected_branches_controller.rb b/app/controllers/projects/protected_branches_controller.rb
index 02160d973b3..f45df38b87c 100644
--- a/app/controllers/projects/protected_branches_controller.rb
+++ b/app/controllers/projects/protected_branches_controller.rb
@@ -24,7 +24,7 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController
)
respond_to do |format|
- format.json { render :json => protected_branch, status: :ok }
+ format.json { render json: protected_branch, status: :ok }
end
else
respond_to do |format|
diff --git a/app/controllers/projects/raw_controller.rb b/app/controllers/projects/raw_controller.rb
index fdbc4c5a098..84888265dc1 100644
--- a/app/controllers/projects/raw_controller.rb
+++ b/app/controllers/projects/raw_controller.rb
@@ -2,7 +2,7 @@
class Projects::RawController < Projects::ApplicationController
include ExtractsPath
- # Authorize
+ before_filter :assign_ref_vars
before_filter :authorize_download_code!
before_filter :require_non_empty_project
diff --git a/app/controllers/projects/refs_controller.rb b/app/controllers/projects/refs_controller.rb
index 5d9336bdc49..cede0ebe0ae 100644
--- a/app/controllers/projects/refs_controller.rb
+++ b/app/controllers/projects/refs_controller.rb
@@ -1,7 +1,7 @@
class Projects::RefsController < Projects::ApplicationController
include ExtractsPath
- # Authorize
+ before_filter :assign_ref_vars
before_filter :authorize_download_code!
before_filter :require_non_empty_project
@@ -41,9 +41,9 @@ class Projects::RefsController < Projects::ApplicationController
@path = params[:path]
contents = []
- contents += tree.trees
- contents += tree.blobs
- contents += tree.submodules
+ contents.push(*tree.trees)
+ contents.push(*tree.blobs)
+ contents.push(*tree.submodules)
@logs = contents[@offset, @limit].to_a.map do |content|
file = @path ? File.join(@path, content.name) : content.name
diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb
index b2ce99aeb45..5b35cc90413 100644
--- a/app/controllers/projects/services_controller.rb
+++ b/app/controllers/projects/services_controller.rb
@@ -9,7 +9,7 @@ class Projects::ServicesController < Projects::ApplicationController
def index
@project.build_missing_services
- @services = @project.services.reload
+ @services = @project.services.visible.reload
end
def edit
@@ -17,7 +17,8 @@ class Projects::ServicesController < Projects::ApplicationController
def update
if @service.update_attributes(service_params)
- redirect_to edit_project_service_path(@project, @service.to_param)
+ redirect_to edit_project_service_path(@project, @service.to_param),
+ notice: 'Successfully updated.'
else
render 'edit'
end
@@ -25,9 +26,13 @@ class Projects::ServicesController < Projects::ApplicationController
def test
data = Gitlab::PushDataBuilder.build_sample(project, current_user)
- @service.execute(data)
+ if @service.execute(data)
+ message = { notice: 'We sent a request to the provided URL' }
+ else
+ message = { alert: 'We tried to send a request to the provided URL but error occured' }
+ end
- redirect_to :back
+ redirect_to :back, message
end
private
@@ -41,7 +46,8 @@ class Projects::ServicesController < Projects::ApplicationController
:title, :token, :type, :active, :api_key, :subdomain,
:room, :recipients, :project_url, :webhook,
:user_key, :device, :priority, :sound, :bamboo_url, :username, :password,
- :build_key, :server, :teamcity_url, :build_type
+ :build_key, :server, :teamcity_url, :build_type,
+ :description, :issues_url, :new_issue_url
)
end
end
diff --git a/app/controllers/projects/tree_controller.rb b/app/controllers/projects/tree_controller.rb
index 4d033b36848..5b52640a4e1 100644
--- a/app/controllers/projects/tree_controller.rb
+++ b/app/controllers/projects/tree_controller.rb
@@ -1,7 +1,12 @@
# Controller for viewing a repository's file structure
-class Projects::TreeController < Projects::BaseTreeController
- def show
+class Projects::TreeController < Projects::ApplicationController
+ include ExtractsPath
+
+ before_filter :assign_ref_vars
+ before_filter :authorize_download_code!
+ before_filter :require_non_empty_project, except: [:new, :create]
+ def show
if tree.entries.empty?
if @repository.blob_at(@commit.id, @path)
redirect_to project_blob_path(@project, File.join(@ref, @path)) and return
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index e541b6fd872..ebe48265c63 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -14,7 +14,7 @@ class ProjectsController < ApplicationController
end
def edit
- render 'edit', layout: "project_settings"
+ render 'edit', layout: 'project_settings'
end
def create
@@ -36,7 +36,7 @@ class ProjectsController < ApplicationController
format.html { redirect_to edit_project_path(@project), notice: 'Project was successfully updated.' }
format.js
else
- format.html { render "edit", layout: "project_settings" }
+ format.html { render 'edit', layout: 'project_settings' }
format.js
end
end
@@ -66,17 +66,17 @@ class ProjectsController < ApplicationController
format.html do
if @project.repository_exists?
if @project.empty_repo?
- render "projects/empty", layout: user_layout
+ render 'projects/empty', layout: user_layout
else
@last_push = current_user.recent_push(@project.id) if current_user
render :show, layout: user_layout
end
else
- render "projects/no_repo", layout: user_layout
+ render 'projects/no_repo', layout: user_layout
end
end
- format.json { pager_json("events/_events", @events.count) }
+ format.json { pager_json('events/_events', @events.count) }
end
end
@@ -87,9 +87,9 @@ class ProjectsController < ApplicationController
respond_to do |format|
format.html do
- flash[:alert] = "Project deleted."
+ flash[:alert] = 'Project deleted.'
- if request.referer.include?("/admin")
+ if request.referer.include?('/admin')
redirect_to admin_projects_path
else
redirect_to projects_dashboard_path
@@ -101,16 +101,18 @@ class ProjectsController < ApplicationController
def autocomplete_sources
note_type = params['type']
note_id = params['type_id']
+ autocomplete = ::Projects::AutocompleteService.new(@project)
participants = ::Projects::ParticipantsService.new(@project).execute(note_type, note_id)
+
@suggestions = {
- emojis: Emoji.names.map { |e| { name: e, path: view_context.image_url("emoji/#{e}.png") } },
- issues: @project.issues.select([:iid, :title, :description]),
- mergerequests: @project.merge_requests.select([:iid, :title, :description]),
+ emojis: autocomplete_emojis,
+ issues: autocomplete.issues,
+ mergerequests: autocomplete.merge_requests,
members: participants
}
respond_to do |format|
- format.json { render :json => @suggestions }
+ format.json { render json: @suggestions }
end
end
@@ -139,7 +141,7 @@ class ProjectsController < ApplicationController
if link_to_image
format.json { render json: { link: link_to_image } }
else
- format.json { render json: "Invalid file.", status: :unprocessable_entity }
+ format.json { render json: 'Invalid file.', status: :unprocessable_entity }
end
end
end
@@ -170,14 +172,25 @@ class ProjectsController < ApplicationController
end
def user_layout
- current_user ? "projects" : "public_projects"
+ current_user ? 'projects' : 'public_projects'
end
def project_params
params.require(:project).permit(
:name, :path, :description, :issues_tracker, :tag_list,
:issues_enabled, :merge_requests_enabled, :snippets_enabled, :issues_tracker_id, :default_branch,
- :wiki_enabled, :visibility_level, :import_url, :last_activity_at, :namespace_id
+ :wiki_enabled, :visibility_level, :import_url, :last_activity_at, :namespace_id, :avatar
)
end
+
+ def autocomplete_emojis
+ Rails.cache.fetch("autocomplete-emoji-#{Emoji::VERSION}") do
+ Emoji.names.map do |e|
+ {
+ name: e,
+ path: view_context.image_url("emoji/#{e}.png")
+ }
+ end
+ end
+ end
end
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 67af1801bda..57d8ef09faf 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -1,16 +1,12 @@
class UsersController < ApplicationController
- skip_before_filter :authenticate_user!, only: [:show]
+ skip_before_filter :authenticate_user!
+ before_filter :set_user
layout :determine_layout
def show
- @user = User.find_by_username!(params[:username])
-
- unless current_user || @user.public_profile?
- return authenticate_user!
- end
-
# Projects user can view
- authorized_projects_ids = ProjectsFinder.new.execute(current_user).pluck(:id)
+ visible_projects = ProjectsFinder.new.execute(current_user)
+ authorized_projects_ids = visible_projects.pluck(:id)
@projects = @user.personal_projects.
where(id: authorized_projects_ids)
@@ -30,6 +26,16 @@ class UsersController < ApplicationController
end
end
+ def calendar
+ visible_projects = ProjectsFinder.new.execute(current_user)
+ calendar = Gitlab::CommitsCalendar.new(visible_projects, @user)
+ @timestamps = calendar.timestamps
+ @starting_year = calendar.starting_year
+ @starting_month = calendar.starting_month
+
+ render 'calendar', layout: false
+ end
+
def determine_layout
if current_user
'navless'
@@ -37,4 +43,14 @@ class UsersController < ApplicationController
'public_users'
end
end
+
+ private
+
+ def set_user
+ @user = User.find_by_username!(params[:username])
+
+ unless current_user || @user.public_profile?
+ return authenticate_user!
+ end
+ end
end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index f65e04af205..d00f1aac2d6 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -50,6 +50,38 @@ module ApplicationHelper
args.any? { |v| v.to_s.downcase == action_name }
end
+ def project_icon(project_id, options = {})
+ project = Project.find_with_namespace(project_id)
+ if project.avatar.present?
+ image_tag project.avatar.url, options
+ elsif project.avatar_in_git
+ image_tag project_avatar_path(project), options
+ else # generated icon
+ project_identicon(project, options)
+ end
+ end
+
+ def project_identicon(project, options = {})
+ allowed_colors = {
+ red: 'FFEBEE',
+ purple: 'F3E5F5',
+ indigo: 'E8EAF6',
+ blue: 'E3F2FD',
+ teal: 'E0F2F1',
+ orange: 'FBE9E7',
+ gray: 'EEEEEE'
+ }
+
+ options[:class] ||= ''
+ options[:class] << ' identicon'
+ bg_key = project.id % 7
+
+ content_tag(:div, class: options[:class],
+ style: "background-color: ##{ allowed_colors.values[bg_key] }; color: #555") do
+ project.name[0, 1].upcase
+ end
+ end
+
def group_icon(group_path)
group = Group.find_by(path: group_path)
if group && group.avatar.present?
@@ -82,24 +114,24 @@ module ApplicationHelper
if project.repo_exists?
time_ago_with_tooltip(project.repository.commit.committed_date)
else
- "Never"
+ 'Never'
end
rescue
- "Never"
+ 'Never'
end
def grouped_options_refs
repository = @project.repository
options = [
- ["Branches", repository.branch_names],
- ["Tags", VersionSorter.rsort(repository.tag_names)]
+ ['Branches', repository.branch_names],
+ ['Tags', VersionSorter.rsort(repository.tag_names)]
]
# If reference is commit id - we should add it to branch/tag selectbox
if(@ref && !options.flatten.include?(@ref) &&
@ref =~ /^[0-9a-zA-Z]{6,52}$/)
- options << ["Commit", [@ref]]
+ options << ['Commit', [@ref]]
end
grouped_options_for_select(options, @ref || @project.default_branch)
@@ -161,7 +193,7 @@ module ApplicationHelper
path = controller.controller_path.split('/')
namespace = path.first if path.second
- [namespace, controller.controller_name, controller.action_name].compact.join(":")
+ [namespace, controller.controller_name, controller.action_name].compact.join(':')
end
# shortcut for gitlab config
@@ -176,13 +208,13 @@ module ApplicationHelper
def search_placeholder
if @project && @project.persisted?
- "Search in this project"
+ 'Search in this project'
elsif @snippet || @snippets || @show_snippets
'Search snippets'
elsif @group && @group.persisted?
- "Search in this group"
+ 'Search in this group'
else
- "Search"
+ 'Search'
end
end
@@ -193,7 +225,7 @@ module ApplicationHelper
def time_ago_with_tooltip(date, placement = 'top', html_class = 'time_ago')
capture_haml do
haml_tag :time, date.to_s,
- class: html_class, datetime: date.getutc.iso8601, title: date.stamp("Aug 21, 2011 9:23pm"),
+ class: html_class, datetime: date.getutc.iso8601, title: date.stamp('Aug 21, 2011 9:23pm'),
data: { toggle: 'tooltip', placement: placement }
haml_tag :script, "$('." + html_class + "').timeago().tooltip()"
@@ -215,15 +247,6 @@ module ApplicationHelper
Gitlab::MarkdownHelper.gitlab_markdown?(filename)
end
- def spinner(text = nil, visible = false)
- css_class = "loading"
- css_class << " hide" unless visible
-
- content_tag :div, class: css_class do
- content_tag(:i, nil, class: 'fa fa-spinner fa-spin') + text
- end
- end
-
def link_to(name = nil, options = nil, html_options = nil, &block)
begin
uri = URI(options)
@@ -234,17 +257,17 @@ module ApplicationHelper
absolute_uri = nil
end
- # Add "nofollow" only to external links
+ # Add 'nofollow' only to external links
if host && host != Gitlab.config.gitlab.host && absolute_uri
if html_options
if html_options[:rel]
- html_options[:rel] << " nofollow"
+ html_options[:rel] << ' nofollow'
else
- html_options.merge!(rel: "nofollow")
+ html_options.merge!(rel: 'nofollow')
end
else
html_options = Hash.new
- html_options[:rel] = "nofollow"
+ html_options[:rel] = 'nofollow'
end
end
diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb
index 3a282803963..e75eebd2da9 100644
--- a/app/helpers/blob_helper.rb
+++ b/app/helpers/blob_helper.rb
@@ -19,4 +19,42 @@ module BlobHelper
def no_highlight_files
%w(credits changelog copying copyright license authors)
end
+
+ def edit_blob_link(project, ref, path, options = {})
+ blob =
+ begin
+ project.repository.blob_at(ref, path)
+ rescue
+ nil
+ end
+
+ if blob && blob.text?
+ text = 'Edit'
+ after = options[:after] || ''
+ from_mr = options[:from_merge_request_id]
+ link_opts = {}
+ link_opts[:from_merge_request_id] = from_mr if from_mr
+ cls = 'btn btn-small'
+ if allowed_tree_edit?(project, ref)
+ link_to text, project_edit_blob_path(project, tree_join(ref, path),
+ link_opts), class: cls
+ else
+ content_tag :span, text, class: cls + ' disabled'
+ end + after.html_safe
+ else
+ ''
+ end
+ end
+
+ def leave_edit_message
+ "Leave edit mode?\nAll unsaved changes will be lost."
+ end
+
+ def editing_preview_title(filename)
+ if Gitlab::MarkdownHelper.previewable?(filename)
+ 'Preview'
+ else
+ 'Preview changes'
+ end
+ end
end
diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb
index 36adeadd8a5..1a322ac048f 100644
--- a/app/helpers/commits_helper.rb
+++ b/app/helpers/commits_helper.rb
@@ -44,7 +44,7 @@ module CommitsHelper
parts = @path.split('/')
parts.each_with_index do |part, i|
- crumbs += content_tag(:li) do
+ crumbs << content_tag(:li) do
# The text is just the individual part, but the link needs all the parts before it
link_to part, project_commits_path(@project, tree_join(@ref, parts[0..i].join('/')))
end
@@ -62,7 +62,25 @@ module CommitsHelper
# Returns the sorted alphabetically links to branches, separated by a comma
def commit_branches_links(project, branches)
- branches.sort.map { |branch| link_to(branch, project_tree_path(project, branch)) }.join(", ").html_safe
+ branches.sort.map do |branch|
+ link_to(project_tree_path(project, branch)) do
+ content_tag :span, class: 'label label-gray' do
+ icon('code-fork') + ' ' + branch
+ end
+ end
+ end.join(" ").html_safe
+ end
+
+ # Returns the sorted links to tags, separated by a comma
+ def commit_tags_links(project, tags)
+ sorted = VersionSorter.rsort(tags)
+ sorted.map do |tag|
+ link_to(project_commits_path(project, project.repository.find_tag(tag).name)) do
+ content_tag :span, class: 'label label-gray' do
+ icon('tag') + ' ' + tag
+ end
+ end
+ end.join(" ").html_safe
end
def link_to_browse_code(project, commit)
diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb
index a15af0be01a..8c921cba543 100644
--- a/app/helpers/diff_helper.rb
+++ b/app/helpers/diff_helper.rb
@@ -135,4 +135,19 @@ module DiffHelper
'Side-by-side'
end
end
+
+ def submodule_link(blob, ref)
+ tree, commit = submodule_links(blob, ref)
+ commit_id = if commit.nil?
+ blob.id[0..10]
+ else
+ link_to "#{blob.id[0..10]}", commit
+ end
+
+ [
+ content_tag(:span, link_to(truncate(blob.name, length: 40), tree)),
+ '@',
+ content_tag(:span, commit_id, class: 'monospace'),
+ ].join(' ').html_safe
+ end
end
diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb
index 903a5009616..d05f6df5f9f 100644
--- a/app/helpers/events_helper.rb
+++ b/app/helpers/events_helper.rb
@@ -27,18 +27,17 @@ module EventsHelper
content_tag :li, class: "filter_icon #{active}" do
link_to request.path, class: 'has_tooltip event_filter_link', id: "#{key}_event_filter", 'data-original-title' => tooltip do
- content_tag(:i, nil, class: icon_for_event[key]) +
- content_tag(:span, ' ' + tooltip)
+ icon(icon_for_event[key]) + content_tag(:span, ' ' + tooltip)
end
end
end
def icon_for_event
{
- EventFilter.push => 'fa fa-upload',
- EventFilter.merged => 'fa fa-check-square-o',
- EventFilter.comments => 'fa fa-comments',
- EventFilter.team => 'fa fa-user',
+ EventFilter.push => 'upload',
+ EventFilter.merged => 'check-square-o',
+ EventFilter.comments => 'comments',
+ EventFilter.team => 'user',
}
end
diff --git a/app/helpers/graph_helper.rb b/app/helpers/graph_helper.rb
index 7cb1b6f8d1a..e1dda20de85 100644
--- a/app/helpers/graph_helper.rb
+++ b/app/helpers/graph_helper.rb
@@ -1,10 +1,10 @@
module GraphHelper
def get_refs(repo, commit)
refs = ""
- refs += commit.ref_names(repo).join(" ")
+ refs << commit.ref_names(repo).join(' ')
# append note count
- refs += "[#{@graph.notes[commit.id]}]" if @graph.notes[commit.id] > 0
+ refs << "[#{@graph.notes[commit.id]}]" if @graph.notes[commit.id] > 0
refs
end
diff --git a/app/helpers/icons_helper.rb b/app/helpers/icons_helper.rb
index aaa8f8d0077..18260f0ed4d 100644
--- a/app/helpers/icons_helper.rb
+++ b/app/helpers/icons_helper.rb
@@ -1,21 +1,39 @@
module IconsHelper
+ # Creates an icon tag given icon name(s) and possible icon modifiers.
+ #
+ # Right now this method simply delegates directly to `fa_icon` from the
+ # font-awesome-rails gem, but should we ever use a different icon pack in the
+ # future we won't have to change hundreds of method calls.
+ def icon(names, options = {})
+ fa_icon(names, options)
+ end
+
+ def spinner(text = nil, visible = false)
+ css_class = 'loading'
+ css_class << ' hide' unless visible
+
+ content_tag :div, class: css_class do
+ icon('spinner spin') + text
+ end
+ end
+
def boolean_to_icon(value)
if value.to_s == "true"
- content_tag :i, nil, class: 'fa fa-circle cgreen'
+ icon('circle', class: 'cgreen')
else
- content_tag :i, nil, class: 'fa fa-power-off clgray'
+ icon('power-off', class: 'clgray')
end
end
def public_icon
- content_tag :i, nil, class: 'fa fa-globe'
+ icon('globe')
end
def internal_icon
- content_tag :i, nil, class: 'fa fa-shield'
+ icon('shield')
end
def private_icon
- content_tag :i, nil, class: 'fa fa-lock'
+ icon('lock')
end
end
diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb
index bcf108c5c48..e1c1078344e 100644
--- a/app/helpers/issues_helper.rb
+++ b/app/helpers/issues_helper.rb
@@ -16,45 +16,25 @@ module IssuesHelper
def url_for_project_issues(project = @project)
return '' if project.nil?
- if project.used_default_issues_tracker? || !external_issues_tracker_enabled?
- project_issues_path(project)
- else
- url = Gitlab.config.issues_tracker[project.issues_tracker]['project_url']
- url.gsub(':project_id', project.id.to_s).
- gsub(':issues_tracker_id', project.issues_tracker_id.to_s)
- end
+ project.issues_tracker.project_url
end
def url_for_new_issue(project = @project)
return '' if project.nil?
- if project.used_default_issues_tracker? || !external_issues_tracker_enabled?
- url = new_project_issue_path project_id: project
- else
- issues_tracker = Gitlab.config.issues_tracker[project.issues_tracker]
- url = issues_tracker['new_issue_url']
- url.gsub(':project_id', project.id.to_s).
- gsub(':issues_tracker_id', project.issues_tracker_id.to_s)
- end
+ project.issues_tracker.new_issue_url
end
def url_for_issue(issue_iid, project = @project)
return '' if project.nil?
- if project.used_default_issues_tracker? || !external_issues_tracker_enabled?
- url = project_issue_url project_id: project, id: issue_iid
- else
- url = Gitlab.config.issues_tracker[project.issues_tracker]['issues_url']
- url.gsub(':id', issue_iid.to_s).
- gsub(':project_id', project.id.to_s).
- gsub(':issues_tracker_id', project.issues_tracker_id.to_s)
- end
+ project.issues_tracker.issue_url(issue_iid)
end
def title_for_issue(issue_iid, project = @project)
return '' if project.nil?
- if project.used_default_issues_tracker?
+ if project.default_issues_tracker?
issue = project.issues.where(iid: issue_iid).first
return issue.title if issue
end
@@ -69,7 +49,7 @@ module IssuesHelper
ts << capture_haml do
haml_tag :span do
haml_concat '&middot;'
- haml_concat '<i class="fa fa-edit" title="edited"></i> '
+ haml_concat icon('edit', title: 'edited')
haml_concat time_ago_with_tooltip(issue.updated_at, 'bottom', 'issue_edited_ago')
end
end
@@ -77,11 +57,6 @@ module IssuesHelper
ts.html_safe
end
- # Checks if issues_tracker setting exists in gitlab.yml
- def external_issues_tracker_enabled?
- Gitlab.config.issues_tracker && Gitlab.config.issues_tracker.values.any?
- end
-
def bulk_update_milestone_options
options_for_select(['None (backlog)']) +
options_from_collection_for_select(project_active_milestones, 'id',
diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb
index 8f493f5d331..d41d5617396 100644
--- a/app/helpers/notes_helper.rb
+++ b/app/helpers/notes_helper.rb
@@ -22,7 +22,7 @@ module NotesHelper
ts << capture_haml do
haml_tag :span do
haml_concat '&middot;'
- haml_concat '<i class="fa fa-edit" title="edited"></i> '
+ haml_concat icon('edit', title: 'edited')
haml_concat time_ago_with_tooltip(note.updated_at, 'bottom', 'note_edited_ago')
end
end
@@ -57,7 +57,7 @@ module NotesHelper
button_tag(class: 'btn add-diff-note js-add-diff-note-button',
data: data,
title: 'Add a comment to this line') do
- content_tag :i, nil, class: 'fa fa-comment-o'
+ icon('comment-o')
end
end
@@ -74,7 +74,7 @@ module NotesHelper
button_tag class: 'btn reply-btn js-discussion-reply-button',
data: data, title: 'Add a reply' do
- link_text = content_tag(:i, nil, class: 'fa fa-comment')
+ link_text = icon('comment')
link_text << ' Reply'
end
end
diff --git a/app/helpers/notifications_helper.rb b/app/helpers/notifications_helper.rb
index bad380e98a8..f771fe761ef 100644
--- a/app/helpers/notifications_helper.rb
+++ b/app/helpers/notifications_helper.rb
@@ -1,13 +1,13 @@
module NotificationsHelper
def notification_icon(notification)
if notification.disabled?
- content_tag :i, nil, class: 'fa fa-volume-off ns-mute'
+ icon('volume-off', class: 'ns-mute')
elsif notification.participating?
- content_tag :i, nil, class: 'fa fa-volume-down ns-part'
+ icon('volume-down', class: 'ns-part')
elsif notification.watch?
- content_tag :i, nil, class: 'fa fa-volume-up ns-watch'
+ icon('volume-up', class: 'ns-watch')
else
- content_tag :i, nil, class: 'fa fa-circle-o ns-default'
+ icon('circle-o', class: 'ns-default')
end
end
end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 9a31d485188..0b01be79623 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -72,21 +72,9 @@ module ProjectsHelper
@project.milestones.active.order("due_date, title ASC")
end
- def project_issues_trackers(current_tracker = nil)
- values = Project.issues_tracker.values.map do |tracker_key|
- if tracker_key.to_sym == :gitlab
- ['GitLab', tracker_key]
- else
- [Gitlab.config.issues_tracker[tracker_key]['title'] || tracker_key, tracker_key]
- end
- end
-
- options_for_select(values, current_tracker)
- end
-
def link_to_toggle_star(title, starred, signed_in)
cls = 'star-btn'
- cls += ' disabled' unless signed_in
+ cls << ' disabled' unless signed_in
toggle_html = content_tag('span', class: 'toggle') do
toggle_text = if starred
@@ -95,7 +83,7 @@ module ProjectsHelper
' Star'
end
- content_tag('i', ' ', class: 'fa fa-star') + toggle_text
+ icon('star') + toggle_text
end
count_html = content_tag('span', class: 'count') do
@@ -119,7 +107,7 @@ module ProjectsHelper
end
def link_to_toggle_fork
- out = content_tag(:i, '', class: 'fa fa-code-fork')
+ out = icon('code-fork')
out << ' Fork'
out << content_tag(:span, class: 'count') do
@project.forks_count.to_s
@@ -187,7 +175,13 @@ module ProjectsHelper
"Issues - " + title
end
elsif current_controller?(:blob)
- "#{@project.path}\/#{@blob.path} at #{@ref} - " + title
+ if current_action?(:new) || current_action?(:create)
+ "New file at #{@ref}"
+ elsif current_action?(:show)
+ "#{@blob.path} at #{@ref}"
+ elsif @blob
+ "Edit file #{@blob.path} at #{@ref}"
+ end
elsif current_controller?(:commits)
"Commits at #{@ref} - " + title
elsif current_controller?(:merge_requests)
@@ -257,7 +251,7 @@ module ProjectsHelper
end
def github_import_enabled?
- Gitlab.config.omniauth.enabled && enabled_oauth_providers.include?(:github)
+ enabled_oauth_providers.include?(:github)
end
end
diff --git a/app/helpers/submodule_helper.rb b/app/helpers/submodule_helper.rb
index 09e5c08e621..841e7fd17f6 100644
--- a/app/helpers/submodule_helper.rb
+++ b/app/helpers/submodule_helper.rb
@@ -2,8 +2,8 @@ module SubmoduleHelper
include Gitlab::ShellAdapter
# links to files listing for submodule if submodule is a project on this server
- def submodule_links(submodule_item)
- url = @repository.submodule_url_for(@ref, submodule_item.path)
+ def submodule_links(submodule_item, ref = nil)
+ url = @repository.submodule_url_for(ref, submodule_item.path)
return url, nil unless url =~ /([^\/:]+\/[^\/]+\.git)\Z/
diff --git a/app/helpers/tags_helper.rb b/app/helpers/tags_helper.rb
index ef89bb32c6d..fb85544df2d 100644
--- a/app/helpers/tags_helper.rb
+++ b/app/helpers/tags_helper.rb
@@ -6,7 +6,7 @@ module TagsHelper
def tag_list(project)
html = ''
project.tag_list.each do |tag|
- html += link_to tag, tag_path(tag)
+ html << link_to(tag, tag_path(tag))
end
html.html_safe
diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb
index 133b0dfae9b..b6fb7a8aa5a 100644
--- a/app/helpers/tree_helper.rb
+++ b/app/helpers/tree_helper.rb
@@ -10,13 +10,16 @@ module TreeHelper
tree = ""
# Render folders if we have any
- tree += render partial: 'projects/tree/tree_item', collection: folders, locals: {type: 'folder'} if folders.present?
+ tree << render(partial: 'projects/tree/tree_item', collection: folders,
+ locals: { type: 'folder' }) if folders.present?
# Render files if we have any
- tree += render partial: 'projects/tree/blob_item', collection: files, locals: {type: 'file'} if files.present?
+ tree << render(partial: 'projects/tree/blob_item', collection: files,
+ locals: { type: 'file' }) if files.present?
# Render submodules if we have any
- tree += render partial: 'projects/tree/submodule_item', collection: submodules if submodules.present?
+ tree << render(partial: 'projects/tree/submodule_item',
+ collection: submodules) if submodules.present?
tree.html_safe
end
@@ -35,13 +38,8 @@ module TreeHelper
#
# type - String type of the tree item; either 'folder' or 'file'
def tree_icon(type)
- icon_class = if type == 'folder'
- 'fa fa-folder'
- else
- 'fa fa-file-o'
- end
-
- content_tag :i, nil, class: icon_class
+ icon_class = type == 'folder' ? 'folder' : 'file-o'
+ icon(icon_class)
end
def tree_hex_class(content)
@@ -61,32 +59,6 @@ module TreeHelper
::Gitlab::GitAccess.can_push_to_branch?(current_user, project, ref)
end
- def edit_blob_link(project, ref, path, options = {})
- blob =
- begin
- project.repository.blob_at(ref, path)
- rescue
- nil
- end
-
- if blob && blob.text?
- text = 'Edit'
- after = options[:after] || ''
- from_mr = options[:from_merge_request_id]
- link_opts = {}
- link_opts[:from_merge_request_id] = from_mr if from_mr
- cls = 'btn btn-small'
- if allowed_tree_edit?(project, ref)
- link_to text, project_edit_tree_path(project, tree_join(ref, path),
- link_opts), class: cls
- else
- content_tag :span, text, class: cls + ' disabled'
- end + after.html_safe
- else
- ''
- end
- end
-
def tree_breadcrumbs(tree, max_links = 2)
if @path.present?
part_path = ""
@@ -109,7 +81,7 @@ module TreeHelper
tree_join(@ref, file)
end
- # returns the relative path of the first subdir that doesn't have only one directory descendand
+ # returns the relative path of the first subdir that doesn't have only one directory descendant
def flatten_tree(tree)
subtree = Gitlab::Git::Tree.where(@repository, @commit.id, tree.path)
if subtree.count == 1 && subtree.first.dir?
@@ -118,16 +90,4 @@ module TreeHelper
return tree.name
end
end
-
- def leave_edit_message
- "Leave edit mode?\nAll unsaved changes will be lost."
- end
-
- def editing_preview_title(filename)
- if Gitlab::MarkdownHelper.previewable?(filename)
- 'Preview'
- else
- 'Diff'
- end
- end
end
diff --git a/app/mailers/emails/merge_requests.rb b/app/mailers/emails/merge_requests.rb
index 9ecdac87d72..7f6c855c301 100644
--- a/app/mailers/emails/merge_requests.rb
+++ b/app/mailers/emails/merge_requests.rb
@@ -56,7 +56,7 @@ module Emails
end
end
- # Over rides default behavour to show source/target
+ # Over rides default behaviour to show source/target
# Formats arguments into a String suitable for use as an email subject
#
# extra - Extra Strings to be inserted into the subject
diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb
index 6d671e6e0bd..5ae07d771fa 100644
--- a/app/mailers/notify.rb
+++ b/app/mailers/notify.rb
@@ -26,8 +26,8 @@ class Notify < ActionMailer::Base
delay_for(2.seconds)
end
- def test_email(recepient_email, subject, body)
- mail(to: recepient_email,
+ def test_email(recipient_email, subject, body)
+ mail(to: recipient_email,
subject: subject,
body: body.html_safe,
content_type: 'text/html'
diff --git a/app/models/ability.rb b/app/models/ability.rb
index 97a72bf3635..890417e780d 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -73,28 +73,28 @@ class Ability
# Rules based on role in project
if team.master?(user)
- rules += project_master_rules
+ rules.push(*project_master_rules)
elsif team.developer?(user)
- rules += project_dev_rules
+ rules.push(*project_dev_rules)
elsif team.reporter?(user)
- rules += project_report_rules
+ rules.push(*project_report_rules)
elsif team.guest?(user)
- rules += project_guest_rules
+ rules.push(*project_guest_rules)
end
if project.public? || project.internal?
- rules += public_project_rules
+ rules.push(*public_project_rules)
end
if project.owner == user || user.admin?
- rules += project_admin_rules
+ rules.push(*project_admin_rules)
end
if project.group && project.group.has_owner?(user)
- rules += project_admin_rules
+ rules.push(*project_admin_rules)
end
if project.archived?
@@ -193,17 +193,17 @@ class Ability
# Only group masters and group owners can create new projects in group
if group.has_master?(user) || group.has_owner?(user) || user.admin?
- rules += [
+ rules.push(*[
:create_projects,
- ]
+ ])
end
# Only group owner and administrators can manage group
if group.has_owner?(user) || user.admin?
- rules += [
+ rules.push(*[
:manage_group,
:manage_namespace
- ]
+ ])
end
rules.flatten
@@ -214,10 +214,10 @@ class Ability
# Only namespace owner and administrators can manage it
if namespace.owner == user || user.admin?
- rules += [
+ rules.push(*[
:create_projects,
:manage_namespace
- ]
+ ])
end
rules.flatten
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index aed4068f309..45ae79a75cc 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -1,3 +1,18 @@
+# == Schema Information
+#
+# Table name: application_settings
+#
+# 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)
+#
+
class ApplicationSetting < ActiveRecord::Base
validates :home_page_url, allow_blank: true,
format: { with: URI::regexp(%w(http https)), message: "should be a valid url" },
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index f49708fd6eb..fb038a3cc3f 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -88,7 +88,7 @@ module Issuable
# Return the number of -1 comments (downvotes)
def downvotes
- notes.select(&:downvote?).size
+ filter_superceded_votes(notes.select(&:downvote?), notes).size
end
def downvotes_in_percent
@@ -101,7 +101,7 @@ module Issuable
# Return the number of +1 comments (upvotes)
def upvotes
- notes.select(&:upvote?).size
+ filter_superceded_votes(notes.select(&:upvote?), notes).size
end
def upvotes_in_percent
@@ -124,10 +124,12 @@ module Issuable
users << assignee if is_assigned?
mentions = []
mentions << self.mentioned_users
+
notes.each do |note|
users << note.author
mentions << note.mentioned_users
end
+
users.concat(mentions.reduce([], :|)).uniq
end
@@ -149,9 +151,23 @@ module Issuable
def add_labels_by_names(label_names)
label_names.each do |label_name|
- label = project.labels.create_with(
- color: Label::DEFAULT_COLOR).find_or_create_by(title: label_name.strip)
+ label = project.labels.create_with(color: Label::DEFAULT_COLOR).
+ find_or_create_by(title: label_name.strip)
self.labels << label
end
end
+
+ private
+
+ def filter_superceded_votes(votes, notes)
+ filteredvotes = [] + votes
+
+ votes.each do |vote|
+ if vote.superceded?(notes)
+ filteredvotes.delete(vote)
+ end
+ end
+
+ filteredvotes
+ end
end
diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb
index 6c1aa99668a..66f83b932d4 100644
--- a/app/models/concerns/mentionable.rb
+++ b/app/models/concerns/mentionable.rb
@@ -50,7 +50,7 @@ module Mentionable
matches.each do |match|
identifier = match.delete "@"
if identifier == "all"
- users += project.team.members.flatten
+ users.push(*project.team.members.flatten)
else
id = User.find_by(username: identifier).try(:id)
users << User.find(id) unless id.blank?
diff --git a/app/models/group.rb b/app/models/group.rb
index 733afa2fc07..e098dfb3cdf 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -25,6 +25,9 @@ class Group < Namespace
mount_uploader :avatar, AttachmentUploader
+ after_create :post_create_hook
+ after_destroy :post_destroy_hook
+
def human_name
name
end
@@ -74,6 +77,18 @@ class Group < Namespace
projects.public_only.any?
end
+ def post_create_hook
+ system_hook_service.execute_hooks_for(self, :create)
+ end
+
+ def post_destroy_hook
+ system_hook_service.execute_hooks_for(self, :destroy)
+ end
+
+ def system_hook_service
+ SystemHooksService.new
+ end
+
class << self
def search(query)
where("LOWER(namespaces.name) LIKE :query", query: "%#{query.downcase}%")
diff --git a/app/models/identity.rb b/app/models/identity.rb
index 5fb1850c30b..c7cdb63e3d2 100644
--- a/app/models/identity.rb
+++ b/app/models/identity.rb
@@ -1,5 +1,15 @@
+# == Schema Information
+#
+# Table name: identities
+#
+# id :integer not null, primary key
+# extern_uid :string(255)
+# provider :string(255)
+# user_id :integer
+#
+
class Identity < ActiveRecord::Base
belongs_to :user
validates :extern_uid, allow_blank: true, uniqueness: {scope: :provider}
-end \ No newline at end of file
+end
diff --git a/app/models/key.rb b/app/models/key.rb
index 65a426d1f8b..d2d1af68822 100644
--- a/app/models/key.rb
+++ b/app/models/key.rb
@@ -19,7 +19,7 @@ class Key < ActiveRecord::Base
belongs_to :user
- before_validation :strip_white_space, :generate_fingerpint
+ before_validation :strip_white_space, :generate_fingerprint
validates :title, presence: true, length: { within: 0..255 }
validates :key, presence: true, length: { within: 0..5000 }, format: { with: /\A(ssh|ecdsa)-.*\Z/ }, uniqueness: true
@@ -76,7 +76,7 @@ class Key < ActiveRecord::Base
private
- def generate_fingerpint
+ def generate_fingerprint
self.fingerprint = nil
return unless key.present?
diff --git a/app/models/members/group_member.rb b/app/models/members/group_member.rb
index b7f296b13fb..28d0b4483b4 100644
--- a/app/models/members/group_member.rb
+++ b/app/models/members/group_member.rb
@@ -27,8 +27,9 @@ class GroupMember < Member
scope :with_group, ->(group) { where(source_id: group.id) }
scope :with_user, ->(user) { where(user_id: user.id) }
- after_create :notify_create
+ after_create :post_create_hook
after_update :notify_update
+ after_destroy :post_destroy_hook
def self.access_level_roles
Gitlab::Access.options_with_owner
@@ -42,8 +43,9 @@ class GroupMember < Member
access_level
end
- def notify_create
+ def post_create_hook
notification_service.new_group_member(self)
+ system_hook_service.execute_hooks_for(self, :create)
end
def notify_update
@@ -52,6 +54,14 @@ class GroupMember < Member
end
end
+ def post_destroy_hook
+ system_hook_service.execute_hooks_for(self, :destroy)
+ end
+
+ def system_hook_service
+ SystemHooksService.new
+ end
+
def notification_service
NotificationService.new
end
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index de0ee0e2c5a..715257f905f 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -18,6 +18,7 @@
# iid :integer
# description :text
# position :integer default(0)
+# locked_at :datetime
#
require Rails.root.join("app/models/commit")
@@ -250,7 +251,8 @@ class MergeRequest < ActiveRecord::Base
def closes_issues
if target_branch == project.default_branch
issues = commits.flat_map { |c| c.closes_issues(project) }
- issues += Gitlab::ClosingIssueExtractor.closed_by_message_in_project(description, project)
+ issues.push(*Gitlab::ClosingIssueExtractor.
+ closed_by_message_in_project(description, project))
issues.uniq.sort_by(&:id)
else
[]
@@ -330,7 +332,7 @@ class MergeRequest < ActiveRecord::Base
end
# Return array of possible target branches
- # dependes on target project of MR
+ # depends on target project of MR
def target_branches
if target_project.nil?
[]
@@ -340,7 +342,7 @@ class MergeRequest < ActiveRecord::Base
end
# Return array of possible source branches
- # dependes on source project of MR
+ # depends on source project of MR
def source_branches
if source_project.nil?
[]
diff --git a/app/models/network/graph.rb b/app/models/network/graph.rb
index 43979b5e807..f4e90125373 100644
--- a/app/models/network/graph.rb
+++ b/app/models/network/graph.rb
@@ -84,7 +84,7 @@ module Network
skip += self.class.max_count
end
else
- # Cant't find the target commit in the repo.
+ # Can't find the target commit in the repo.
offset = 0
end
end
@@ -226,7 +226,7 @@ module Network
reserved = []
for day in time_range
- reserved += @reserved[day]
+ reserved.push(*@reserved[day])
end
reserved.uniq!
diff --git a/app/models/note.rb b/app/models/note.rb
index e99bc2668d6..0b988cc3e0f 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -459,6 +459,26 @@ class Note < ActiveRecord::Base
)
end
+ def superceded?(notes)
+ return false unless vote?
+
+ notes.each do |note|
+ next if note == self
+
+ if note.vote? &&
+ self[:author_id] == note[:author_id] &&
+ self[:created_at] <= note[:created_at]
+ return true
+ end
+ end
+
+ false
+ end
+
+ def vote?
+ upvote? || downvote?
+ end
+
def votable?
for_issue? || (for_merge_request? && !for_diff_line?)
end
@@ -480,7 +500,7 @@ class Note < ActiveRecord::Base
end
# FIXME: Hack for polymorphic associations with STI
- # For more information wisit http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#label-Polymorphic+Associations
+ # For more information visit http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#label-Polymorphic+Associations
def noteable_type=(sType)
super(sType.to_s.classify.constantize.base_class.to_s)
end
diff --git a/app/models/project.rb b/app/models/project.rb
index a22f852de6b..f3dddc28adb 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -24,8 +24,14 @@
# import_status :string(255)
# repository_size :float default(0.0)
# star_count :integer default(0), not null
+# import_type :string(255)
+# import_source :string(255)
+# avatar :string(255)
#
+require 'carrierwave/orm/activerecord'
+require 'file_size_validator'
+
class Project < ActiveRecord::Base
include Gitlab::ShellAdapter
include Gitlab::VisibilityLevel
@@ -47,8 +53,8 @@ class Project < ActiveRecord::Base
attr_accessor :new_default_branch
# Relations
- belongs_to :creator, foreign_key: "creator_id", class_name: "User"
- belongs_to :group, -> { where(type: Group) }, foreign_key: "namespace_id"
+ belongs_to :creator, foreign_key: 'creator_id', class_name: 'User'
+ belongs_to :group, -> { where(type: Group) }, foreign_key: 'namespace_id'
belongs_to :namespace
has_one :last_event, -> {order 'events.created_at DESC'}, class_name: 'Event', foreign_key: 'project_id'
@@ -68,20 +74,26 @@ class Project < ActiveRecord::Base
has_one :bamboo_service, dependent: :destroy
has_one :teamcity_service, dependent: :destroy
has_one :pushover_service, dependent: :destroy
+ has_one :jira_service, dependent: :destroy
+ has_one :redmine_service, dependent: :destroy
+ has_one :custom_issue_tracker_service, dependent: :destroy
+ has_one :gitlab_issue_tracker_service, dependent: :destroy
+
has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id"
+
has_one :forked_from_project, through: :forked_project_link
# Merge Requests for target project should be removed with it
- has_many :merge_requests, dependent: :destroy, foreign_key: "target_project_id"
+ has_many :merge_requests, dependent: :destroy, foreign_key: 'target_project_id'
# Merge requests from source project should be kept when source project was removed
- has_many :fork_merge_requests, foreign_key: "source_project_id", class_name: MergeRequest
+ has_many :fork_merge_requests, foreign_key: 'source_project_id', class_name: MergeRequest
has_many :issues, -> { order 'issues.state DESC, issues.created_at DESC' }, dependent: :destroy
has_many :labels, dependent: :destroy
has_many :services, dependent: :destroy
has_many :events, dependent: :destroy
has_many :milestones, dependent: :destroy
has_many :notes, dependent: :destroy
- has_many :snippets, dependent: :destroy, class_name: "ProjectSnippet"
- has_many :hooks, dependent: :destroy, class_name: "ProjectHook"
+ has_many :snippets, dependent: :destroy, class_name: 'ProjectSnippet'
+ has_many :hooks, dependent: :destroy, class_name: 'ProjectHook'
has_many :protected_branches, dependent: :destroy
has_many :project_members, dependent: :destroy, as: :source, class_name: 'ProjectMember'
has_many :users, through: :project_members
@@ -113,28 +125,31 @@ class Project < ActiveRecord::Base
validates_uniqueness_of :name, scope: :namespace_id
validates_uniqueness_of :path, scope: :namespace_id
validates :import_url,
- format: { with: URI::regexp(%w(git http https)), message: "should be a valid url" },
+ format: { with: URI::regexp(%w(git http https)), message: 'should be a valid url' },
if: :import?
validates :star_count, numericality: { greater_than_or_equal_to: 0 }
validate :check_limit, on: :create
+ validate :avatar_type,
+ if: ->(project) { project.avatar && project.avatar_changed? }
+ validates :avatar, file_size: { maximum: 200.kilobytes.to_i }
+
+ mount_uploader :avatar, AttachmentUploader
# Scopes
- scope :without_user, ->(user) { where("projects.id NOT IN (:ids)", ids: user.authorized_projects.map(&:id) ) }
- scope :without_team, ->(team) { team.projects.present? ? where("projects.id NOT IN (:ids)", ids: team.projects.map(&:id)) : scoped }
- scope :not_in_group, ->(group) { where("projects.id NOT IN (:ids)", ids: group.project_ids ) }
- scope :in_team, ->(team) { where("projects.id IN (:ids)", ids: team.projects.map(&:id)) }
+ scope :without_user, ->(user) { where('projects.id NOT IN (:ids)', ids: user.authorized_projects.map(&:id) ) }
+ scope :without_team, ->(team) { team.projects.present? ? where('projects.id NOT IN (:ids)', ids: team.projects.map(&:id)) : scoped }
+ scope :not_in_group, ->(group) { where('projects.id NOT IN (:ids)', ids: group.project_ids ) }
+ scope :in_team, ->(team) { where('projects.id IN (:ids)', ids: team.projects.map(&:id)) }
scope :in_namespace, ->(namespace) { where(namespace_id: namespace.id) }
scope :in_group_namespace, -> { joins(:group) }
- scope :sorted_by_activity, -> { reorder("projects.last_activity_at DESC") }
- scope :sorted_by_stars, -> { reorder("projects.star_count DESC") }
+ scope :sorted_by_activity, -> { reorder('projects.last_activity_at DESC') }
+ scope :sorted_by_stars, -> { reorder('projects.star_count DESC') }
scope :personal, ->(user) { where(namespace_id: user.namespace_id) }
- scope :joined, ->(user) { where("namespace_id != ?", user.namespace_id) }
+ scope :joined, ->(user) { where('namespace_id != ?', user.namespace_id) }
scope :public_only, -> { where(visibility_level: Project::PUBLIC) }
scope :public_and_internal_only, -> { where(visibility_level: Project.public_and_internal_levels) }
scope :non_archived, -> { where(archived: false) }
- enumerize :issues_tracker, in: (Gitlab.config.issues_tracker.keys).append(:gitlab), default: :gitlab
-
state_machine :import_status, initial: :none do
event :import_start do
transition [:none, :finished] => :started
@@ -170,7 +185,7 @@ class Project < ActiveRecord::Base
def publicish(user)
visibility_levels = [Project::PUBLIC]
- visibility_levels += [Project::INTERNAL] if user
+ visibility_levels << Project::INTERNAL if user
where(visibility_level: visibility_levels)
end
@@ -179,26 +194,26 @@ class Project < ActiveRecord::Base
end
def active
- joins(:issues, :notes, :merge_requests).order("issues.created_at, notes.created_at, merge_requests.created_at DESC")
+ joins(:issues, :notes, :merge_requests).order('issues.created_at, notes.created_at, merge_requests.created_at DESC')
end
def search(query)
- joins(:namespace).where("projects.archived = ?", false).
- where("LOWER(projects.name) LIKE :query OR
+ joins(:namespace).where('projects.archived = ?', false).
+ where('LOWER(projects.name) LIKE :query OR
LOWER(projects.path) LIKE :query OR
LOWER(namespaces.name) LIKE :query OR
- LOWER(projects.description) LIKE :query",
+ LOWER(projects.description) LIKE :query',
query: "%#{query.try(:downcase)}%")
end
def search_by_title(query)
- where("projects.archived = ?", false).where("LOWER(projects.name) LIKE :query", query: "%#{query.downcase}%")
+ where('projects.archived = ?', false).where('LOWER(projects.name) LIKE :query', query: "%#{query.downcase}%")
end
def find_with_namespace(id)
- return nil unless id.include?("/")
+ return nil unless id.include?('/')
- id = id.split("/")
+ id = id.split('/')
namespace = Namespace.find_by(path: id.first)
return nil unless namespace
@@ -216,7 +231,7 @@ class Project < ActiveRecord::Base
when 'recently_updated' then reorder('projects.updated_at DESC')
when 'last_updated' then reorder('projects.updated_at ASC')
when 'largest_repository' then reorder('projects.repository_size DESC')
- else reorder("namespaces.path, projects.name ASC")
+ else reorder('namespaces.path, projects.name ASC')
end
end
end
@@ -266,19 +281,19 @@ class Project < ActiveRecord::Base
end
def to_param
- namespace.path + "/" + path
+ namespace.path + '/' + path
end
def web_url
- [gitlab_config.url, path_with_namespace].join("/")
+ [gitlab_config.url, path_with_namespace].join('/')
end
def web_url_without_protocol
- web_url.split("://")[1]
+ web_url.split('://')[1]
end
def build_commit_note(commit)
- notes.new(commit_id: commit.id, noteable_type: "Commit")
+ notes.new(commit_id: commit.id, noteable_type: 'Commit')
end
def last_activity
@@ -294,19 +309,43 @@ class Project < ActiveRecord::Base
end
def issue_exists?(issue_id)
- if used_default_issues_tracker?
+ if default_issues_tracker?
self.issues.where(iid: issue_id).first.present?
else
true
end
end
- def used_default_issues_tracker?
- self.issues_tracker == Project.issues_tracker.default_value
+ def default_issue_tracker
+ gitlab_issue_tracker_service ||= create_gitlab_issue_tracker_service
+ end
+
+ def issues_tracker
+ if external_issue_tracker
+ external_issue_tracker
+ else
+ default_issue_tracker
+ end
+ end
+
+ def default_issues_tracker?
+ if external_issue_tracker
+ false
+ else
+ true
+ end
+ end
+
+ def external_issues_trackers
+ services.select(&:issue_tracker?).reject(&:default?)
+ end
+
+ def external_issue_tracker
+ @external_issues_tracker ||= external_issues_trackers.select(&:activated?).first
end
def can_have_issues_tracker_id?
- self.issues_enabled && !self.used_default_issues_tracker?
+ self.issues_enabled && !self.default_issues_tracker?
end
def build_missing_services
@@ -321,7 +360,7 @@ class Project < ActiveRecord::Base
def available_services_names
%w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla
- emails_on_push gemnasium slack pushover buildbox bamboo teamcity)
+ emails_on_push gemnasium slack pushover buildbox bamboo teamcity jira redmine custom_issue_tracker)
end
def gitlab_ci?
@@ -336,6 +375,24 @@ class Project < ActiveRecord::Base
@ci_service ||= ci_services.select(&:activated?).first
end
+ def avatar_type
+ unless self.avatar.image?
+ self.errors.add :avatar, 'only images allowed'
+ end
+ end
+
+ def avatar_in_git
+ @avatar_file ||= 'logo.png' if repository.blob_at_branch('master', 'logo.png')
+ @avatar_file ||= 'logo.jpg' if repository.blob_at_branch('master', 'logo.jpg')
+ @avatar_file ||= 'logo.gif' if repository.blob_at_branch('master', 'logo.gif')
+ @avatar_file
+ end
+
+ # For compatibility with old code
+ def code
+ path
+ end
+
def items_for(entity)
case entity
when 'issue' then
@@ -358,7 +415,7 @@ class Project < ActiveRecord::Base
end
def team_member_by_name_or_email(name = nil, email = nil)
- user = users.where("name like ? or email like ?", name, email).first
+ user = users.where('name like ? or email like ?', name, email).first
project_members.where(user: user) if user
end
@@ -370,7 +427,7 @@ class Project < ActiveRecord::Base
def name_with_namespace
@name_with_namespace ||= begin
if namespace
- namespace.human_name + " / " + name
+ namespace.human_name + ' / ' + name
else
name
end
@@ -405,7 +462,7 @@ class Project < ActiveRecord::Base
def valid_repo?
repository.exists?
rescue
- errors.add(:path, "Invalid repository path")
+ errors.add(:path, 'Invalid repository path')
false
end
@@ -464,7 +521,7 @@ class Project < ActiveRecord::Base
end
def http_url_to_repo
- [gitlab_config.url, "/", path_with_namespace, ".git"].join('')
+ [gitlab_config.url, '/', path_with_namespace, '.git'].join('')
end
# Check if current branch name is marked as protected in the system
@@ -527,6 +584,7 @@ class Project < ActiveRecord::Base
# Since we do cache @event we need to reset cache in special cases:
# * when project was moved
# * when project was renamed
+ # * when the project avatar changes
# Events cache stored like events/23-20130109142513.
# The cache key includes updated_at timestamp.
# Thus it will automatically generate a new fragment
@@ -591,7 +649,7 @@ class Project < ActiveRecord::Base
if gitlab_shell.add_repository(path_with_namespace)
true
else
- errors.add(:base, "Failed to create repository")
+ errors.add(:base, 'Failed to create repository')
false
end
end
@@ -604,7 +662,7 @@ class Project < ActiveRecord::Base
ProjectWiki.new(self, self.owner).wiki
true
rescue ProjectWiki::CouldNotCreateWikiError => ex
- errors.add(:base, "Failed create wiki")
+ errors.add(:base, 'Failed create wiki')
false
end
end
diff --git a/app/models/project_contributions.rb b/app/models/project_contributions.rb
new file mode 100644
index 00000000000..8ab2d814a94
--- /dev/null
+++ b/app/models/project_contributions.rb
@@ -0,0 +1,23 @@
+class ProjectContributions
+ attr_reader :project, :user
+
+ def initialize(project, user)
+ @project, @user = project, user
+ end
+
+ def commits_log
+ repository = project.repository
+
+ if !repository.exists? || repository.empty?
+ return {}
+ end
+
+ Rails.cache.fetch(cache_key) do
+ repository.commits_per_day_for_user(user)
+ end
+ end
+
+ def cache_key
+ "#{Date.today.to_s}-commits-log-#{project.id}-#{user.email}"
+ end
+end
diff --git a/app/models/project_services/bamboo_service.rb b/app/models/project_services/bamboo_service.rb
index b9eec9ab21e..16e1b83da4b 100644
--- a/app/models/project_services/bamboo_service.rb
+++ b/app/models/project_services/bamboo_service.rb
@@ -1,3 +1,17 @@
+# == 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
+#
+
class BambooService < CiService
include HTTParty
diff --git a/app/models/project_services/custom_issue_tracker_service.rb b/app/models/project_services/custom_issue_tracker_service.rb
new file mode 100644
index 00000000000..b6b79589f1d
--- /dev/null
+++ b/app/models/project_services/custom_issue_tracker_service.rb
@@ -0,0 +1,52 @@
+# == 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
+#
+
+class CustomIssueTrackerService < IssueTrackerService
+
+ prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
+
+ def title
+ if self.properties && self.properties['title'].present?
+ self.properties['title']
+ else
+ 'Custom Issue Tracker'
+ end
+ end
+
+ def description
+ if self.properties && self.properties['description'].present?
+ self.properties['description']
+ else
+ 'Custom issue tracker'
+ end
+ end
+
+ def to_param
+ 'custom_issue_tracker'
+ end
+
+ def fields
+ [
+ { type: 'text', name: 'title', placeholder: title },
+ { type: 'text', name: 'description', placeholder: description },
+ { type: 'text', name: 'project_url', placeholder: 'Project url' },
+ { type: 'text', name: 'issues_url', placeholder: 'Issue url'},
+ { type: 'text', name: 'new_issue_url', placeholder: 'New Issue url'}
+ ]
+ end
+
+ def initialize_properties
+ self.properties = {} if properties.nil?
+ end
+end
diff --git a/app/models/project_services/gitlab_issue_tracker_service.rb b/app/models/project_services/gitlab_issue_tracker_service.rb
new file mode 100644
index 00000000000..25e399883b7
--- /dev/null
+++ b/app/models/project_services/gitlab_issue_tracker_service.rb
@@ -0,0 +1,39 @@
+# == 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
+#
+
+class GitlabIssueTrackerService < IssueTrackerService
+ include Rails.application.routes.url_helpers
+ prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
+
+
+ def default?
+ true
+ end
+
+ def to_param
+ 'gitlab'
+ end
+
+ def project_url
+ project_issues_path(project)
+ end
+
+ def new_issue_url
+ new_project_issue_path project_id: project
+ end
+
+ def issue_url(iid)
+ "#{Gitlab.config.gitlab.url}#{project_issue_path(project_id: project, id: iid)}"
+ end
+end
diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb
new file mode 100644
index 00000000000..acc8b33178c
--- /dev/null
+++ b/app/models/project_services/issue_tracker_service.rb
@@ -0,0 +1,88 @@
+# == 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
+#
+
+class IssueTrackerService < Service
+
+ validates :project_url, :issues_url, :new_issue_url, presence: true, if: :activated?
+
+ def category
+ :issue_tracker
+ end
+
+ def default?
+ 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
+
+ def fields
+ [
+ { type: 'text', name: 'description', placeholder: description },
+ { type: 'text', name: 'project_url', placeholder: 'Project url' },
+ { type: 'text', name: 'issues_url', placeholder: 'Issue url'},
+ { type: 'text', name: 'new_issue_url', placeholder: 'New Issue url'}
+ ]
+ end
+
+ def initialize_properties
+ if properties.nil?
+ 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']
+ }
+ else
+ self.properties = {}
+ end
+ end
+ end
+
+ private
+
+ def enabled_in_gitlab_config
+ Gitlab.config.issues_tracker &&
+ Gitlab.config.issues_tracker.values.any? &&
+ issues_tracker
+ end
+
+ def issues_tracker
+ Gitlab.config.issues_tracker[to_param]
+ end
+
+ def set_project_url
+ id = self.project.issues_tracker_id
+
+ if id
+ issues_tracker['project_url'].gsub(":issues_tracker_id", id)
+ else
+ issues_tracker['project_url']
+ end
+ end
+end
diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb
new file mode 100644
index 00000000000..7a32b0e8c2c
--- /dev/null
+++ b/app/models/project_services/jira_service.rb
@@ -0,0 +1,38 @@
+# == 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
+#
+
+class JiraService < IssueTrackerService
+
+ prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
+
+ def title
+ if self.properties && self.properties['title'].present?
+ self.properties['title']
+ else
+ 'JIRA'
+ end
+ end
+
+ def description
+ if self.properties && self.properties['description'].present?
+ self.properties['description']
+ else
+ 'Jira issue tracker'
+ end
+ end
+
+ def to_param
+ 'jira'
+ end
+end
diff --git a/app/models/project_services/redmine_service.rb b/app/models/project_services/redmine_service.rb
new file mode 100644
index 00000000000..547b2401832
--- /dev/null
+++ b/app/models/project_services/redmine_service.rb
@@ -0,0 +1,38 @@
+# == 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
+#
+
+class RedmineService < IssueTrackerService
+
+ prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
+
+ def title
+ if self.properties && self.properties['title'].present?
+ self.properties['title']
+ else
+ 'Redmine'
+ end
+ end
+
+ def description
+ if self.properties && self.properties['description'].present?
+ self.properties['description']
+ else
+ 'Redmine issue tracker'
+ end
+ end
+
+ def to_param
+ 'redmine'
+ end
+end
diff --git a/app/models/project_services/teamcity_service.rb b/app/models/project_services/teamcity_service.rb
index 52b5862e4d1..dca718b5e8c 100644
--- a/app/models/project_services/teamcity_service.rb
+++ b/app/models/project_services/teamcity_service.rb
@@ -1,3 +1,17 @@
+# == 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
+#
+
class TeamcityService < CiService
include HTTParty
diff --git a/app/models/project_team.rb b/app/models/project_team.rb
index 657ee23ae23..bc9c3ce58f6 100644
--- a/app/models/project_team.rb
+++ b/app/models/project_team.rb
@@ -160,7 +160,7 @@ class ProjectTeam
end
user_ids = project_members.pluck(:user_id)
- user_ids += group_members.pluck(:user_id) if group
+ user_ids.push(*group_members.pluck(:user_id)) if group
User.where(id: user_ids)
end
diff --git a/app/models/protected_branch.rb b/app/models/protected_branch.rb
index 1b06dd77523..97207ba1272 100644
--- a/app/models/protected_branch.rb
+++ b/app/models/protected_branch.rb
@@ -2,11 +2,12 @@
#
# Table name: protected_branches
#
-# id :integer not null, primary key
-# project_id :integer not null
-# name :string(255) not null
-# created_at :datetime
-# updated_at :datetime
+# id :integer not null, primary key
+# project_id :integer not null
+# name :string(255) not null
+# created_at :datetime
+# updated_at :datetime
+# developers_can_push :boolean default(FALSE), not null
#
class ProtectedBranch < ActiveRecord::Base
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 93994123a90..4e45a6723b8 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -30,7 +30,7 @@ class Repository
commit = Gitlab::Git::Commit.find(raw_repository, id)
commit = Commit.new(commit) if commit
commit
- rescue Rugged::OdbError => ex
+ rescue Rugged::OdbError
nil
end
@@ -61,25 +61,25 @@ class Repository
end
def add_branch(branch_name, ref)
- Rails.cache.delete(cache_key(:branch_names))
+ cache.expire(:branch_names)
gitlab_shell.add_branch(path_with_namespace, branch_name, ref)
end
def add_tag(tag_name, ref, message = nil)
- Rails.cache.delete(cache_key(:tag_names))
+ cache.expire(:tag_names)
gitlab_shell.add_tag(path_with_namespace, tag_name, ref, message)
end
def rm_branch(branch_name)
- Rails.cache.delete(cache_key(:branch_names))
+ cache.expire(:branch_names)
gitlab_shell.rm_branch(path_with_namespace, branch_name)
end
def rm_tag(tag_name)
- Rails.cache.delete(cache_key(:tag_names))
+ cache.expire(:tag_names)
gitlab_shell.rm_tag(path_with_namespace, tag_name)
end
@@ -97,19 +97,15 @@ class Repository
end
def branch_names
- Rails.cache.fetch(cache_key(:branch_names)) do
- raw_repository.branch_names
- end
+ cache.fetch(:branch_names) { raw_repository.branch_names }
end
def tag_names
- Rails.cache.fetch(cache_key(:tag_names)) do
- raw_repository.tag_names
- end
+ cache.fetch(:tag_names) { raw_repository.tag_names }
end
def commit_count
- Rails.cache.fetch(cache_key(:commit_count)) do
+ cache.fetch(:commit_count) do
begin
raw_repository.commit_count(self.root_ref)
rescue
@@ -121,26 +117,21 @@ class Repository
# Return repo size in megabytes
# Cached in redis
def size
- Rails.cache.fetch(cache_key(:size)) do
- raw_repository.size
- end
+ cache.fetch(:size) { raw_repository.size }
end
def expire_cache
- Rails.cache.delete(cache_key(:size))
- Rails.cache.delete(cache_key(:branch_names))
- Rails.cache.delete(cache_key(:tag_names))
- Rails.cache.delete(cache_key(:commit_count))
- Rails.cache.delete(cache_key(:graph_log))
- Rails.cache.delete(cache_key(:readme))
- Rails.cache.delete(cache_key(:version))
- Rails.cache.delete(cache_key(:contribution_guide))
+ %i(size branch_names tag_names commit_count graph_log
+ readme version contribution_guide).each do |key|
+ cache.expire(key)
+ end
end
def graph_log
- Rails.cache.fetch(cache_key(:graph_log)) do
+ cache.fetch(:graph_log) do
commits = raw_repository.log(limit: 6000, skip_merges: true,
ref: root_ref)
+
commits.map do |rugged_commit|
commit = Gitlab::Git::Commit.new(rugged_commit)
@@ -148,14 +139,30 @@ class Repository
author_name: commit.author_name.force_encoding('UTF-8'),
author_email: commit.author_email.force_encoding('UTF-8'),
additions: commit.stats.additions,
- deletions: commit.stats.deletions
+ deletions: commit.stats.deletions,
}
end
end
end
- def cache_key(type)
- "#{type}:#{path_with_namespace}"
+ def timestamps_by_user_log(user)
+ args = %W(git log --author=#{user.email} --since=#{(Date.today - 1.year).to_s} --pretty=format:%cd --date=short)
+ dates = Gitlab::Popen.popen(args, path_to_repo).first.split("\n")
+
+ if dates.present?
+ dates
+ else
+ []
+ end
+ end
+
+ def commits_per_day_for_user(user)
+ timestamps_by_user_log(user).
+ group_by { |commit_date| commit_date }.
+ inject({}) do |hash, (timestamp_date, commits)|
+ hash[timestamp_date] = commits.count
+ hash
+ end
end
def method_missing(m, *args, &block)
@@ -177,13 +184,11 @@ class Repository
end
def readme
- Rails.cache.fetch(cache_key(:readme)) do
- tree(:head).readme
- end
+ cache.fetch(:readme) { tree(:head).readme }
end
def version
- Rails.cache.fetch(cache_key(:version)) do
+ cache.fetch(:version) do
tree(:head).blobs.find do |file|
file.name.downcase == 'version'
end
@@ -191,9 +196,7 @@ class Repository
end
def contribution_guide
- Rails.cache.fetch(cache_key(:contribution_guide)) do
- tree(:head).contribution_guide
- end
+ cache.fetch(:contribution_guide) { tree(:head).contribution_guide }
end
def head_commit
@@ -312,4 +315,27 @@ class Repository
[]
end
end
+
+ def tag_names_contains(sha)
+ args = %W(git tag --contains #{sha})
+ names = Gitlab::Popen.popen(args, path_to_repo).first
+
+ if names.respond_to?(:split)
+ names = names.split("\n").map(&:strip)
+
+ names.each do |name|
+ name.slice! '* '
+ end
+
+ names
+ else
+ []
+ end
+ end
+
+ private
+
+ def cache
+ @cache ||= RepositoryCache.new(path_with_namespace)
+ end
end
diff --git a/app/models/service.rb b/app/models/service.rb
index 71c8aa39e45..15948e63e41 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -26,6 +26,8 @@ class Service < ActiveRecord::Base
validates :project_id, presence: true
+ scope :visible, -> { where.not(type: 'GitlabIssueTrackerService') }
+
def activated?
active
end
@@ -86,4 +88,12 @@ class Service < ActiveRecord::Base
def async_execute(data)
Sidekiq::Client.enqueue(ProjectServiceWorker, id, data)
end
+
+ def issue_tracker?
+ self.category == :issue_tracker
+ end
+
+ def self.issue_tracker_service_list
+ Service.select(&:issue_tracker?).map{ |s| s.to_param }
+ end
end
diff --git a/app/models/user.rb b/app/models/user.rb
index 743410c22e3..69fe674df83 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -26,8 +26,6 @@
# bio :string(255)
# failed_attempts :integer default(0)
# locked_at :datetime
-# extern_uid :string(255)
-# provider :string(255)
# username :string(255)
# can_create_group :boolean default(TRUE), not null
# can_create_team :boolean default(TRUE), not null
@@ -36,7 +34,6 @@
# 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
@@ -44,6 +41,8 @@
# 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)
#
require 'carrierwave/orm/activerecord'
@@ -298,8 +297,8 @@ class User < ActiveRecord::Base
def authorized_projects
@authorized_projects ||= begin
project_ids = personal_projects.pluck(:id)
- project_ids += groups_projects.pluck(:id)
- project_ids += projects.pluck(:id).uniq
+ project_ids.push(*groups_projects.pluck(:id))
+ project_ids.push(*projects.pluck(:id).uniq)
Project.where(id: project_ids).joins(:namespace).order('namespaces.name ASC')
end
end
@@ -497,7 +496,7 @@ class User < ActiveRecord::Base
def avatar_url(size = nil)
if avatar.present?
- [gitlab_config.url, avatar.url].join("/")
+ [gitlab_config.url, avatar.url].join
else
GravatarService.new.execute(email, size)
end
diff --git a/app/services/files/create_service.rb b/app/services/files/create_service.rb
index b90adeef00a..2c457ef2cef 100644
--- a/app/services/files/create_service.rb
+++ b/app/services/files/create_service.rb
@@ -9,10 +9,6 @@ module Files
return error("You are not allowed to create file in this branch")
end
- unless repository.branch_names.include?(ref)
- return error("You can only create files if you are on top of a branch")
- end
-
file_name = File.basename(path)
file_path = path
@@ -23,12 +19,21 @@ module Files
)
end
- blob = repository.blob_at_branch(ref, file_path)
+ if project.empty_repo?
+ # everything is ok because repo does not have a commits yet
+ else
+ unless repository.branch_names.include?(ref)
+ return error("You can only create files if you are on top of a branch")
+ end
- if blob
- return error("Your changes could not be committed, because file with such name exists")
+ blob = repository.blob_at_branch(ref, file_path)
+
+ if blob
+ return error("Your changes could not be committed, because file with such name exists")
+ end
end
+
new_file_action = Gitlab::Satellite::NewFileAction.new(current_user, project, ref, file_path)
created_successfully = new_file_action.commit!(
params[:content],
diff --git a/app/services/issues/close_service.rb b/app/services/issues/close_service.rb
index ffed13a12e1..f670019cc63 100644
--- a/app/services/issues/close_service.rb
+++ b/app/services/issues/close_service.rb
@@ -2,9 +2,9 @@ module Issues
class CloseService < Issues::BaseService
def execute(issue, commit = nil)
if issue.close
- notification_service.close_issue(issue, current_user)
event_service.close_issue(issue, current_user)
create_note(issue, commit)
+ notification_service.close_issue(issue, current_user)
execute_hooks(issue, 'close')
end
diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb
index 0ee9635ed99..83e413d7248 100644
--- a/app/services/issues/update_service.rb
+++ b/app/services/issues/update_service.rb
@@ -23,8 +23,8 @@ module Issues
end
if issue.previous_changes.include?('assignee_id')
- notification_service.reassigned_issue(issue, current_user)
create_assignee_note(issue)
+ notification_service.reassigned_issue(issue, current_user)
end
issue.notice_added_references(issue.project, current_user)
diff --git a/app/services/merge_requests/auto_merge_service.rb b/app/services/merge_requests/auto_merge_service.rb
index b5d90a74e15..378b39bb9d6 100644
--- a/app/services/merge_requests/auto_merge_service.rb
+++ b/app/services/merge_requests/auto_merge_service.rb
@@ -11,9 +11,9 @@ module MergeRequests
if Gitlab::Satellite::MergeAction.new(current_user, merge_request).merge!(commit_message)
merge_request.merge
- notification_service.merge_mr(merge_request, current_user)
create_merge_event(merge_request, current_user)
create_note(merge_request)
+ notification_service.merge_mr(merge_request, current_user)
execute_hooks(merge_request)
true
diff --git a/app/services/merge_requests/close_service.rb b/app/services/merge_requests/close_service.rb
index 4249a84f382..47454f9f0c2 100644
--- a/app/services/merge_requests/close_service.rb
+++ b/app/services/merge_requests/close_service.rb
@@ -7,8 +7,8 @@ module MergeRequests
if merge_request.close
event_service.close_mr(merge_request, current_user)
- notification_service.close_mr(merge_request, current_user)
create_note(merge_request)
+ notification_service.close_mr(merge_request, current_user)
execute_hooks(merge_request, 'close')
end
diff --git a/app/services/merge_requests/merge_service.rb b/app/services/merge_requests/merge_service.rb
index 1e1614028f7..327ead4ff3f 100644
--- a/app/services/merge_requests/merge_service.rb
+++ b/app/services/merge_requests/merge_service.rb
@@ -9,9 +9,9 @@ module MergeRequests
def execute(merge_request, commit_message)
merge_request.merge
- notification_service.merge_mr(merge_request, current_user)
create_merge_event(merge_request, current_user)
create_note(merge_request)
+ notification_service.merge_mr(merge_request, current_user)
execute_hooks(merge_request, 'merge')
true
diff --git a/app/services/merge_requests/reopen_service.rb b/app/services/merge_requests/reopen_service.rb
index a2a9c933f63..8279ad2001b 100644
--- a/app/services/merge_requests/reopen_service.rb
+++ b/app/services/merge_requests/reopen_service.rb
@@ -3,8 +3,8 @@ module MergeRequests
def execute(merge_request)
if merge_request.reopen
event_service.reopen_mr(merge_request, current_user)
- notification_service.reopen_mr(merge_request, current_user)
create_note(merge_request)
+ notification_service.reopen_mr(merge_request, current_user)
execute_hooks(merge_request, 'reopen')
merge_request.reload_code
merge_request.mark_as_unchecked
diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb
index 56c8510e0ae..10c401756eb 100644
--- a/app/services/merge_requests/update_service.rb
+++ b/app/services/merge_requests/update_service.rb
@@ -33,8 +33,8 @@ module MergeRequests
end
if merge_request.previous_changes.include?('assignee_id')
- notification_service.reassigned_merge_request(merge_request, current_user)
create_assignee_note(merge_request)
+ notification_service.reassigned_merge_request(merge_request, current_user)
end
merge_request.notice_added_references(merge_request.project, current_user)
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index 5a89c5d2936..2fc63b9f4b7 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -242,7 +242,7 @@ class NotificationService
users
end
- # Build a list of users based on group notifcation settings
+ # Build a list of users based on group notification settings
def select_users_group_setting(project, project_members, global_setting, users_global_level_watch)
uids = users_group_notification(project, Notification::N_WATCH)
@@ -314,15 +314,7 @@ class NotificationService
end
def new_resource_email(target, project, method)
- if target.respond_to?(:participants)
- recipients = target.participants
- else
- recipients = []
- end
-
- recipients = reject_muted_users(recipients, project)
- recipients = reject_mention_users(recipients, project)
- recipients = recipients.concat(project_watchers(project)).uniq
+ recipients = build_recipients(target, project)
recipients.delete(target.author)
recipients.each do |recipient|
@@ -331,9 +323,7 @@ class NotificationService
end
def close_resource_email(target, project, current_user, method)
- recipients = reject_muted_users([target.author, target.assignee], project)
- recipients = reject_mention_users(recipients, project)
- recipients = recipients.concat(project_watchers(project)).uniq
+ recipients = build_recipients(target, project)
recipients.delete(current_user)
recipients.each do |recipient|
@@ -343,17 +333,7 @@ class NotificationService
def reassign_resource_email(target, project, current_user, method)
assignee_id_was = previous_record(target, "assignee_id")
-
- recipients = User.where(id: [target.assignee_id, assignee_id_was])
-
- # Add watchers to email list
- recipients = recipients.concat(project_watchers(project))
-
- # reject users with disabled notifications
- recipients = reject_muted_users(recipients, project)
- recipients = reject_mention_users(recipients, project)
-
- # Reject me from recipients if I reassign an item
+ recipients = build_recipients(target, project)
recipients.delete(current_user)
recipients.each do |recipient|
@@ -362,9 +342,7 @@ class NotificationService
end
def reopen_resource_email(target, project, current_user, method, status)
- recipients = reject_muted_users([target.author, target.assignee], project)
- recipients = reject_mention_users(recipients, project)
- recipients = recipients.concat(project_watchers(project)).uniq
+ recipients = build_recipients(target, project)
recipients.delete(current_user)
recipients.each do |recipient|
@@ -372,6 +350,20 @@ class NotificationService
end
end
+ def build_recipients(target, project)
+ recipients =
+ if target.respond_to?(:participants)
+ target.participants
+ else
+ [target.author, target.assignee]
+ end
+
+ recipients = reject_muted_users(recipients, project)
+ recipients = reject_mention_users(recipients, project)
+ recipients = recipients.concat(project_watchers(project)).uniq
+ recipients
+ end
+
def mailer
Notify.delay
end
diff --git a/app/services/oauth2/access_token_validation_service.rb b/app/services/oauth2/access_token_validation_service.rb
index 95283489753..5a3b94129f1 100644
--- a/app/services/oauth2/access_token_validation_service.rb
+++ b/app/services/oauth2/access_token_validation_service.rb
@@ -13,7 +13,7 @@ module Oauth2::AccessTokenValidationService
elsif token.revoked?
return REVOKED
- elsif !self.sufficent_scope?(token, scopes)
+ elsif !self.sufficient_scope?(token, scopes)
return INSUFFICIENT_SCOPE
else
@@ -24,7 +24,7 @@ module Oauth2::AccessTokenValidationService
protected
# True if the token's scope is a superset of required scopes,
# or the required scopes is empty.
- def sufficent_scope?(token, scopes)
+ def sufficient_scope?(token, scopes)
if scopes.blank?
# if no any scopes required, the scopes of token is sufficient.
return true
diff --git a/app/services/projects/autocomplete_service.rb b/app/services/projects/autocomplete_service.rb
new file mode 100644
index 00000000000..7408e09ed1e
--- /dev/null
+++ b/app/services/projects/autocomplete_service.rb
@@ -0,0 +1,15 @@
+module Projects
+ class AutocompleteService < BaseService
+ def initialize(project)
+ @project = project
+ end
+
+ def issues
+ @project.issues.opened.select([:iid, :title])
+ end
+
+ def merge_requests
+ @project.merge_requests.opened.select([:iid, :title])
+ end
+ end
+end
diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb
index 31226b7504b..139de70114b 100644
--- a/app/services/projects/create_service.rb
+++ b/app/services/projects/create_service.rb
@@ -7,7 +7,7 @@ module Projects
def execute
@project = Project.new(params)
- # Reset visibility levet if is not allowed to set it
+ # Reset visibility level if is not allowed to set it
unless Gitlab::VisibilityLevel.allowed_for?(current_user, params[:visibility_level])
@project.visibility_level = default_features.visibility_level
end
diff --git a/app/services/projects/fork_service.rb b/app/services/projects/fork_service.rb
index 4930660055a..6b0d4aca3e1 100644
--- a/app/services/projects/fork_service.rb
+++ b/app/services/projects/fork_service.rb
@@ -14,6 +14,9 @@ module Projects
project.name = @from_project.name
project.path = @from_project.path
project.creator = @current_user
+ if @from_project.avatar.present? && @from_project.avatar.image?
+ project.avatar = @from_project.avatar
+ end
if namespace = @params[:namespace]
project.namespace = namespace
@@ -39,16 +42,16 @@ module Projects
end
#Now fork the repo
unless gitlab_shell.fork_repository(@from_project.path_with_namespace, project.namespace.path)
- raise "forking failed in gitlab-shell"
+ raise 'forking failed in gitlab-shell'
end
project.ensure_satellite_exists
end
rescue => ex
- project.errors.add(:base, "Fork transaction failed.")
+ project.errors.add(:base, 'Fork transaction failed.')
project.destroy
end
else
- project.errors.add(:base, "Invalid fork destination")
+ project.errors.add(:base, 'Invalid fork destination')
end
project
diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb
index 44e494525b3..46f6e91e808 100644
--- a/app/services/system_hooks_service.rb
+++ b/app/services/system_hooks_service.rb
@@ -60,6 +60,26 @@ class SystemHooksService
access_level: model.human_access,
project_visibility: Project.visibility_levels.key(model.project.visibility_level_field).downcase
})
+ when Group
+ owner = model.owner
+
+ data.merge!(
+ name: model.name,
+ path: model.path,
+ group_id: model.id,
+ owner_name: owner.respond_to?(:name) ? owner.name : nil,
+ owner_email: owner.respond_to?(:email) ? owner.email : nil,
+ )
+ when GroupMember
+ data.merge!(
+ group_name: model.group.name,
+ group_path: model.group.path,
+ group_id: model.group.id,
+ user_name: model.user.name,
+ user_email: model.user.email,
+ user_id: model.user.id,
+ group_access: model.human_access,
+ )
end
end
@@ -68,6 +88,9 @@ class SystemHooksService
when ProjectMember
return "user_add_to_team" if event == :create
return "user_remove_from_team" if event == :destroy
+ when GroupMember
+ return 'user_add_to_group' if event == :create
+ return 'user_remove_from_group' if event == :destroy
else
"#{model.class.name.downcase}_#{event.to_s}"
end
diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml
index dd95af426c4..32e0e4a6848 100644
--- a/app/views/admin/dashboard/index.html.haml
+++ b/app/views/admin/dashboard/index.html.haml
@@ -32,7 +32,7 @@
%span.light.pull-right
= Milestone.count
%p
- Active users last 30 days
+ Users who signed in during last 30 days
%span.light.pull-right
= User.where("current_sign_in_at > ?", 30.days.ago).count
.col-md-4
diff --git a/app/views/dashboard/_project.html.haml b/app/views/dashboard/_project.html.haml
index 89ed5102754..76b95264fd8 100644
--- a/app/views/dashboard/_project.html.haml
+++ b/app/views/dashboard/_project.html.haml
@@ -1,6 +1,8 @@
= link_to project_path(project), class: dom_class(project) do
.dash-project-access-icon
= visibility_level_icon(project.visibility_level)
+ .dash-project-avatar
+ = project_icon(project.to_param, alt: '', class: 'avatar s24')
%span.str-truncated
%span.namespace-name
- if project.namespace
diff --git a/app/views/dashboard/_projects.html.haml b/app/views/dashboard/_projects.html.haml
index 304aa17eba8..0596738342f 100644
--- a/app/views/dashboard/_projects.html.haml
+++ b/app/views/dashboard/_projects.html.haml
@@ -3,8 +3,8 @@
.input-group
= search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'dash-filter form-control'
- if current_user.can_create_project?
- .input-group-addon
- = link_to new_project_path, class: "" do
+ .input-group-addon.dash-new-project
+ = link_to new_project_path do
%strong New project
%ul.well-list.dash-list
diff --git a/app/views/dashboard/projects.html.haml b/app/views/dashboard/projects.html.haml
index 944441669e7..f60bcc72e1d 100644
--- a/app/views/dashboard/projects.html.haml
+++ b/app/views/dashboard/projects.html.haml
@@ -11,6 +11,8 @@
- @projects.each do |project|
%li.my-project-row
%h4.project-title
+ .project-avatar
+ = project_icon(project.to_param, alt: '', class: 'avatar s60')
.project-access-icon
= visibility_level_icon(project.visibility_level)
= link_to project_path(project), class: dom_class(project) do
diff --git a/app/views/events/_event.html.haml b/app/views/events/_event.html.haml
index 61383315373..c7976ba564f 100644
--- a/app/views/events/_event.html.haml
+++ b/app/views/events/_event.html.haml
@@ -11,5 +11,4 @@
- elsif event.note?
= render "events/event/note", event: event
- else
- = render "events/event/common", event: event
-
+ = render "events/event/common", event: event \ No newline at end of file
diff --git a/app/views/groups/_new_group_member.html.haml b/app/views/groups/_new_group_member.html.haml
index e590ddbf931..ed00153de7e 100644
--- a/app/views/groups/_new_group_member.html.haml
+++ b/app/views/groups/_new_group_member.html.haml
@@ -5,7 +5,11 @@
.form-group
= f.label :access_level, "Group Access", class: 'control-label'
- .col-sm-10= select_tag :access_level, options_for_select(GroupMember.access_level_roles, @users_group.access_level), class: "project-access-select select2"
+ .col-sm-10
+ = select_tag :access_level, options_for_select(GroupMember.access_level_roles, @users_group.access_level), class: "project-access-select select2"
+ .help-block
+ Read more about role permissions
+ %strong= link_to "here", help_page_path("permissions", "permissions"), class: "vlink"
.form-actions
= f.submit 'Add users into group', class: "btn btn-create"
diff --git a/app/views/groups/_projects.html.haml b/app/views/groups/_projects.html.haml
index 2c65b3049e3..34221595fd7 100644
--- a/app/views/groups/_projects.html.haml
+++ b/app/views/groups/_projects.html.haml
@@ -14,6 +14,8 @@
= link_to project_path(project), class: dom_class(project) do
.dash-project-access-icon
= visibility_level_icon(project.visibility_level)
+ .dash-project-avatar
+ = project_icon(project.to_param, alt: '', class: 'avatar s24')
%span.str-truncated
%span.project-name
= project.name
diff --git a/app/views/groups/_settings_nav.html.haml b/app/views/groups/_settings_nav.html.haml
index 35180792a0d..e6aee22e529 100644
--- a/app/views/groups/_settings_nav.html.haml
+++ b/app/views/groups/_settings_nav.html.haml
@@ -1,11 +1,11 @@
%ul.sidebar-subnav
= nav_link(path: 'groups#edit') do
- = link_to edit_group_path(@group) do
+ = link_to edit_group_path(@group), title: 'Group' do
%i.fa.fa-pencil-square-o
%span
Group
= nav_link(path: 'groups#projects') do
- = link_to projects_group_path(@group) do
+ = link_to projects_group_path(@group), title: 'Projects' do
%i.fa.fa-folder
%span
Projects
diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml
index 81f0e1dd2d8..484bebca2d8 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 s90"
+ = image_tag group_icon(@group.path), class: "avatar avatar-tile s90"
.clearfix
%h2
= @group.name
diff --git a/app/views/layouts/_head_panel.html.haml b/app/views/layouts/_head_panel.html.haml
index bdf27562c26..77bfe4f996e 100644
--- a/app/views/layouts/_head_panel.html.haml
+++ b/app/views/layouts/_head_panel.html.haml
@@ -43,6 +43,6 @@
%i.fa.fa-sign-out
%li.hidden-xs
= link_to current_user, class: "profile-pic", id: 'profile-pic' do
- = image_tag avatar_icon(current_user.email, 26), alt: 'User activity'
+ = image_tag avatar_icon(current_user.email, 60), alt: 'User activity'
= render 'shared/outdated_browser'
diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml
index 621365fa6aa..1263f44eca9 100644
--- a/app/views/layouts/_page.html.haml
+++ b/app/views/layouts/_page.html.haml
@@ -1,5 +1,6 @@
- if defined?(sidebar)
.page-with-sidebar
+ = render "layouts/broadcast"
.sidebar-wrapper
= render(sidebar)
.content-wrapper
diff --git a/app/views/layouts/admin.html.haml b/app/views/layouts/admin.html.haml
index fb62d5fea0a..dc8652cb145 100644
--- a/app/views/layouts/admin.html.haml
+++ b/app/views/layouts/admin.html.haml
@@ -2,6 +2,5 @@
%html{ lang: "en"}
= render "layouts/head", title: "Admin area"
%body{class: "#{app_theme} #{theme_type} admin", :'data-page' => body_data_page}
- = render "layouts/broadcast"
= render "layouts/head_panel", title: "Admin area"
= render 'layouts/page', sidebar: 'layouts/nav/admin'
diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml
index d40c9753b10..e5420a13605 100644
--- a/app/views/layouts/application.html.haml
+++ b/app/views/layouts/application.html.haml
@@ -2,6 +2,5 @@
%html{ lang: "en"}
= render "layouts/head", title: "Dashboard"
%body{class: "#{app_theme} #{theme_type} application", :'data-page' => body_data_page }
- = render "layouts/broadcast"
= render "layouts/head_panel", title: "Dashboard"
= render 'layouts/page', sidebar: 'layouts/nav/dashboard'
diff --git a/app/views/layouts/group.html.haml b/app/views/layouts/group.html.haml
index 72b0d03908d..98edcf3a140 100644
--- a/app/views/layouts/group.html.haml
+++ b/app/views/layouts/group.html.haml
@@ -2,6 +2,5 @@
%html{ lang: "en"}
= render "layouts/head", title: group_head_title
%body{class: "#{app_theme} #{theme_type} application", :'data-page' => body_data_page}
- = render "layouts/broadcast"
= render "layouts/head_panel", title: @group.name
= render 'layouts/page', sidebar: 'layouts/nav/group'
diff --git a/app/views/layouts/nav/_admin.html.haml b/app/views/layouts/nav/_admin.html.haml
index d9c6670d1bc..4813a4f16f5 100644
--- a/app/views/layouts/nav/_admin.html.haml
+++ b/app/views/layouts/nav/_admin.html.haml
@@ -5,49 +5,50 @@
%span
Overview
= nav_link(controller: :projects) do
- = link_to admin_projects_path do
+ = link_to admin_projects_path, title: 'Projects' do
%i.fa.fa-cube
%span
Projects
= nav_link(controller: :users) do
- = link_to admin_users_path do
+ = link_to admin_users_path, title: 'Users' do
%i.fa.fa-user
%span
Users
= nav_link(controller: :groups) do
- = link_to admin_groups_path do
+ = link_to admin_groups_path, title: 'Groups' do
%i.fa.fa-group
%span
Groups
= nav_link(controller: :logs) do
- = link_to admin_logs_path do
+ = link_to admin_logs_path, title: 'Logs' do
%i.fa.fa-file-text
%span
Logs
= nav_link(controller: :broadcast_messages) do
- = link_to admin_broadcast_messages_path do
+ = link_to admin_broadcast_messages_path, title: 'Broadcast Messages' do
%i.fa.fa-bullhorn
%span
Messages
= nav_link(controller: :hooks) do
- = link_to admin_hooks_path do
+ = link_to admin_hooks_path, title: 'Hooks' do
%i.fa.fa-external-link
%span
Hooks
= nav_link(controller: :background_jobs) do
- = link_to admin_background_jobs_path do
+ = link_to admin_background_jobs_path, title: 'Background Jobs' do
%i.fa.fa-cog
%span
Background Jobs
- = nav_link(controller: :application_settings) do
- = link_to admin_application_settings_path do
- %i.fa.fa-cogs
- %span
- Settings
-
= nav_link(controller: :applications) do
- = link_to admin_applications_path do
+ = link_to admin_applications_path, title: 'Applications' do
%i.fa.fa-cloud
%span
Applications
+
+ = nav_link(controller: :application_settings, html_options: { class: 'separate-item'}) do
+ = link_to admin_application_settings_path, title: 'Settings' do
+ %i.fa.fa-cogs
+ %span
+ Settings
+
diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml
index a2eaa2d83c5..48c7c999427 100644
--- a/app/views/layouts/nav/_dashboard.html.haml
+++ b/app/views/layouts/nav/_dashboard.html.haml
@@ -5,24 +5,24 @@
%span
Activity
= nav_link(path: 'dashboard#projects') do
- = link_to projects_dashboard_path, class: 'shortcuts-projects' do
+ = link_to projects_dashboard_path, title: 'Projects', class: 'shortcuts-projects' do
%i.fa.fa-cube
%span
Projects
= nav_link(path: 'dashboard#issues') do
- = link_to assigned_issues_dashboard_path, class: 'shortcuts-issues' do
+ = link_to assigned_issues_dashboard_path, title: 'Issues', class: 'shortcuts-issues' do
%i.fa.fa-exclamation-circle
%span
Issues
%span.count= current_user.assigned_issues.opened.count
= nav_link(path: 'dashboard#merge_requests') do
- = link_to assigned_mrs_dashboard_path, class: 'shortcuts-merge_requests' do
+ = link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'shortcuts-merge_requests' do
%i.fa.fa-tasks
%span
Merge Requests
%span.count= current_user.assigned_merge_requests.opened.count
= nav_link(controller: :help) do
- = link_to help_path do
+ = link_to help_path, title: 'Help' do
%i.fa.fa-question-circle
%span
Help
diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml
index 54468d077ab..ddd3df19eec 100644
--- a/app/views/layouts/nav/_group.html.haml
+++ b/app/views/layouts/nav/_group.html.haml
@@ -6,33 +6,33 @@
Activity
- if current_user
= nav_link(controller: [:group, :milestones]) do
- = link_to group_milestones_path(@group) do
+ = link_to group_milestones_path(@group), title: 'Milestones' do
%i.fa.fa-clock-o
%span
Milestones
= nav_link(path: 'groups#issues') do
- = link_to issues_group_path(@group) do
+ = link_to issues_group_path(@group), title: 'Issues' do
%i.fa.fa-exclamation-circle
%span
Issues
- if current_user
%span.count= Issue.opened.of_group(@group).count
= nav_link(path: 'groups#merge_requests') do
- = link_to merge_requests_group_path(@group) do
+ = link_to merge_requests_group_path(@group), title: 'Merge Requests' do
%i.fa.fa-tasks
%span
Merge Requests
- if current_user
%span.count= MergeRequest.opened.of_group(@group).count
= nav_link(path: 'groups#members') do
- = link_to members_group_path(@group) do
+ = link_to members_group_path(@group), title: 'Members' do
%i.fa.fa-users
%span
Members
- if can?(current_user, :manage_group, @group)
= nav_link(html_options: { class: "#{"active" if group_settings_page?} separate-item" }) do
- = link_to edit_group_path(@group), class: "tab no-highlight" do
+ = link_to edit_group_path(@group), title: 'Settings', class: "tab no-highlight" do
%i.fa.fa-cogs
%span
Settings
diff --git a/app/views/layouts/nav/_profile.html.haml b/app/views/layouts/nav/_profile.html.haml
index cc50b9b570a..0914d2a167a 100644
--- a/app/views/layouts/nav/_profile.html.haml
+++ b/app/views/layouts/nav/_profile.html.haml
@@ -5,52 +5,51 @@
%span
Profile
= nav_link(controller: :accounts) do
- = link_to profile_account_path do
+ = link_to profile_account_path, title: 'Account' do
%i.fa.fa-gear
%span
Account
= nav_link(path: ['profiles#applications', 'applications#edit', 'applications#show', 'applications#new']) do
- = link_to applications_profile_path do
+ = link_to applications_profile_path, title: 'Applications' do
%i.fa.fa-cloud
%span
Applications
= nav_link(controller: :emails) do
- = link_to profile_emails_path do
+ = link_to profile_emails_path, title: 'Emails' do
%i.fa.fa-envelope-o
%span
Emails
%span.count= current_user.emails.count + 1
- unless current_user.ldap_user?
= nav_link(controller: :passwords) do
- = link_to edit_profile_password_path do
+ = link_to edit_profile_password_path, title: 'Password' do
%i.fa.fa-lock
%span
Password
= nav_link(controller: :notifications) do
- = link_to profile_notifications_path do
+ = link_to profile_notifications_path, title: 'Notifications' do
%i.fa.fa-inbox
%span
Notifications
= nav_link(controller: :keys) do
- = link_to profile_keys_path do
+ = link_to profile_keys_path, title: 'SSH Keys' do
%i.fa.fa-key
%span
SSH Keys
%span.count= current_user.keys.count
= nav_link(path: 'profiles#design') do
- = link_to design_profile_path do
+ = link_to design_profile_path, title: 'Design' do
%i.fa.fa-image
%span
Design
= nav_link(controller: :groups) do
- = link_to profile_groups_path 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 do
+ = link_to history_profile_path, title: 'History' do
%i.fa.fa-history
%span
History
-
diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml
index 94cee0bd50f..6c2d5966cbe 100644
--- a/app/views/layouts/nav/_project.html.haml
+++ b/app/views/layouts/nav/_project.html.haml
@@ -6,45 +6,44 @@
Project
- if project_nav_tab? :files
= nav_link(controller: %w(tree blob blame edit_tree new_tree)) do
- = link_to project_tree_path(@project, @ref || @repository.root_ref), class: 'shortcuts-tree' do
+ = link_to project_tree_path(@project, @ref || @repository.root_ref), title: 'Files', class: 'shortcuts-tree' do
%i.fa.fa-files-o
%span
Files
-
- if project_nav_tab? :commits
= nav_link(controller: %w(commit commits compare repositories tags branches)) do
- = link_to project_commits_path(@project, @ref || @repository.root_ref), class: 'shortcuts-commits' do
+ = link_to project_commits_path(@project, @ref || @repository.root_ref), title: 'Commits', class: 'shortcuts-commits' do
%i.fa.fa-history
%span
Commits
- if project_nav_tab? :network
= nav_link(controller: %w(network)) do
- = link_to project_network_path(@project, @ref || @repository.root_ref), class: 'shortcuts-network' do
+ = link_to project_network_path(@project, @ref || @repository.root_ref), title: 'Network', class: 'shortcuts-network' do
%i.fa.fa-code-fork
%span
Network
- if project_nav_tab? :graphs
= nav_link(controller: %w(graphs)) do
- = link_to project_graph_path(@project, @ref || @repository.root_ref), class: 'shortcuts-graphs' do
+ = link_to project_graph_path(@project, @ref || @repository.root_ref), title: 'Graphs', class: 'shortcuts-graphs' do
%i.fa.fa-area-chart
%span
Graphs
- if project_nav_tab? :issues
= nav_link(controller: %w(issues milestones labels)) do
- = link_to url_for_project_issues, class: 'shortcuts-issues' do
+ = link_to url_for_project_issues, title: 'Issues', class: 'shortcuts-issues' do
%i.fa.fa-exclamation-circle
%span
Issues
- - if @project.used_default_issues_tracker?
+ - if @project.default_issues_tracker?
%span.count.issue_counter= @project.issues.opened.count
- if project_nav_tab? :merge_requests
= nav_link(controller: :merge_requests) do
- = link_to project_merge_requests_path(@project), class: 'shortcuts-merge_requests' do
+ = link_to project_merge_requests_path(@project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do
%i.fa.fa-tasks
%span
Merge Requests
@@ -52,21 +51,21 @@
- if project_nav_tab? :wiki
= nav_link(controller: :wikis) do
- = link_to project_wiki_path(@project, :home), class: 'shortcuts-wiki' do
+ = link_to project_wiki_path(@project, :home), title: 'Wiki', class: 'shortcuts-wiki' do
%i.fa.fa-book
%span
Wiki
- if project_nav_tab? :snippets
= nav_link(controller: :snippets) do
- = link_to project_snippets_path(@project), class: 'shortcuts-snippets' do
+ = link_to project_snippets_path(@project), title: 'Snippets', class: 'shortcuts-snippets' do
%i.fa.fa-file-text-o
%span
Snippets
- if project_nav_tab? :settings
= nav_link(html_options: {class: "#{project_tab_class} separate-item"}) do
- = link_to edit_project_path(@project), class: "stat-tab tab no-highlight" do
+ = link_to edit_project_path(@project), title: 'Settings', class: "stat-tab tab no-highlight" do
%i.fa.fa-cogs
%span
Settings
diff --git a/app/views/layouts/profile.html.haml b/app/views/layouts/profile.html.haml
index 941084cc4ad..89d816061e2 100644
--- a/app/views/layouts/profile.html.haml
+++ b/app/views/layouts/profile.html.haml
@@ -2,6 +2,5 @@
%html{ lang: "en"}
= render "layouts/head", title: "Profile"
%body{class: "#{app_theme} #{theme_type} profile", :'data-page' => body_data_page}
- = render "layouts/broadcast"
= render "layouts/head_panel", title: "Profile"
= render 'layouts/page', sidebar: 'layouts/nav/profile'
diff --git a/app/views/layouts/project_settings.html.haml b/app/views/layouts/project_settings.html.haml
index 0f20bf38bfd..d2c9c2a991c 100644
--- a/app/views/layouts/project_settings.html.haml
+++ b/app/views/layouts/project_settings.html.haml
@@ -2,7 +2,6 @@
%html{ lang: "en"}
= render "layouts/head", title: @project.name_with_namespace
%body{class: "#{app_theme} #{theme_type} project", :'data-page' => body_data_page, :'data-project-id' => @project.id }
- = render "layouts/broadcast"
= render "layouts/head_panel", title: project_title(@project)
= render "layouts/init_auto_complete"
- @project_settings_nav = true
diff --git a/app/views/layouts/projects.html.haml b/app/views/layouts/projects.html.haml
index d4ee53db55c..c44a40c9c12 100644
--- a/app/views/layouts/projects.html.haml
+++ b/app/views/layouts/projects.html.haml
@@ -2,7 +2,6 @@
%html{ lang: "en"}
= render "layouts/head", title: project_head_title
%body{class: "#{app_theme} #{theme_type} project", :'data-page' => body_data_page, :'data-project-id' => @project.id }
- = render "layouts/broadcast"
= render "layouts/head_panel", title: project_title(@project)
= render "layouts/init_auto_complete"
= render 'layouts/page', sidebar: 'layouts/nav/project'
diff --git a/app/views/layouts/public_group.html.haml b/app/views/layouts/public_group.html.haml
index 64794104ac5..ae3d2bd8a89 100644
--- a/app/views/layouts/public_group.html.haml
+++ b/app/views/layouts/public_group.html.haml
@@ -2,6 +2,5 @@
%html{ lang: "en"}
= render "layouts/head", title: group_head_title
%body{class: "#{app_theme} #{theme_type} application", :'data-page' => body_data_page}
- = render "layouts/broadcast"
= render "layouts/public_head_panel", title: "group: #{@group.name}"
= render 'layouts/page', sidebar: 'layouts/nav/group'
diff --git a/app/views/layouts/public_projects.html.haml b/app/views/layouts/public_projects.html.haml
index 5964a29d522..027e9a53139 100644
--- a/app/views/layouts/public_projects.html.haml
+++ b/app/views/layouts/public_projects.html.haml
@@ -2,6 +2,5 @@
%html{ lang: "en"}
= render "layouts/head", title: @project.name_with_namespace
%body{class: "#{app_theme} #{theme_type} application", :'data-page' => body_data_page}
- = render "layouts/broadcast"
= render "layouts/public_head_panel", title: project_title(@project)
= render 'layouts/page', sidebar: 'layouts/nav/project'
diff --git a/app/views/layouts/public_users.html.haml b/app/views/layouts/public_users.html.haml
index 0510ce34a7f..37767df33d2 100644
--- a/app/views/layouts/public_users.html.haml
+++ b/app/views/layouts/public_users.html.haml
@@ -2,6 +2,5 @@
%html{ lang: "en"}
= render "layouts/head", title: @title
%body{class: "#{app_theme} #{theme_type} application", :'data-page' => body_data_page}
- = render "layouts/broadcast"
= render "layouts/public_head_panel", title: @title
= render 'layouts/page'
diff --git a/app/views/profiles/notifications/show.html.haml b/app/views/profiles/notifications/show.html.haml
index 96fe91b9b20..bc6f76a2661 100644
--- a/app/views/profiles/notifications/show.html.haml
+++ b/app/views/profiles/notifications/show.html.haml
@@ -20,7 +20,7 @@
= radio_button_tag :notification_level, Notification::N_MENTION, @notification.mention?, class: 'trigger-submit'
.level-title
Mention
- %p You will receive notifications only for comments where you was @mentioned
+ %p You will receive notifications only for comments in which you were @mentioned
.radio
= label_tag nil, class: '' do
diff --git a/app/views/projects/_blob_editor.html.haml b/app/views/projects/_blob_editor.html.haml
deleted file mode 100644
index 1fb74b55c41..00000000000
--- a/app/views/projects/_blob_editor.html.haml
+++ /dev/null
@@ -1,15 +0,0 @@
-.file-holder.file
- .file-title
- %i.icon-file
- %span.file_name
- %span.monospace.light #{ref}
- - if local_assigns[:path]
- = ': ' + local_assigns[:path]
- .file-content.code
- %pre.js-edit-mode-pane#editor
- = params[:content] || local_assigns[:blob_data]
- - if local_assigns[:path]
- .js-edit-mode-pane#preview.hide
- .center
- %h2
- %i.icon-spinner.icon-spin
diff --git a/app/views/projects/_github_import_modal.html.haml b/app/views/projects/_github_import_modal.html.haml
new file mode 100644
index 00000000000..02c9ef45f2b
--- /dev/null
+++ b/app/views/projects/_github_import_modal.html.haml
@@ -0,0 +1,22 @@
+%div#github_import_modal.modal.hide
+ .modal-dialog
+ .modal-content
+ .modal-header
+ %a.close{href: "#", "data-dismiss" => "modal"} ×
+ %h3 GitHub OAuth import
+ .modal-body
+ You need to setup integration with GitHub first.
+ = link_to 'How to setup integration with GitHub', 'https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/integration/github.md'
+
+
+:javascript
+ $(function(){
+ var import_modal = $('#github_import_modal').modal({modal: true, show:false});
+ $('.how_to_import_link').bind("click", function(e){
+ e.preventDefault();
+ import_modal.show();
+ });
+ $('.modal-header .close').bind("click", function(){
+ import_modal.hide();
+ })
+ })
diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml
index 30d063c7a36..2ed49f83a7a 100644
--- a/app/views/projects/_home_panel.html.haml
+++ b/app/views/projects/_home_panel.html.haml
@@ -1,5 +1,7 @@
- empty_repo = @project.empty_repo?
.project-home-panel{:class => ("empty-project" if empty_repo)}
+ .project-identicon-holder
+ = project_icon(@project.to_param, alt: '', class: 'avatar')
.project-home-row
.project-home-desc
- if @project.description.present?
diff --git a/app/views/projects/_settings_nav.html.haml b/app/views/projects/_settings_nav.html.haml
index 64eda0bf286..646e48a1e1d 100644
--- a/app/views/projects/_settings_nav.html.haml
+++ b/app/views/projects/_settings_nav.html.haml
@@ -1,31 +1,31 @@
%ul.project-settings-nav.sidebar-subnav
= nav_link(path: 'projects#edit') do
- = link_to edit_project_path(@project), class: "stat-tab tab " do
+ = link_to edit_project_path(@project), title: 'Project', class: "stat-tab tab " do
%i.fa.fa-pencil-square-o
%span
Project
= nav_link(controller: [:team_members, :teams]) do
- = link_to project_team_index_path(@project), class: "team-tab tab" do
+ = link_to project_team_index_path(@project), title: 'Members', class: "team-tab tab" do
%i.fa.fa-users
%span
Members
= nav_link(controller: :deploy_keys) do
- = link_to project_deploy_keys_path(@project) do
+ = link_to project_deploy_keys_path(@project), title: 'Deploy Keys' do
%i.fa.fa-key
%span
Deploy Keys
= nav_link(controller: :hooks) do
- = link_to project_hooks_path(@project) do
+ = link_to project_hooks_path(@project), title: 'Web Hooks' do
%i.fa.fa-link
%span
Web Hooks
= nav_link(controller: :services) do
- = link_to project_services_path(@project) do
+ = link_to project_services_path(@project), title: 'Services' do
%i.fa.fa-cogs
%span
Services
= nav_link(controller: :protected_branches) do
- = link_to project_protected_branches_path(@project) do
+ = link_to project_protected_branches_path(@project), title: 'Protected Branches' do
%i.fa.fa-lock
%span
Protected branches
diff --git a/app/views/projects/blob/_editor.html.haml b/app/views/projects/blob/_editor.html.haml
new file mode 100644
index 00000000000..96f188e4aa7
--- /dev/null
+++ b/app/views/projects/blob/_editor.html.haml
@@ -0,0 +1,25 @@
+.file-holder.file
+ .file-title
+ .editor-ref
+ %i.fa.fa-code-fork
+ = ref
+ %span.editor-file-name
+ - if @path
+ %span.monospace
+ = @path
+
+ - if current_action?(:new) || current_action?(:create)
+ \/
+ = text_field_tag 'file_name', params[:file_name], placeholder: "File name",
+ required: true, class: 'form-control new-file-name'
+ .pull-right
+ = select_tag :encoding, options_for_select([ "base64", "text" ], "text"), class: 'form-control'
+
+ .file-content.code
+ %pre.js-edit-mode-pane#editor
+ = params[:content] || local_assigns[:blob_data]
+ - if local_assigns[:path]
+ .js-edit-mode-pane#preview.hide
+ .center
+ %h2
+ %i.icon-spinner.icon-spin
diff --git a/app/views/projects/blob/edit.html.haml b/app/views/projects/blob/edit.html.haml
new file mode 100644
index 00000000000..b150b639888
--- /dev/null
+++ b/app/views/projects/blob/edit.html.haml
@@ -0,0 +1,24 @@
+.file-editor
+ %ul.nav.nav-tabs.js-edit-mode
+ %li.active
+ = link_to '#editor' do
+ %i.fa.fa-edit
+ Edit file
+
+ %li
+ = link_to '#preview', 'data-preview-url' => project_preview_blob_path(@project, @id) do
+ %i.fa.fa-eye
+ = editing_preview_title(@blob.name)
+
+ = form_tag(project_update_blob_path(@project, @id), method: :put, class: "form-horizontal") do
+ = render 'projects/blob/editor', ref: @ref, path: @path, blob_data: @blob.data
+ = render 'shared/commit_message_container', params: params,
+ placeholder: "Update #{@blob.name}"
+ = hidden_field_tag 'last_commit', @last_commit
+ = hidden_field_tag 'content', '', id: "file-content"
+ = hidden_field_tag 'from_merge_request_id', params[:from_merge_request_id]
+ = render 'projects/commit_button', ref: @ref,
+ cancel_path: @after_edit_path
+
+:javascript
+ blob = new EditBlob(gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}", "#{@blob.language.try(:ace_mode)}")
diff --git a/app/views/projects/blob/new.html.haml b/app/views/projects/blob/new.html.haml
new file mode 100644
index 00000000000..df6aedbe17d
--- /dev/null
+++ b/app/views/projects/blob/new.html.haml
@@ -0,0 +1,12 @@
+%h3.page-title New file
+.file-editor
+ = form_tag(project_create_blob_path(@project, @id), method: :post, class: 'form-horizontal form-new-file') do
+ = render 'projects/blob/editor', ref: @ref
+ = render 'shared/commit_message_container', params: params,
+ placeholder: 'Add new file'
+ = hidden_field_tag 'content', '', id: 'file-content'
+ = render 'projects/commit_button', ref: @ref,
+ cancel_path: project_tree_path(@project, @id)
+
+:javascript
+ blob = new NewBlob(gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}", null)
diff --git a/app/views/projects/edit_tree/preview.html.haml b/app/views/projects/blob/preview.html.haml
index e7c3460ad78..e7c3460ad78 100644
--- a/app/views/projects/edit_tree/preview.html.haml
+++ b/app/views/projects/blob/preview.html.haml
diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml
index a6623240da1..2719bcc33bc 100644
--- a/app/views/projects/branches/new.html.haml
+++ b/app/views/projects/branches/new.html.haml
@@ -5,7 +5,7 @@
%h3.page-title
%i.fa.fa-code-fork
New branch
-= form_tag project_branches_path, method: :post, class: "form-horizontal" do
+= form_tag project_branches_path, method: :post, id: "new-branch-form", class: "form-horizontal" do
.form-group
= label_tag :branch_name, 'Name for new branch', class: 'control-label'
.col-sm-10
@@ -19,6 +19,7 @@
= link_to 'Cancel', project_branches_path(@project), class: 'btn btn-cancel'
:javascript
+ disableButtonIfAnyEmptyField($("#new-branch-form"), ".form-control", ".btn-create");
var availableTags = #{@project.repository.ref_names.to_json};
$("#ref").autocomplete({
diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml
index e149f017f84..b41fb1437f2 100644
--- a/app/views/projects/commit/_commit_box.html.haml
+++ b/app/views/projects/commit/_commit_box.html.haml
@@ -37,18 +37,23 @@
- @commit.parents.each do |parent|
= link_to parent.short_id, project_commit_path(@project, parent)
-- if @branches.any?
- .commit-info-row
- %span.cgray
- Exists in
+.commit-info-row
+ - if @branches.any?
%span
- branch = commit_default_branch(@project, @branches)
- = link_to(branch, project_tree_path(@project, branch))
- - if @branches.any?
- and in
- = link_to("#{pluralize(@branches.count, "other branch")}", "#", class: "js-details-expand")
+ = link_to(project_tree_path(@project, branch)) do
+ %span.label.label-gray
+ %i.fa.fa-code-fork
+ = branch
+ - if @branches.any? || @tags.any?
+ = link_to("#", class: "js-details-expand") do
+ %span.label.label-gray
+ \...
%span.js-details-content.hide
- = commit_branches_links(@project, @branches)
+ - if @branches.any?
+ = commit_branches_links(@project, @branches)
+ - if @tags.any?
+ = commit_tags_links(@project, @tags)
.commit-box
%h3.commit-title
diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml
index 34d13502231..8d080f710d8 100644
--- a/app/views/projects/diffs/_file.html.haml
+++ b/app/views/projects/diffs/_file.html.haml
@@ -9,6 +9,9 @@
.diff-btn-group
- if @commit.parent_ids.present?
= view_file_btn(@commit.parent_id, diff_file, project)
+ - elsif diff_file.diff.submodule?
+ - submodule_item = project.repository.blob_at(@commit.id, diff_file.file_path)
+ = submodule_link(submodule_item, @commit.id)
- else
- if diff_file.renamed_file
%span= "#{diff_file.old_path} renamed to #{diff_file.new_path}"
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index f2bb56b5664..31bdbb562a1 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -7,7 +7,8 @@
%p.light Some settings, such as "Transfer Project", are hidden inside the danger area below.
%hr
.panel-body
- = form_for @project, remote: true, html: { class: "edit_project form-horizontal" } do |f|
+ = form_for @project, remote: true, html: { multipart: true, class: "edit_project form-horizontal" }, authenticity_token: true do |f|
+
%fieldset
.form-group.project_name_holder
= f.label :name, class: 'control-label' do
@@ -50,15 +51,6 @@
= f.check_box :issues_enabled
%span.descr Lightweight issue tracking system for this project
- - if Project.issues_tracker.values.count > 1
- .form-group
- = f.label :issues_tracker, "Issues tracker", class: 'control-label'
- .col-sm-10= f.select(:issues_tracker, project_issues_trackers(@project.issues_tracker), {}, { disabled: !@project.issues_enabled })
-
- .form-group
- = f.label :issues_tracker_id, "Project name or id in issues tracker", class: 'control-label'
- .col-sm-10= f.text_field :issues_tracker_id, disabled: !@project.can_have_issues_tracker_id?, class: 'form-control'
-
.form-group
= f.label :merge_requests_enabled, "Merge Requests", class: 'control-label'
.col-sm-10
@@ -80,6 +72,32 @@
= f.check_box :snippets_enabled
%span.descr Share code pastes with others out of git repository
+ %fieldset.features
+ %legend
+ Project avatar:
+ .form-group
+ .col-sm-2
+ .col-sm-10
+ - if @project.avatar?
+ = project_icon(@project.to_param, alt: '', class: 'avatar s160')
+ %p.light
+ - if @project.avatar_in_git
+ Project avatar in repository: #{ @project.avatar_in_git }
+ %p.light
+ - if @project.avatar?
+ You can change your project avatar here
+ - else
+ You can upload an project avatar here
+ %a.choose-btn.btn.btn-small.js-choose-project-avatar-button
+ %i.icon-paper-clip
+ %span Choose File ...
+ &nbsp;
+ %span.file_name.js-avatar-filename File name...
+ = f.file_field :avatar, class: "js-project-avatar-input hidden"
+ .light The maximum file size allowed is 200KB.
+ - if @project.avatar?
+ %hr
+ = link_to 'Remove avatar', project_avatar_path(@project), data: { confirm: "Project avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-small remove-avatar"
.form-actions
= f.submit 'Save changes', class: "btn btn-save"
diff --git a/app/views/projects/edit_tree/show.html.haml b/app/views/projects/edit_tree/show.html.haml
deleted file mode 100644
index 7e0789853af..00000000000
--- a/app/views/projects/edit_tree/show.html.haml
+++ /dev/null
@@ -1,57 +0,0 @@
-.file-editor
- %ul.nav.nav-tabs.js-edit-mode
- %li.active
- = link_to 'Edit', '#editor'
- %li
- = link_to editing_preview_title(@blob.name), '#preview', 'data-preview-url' => preview_project_edit_tree_path(@project, @id)
-
- = form_tag(project_edit_tree_path(@project, @id), method: :put, class: "form-horizontal") do
- = render 'projects/blob_editor', ref: @ref, path: @path, blob_data: @blob.data
- = render 'shared/commit_message_container', params: params,
- placeholder: "Update #{@blob.name}"
- = hidden_field_tag 'last_commit', @last_commit
- = hidden_field_tag 'content', '', id: "file-content"
- = hidden_field_tag 'from_merge_request_id', params[:from_merge_request_id]
- = render 'projects/commit_button', ref: @ref,
- cancel_path: @after_edit_path
-
-:javascript
- ace.config.set("modePath", gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}/ace")
- ace.config.loadModule("ace/ext/searchbox");
- var ace_mode = "#{@blob.language.try(:ace_mode)}";
- var editor = ace.edit("editor");
- if (ace_mode) {
- editor.getSession().setMode('ace/mode/' + ace_mode);
- }
-
- disableButtonIfEmptyField("#commit_message", ".js-commit-button");
-
- $(".js-commit-button").click(function(){
- $("#file-content").val(editor.getValue());
- $(".file-editor form").submit();
- });
-
- var editModePanes = $('.js-edit-mode-pane'),
- editModeLinks = $('.js-edit-mode a');
-
- editModeLinks.click(function(event) {
- event.preventDefault();
-
- var currentLink = $(this),
- paneId = currentLink.attr('href'),
- currentPane = editModePanes.filter(paneId);
-
- editModeLinks.parent().removeClass('active hover');
- currentLink.parent().addClass('active hover');
- editModePanes.hide();
-
- if (paneId == '#preview') {
- currentPane.fadeIn(200);
- $.post(currentLink.data('preview-url'), { content: editor.getValue() }, function(response) {
- currentPane.empty().append(response);
- })
- } else {
- currentPane.fadeIn(200);
- editor.focus()
- }
- })
diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml
index 59f19c8b7a3..36628195b4e 100644
--- a/app/views/projects/empty.html.haml
+++ b/app/views/projects/empty.html.haml
@@ -1,5 +1,19 @@
+- if current_user && can?(current_user, :download_code, @project)
+ = render 'shared/no_ssh'
+
= render "home_panel"
+.center.well
+ %h3
+ The repository for this project is empty
+ %h4
+ You can
+ = link_to project_new_blob_path(@project, 'master'), class: 'btn btn-new btn-lg' do
+ add a file
+ &nbsp;or push it via command line.
+
+%h4
+ %strong Command line instructions
%div.git-empty
%fieldset
%legend Git global setup
diff --git a/app/views/projects/forks/new.html.haml b/app/views/projects/forks/new.html.haml
index 54f2cef023b..959d5f08d47 100644
--- a/app/views/projects/forks/new.html.haml
+++ b/app/views/projects/forks/new.html.haml
@@ -1,5 +1,6 @@
%h3.page-title Fork project
-%p.lead Select namespace where to fork this project
+%p.lead
+ Click to fork the project to a user or group
%hr
.fork-namespaces
diff --git a/app/views/projects/issues/_issues.html.haml b/app/views/projects/issues/_issues.html.haml
index 010ca3b68b3..816851a8abe 100644
--- a/app/views/projects/issues/_issues.html.haml
+++ b/app/views/projects/issues/_issues.html.haml
@@ -1,6 +1,6 @@
.append-bottom-10
.check-all-holder
- = check_box_tag "check_all_issues", nil, false, class: "check_all_issues left"
+ = check_box_tag "check_all_issues", nil, false, class: "check_all_issues left", disabled: !can?(current_user, :modify_issue, @project)
= render 'shared/issuable_filter'
.clearfix
diff --git a/app/views/projects/merge_requests/show/_mr_accept.html.haml b/app/views/projects/merge_requests/show/_mr_accept.html.haml
index 11a111e5faa..f8ee6973637 100644
--- a/app/views/projects/merge_requests/show/_mr_accept.html.haml
+++ b/app/views/projects/merge_requests/show/_mr_accept.html.haml
@@ -45,10 +45,17 @@
.automerge_widget.cannot_be_merged.hide
%h4
This request can't be merged with GitLab.
- %p
You should do it manually with
%strong
- = link_to "command line", "#modal_merge_info", class: "how_to_merge_link", title: "How To Merge", "data-toggle" => "modal"
+ = link_to "#modal_merge_info", class: "underlined-link how_to_merge_link", title: "How To Merge", "data-toggle" => "modal" do
+ command line
+
+ %p
+ %button.btn.disabled
+ %i.fa.fa-warning
+ Accept Merge Request
+ &nbsp;
+ This usually happens when git can not resolve conflicts between branches automatically.
.automerge_widget.unchecked
%p
diff --git a/app/views/projects/merge_requests/show/_mr_ci.html.haml b/app/views/projects/merge_requests/show/_mr_ci.html.haml
index 941b15d3b32..ee7fd0ef157 100644
--- a/app/views/projects/merge_requests/show/_mr_ci.html.haml
+++ b/app/views/projects/merge_requests/show/_mr_ci.html.haml
@@ -3,21 +3,21 @@
%i.fa.fa-check
%span CI build passed
for #{@merge_request.last_commit_short_sha}.
- = link_to "Build page", ci_build_details_path(@merge_request)
+ = link_to "Build page", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink"
.ci_widget.ci-failed{style: "display:none"}
%i.fa.fa-times
%span CI build failed
for #{@merge_request.last_commit_short_sha}.
- = link_to "Build page", ci_build_details_path(@merge_request)
+ = link_to "Build page", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink"
- [:running, :pending].each do |status|
.ci_widget{class: "ci-#{status}", style: "display:none"}
%i.fa.fa-clock-o
%span CI build #{status}
for #{@merge_request.last_commit_short_sha}.
- = link_to "Build page", ci_build_details_path(@merge_request)
+ = link_to "Build page", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink"
.ci_widget
%i.fa.fa-spinner
diff --git a/app/views/projects/merge_requests/show/_remove_source_branch.html.haml b/app/views/projects/merge_requests/show/_remove_source_branch.html.haml
index 4fe5935bcf3..9bf6a9d081c 100644
--- a/app/views/projects/merge_requests/show/_remove_source_branch.html.haml
+++ b/app/views/projects/merge_requests/show/_remove_source_branch.html.haml
@@ -12,6 +12,6 @@
Failed to remove source branch '#{@merge_request.source_branch}'
.remove_source_branch_in_progress.hide
- %i.fa.fa-refresh.fa-spin
+ %i.fa.fa-spinner.fa-spin
&nbsp;
Removing source branch '#{@merge_request.source_branch}'. Please wait. Page will be automatically reloaded. &nbsp;
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index ccd02acd761..3e0f9cbd80b 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -40,13 +40,18 @@
The import will time out after 4 minutes. For big repositories, use a clone/push combination.
For SVN repositories, check #{link_to "this migrating from SVN doc.", "http://doc.gitlab.com/ce/workflow/migrating_from_svn.html"}
- - if github_import_enabled?
- .project-import.form-group
- .col-sm-2
- .col-sm-10
+ .project-import.form-group
+ .col-sm-2
+ .col-sm-10
+ - if github_import_enabled?
= link_to status_github_import_path do
%i.fa.fa-github
Import projects from GitHub
+ - else
+ = link_to '#', class: 'how_to_import_link light' do
+ %i.fa.fa-github
+ Import projects from GitHub
+ = render 'github_import_modal'
%hr.prepend-botton-10
diff --git a/app/views/projects/new_tree/show.html.haml b/app/views/projects/new_tree/show.html.haml
deleted file mode 100644
index cf7b768694f..00000000000
--- a/app/views/projects/new_tree/show.html.haml
+++ /dev/null
@@ -1,38 +0,0 @@
-%h3.page-title New file
-%hr
-.file-editor
- = form_tag(project_new_tree_path(@project, @id), method: :put, class: 'form-horizontal form-new-file') do
- .form-group.commit_message-group
- = label_tag 'file_name', class: 'control-label' do
- File name
- .col-sm-10
- .input-group
- %span.input-group-addon
- = @path[-1] == "/" ? @path : @path + "/"
- = text_field_tag 'file_name', params[:file_name], placeholder: "sample.rb", required: true, class: 'form-control'
- %span.input-group-addon
- on
- %span= @ref
-
- .form-group.commit_message-group
- = label_tag :encoding, class: "control-label" do
- Encoding
- .col-sm-10
- = select_tag :encoding, options_for_select([ "base64", "text" ], "text"), class: 'form-control'
- = render 'projects/blob_editor', ref: @ref
- = render 'shared/commit_message_container', params: params,
- placeholder: 'Add new file'
- = hidden_field_tag 'content', '', id: 'file-content'
- = render 'projects/commit_button', ref: @ref,
- cancel_path: project_tree_path(@project, @id)
-
-:javascript
- ace.config.set("modePath", gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}/ace-src-noconflict")
- var editor = ace.edit("editor");
-
- disableButtonIfAnyEmptyField($('.form-new-file'), '.form-control', '.btn-create')
-
- $(".js-commit-button").click(function(){
- $("#file-content").val(editor.getValue());
- $(".file-editor form").submit();
- });
diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml
index 691c169b620..88c7b7ccf1a 100644
--- a/app/views/projects/notes/_note.html.haml
+++ b/app/views/projects/notes/_note.html.haml
@@ -28,14 +28,24 @@
%span.note-last-update
= note_timestamp(note)
- - if note.upvote?
- %span.vote.upvote.label.label-success
- %i.fa.fa-thumbs-up
- \+1
- - if note.downvote?
- %span.vote.downvote.label.label-danger
- %i.fa.fa-thumbs-down
- \-1
+ - if note.superceded?(@notes)
+ - if note.upvote?
+ %span.vote.upvote.label.label-gray.strikethrough
+ %i.fa.fa-thumbs-up
+ \+1
+ - if note.downvote?
+ %span.vote.downvote.label.label-gray.strikethrough
+ %i.fa.fa-thumbs-down
+ \-1
+ - else
+ - if note.upvote?
+ %span.vote.upvote.label.label-success
+ %i.fa.fa-thumbs-up
+ \+1
+ - if note.downvote?
+ %span.vote.downvote.label.label-danger
+ %i.fa.fa-thumbs-down
+ \-1
.note-body
diff --git a/app/views/projects/services/index.html.haml b/app/views/projects/services/index.html.haml
index 7271dd830ca..4604c0afd8d 100644
--- a/app/views/projects/services/index.html.haml
+++ b/app/views/projects/services/index.html.haml
@@ -1,13 +1,22 @@
%h3.page-title Project services
%p.light Project services allow you to integrate GitLab with other applications
-%hr
-%ul.bordered-list
+%table.table
+ %thead
+ %tr
+ %th
+ %th Service
+ %th Desription
+ %th Last edit
- @services.sort_by(&:title).each do |service|
- %li
- %h4
+ %tr
+ %td
+ = boolean_to_icon service.activated?
+ %td
= link_to edit_project_service_path(@project, service.to_param) do
- = service.title
- .pull-right
- = boolean_to_icon service.activated?
- %p= service.description
+ %strong= service.title
+ %td
+ = service.description
+ %td.light
+ = time_ago_in_words service.updated_at
+ ago
diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml
index af6e4567c1b..737a34decde 100644
--- a/app/views/projects/show.html.haml
+++ b/app/views/projects/show.html.haml
@@ -68,11 +68,11 @@
- @project.ci_services.each do |ci_service|
- if ci_service.active? && ci_service.respond_to?(:builds_path)
- if ci_service.respond_to?(:status_img_path)
- = link_to ci_service.builds_path do
+ = link_to ci_service.builds_path, :'data-no-turbolink' => 'data-no-turbolink' do
= image_tag ci_service.status_img_path, alt: "build status"
- else
%span.light CI provided by
- = link_to ci_service.title, ci_service.builds_path
+ = link_to ci_service.title, ci_service.builds_path, :'data-no-turbolink' => 'data-no-turbolink'
- if readme
.tab-pane#tab-readme
diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml
index ad7ff8d3db8..289c52a2e3f 100644
--- a/app/views/projects/tags/new.html.haml
+++ b/app/views/projects/tags/new.html.haml
@@ -5,7 +5,7 @@
%h3.page-title
%i.fa.fa-code-fork
New tag
-= form_tag project_tags_path, method: :post, class: "form-horizontal" do
+= form_tag project_tags_path, method: :post, id: "new-tag-form", class: "form-horizontal" do
.form-group
= label_tag :tag_name, 'Name for new tag', class: 'control-label'
.col-sm-10
@@ -25,6 +25,7 @@
= link_to 'Cancel', project_tags_path(@project), class: 'btn btn-cancel'
:javascript
+ disableButtonIfAnyEmptyField($("#new-tag-form"), ".form-control", ".btn-create");
var availableTags = #{@project.repository.ref_names.to_json};
$("#ref").autocomplete({
diff --git a/app/views/projects/team_members/_form.html.haml b/app/views/projects/team_members/_form.html.haml
index 2bf61fa12bb..ddf8cb76f78 100644
--- a/app/views/projects/team_members/_form.html.haml
+++ b/app/views/projects/team_members/_form.html.haml
@@ -17,7 +17,12 @@
%p 2. Set access level for them
.form-group
= f.label :access_level, "Project Access", class: 'control-label'
- .col-sm-10= select_tag :access_level, options_for_select(Gitlab::Access.options, @user_project_relation.access_level), class: "project-access-select select2"
+ .col-sm-10
+ = select_tag :access_level, options_for_select(Gitlab::Access.options, @user_project_relation.access_level), class: "project-access-select select2"
+ .help-block
+ Read more about role permissions
+ %strong= link_to "here", help_page_path("permissions", "permissions"), class: "vlink"
+
.form-actions
= f.submit 'Add users', class: "btn btn-create"
diff --git a/app/views/projects/tree/_submodule_item.html.haml b/app/views/projects/tree/_submodule_item.html.haml
index 46e9be4af83..20c70cac699 100644
--- a/app/views/projects/tree/_submodule_item.html.haml
+++ b/app/views/projects/tree/_submodule_item.html.haml
@@ -1,14 +1,6 @@
-- tree, commit = submodule_links(submodule_item)
%tr{ class: "tree-item" }
%td.tree-item-file-name
%i.fa.fa-archive
- %span
- = link_to truncate(submodule_item.name, length: 40), tree
- @
- %span.monospace
- - if commit.nil?
- #{truncate_sha(submodule_item.id)}
- - else
- = link_to "#{truncate_sha(submodule_item.id)}", commit
+ = submodule_link(submodule_item, @ref)
%td
%td.hidden-xs
diff --git a/app/views/projects/tree/_tree.html.haml b/app/views/projects/tree/_tree.html.haml
index 68ccd4d61bb..f902440b3f1 100644
--- a/app/views/projects/tree/_tree.html.haml
+++ b/app/views/projects/tree/_tree.html.haml
@@ -10,7 +10,7 @@
= link_to title, '#'
- if current_user && can_push_branch?(@project, @ref)
%li
- = link_to project_new_tree_path(@project, @id), title: 'New file', id: 'new-file-link' do
+ = link_to project_new_blob_path(@project, @id), title: 'New file', id: 'new-file-link' do
%small
%i.fa.fa-plus
diff --git a/app/views/users/_groups.html.haml b/app/views/users/_groups.html.haml
index ea008c2dede..32a1dc83b57 100644
--- a/app/views/users/_groups.html.haml
+++ b/app/views/users/_groups.html.haml
@@ -1,3 +1,4 @@
-- groups.each do |group|
- = link_to group, class: 'profile-groups-avatars', :title => group.name do
- - image_tag group_icon(group.path)
+.clearfix
+ - groups.each do |group|
+ = link_to group, class: 'profile-groups-avatars', title: group.name do
+ = image_tag group_icon(group.path), class: 'avatar avatar-inline s40'
diff --git a/app/views/users/calendar.html.haml b/app/views/users/calendar.html.haml
new file mode 100644
index 00000000000..13bdc5ed1e7
--- /dev/null
+++ b/app/views/users/calendar.html.haml
@@ -0,0 +1,8 @@
+%h4 Calendar
+#cal-heatmap.calendar
+ :javascript
+ new calendar(
+ #{@timestamps.to_json},
+ #{@starting_year},
+ #{@starting_month}
+ );
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index 54f2666ce5d..b05918b019e 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -1,7 +1,7 @@
.row
.col-md-8
%h3.page-title
- = image_tag avatar_icon(@user.email, 90), class: "avatar s90", alt: ''
+ = image_tag avatar_icon(@user.email, 90), class: "avatar avatar-tile s90", alt: ''
= @user.name
- if @user == current_user
.pull-right
@@ -15,11 +15,16 @@
.clearfix
- if @groups.any?
- %h4 Groups:
+ %h4 Groups
= render 'groups', groups: @groups
%hr
+
+ .user-calendar
+ %h4.center.light
+ %i.fa.fa-spinner.fa-spin
+ %hr
%h4
- User Activity:
+ User Activity
- if current_user
%span.rss-icon.pull-right
@@ -32,3 +37,8 @@
= render 'profile', user: @user
- if @projects.present?
= render 'projects', projects: @projects
+
+
+:coffeescript
+ $ ->
+ $(".user-calendar").load("#{user_calendar_path}")
diff --git a/config/application.rb b/config/application.rb
index 8a280de6fac..24ba219cf3a 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -12,11 +12,11 @@ module Gitlab
# -- all .rb files in that directory are automatically loaded.
# Custom directories with classes and modules you want to be autoloadable.
- config.autoload_paths += %W(#{config.root}/lib
- #{config.root}/app/models/hooks
- #{config.root}/app/models/concerns
- #{config.root}/app/models/project_services
- #{config.root}/app/models/members)
+ config.autoload_paths.push(*%W(#{config.root}/lib
+ #{config.root}/app/models/hooks
+ #{config.root}/app/models/concerns
+ #{config.root}/app/models/project_services
+ #{config.root}/app/models/members))
# Only load the plugins named here, in the order given (default is alphabetical).
# :all can be used as a placeholder for all plugins not explicitly named.
@@ -31,7 +31,7 @@ module Gitlab
config.encoding = "utf-8"
# Configure sensitive parameters which will be filtered from the log file.
- config.filter_parameters += [:password]
+ config.filter_parameters.push(*[:password])
# Enable escaping HTML in JSON.
config.active_support.escape_html_entities_in_json = true
@@ -70,7 +70,10 @@ module Gitlab
config.middleware.use Rack::Cors do
allow do
origins '*'
- resource '/api/*', headers: :any, methods: [:get, :post, :options, :put, :delete]
+ resource '/api/*',
+ headers: :any,
+ methods: [:get, :post, :options, :put, :delete],
+ expose: ['Link']
end
end
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index 92f601282e0..59af49c0180 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -60,7 +60,7 @@ production: &base
## Users can create accounts
# This also allows normal users to sign up for accounts themselves
- # default: false - By default GitLab administrators must create all new accounts
+ # default: true - By default users can sign up themselves
# signup_enabled: true
## Standard login settings
@@ -77,7 +77,7 @@ production: &base
# This happens when the commit is pushed or merged into the default branch of a project.
# When not specified the default issue_closing_pattern as specified below will be used.
# Tip: you can test your closing pattern at http://rubular.com
- # issue_closing_pattern: '([Cc]lose[sd]|[Ff]ixe[sd]) #(\d+)'
+ # issue_closing_pattern: '((?:[Cc]los(?:e[sd]|ing)|[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?#\d+(?:(?:, *| +and +)?))+)'
## Default project features settings
default_projects_features:
@@ -153,9 +153,9 @@ production: &base
label: 'LDAP'
host: '_your_ldap_server'
- port: 636
+ port: 389
uid: 'sAMAccountName'
- method: 'ssl' # "tls" or "ssl" or "plain"
+ method: 'plain' # "tls" or "ssl" or "plain"
bind_dn: '_the_full_dn_of_the_user_you_will_bind_with'
password: '_the_password_of_the_bind_user'
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index cdb958aa6a6..1ec842761ff 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -109,7 +109,7 @@ Settings.gitlab['signup_enabled'] ||= true if Settings.gitlab['signup_enabled'].
Settings.gitlab['signin_enabled'] ||= true if Settings.gitlab['signin_enabled'].nil?
Settings.gitlab['restricted_visibility_levels'] = Settings.send(:verify_constant_array, Gitlab::VisibilityLevel, Settings.gitlab['restricted_visibility_levels'], [])
Settings.gitlab['username_changing_enabled'] = true if Settings.gitlab['username_changing_enabled'].nil?
-Settings.gitlab['issue_closing_pattern'] = '([Cc]lose[sd]|[Ff]ixe[sd]) #(\d+)' if Settings.gitlab['issue_closing_pattern'].nil?
+Settings.gitlab['issue_closing_pattern'] = '((?:[Cc]los(?:e[sd]|ing)|[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?#\d+(?:(?:, *| +and +)?))+)' if Settings.gitlab['issue_closing_pattern'].nil?
Settings.gitlab['default_projects_features'] ||= {}
Settings.gitlab['webhook_timeout'] ||= 10
Settings.gitlab.default_projects_features['issues'] = true if Settings.gitlab.default_projects_features['issues'].nil?
diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb
index c6eb3e51036..79abe3c695d 100644
--- a/config/initializers/devise.rb
+++ b/config/initializers/devise.rb
@@ -145,7 +145,8 @@ Devise.setup do |config|
# Time interval you can reset your password with a reset password key.
# Don't put a too small interval or your users won't have the time to
# change their passwords.
- config.reset_password_within = 2.hours
+ # When someone else invites you to GitLab this time is also used so it should be pretty long.
+ config.reset_password_within = 2.days
# ==> Configuration for :encryptable
# Allow you to use another encryption algorithm besides bcrypt (default). You can use
diff --git a/config/initializers/doorkeeper.rb b/config/initializers/doorkeeper.rb
index 23d9852725b..4819ab273dc 100644
--- a/config/initializers/doorkeeper.rb
+++ b/config/initializers/doorkeeper.rb
@@ -36,6 +36,12 @@ Doorkeeper.configure do
# Issue access tokens with refresh token (disabled by default)
use_refresh_token
+ # Forces the usage of the HTTPS protocol in non-native redirect uris (enabled
+ # by default in non-development environments). OAuth2 delegates security in
+ # communication to the HTTPS protocol so it is wise to keep this enabled.
+ #
+ force_ssl_in_redirect_uri false
+
# Provide support for an owner to be assigned to each registered application (disabled by default)
# Optional parameter :confirmation => true (default false) if you want to enforce ownership of
# a registered application
diff --git a/config/routes.rb b/config/routes.rb
index ef3c5aedfcb..e122777314a 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -10,8 +10,8 @@ Gitlab::Application.routes.draw do
#
# Search
#
- get 'search' => "search#show"
- get 'search/autocomplete' => "search#autocomplete", as: :search_autocomplete
+ get 'search' => 'search#show'
+ get 'search/autocomplete' => 'search#autocomplete', as: :search_autocomplete
# API
API::API.logger Rails.logger
@@ -20,9 +20,9 @@ Gitlab::Application.routes.draw do
# Get all keys of user
get ':username.keys' => 'profiles/keys#get_keys' , constraints: { username: /.*/ }
- constraint = lambda { |request| request.env["warden"].authenticate? and request.env['warden'].user.admin? }
+ constraint = lambda { |request| request.env['warden'].authenticate? and request.env['warden'].user.admin? }
constraints constraint do
- mount Sidekiq::Web, at: "/admin/sidekiq", as: :sidekiq
+ mount Sidekiq::Web, at: '/admin/sidekiq', as: :sidekiq
end
# Enable Grack support
@@ -46,10 +46,10 @@ Gitlab::Application.routes.draw do
#
resources :snippets do
member do
- get "raw"
+ get 'raw'
end
end
- get "/s/:username" => "snippets#user_index", as: :user_snippets, constraints: { username: /.*/ }
+ get '/s/:username' => 'snippets#user_index', as: :user_snippets, constraints: { username: /.*/ }
#
# Github importer area
@@ -61,7 +61,7 @@ Gitlab::Application.routes.draw do
end
#
- # Explroe area
+ # Explore area
#
namespace :explore do
resources :projects, only: [:index] do
@@ -72,12 +72,12 @@ Gitlab::Application.routes.draw do
end
resources :groups, only: [:index]
- root to: "projects#trending"
+ root to: 'projects#trending'
end
# Compatibility with old routing
- get 'public' => "explore/projects#index"
- get 'public/projects' => "explore/projects#index"
+ get 'public' => 'explore/projects#index'
+ get 'public/projects' => 'explore/projects#index'
#
# Attachments serving
@@ -122,7 +122,7 @@ Gitlab::Application.routes.draw do
resource :application_settings, only: [:show, :update]
- root to: "dashboard#index"
+ root to: 'dashboard#index'
end
#
@@ -157,13 +157,16 @@ Gitlab::Application.routes.draw do
end
end
+ get 'u/:username/calendar' => 'users#calendar', as: :user_calendar,
+ constraints: { username: /(?:[^.]|\.(?!atom$))+/, format: /atom/ }
+
get '/u/:username' => 'users#show', as: :user,
constraints: { username: /(?:[^.]|\.(?!atom$))+/, format: /atom/ }
#
# Dashboard Area
#
- resource :dashboard, controller: "dashboard", only: [:show] do
+ resource :dashboard, controller: 'dashboard', only: [:show] do
member do
get :projects
get :issues
@@ -194,12 +197,12 @@ Gitlab::Application.routes.draw do
devise_for :users, controllers: { omniauth_callbacks: :omniauth_callbacks, registrations: :registrations , passwords: :passwords, sessions: :sessions, confirmations: :confirmations }
devise_scope :user do
- get "/users/auth/:provider/omniauth_error" => "omniauth_callbacks#omniauth_error", as: :omniauth_error
+ get '/users/auth/:provider/omniauth_error' => 'omniauth_callbacks#omniauth_error', as: :omniauth_error
end
#
# Project Area
#
- resources :projects, constraints: { id: /[a-zA-Z.0-9_\-]+\/[a-zA-Z.0-9_\-]+/ }, except: [:new, :create, :index], path: "/" do
+ resources :projects, constraints: { id: /[a-zA-Z.0-9_\-]+\/[a-zA-Z.0-9_\-]+/ }, except: [:new, :create, :index], path: '/' do
member do
put :transfer
post :archive
@@ -211,16 +214,20 @@ Gitlab::Application.routes.draw do
end
scope module: :projects do
+ # Blob routes:
+ get '/new/:id', to: 'blob#new', constraints: {id: /.+/}, as: 'new_blob'
+ post '/create/:id', to: 'blob#create', constraints: {id: /.+/}, as: 'create_blob'
+ get '/edit/:id', to: 'blob#edit', constraints: {id: /.+/}, as: 'edit_blob'
+ put '/update/:id', to: 'blob#update', constraints: {id: /.+/}, as: 'update_blob'
+ post '/preview/:id', to: 'blob#preview', constraints: {id: /.+/}, as: 'preview_blob'
+
resources :blob, only: [:show, :destroy], constraints: { id: /.+/, format: false } do
get :diff, on: :member
end
+
resources :raw, only: [:show], constraints: {id: /.+/}
resources :tree, only: [:show], constraints: {id: /.+/, format: /(html|js)/ }
- resources :edit_tree, only: [:show, :update], constraints: { id: /.+/ }, path: 'edit' do
- # Cannot be GET to differentiate from GET paths that end in preview.
- post :preview, on: :member
- end
- resources :new_tree, only: [:show, :update], constraints: {id: /.+/}, path: 'new'
+ resource :avatar, only: [:show, :destroy]
resources :commit, only: [:show], constraints: {id: /[[:alnum:]]{6,40}/}
resources :commits, only: [:show], constraints: {id: /(?:[^.]|\.(?!atom$))+/, format: /atom/}
resources :compare, only: [:index, :create]
@@ -237,7 +244,7 @@ Gitlab::Application.routes.draw do
resources :snippets, constraints: {id: /\d+/} do
member do
- get "raw"
+ get 'raw'
end
end
@@ -249,7 +256,7 @@ Gitlab::Application.routes.draw do
end
member do
- get "history"
+ get 'history'
end
end
@@ -258,7 +265,7 @@ Gitlab::Application.routes.draw do
resource :repository, only: [:show, :create] do
member do
- get "archive", constraints: { format: Gitlab::Regex.archive_formats_regex }
+ get 'archive', constraints: { format: Gitlab::Regex.archive_formats_regex }
end
end
@@ -281,13 +288,13 @@ Gitlab::Application.routes.draw do
resources :refs, only: [] do
collection do
- get "switch"
+ get 'switch'
end
member do
# tree viewer logs
- get "logs_tree", constraints: { id: Gitlab::Regex.git_reference_regex }
- get "logs_tree/:path" => "refs#logs_tree",
+ get 'logs_tree', constraints: { id: Gitlab::Regex.git_reference_regex }
+ get 'logs_tree/:path' => 'refs#logs_tree',
as: :logs_file,
constraints: {
id: Gitlab::Regex.git_reference_regex,
@@ -353,10 +360,11 @@ Gitlab::Application.routes.draw do
delete :delete_attachment
end
end
+
end
end
- get ':id' => "namespaces#show", constraints: {id: /(?:[^.]|\.(?!atom$))+/, format: /atom/}
+ get ':id' => 'namespaces#show', constraints: {id: /(?:[^.]|\.(?!atom$))+/, format: /atom/}
- root to: "dashboard#show"
+ root to: 'dashboard#show'
end
diff --git a/db/migrate/20140125162722_add_avatar_to_projects.rb b/db/migrate/20140125162722_add_avatar_to_projects.rb
new file mode 100644
index 00000000000..9523ac722f2
--- /dev/null
+++ b/db/migrate/20140125162722_add_avatar_to_projects.rb
@@ -0,0 +1,5 @@
+class AddAvatarToProjects < ActiveRecord::Migration
+ def change
+ add_column :projects, :avatar, :string
+ end
+end
diff --git a/db/migrate/20150116234545_add_gitlab_access_token_to_user.rb b/db/migrate/20150116234545_add_gitlab_access_token_to_user.rb
new file mode 100644
index 00000000000..c28ba3197ac
--- /dev/null
+++ b/db/migrate/20150116234545_add_gitlab_access_token_to_user.rb
@@ -0,0 +1,5 @@
+class AddGitlabAccessTokenToUser < ActiveRecord::Migration
+ def change
+ add_column :users, :gitlab_access_token, :string
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index b453164d712..3f9ceb84e5d 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: 20150116234544) do
+ActiveRecord::Schema.define(version: 20150116234545) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -327,6 +327,7 @@ ActiveRecord::Schema.define(version: 20150116234544) do
t.integer "star_count", default: 0, null: false
t.string "import_type"
t.string "import_source"
+ t.string "avatar"
end
add_index "projects", ["creator_id"], name: "index_projects_on_creator_id", using: :btree
@@ -433,6 +434,7 @@ ActiveRecord::Schema.define(version: 20150116234544) do
t.string "website_url", default: "", null: false
t.datetime "last_credential_check_at"
t.string "github_access_token"
+ t.string "gitlab_access_token"
end
add_index "users", ["admin"], name: "index_users_on_admin", using: :btree
diff --git a/doc/README.md b/doc/README.md
index 3c8f8ad3d03..8c6d13e8506 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -9,7 +9,7 @@
- [Public access](public_access/public_access.md) Learn how you can allow public and internal access to projects.
- [SSH](ssh/README.md) Setup your ssh keys and deploy keys for secure access to your projects.
- [Web hooks](web_hooks/web_hooks.md) Let GitLab notify you when new code has been pushed to your project.
-- [Workflow](workflow/README.md) Learn how to get the maximum out of GitLab.
+- [Workflow](workflow/README.md) Using GitLab functionality and importing projects from GitHub and SVN.
## Administrator documentation
diff --git a/doc/api/groups.md b/doc/api/groups.md
index e6893d71774..9217c7a7f24 100644
--- a/doc/api/groups.md
+++ b/doc/api/groups.md
@@ -20,7 +20,7 @@ GET /groups
]
```
-You can search for groups by name or path with: `/groups?search=Rails`
+You can search for groups by name or path, see below.
## Details of a group
@@ -73,6 +73,26 @@ Parameters:
- `id` (required) - The ID of a user group
+## Search for group
+
+Get all groups that match your string in their name or path.
+
+```
+GET /groups?search=foobar
+```
+
+```json
+[
+ {
+ "id": 1,
+ "name": "Foobar Group",
+ "path": "foo-bar",
+ "owner_id": 18,
+ "description": "An interesting group"
+ }
+]
+```
+
## Group members
**Group access levels**
diff --git a/doc/api/milestones.md b/doc/api/milestones.md
index 2f525327504..d48b3bcce8a 100644
--- a/doc/api/milestones.md
+++ b/doc/api/milestones.md
@@ -72,3 +72,16 @@ Parameters:
- `description` (optional) - The description of a milestone
- `due_date` (optional) - The due date of the milestone
- `state_event` (optional) - The state event of the milestone (close|activate)
+
+## Get all issues assigned to a single milestone
+
+Gets all issues assigned to a single project milestone.
+
+```
+GET /projects/:id/milestones/:milestone_id/issues
+```
+
+Parameters:
+
+- `id` (required) - The ID of a project
+- `milestone_id` (required) - The ID of a project milestone
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 027a8ec2e7f..d7804689c25 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -287,6 +287,31 @@ Parameters:
- `visibility_level` (optional)
- `import_url` (optional)
+### Edit project
+
+Updates an existing project
+
+```
+PUT /projects/:id
+```
+
+Parameters:
+
+- `id` (required) - The ID of a project
+- `name` (optional) - project name
+- `path` (optional) - repository name for project
+- `description` (optional) - short project description
+- `default_branch` (optional)
+- `issues_enabled` (optional)
+- `merge_requests_enabled` (optional)
+- `wiki_enabled` (optional)
+- `snippets_enabled` (optional)
+- `public` (optional) - if `true` same as setting visibility_level = 20
+- `visibility_level` (optional)
+
+On success, method returns 200 with the updated project. If parameters are
+invalid, 400 is returned.
+
### Fork project
Forks a project into the user namespace of the authenticated user.
diff --git a/doc/api/users.md b/doc/api/users.md
index b30a31deccc..71fa62bdd65 100644
--- a/doc/api/users.md
+++ b/doc/api/users.md
@@ -322,6 +322,31 @@ Parameters:
- `title` (required) - new SSH Key's title
- `key` (required) - new SSH key
+```json
+{
+ "created_at": "2015-01-21T17:44:33.512Z",
+ "key": "ssh-dss AAAAB3NzaC1kc3MAAACBAMLrhYgI3atfrSD6KDas1b/3n6R/HP+bLaHHX6oh+L1vg31mdUqK0Ac/NjZoQunavoyzqdPYhFz9zzOezCrZKjuJDS3NRK9rspvjgM0xYR4d47oNZbdZbwkI4cTv/gcMlquRy0OvpfIvJtjtaJWMwTLtM5VhRusRuUlpH99UUVeXAAAAFQCVyX+92hBEjInEKL0v13c/egDCTQAAAIEAvFdWGq0ccOPbw4f/F8LpZqvWDydAcpXHV3thwb7WkFfppvm4SZte0zds1FJ+Hr8Xzzc5zMHe6J4Nlay/rP4ewmIW7iFKNBEYb/yWa+ceLrs+TfR672TaAgO6o7iSRofEq5YLdwgrwkMmIawa21FrZ2D9SPao/IwvENzk/xcHu7YAAACAQFXQH6HQnxOrw4dqf0NqeKy1tfIPxYYUZhPJfo9O0AmBW2S36pD2l14kS89fvz6Y1g8gN/FwFnRncMzlLY/hX70FSc/3hKBSbH6C6j8hwlgFKfizav21eS358JJz93leOakJZnGb8XlWvz1UJbwCsnR2VEY8Dz90uIk1l/UqHkA= loic@call",
+ "title": "ABC",
+ "id": 4
+}
+```
+
+Will return created key with status `201 Created` on success. If an
+error occurs a `400 Bad Request` is returned with a message explaining the error:
+
+```json
+{
+ "message": {
+ "fingerprint": [
+ "has already been taken"
+ ],
+ "key": [
+ "has already been taken"
+ ]
+ }
+}
+```
+
## Add SSH key for user
Create new key owned by specified user. Available only for admin
diff --git a/doc/development/shell_commands.md b/doc/development/shell_commands.md
index 1e51ad73e32..42f17e19536 100644
--- a/doc/development/shell_commands.md
+++ b/doc/development/shell_commands.md
@@ -108,7 +108,7 @@ In other repositories, such as gitlab-shell you can also use `IO.popen`.
```ruby
# Safe IO.popen example
-logs = IO.popen(%W(git log), chdir: repo_dir).read
+logs = IO.popen(%W(git log), chdir: repo_dir) { |p| p.read }
```
Note that unlike `Gitlab::Popen.popen`, `IO.popen` does not capture standard error.
diff --git a/doc/integration/ldap.md b/doc/integration/ldap.md
index 56b0d826adb..125ce31b521 100644
--- a/doc/integration/ldap.md
+++ b/doc/integration/ldap.md
@@ -29,9 +29,9 @@ main: # 'main' is the GitLab 'provider ID' of this LDAP server
label: 'LDAP'
host: '_your_ldap_server'
- port: 636
+ port: 389
uid: 'sAMAccountName'
- method: 'ssl' # "tls" or "ssl" or "plain"
+ method: 'plain' # "tls" or "ssl" or "plain"
bind_dn: '_the_full_dn_of_the_user_you_will_bind_with'
password: '_the_password_of_the_bind_user'
@@ -76,6 +76,9 @@ main: # 'main' is the GitLab 'provider ID' of this LDAP server
EOS
```
+If you are getting 'Connection Refused' errors when trying to connect to the LDAP server please double-check the LDAP `port` and `method` settings used by GitLab.
+Common combinations are `method: 'plain'` and `port: 389`, OR `method: 'ssl'` and `port: 636`.
+
If you are using a GitLab installation from source you can find the LDAP settings in `/home/git/gitlab/config/gitlab.yml`:
```
diff --git a/doc/ssh/ssh.md b/doc/ssh/ssh.md
index d466c1bde72..f9ee627f1f5 100644
--- a/doc/ssh/ssh.md
+++ b/doc/ssh/ssh.md
@@ -18,4 +18,21 @@ Use the code below to show your public key.
cat ~/.ssh/id_rsa.pub
```
-Copy-paste the key to the 'My SSH Keys' section under the 'SSH' tab in your user profile. Please copy the complete key starting with `ssh-` and ending with your username and host.
+Copy-paste the key to the 'My SSH Keys' section under the 'SSH' tab in your user profile. Please copy the complete key starting with `ssh-` and ending with your username and host.
+
+Use code below to copy your public key to the clipboard. Depending on your OS you'll need to use a different command:
+
+**Windows:**
+```bash
+clip < ~/.ssh/id_rsa.pub
+```
+
+**Mac:**
+```bash
+pbcopy < ~/.ssh/id_rsa.pub
+```
+
+**Linux (requires xclip):**
+```bash
+xclip -sel clip < ~/.ssh/id_rsa.pub
+```
diff --git a/doc/system_hooks/system_hooks.md b/doc/system_hooks/system_hooks.md
index 54e6e3a9e3f..41c2732ef77 100644
--- a/doc/system_hooks/system_hooks.md
+++ b/doc/system_hooks/system_hooks.md
@@ -1,6 +1,6 @@
# System hooks
-Your GitLab instance can perform HTTP POST requests on the following events: `project_create`, `project_destroy`, `user_add_to_team`, `user_remove_from_team`, `user_create`, `user_destroy`, `key_create` and `key_destroy`.
+Your GitLab instance can perform HTTP POST requests on the following events: `project_create`, `project_destroy`, `user_add_to_team`, `user_remove_from_team`, `user_create`, `user_destroy`, `key_create`, `key_destroy`, `group_create`, `group_destroy`, `user_add_to_group` and `user_remove_from_group`.
System hooks can be used, e.g. for logging or changing information in a LDAP server.
@@ -50,6 +50,7 @@ System hooks can be used, e.g. for logging or changing information in a LDAP ser
"project_path": "storecloud",
"user_email": "johnsmith@gmail.com",
"user_name": "John Smith",
+ "user_id": 41,
"project_visibility": "private",
}
```
@@ -66,6 +67,7 @@ System hooks can be used, e.g. for logging or changing information in a LDAP ser
"project_path": "storecloud",
"user_email": "johnsmith@gmail.com",
"user_name": "John Smith",
+ "user_id": 41,
"project_visibility": "private",
}
```
@@ -117,3 +119,62 @@ System hooks can be used, e.g. for logging or changing information in a LDAP ser
"id": 4
}
```
+
+**Group created:**
+
+```json
+{
+ "created_at": "2012-07-21T07:30:54Z",
+ "event_name": "group_create",
+ "name": "StormCloud",
+ "owner_email": "johnsmith@gmail.com",
+ "owner_name": "John Smith",
+ "path": "stormcloud",
+ "group_id": 78
+}
+```
+
+**Group removed:**
+
+```json
+{
+ "created_at": "2012-07-21T07:30:54Z",
+ "event_name": "group_destroy",
+ "name": "StoreCloud",
+ "owner_email": "johnsmith@gmail.com",
+ "owner_name": "John Smith",
+ "path": "storecloud",
+ "group_id": 78
+}
+```
+
+**New Group Member:**
+
+```json
+{
+ "created_at": "2012-07-21T07:30:56Z",
+ "event_name": "user_add_to_group",
+ "group_access": "Master",
+ "group_id": 78,
+ "group_name": "StoreCloud",
+ "group_path": "storecloud",
+ "user_email": "johnsmith@gmail.com",
+ "user_name": "John Smith",
+ "user_id": 41
+}
+```
+**Group Member Removed:**
+
+```json
+{
+ "created_at": "2012-07-21T07:30:56Z",
+ "event_name": "user_remove_from_group",
+ "group_access": "Master",
+ "group_id": 78,
+ "group_name": "StoreCloud",
+ "group_path": "storecloud",
+ "user_email": "johnsmith@gmail.com",
+ "user_name": "John Smith",
+ "user_id": 41
+}
+```
diff --git a/doc/update/6.x-or-7.x-to-7.7.md b/doc/update/6.x-or-7.x-to-7.7.md
index 6501a8d2148..0cbea5d5997 100644
--- a/doc/update/6.x-or-7.x-to-7.7.md
+++ b/doc/update/6.x-or-7.x-to-7.7.md
@@ -89,6 +89,9 @@ sudo apt-get install logrotate
# Install pkg-config and cmake, which is needed for the latest versions of rugged
sudo apt-get install pkg-config cmake
+
+# Install Kerberos header files, which are needed for GitLab EE Kerberos support
+sudo apt-get install libkrb5-dev
```
## 5. Configure Redis to use sockets
diff --git a/doc/workflow/README.md b/doc/workflow/README.md
index 1fe63274c29..33176aaba44 100644
--- a/doc/workflow/README.md
+++ b/doc/workflow/README.md
@@ -1,6 +1,6 @@
# Workflow
-- [Workflow](workflow.md)
+- [Feature branch workflow](workflow.md)
- [Project Features](project_features.md)
- [Authorization for merge requests](authorization_for_merge_requests.md)
- [Groups](groups.md)
diff --git a/doc/workflow/workflow.md b/doc/workflow/workflow.md
index ab29cfb670b..f70e41df842 100644
--- a/doc/workflow/workflow.md
+++ b/doc/workflow/workflow.md
@@ -1,4 +1,4 @@
-# Workflow
+# Feature branch workflow
1. Clone project:
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 445fdd6d063..ec0923bd4c7 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.6.2-omnibus.5.3.0.ci.1-1_amd64.deb \
+ wget -q -O $TMP_FILE https://downloads-packages.s3.amazonaws.com/ubuntu-14.04/gitlab_7.7.2-omnibus.5.4.2.ci-1_amd64.deb \
&& dpkg -i $TMP_FILE \
&& rm -f $TMP_FILE
diff --git a/features/project/edit_issuetracker.feature b/features/project/edit_issuetracker.feature
deleted file mode 100644
index cc0de07ca69..00000000000
--- a/features/project/edit_issuetracker.feature
+++ /dev/null
@@ -1,18 +0,0 @@
-Feature: Project Issue Tracker
- Background:
- Given I sign in as a user
- And I own project "Shop"
- And project "Shop" has issues enabled
- And I visit project "Shop" page
-
- Scenario: I set the issue tracker to "GitLab"
- When I visit edit project "Shop" page
- And change the issue tracker to "GitLab"
- And I save project
- Then I the project should have "GitLab" as issue tracker
-
- Scenario: I set the issue tracker to "Redmine"
- When I visit edit project "Shop" page
- And change the issue tracker to "Redmine"
- And I save project
- Then I the project should have "Redmine" as issue tracker
diff --git a/features/project/project.feature b/features/project/project.feature
index 7bb24e013a9..3e1fd54bee8 100644
--- a/features/project/project.feature
+++ b/features/project/project.feature
@@ -5,6 +5,19 @@ Feature: Project
And project "Shop" has push event
And I visit project "Shop" page
+ Scenario: I edit the project avatar
+ Given I visit edit project "Shop" page
+ When I change the project avatar
+ And I should see new project avatar
+ And I should see the "Remove avatar" button
+
+ Scenario: I remove the project avatar
+ Given I visit edit project "Shop" page
+ And I have an project avatar
+ When I remove my project avatar
+ Then I should see the default project avatar
+ And I should not see the "Remove avatar" button
+
@javascript
Scenario: I should see project activity
When I visit project "Shop" page
diff --git a/features/project/source/browse_files.feature b/features/project/source/browse_files.feature
index 6ea64f70092..ee8d0bffa9b 100644
--- a/features/project/source/browse_files.feature
+++ b/features/project/source/browse_files.feature
@@ -34,6 +34,19 @@ Feature: Project Source Browse Files
Then I am redirected to the new file
And I should see its new content
+ @javascript @tricky
+ Scenario: I can create file in empty repo
+ Given I own an empty project
+ And I visit my empty project page
+ And I create bare repo
+ When I click on "add a file" link
+ And I edit code
+ And I fill the new file name
+ And I fill the commit message
+ And I click on "Commit Changes"
+ Then I am redirected to the new file
+ And I should see its new content
+
@javascript
Scenario: If I enter an illegal file name I see an error message
Given I click on "new file" link in repo
diff --git a/features/steps/project/issue_tracker.rb b/features/steps/project/issue_tracker.rb
deleted file mode 100644
index e1700292701..00000000000
--- a/features/steps/project/issue_tracker.rb
+++ /dev/null
@@ -1,31 +0,0 @@
-class Spinach::Features::ProjectIssueTracker < Spinach::FeatureSteps
- include SharedAuthentication
- include SharedProject
- include SharedPaths
-
- step 'project "Shop" has issues enabled' do
- @project = Project.find_by(name: "Shop")
- @project ||= create(:project, name: "Shop", namespace: @user.namespace)
- @project.issues_enabled = true
- end
-
- step 'change the issue tracker to "GitLab"' do
- select 'GitLab', from: 'project_issues_tracker'
- end
-
- step 'I the project should have "GitLab" as issue tracker' do
- find_field('project_issues_tracker').value.should == 'gitlab'
- end
-
- step 'change the issue tracker to "Redmine"' do
- select 'Redmine', from: 'project_issues_tracker'
- end
-
- step 'I the project should have "Redmine" as issue tracker' do
- find_field('project_issues_tracker').value.should == 'redmine'
- end
-
- step 'I save project' do
- click_button 'Save changes'
- end
-end
diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb
index 071ef75dc62..6f421de1aba 100644
--- a/features/steps/project/merge_requests.rb
+++ b/features/steps/project/merge_requests.rb
@@ -173,7 +173,9 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
merge!: true,
)
- click_button "Accept Merge Request"
+ within '.can_be_merged' do
+ click_button "Accept Merge Request"
+ end
end
step 'I should see merged request' do
diff --git a/features/steps/project/project.rb b/features/steps/project/project.rb
index 5e7312d90ff..033d45e0253 100644
--- a/features/steps/project/project.rb
+++ b/features/steps/project/project.rb
@@ -17,17 +17,58 @@ class Spinach::Features::Project < Spinach::FeatureSteps
end
step 'change project path settings' do
- fill_in "project_path", with: "new-path"
- click_button "Rename"
+ fill_in 'project_path', with: 'new-path'
+ click_button 'Rename'
end
step 'I should see project with new path settings' do
- project.path.should == "new-path"
+ project.path.should == 'new-path'
+ end
+
+ step 'I change the project avatar' do
+ attach_file(
+ :project_avatar,
+ File.join(Rails.root, 'public', 'gitlab_logo.png')
+ )
+ click_button 'Save changes'
+ @project.reload
+ end
+
+ step 'I should see new project avatar' do
+ @project.avatar.should be_instance_of AttachmentUploader
+ url = @project.avatar.url
+ url.should == "/uploads/project/avatar/#{ @project.id }/gitlab_logo.png"
+ end
+
+ step 'I should see the "Remove avatar" button' do
+ page.should have_link('Remove avatar')
+ end
+
+ step 'I have an project avatar' do
+ attach_file(
+ :project_avatar,
+ File.join(Rails.root, 'public', 'gitlab_logo.png')
+ )
+ click_button 'Save changes'
+ @project.reload
+ end
+
+ step 'I remove my project avatar' do
+ click_link 'Remove avatar'
+ @project.reload
+ end
+
+ step 'I should see the default project avatar' do
+ @project.avatar?.should be_false
+ end
+
+ step 'I should not see the "Remove avatar" button' do
+ page.should_not have_link('Remove avatar')
end
step 'I should see project "Shop" version' do
within '.project-side' do
- page.should have_content "Version: 6.7.0.pre"
+ page.should have_content 'Version: 6.7.0.pre'
end
end
@@ -45,12 +86,12 @@ class Spinach::Features::Project < Spinach::FeatureSteps
end
step 'I should see project "Forum" README' do
- page.should have_link "README.md"
- page.should have_content "Sample repo for testing gitlab features"
+ page.should have_link 'README.md'
+ page.should have_content 'Sample repo for testing gitlab features'
end
step 'I should see project "Shop" README' do
- page.should have_link "README.md"
- page.should have_content "testme"
+ page.should have_link 'README.md'
+ page.should have_content 'testme'
end
end
diff --git a/features/steps/project/source/browse_files.rb b/features/steps/project/source/browse_files.rb
index 805e6ff0eac..770e8162497 100644
--- a/features/steps/project/source/browse_files.rb
+++ b/features/steps/project/source/browse_files.rb
@@ -58,7 +58,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
step 'I can edit code' do
set_new_content
- evaluate_script('editor.getValue()').should == new_gitignore_content
+ evaluate_script('blob.editor.getValue()').should == new_gitignore_content
end
step 'I edit code' do
@@ -78,7 +78,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
end
step 'I click link "Diff"' do
- click_link 'Diff'
+ click_link 'Preview changes'
end
step 'I click on "Commit Changes"' do
@@ -103,7 +103,6 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
step 'I can see new file page' do
page.should have_content "New file"
- page.should have_content "File name"
page.should have_content "Commit message"
end
@@ -167,10 +166,21 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
expect(page).to have_content('Your changes could not be committed')
end
+ step 'I create bare repo' do
+ click_link 'Create empty bare repository'
+ end
+
+ step 'I click on "add a file" link' do
+ click_link 'add a file'
+
+ # Remove pre-receive hook so we can push without auth
+ FileUtils.rm(File.join(Project.last.repository.path, 'hooks', 'pre-receive'))
+ end
+
private
def set_new_content
- execute_script("editor.setValue('#{new_gitignore_content}')")
+ execute_script("blob.editor.setValue('#{new_gitignore_content}')")
end
# Content of the gitignore file on the seed repository.
diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb
index 33ef6ccacf1..cef48c179b2 100644
--- a/features/steps/shared/paths.rb
+++ b/features/steps/shared/paths.rb
@@ -284,11 +284,11 @@ module SharedPaths
end
step 'I am on the new file page' do
- current_path.should eq(project_new_tree_path(@project, root_ref))
+ current_path.should eq(project_create_blob_path(@project, root_ref))
end
step 'I am on the ".gitignore" edit file page' do
- current_path.should eq(project_edit_tree_path(
+ current_path.should eq(project_edit_blob_path(
@project, File.join(root_ref, '.gitignore')))
end
diff --git a/features/steps/shared/project.rb b/features/steps/shared/project.rb
index 0bd5653538c..cf0be256231 100644
--- a/features/steps/shared/project.rb
+++ b/features/steps/shared/project.rb
@@ -28,6 +28,10 @@ module SharedProject
@project.team << [@user, :master]
end
+ step 'I visit my empty project page' do
+ visit project_path(Project.find_by(name: 'Empty Project'))
+ end
+
step 'project "Shop" has push event' do
@project = Project.find_by(name: "Shop")
diff --git a/lib/api/milestones.rb b/lib/api/milestones.rb
index 2ea49359df0..c5cd73943fb 100644
--- a/lib/api/milestones.rb
+++ b/lib/api/milestones.rb
@@ -75,6 +75,21 @@ module API
render_api_error!("Failed to update milestone #{milestone.errors.messages}", 400)
end
end
+
+ # Get all issues for a single project milestone
+ #
+ # Parameters:
+ # id (required) - The ID of a project
+ # milestone_id (required) - The ID of a project milestone
+ # Example Request:
+ # GET /projects/:id/milestones/:milestone_id/issues
+ get ":id/milestones/:milestone_id/issues" do
+ authorize! :read_milestone, user_project
+
+ @milestone = user_project.milestones.find(params[:milestone_id])
+ present paginate(@milestone.issues), with: Entities::Issue
+ end
+
end
end
end
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 5b0c31f1898..d96288bb982 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -200,6 +200,49 @@ module API
end
end
+ # Update an existing project
+ #
+ # Parameters:
+ # id (required) - the id of a project
+ # name (optional) - name of a project
+ # path (optional) - path of a project
+ # description (optional) - short project description
+ # issues_enabled (optional)
+ # merge_requests_enabled (optional)
+ # wiki_enabled (optional)
+ # snippets_enabled (optional)
+ # public (optional) - if true same as setting visibility_level = 20
+ # visibility_level (optional) - visibility level of a project
+ # Example Request
+ # PUT /projects/:id
+ put ':id' do
+ attrs = attributes_for_keys [:name,
+ :path,
+ :description,
+ :default_branch,
+ :issues_enabled,
+ :merge_requests_enabled,
+ :wiki_enabled,
+ :snippets_enabled,
+ :public,
+ :visibility_level]
+ attrs = map_public_to_visibility_level(attrs)
+ authorize_admin_project
+ authorize! :rename_project, user_project if attrs[:name].present?
+ if attrs[:visibility_level].present?
+ authorize! :change_visibility_level, user_project
+ end
+
+ ::Projects::UpdateService.new(user_project,
+ current_user, attrs).execute
+
+ if user_project.valid?
+ present user_project, with: Entities::Project
+ else
+ render_validation_error!(user_project)
+ end
+ end
+
# Remove project
#
# Parameters:
diff --git a/lib/api/repositories.rb b/lib/api/repositories.rb
index 03a556a2c55..b259914a01c 100644
--- a/lib/api/repositories.rb
+++ b/lib/api/repositories.rb
@@ -58,11 +58,13 @@ module API
# ref_name (optional) - The name of a repository branch or tag, if not given the default branch is used
# Example Request:
# GET /projects/:id/repository/tree
- get ":id/repository/tree" do
+ get ':id/repository/tree' do
ref = params[:ref_name] || user_project.try(:default_branch) || 'master'
path = params[:path] || nil
commit = user_project.repository.commit(ref)
+ not_found!('Tree') unless commit
+
tree = user_project.repository.tree(commit.id, path)
present tree.sorted_entries, with: Entities::RepoTreeObject
@@ -100,14 +102,18 @@ module API
# sha (required) - The blob's sha
# Example Request:
# GET /projects/:id/repository/raw_blobs/:sha
- get ":id/repository/raw_blobs/:sha" do
+ get ':id/repository/raw_blobs/:sha' do
ref = params[:sha]
repo = user_project.repository
- blob = Gitlab::Git::Blob.raw(repo, ref)
+ begin
+ blob = Gitlab::Git::Blob.raw(repo, ref)
+ rescue
+ not_found! 'Blob'
+ end
- not_found! "Blob" unless blob
+ not_found! 'Blob' unless blob
env['api.format'] = :txt
@@ -122,13 +128,23 @@ module API
# sha (optional) - the commit sha to download defaults to the tip of the default branch
# Example Request:
# GET /projects/:id/repository/archive
- get ":id/repository/archive", requirements: { format: Gitlab::Regex.archive_formats_regex } do
+ get ':id/repository/archive',
+ requirements: { format: Gitlab::Regex.archive_formats_regex } do
authorize! :download_code, user_project
- file_path = ArchiveRepositoryService.new.execute(user_project, params[:sha], params[:format])
+
+ begin
+ file_path = ArchiveRepositoryService.new.execute(
+ user_project,
+ params[:sha],
+ params[:format])
+ rescue
+ not_found!('File')
+ end
if file_path && File.exists?(file_path)
data = File.open(file_path, 'rb').read
- header["Content-Disposition"] = "attachment; filename=\"#{File.basename(file_path)}\""
+ basename = File.basename(file_path)
+ header['Content-Disposition'] = "attachment; filename=\"#{basename}\""
content_type MIME::Types.type_for(file_path).first.content_type
env['api.format'] = :binary
present data
@@ -161,7 +177,12 @@ module API
get ':id/repository/contributors' do
authorize! :download_code, user_project
- present user_project.repository.contributors, with: Entities::Contributor
+ begin
+ present user_project.repository.contributors,
+ with: Entities::Contributor
+ rescue
+ not_found!
+ end
end
end
end
diff --git a/lib/extracts_path.rb b/lib/extracts_path.rb
index e51cb30bdd9..19215cfb7e6 100644
--- a/lib/extracts_path.rb
+++ b/lib/extracts_path.rb
@@ -1,17 +1,9 @@
# Module providing methods for dealing with separating a tree-ish string and a
# file path string when combined in a request parameter
module ExtractsPath
- extend ActiveSupport::Concern
-
# Raised when given an invalid file path
class InvalidPathError < StandardError; end
- included do
- if respond_to?(:before_filter)
- before_filter :assign_ref_vars
- end
- end
-
# Given a string containing both a Git tree-ish, such as a branch or tag, and
# a filesystem path joined by forward slashes, attempts to separate the two.
#
diff --git a/lib/gitlab/backend/grack_auth.rb b/lib/gitlab/backend/grack_auth.rb
index 1f71906bc8e..2e393f753e8 100644
--- a/lib/gitlab/backend/grack_auth.rb
+++ b/lib/gitlab/backend/grack_auth.rb
@@ -34,7 +34,7 @@ module Grack
def auth!
if @auth.provided?
return bad_request unless @auth.basic?
-
+
# Authentication with username and password
login, password = @auth.credentials
@@ -71,8 +71,20 @@ module Grack
false
end
+ def oauth_access_token_check(login, password)
+ if login == "oauth2" && git_cmd == 'git-upload-pack' && password.present?
+ token = Doorkeeper::AccessToken.by_token(password)
+ token && token.accessible? && User.find_by(id: token.resource_owner_id)
+ end
+ end
+
def authenticate_user(login, password)
user = Gitlab::Auth.new.find(login, password)
+
+ unless user
+ user = oauth_access_token_check(login, password)
+ end
+
return user if user.present?
# At this point, we know the credentials were wrong. We let Rack::Attack
diff --git a/lib/gitlab/closing_issue_extractor.rb b/lib/gitlab/closing_issue_extractor.rb
index 401e6e047b1..a9fd59f03d9 100644
--- a/lib/gitlab/closing_issue_extractor.rb
+++ b/lib/gitlab/closing_issue_extractor.rb
@@ -3,14 +3,19 @@ module Gitlab
ISSUE_CLOSING_REGEX = Regexp.new(Gitlab.config.gitlab.issue_closing_pattern)
def self.closed_by_message_in_project(message, project)
- md = ISSUE_CLOSING_REGEX.match(message)
- if md
- extractor = Gitlab::ReferenceExtractor.new
- extractor.analyze(md[0], project)
- extractor.issues_for(project)
- else
- []
+ issues = []
+
+ unless message.nil?
+ md = message.scan(ISSUE_CLOSING_REGEX)
+
+ md.each do |ref|
+ extractor = Gitlab::ReferenceExtractor.new
+ extractor.analyze(ref[0], project)
+ issues += extractor.issues_for(project)
+ end
end
+
+ issues.uniq
end
end
end
diff --git a/lib/gitlab/commits_calendar.rb b/lib/gitlab/commits_calendar.rb
new file mode 100644
index 00000000000..2f30d238e6b
--- /dev/null
+++ b/lib/gitlab/commits_calendar.rb
@@ -0,0 +1,33 @@
+module Gitlab
+ class CommitsCalendar
+ attr_reader :timestamps
+
+ def initialize(projects, user)
+ @timestamps = {}
+ date_timestamps = []
+
+ projects.reject(&:forked?).each do |project|
+ date_timestamps << ProjectContributions.new(project, user).commits_log
+ end
+
+ # Sumarrize commits from all projects per days
+ date_timestamps = date_timestamps.inject do |collection, date|
+ collection.merge(date) { |k, old_v, new_v| old_v + new_v }
+ end
+
+ date_timestamps ||= []
+ date_timestamps.each do |date, commits|
+ timestamp = Date.parse(date).to_time.to_i.to_s rescue nil
+ @timestamps[timestamp] = commits if timestamp
+ end
+ end
+
+ def starting_year
+ (Time.now - 1.year).strftime("%Y")
+ end
+
+ def starting_month
+ Date.today.strftime("%m").to_i
+ end
+ end
+end
diff --git a/lib/gitlab/force_push_check.rb b/lib/gitlab/force_push_check.rb
index 6a52cdba608..6ba2c3ad00a 100644
--- a/lib/gitlab/force_push_check.rb
+++ b/lib/gitlab/force_push_check.rb
@@ -4,7 +4,7 @@ module Gitlab
return false if project.empty_repo?
if oldrev != Gitlab::Git::BLANK_SHA && newrev != Gitlab::Git::BLANK_SHA
- missed_refs = IO.popen(%W(git --git-dir=#{project.repository.path_to_repo} rev-list #{oldrev} ^#{newrev})).read
+ missed_refs, _ = Gitlab::Popen.popen(%W(git --git-dir=#{project.repository.path_to_repo} rev-list #{oldrev} ^#{newrev}))
missed_refs.split("\n").size > 0
else
false
diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb
index c7bf2efc628..ea96d04c5ab 100644
--- a/lib/gitlab/git_access.rb
+++ b/lib/gitlab/git_access.rb
@@ -73,7 +73,7 @@ module Gitlab
changes = changes.lines if changes.kind_of?(String)
# Iterate over all changes to find if user allowed all of them to be applied
- changes.each do |change|
+ changes.map(&:strip).reject(&:blank?).each do |change|
status = change_access_check(user, project, change)
unless status.allowed?
# If user does not have access to make at least one change - cancel all push
diff --git a/lib/gitlab/ldap/user.rb b/lib/gitlab/ldap/user.rb
index 3ef494ba137..cfa8692659d 100644
--- a/lib/gitlab/ldap/user.rb
+++ b/lib/gitlab/ldap/user.rb
@@ -40,12 +40,16 @@ module Gitlab
def update_user_attributes
gl_user.email = auth_hash.email
- gl_user.identities.build(provider: auth_hash.provider, extern_uid: auth_hash.uid)
+
+ # Build new identity only if we dont have have same one
+ gl_user.identities.find_or_initialize_by(provider: auth_hash.provider,
+ extern_uid: auth_hash.uid)
+
gl_user
end
def changed?
- gl_user.changed?
+ gl_user.changed? || gl_user.identities.any?(&:changed?)
end
def needs_blocking?
diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb
index 068c342398b..c0e83fb3078 100644
--- a/lib/gitlab/markdown.rb
+++ b/lib/gitlab/markdown.rb
@@ -208,7 +208,7 @@ module Gitlab
end
def reference_issue(identifier, project = @project, prefix_text = nil)
- if project.used_default_issues_tracker? || !external_issues_tracker_enabled?
+ if project.default_issues_tracker?
if project.issue_exists? identifier
url = url_for_issue(identifier, project)
title = title_for_issue(identifier, project)
@@ -220,10 +220,8 @@ module Gitlab
link_to("#{prefix_text}##{identifier}", url, options)
end
else
- config = Gitlab.config
- external_issue_tracker = config.issues_tracker[project.issues_tracker]
- if external_issue_tracker.present?
- reference_external_issue(identifier, external_issue_tracker, project,
+ if project.external_issue_tracker.present?
+ reference_external_issue(identifier, project,
prefix_text)
end
end
@@ -267,10 +265,10 @@ module Gitlab
end
end
- def reference_external_issue(identifier, issue_tracker, project = @project,
+ def reference_external_issue(identifier, project = @project,
prefix_text = nil)
url = url_for_issue(identifier, project)
- title = issue_tracker['title']
+ title = project.external_issue_tracker.title
options = html_options.merge(
title: "Issue in #{title}",
diff --git a/lib/gitlab/satellite/files/new_file_action.rb b/lib/gitlab/satellite/files/new_file_action.rb
index 15e9b7a6f77..5b657c7aba2 100644
--- a/lib/gitlab/satellite/files/new_file_action.rb
+++ b/lib/gitlab/satellite/files/new_file_action.rb
@@ -14,7 +14,14 @@ module Gitlab
prepare_satellite!(repo)
# create target branch in satellite at the corresponding commit from bare repo
- repo.git.checkout({raise: true, timeout: true, b: true}, ref, "origin/#{ref}")
+ current_ref =
+ if @project.empty_repo?
+ # skip this step if we want to add first file to empty repo
+ Satellite::PARKING_BRANCH
+ else
+ repo.git.checkout({raise: true, timeout: true, b: true}, ref, "origin/#{ref}")
+ ref
+ end
file_path_in_satellite = File.join(repo.working_dir, file_path)
dir_name_in_satellite = File.dirname(file_path_in_satellite)
@@ -38,10 +45,9 @@ module Gitlab
# will raise CommandFailed when commit fails
repo.git.commit(raise: true, timeout: true, a: true, m: commit_message)
-
# push commit back to bare repo
# will raise CommandFailed when push fails
- repo.git.push({raise: true, timeout: true}, :origin, ref)
+ repo.git.push({raise: true, timeout: true}, :origin, "#{current_ref}:#{ref}")
# everything worked
true
diff --git a/lib/repository_cache.rb b/lib/repository_cache.rb
new file mode 100644
index 00000000000..fa016a170cd
--- /dev/null
+++ b/lib/repository_cache.rb
@@ -0,0 +1,21 @@
+# Interface to the Redis-backed cache store used by the Repository model
+class RepositoryCache
+ attr_reader :namespace, :backend
+
+ def initialize(namespace, backend = Rails.cache)
+ @namespace = namespace
+ @backend = backend
+ end
+
+ def cache_key(type)
+ "#{type}:#{namespace}"
+ end
+
+ def expire(key)
+ backend.delete(cache_key(key))
+ end
+
+ def fetch(key, &block)
+ backend.fetch(cache_key(key), &block)
+ end
+end
diff --git a/lib/tasks/spinach.rake b/lib/tasks/spinach.rake
index 507b315759d..4aefc18ce14 100644
--- a/lib/tasks/spinach.rake
+++ b/lib/tasks/spinach.rake
@@ -2,9 +2,15 @@ Rake::Task["spinach"].clear if Rake::Task.task_defined?('spinach')
desc "GITLAB | Run spinach"
task :spinach do
+ tags = if ENV['SEMAPHORE']
+ '~@tricky'
+ else
+ '~@semaphore'
+ end
+
cmds = [
%W(rake gitlab:setup),
- %W(spinach),
+ %W(spinach --tags #{tags}),
]
run_commands(cmds)
end
diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb
new file mode 100644
index 00000000000..44225c054f2
--- /dev/null
+++ b/spec/controllers/users_controller_spec.rb
@@ -0,0 +1,27 @@
+require 'spec_helper'
+
+describe UsersController do
+ let(:user) { create(:user, username: "user1", name: "User 1", email: "user1@gitlab.com") }
+
+ before do
+ sign_in(user)
+ end
+
+ describe "GET #show" do
+ render_views
+
+ it "renders the show template" do
+ get :show, username: user.username
+ expect(response.status).to eq(200)
+ expect(response).to render_template("show")
+ end
+ end
+
+ describe "GET #calendar" do
+ it "renders calendar" do
+ get :calendar, username: user.username
+ expect(response).to render_template("calendar")
+ end
+ end
+end
+
diff --git a/spec/factories/merge_requests.rb b/spec/factories/merge_requests.rb
index 0ae8ea5f878..6ce1d7446fe 100644
--- a/spec/factories/merge_requests.rb
+++ b/spec/factories/merge_requests.rb
@@ -18,6 +18,7 @@
# iid :integer
# description :text
# position :integer default(0)
+# locked_at :datetime
#
FactoryGirl.define do
diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb
index 60eb73e4a95..0899a7603fc 100644
--- a/spec/factories/projects.rb
+++ b/spec/factories/projects.rb
@@ -24,6 +24,9 @@
# import_status :string(255)
# repository_size :float default(0.0)
# star_count :integer default(0), not null
+# import_type :string(255)
+# import_source :string(255)
+# avatar :string(255)
#
FactoryGirl.define do
@@ -74,7 +77,19 @@ FactoryGirl.define do
end
factory :redmine_project, parent: :project do
- issues_tracker { "redmine" }
- issues_tracker_id { "project_name_in_redmine" }
+ after :create do |project|
+ project.create_redmine_service(
+ active: true,
+ properties: {
+ 'project_url' => 'http://redmine/projects/project_name_in_redmine',
+ 'issues_url' => "http://redmine/#{project.id}/project_name_in_redmine/:id",
+ 'new_issue_url' => 'http://redmine/projects/project_name_in_redmine/issues/new'
+ }
+ )
+ end
+ after :create do |project|
+ project.issues_tracker = 'redmine'
+ project.issues_tracker_id = 'project_name_in_redmine'
+ end
end
end
diff --git a/spec/features/atom/dashboard_spec.rb b/spec/features/atom/dashboard_spec.rb
index a7f87906b2d..52ade3e2d31 100644
--- a/spec/features/atom/dashboard_spec.rb
+++ b/spec/features/atom/dashboard_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe "Dashboard Feed", feature: true do
describe "GET /" do
- let!(:user) { create(:user) }
+ let!(:user) { create(:user, name: "Jonh") }
context "projects atom feed via private token" do
it "should render projects atom feed" do
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index 9cdbc846b19..a46883b3c99 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -6,15 +6,15 @@ describe ApplicationHelper do
controller.stub(:controller_name).and_return('foo')
end
- it "returns true when controller matches argument" do
+ it 'returns true when controller matches argument' do
current_controller?(:foo).should be_true
end
- it "returns false when controller does not match argument" do
+ it 'returns false when controller does not match argument' do
current_controller?(:bar).should_not be_true
end
- it "should take any number of arguments" do
+ it 'should take any number of arguments' do
current_controller?(:baz, :bar).should_not be_true
current_controller?(:baz, :bar, :foo).should be_true
end
@@ -25,109 +25,131 @@ describe ApplicationHelper do
allow(self).to receive(:action_name).and_return('foo')
end
- it "returns true when action matches argument" do
+ it 'returns true when action matches argument' do
current_action?(:foo).should be_true
end
- it "returns false when action does not match argument" do
+ it 'returns false when action does not match argument' do
current_action?(:bar).should_not be_true
end
- it "should take any number of arguments" do
+ it 'should take any number of arguments' do
current_action?(:baz, :bar).should_not be_true
current_action?(:baz, :bar, :foo).should be_true
end
end
- describe "group_icon" 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
+ it 'should return an url for the avatar' do
group = create(:group)
group.avatar = File.open(avatar_file_path)
group.save!
group_icon(group.path).to_s.should match("/uploads/group/avatar/#{ group.id }/gitlab_logo.png")
end
- it "should give default avatar_icon when no avatar is present" do
+ it 'should give default avatar_icon when no avatar is present' do
group = create(:group)
group.save!
- group_icon(group.path).should match("group_avatar.png")
+ group_icon(group.path).should match('group_avatar.png')
end
end
- describe "avatar_icon" do
+ describe 'project_icon' do
avatar_file_path = File.join(Rails.root, 'public', 'gitlab_logo.png')
- it "should return an url for the avatar" do
+ it 'should return an url for the avatar' do
+ project = create(:project)
+ project.avatar = File.open(avatar_file_path)
+ project.save!
+ project_icon(project.to_param).to_s.should ==
+ "<img alt=\"Gitlab logo\" src=\"/uploads/project/avatar/#{ project.id }/gitlab_logo.png\" />"
+ end
+
+ it 'should give uploaded icon when present' do
+ project = create(:project)
+ project.save!
+
+ Project.any_instance.stub(:avatar_in_git).and_return(true)
+
+ project_icon(project.to_param).to_s.should match(
+ image_tag(project_avatar_path(project)))
+ end
+ end
+
+ describe 'avatar_icon' do
+ avatar_file_path = File.join(Rails.root, 'public', 'gitlab_logo.png')
+
+ it 'should return an url for the avatar' do
user = create(:user)
user.avatar = File.open(avatar_file_path)
user.save!
avatar_icon(user.email).to_s.should match("/uploads/user/avatar/#{ user.id }/gitlab_logo.png")
end
- it "should return an url for the avatar with relative url" do
- Gitlab.config.gitlab.stub(relative_url_root: "/gitlab")
+ it 'should return an url for the avatar with relative url' do
+ Gitlab.config.gitlab.stub(relative_url_root: '/gitlab')
Gitlab.config.gitlab.stub(url: Settings.send(:build_gitlab_url))
user = create(:user)
user.avatar = File.open(avatar_file_path)
user.save!
- avatar_icon(user.email).to_s.should match("/gitlab//uploads/user/avatar/#{ user.id }/gitlab_logo.png")
+ avatar_icon(user.email).to_s.should match("/gitlab/uploads/user/avatar/#{ user.id }/gitlab_logo.png")
end
- it "should call gravatar_icon when no avatar is present" do
+ it 'should call gravatar_icon when no avatar is present' do
user = create(:user, email: 'test@example.com')
user.save!
- avatar_icon(user.email).to_s.should == "http://www.gravatar.com/avatar/55502f40dc8b7c769880b10874abc9d0?s=40&d=identicon"
+ avatar_icon(user.email).to_s.should == 'http://www.gravatar.com/avatar/55502f40dc8b7c769880b10874abc9d0?s=40&d=identicon'
end
end
- describe "gravatar_icon" do
+ describe 'gravatar_icon' do
let(:user_email) { 'user@email.com' }
- it "should return a generic avatar path when Gravatar is disabled" do
+ it 'should return a generic avatar path when Gravatar is disabled' do
ApplicationSetting.any_instance.stub(gravatar_enabled?: false)
gravatar_icon(user_email).should match('no_avatar.png')
end
- it "should return a generic avatar path when email is blank" do
+ it 'should return a generic avatar path when email is blank' do
gravatar_icon('').should match('no_avatar.png')
end
- it "should return default gravatar url" do
+ it 'should return default gravatar url' do
Gitlab.config.gitlab.stub(https: false)
gravatar_icon(user_email).should match('http://www.gravatar.com/avatar/b58c6f14d292556214bd64909bcdb118')
end
- it "should use SSL when appropriate" do
+ it 'should use SSL when appropriate' do
Gitlab.config.gitlab.stub(https: true)
gravatar_icon(user_email).should match('https://secure.gravatar.com')
end
- it "should return custom gravatar path when gravatar_url is set" do
+ it 'should return custom gravatar path when gravatar_url is set' do
allow(self).to receive(:request).and_return(double(:ssl? => false))
Gitlab.config.gravatar.stub(:plain_url).and_return('http://example.local/?s=%{size}&hash=%{hash}')
gravatar_icon(user_email, 20).should == 'http://example.local/?s=20&hash=b58c6f14d292556214bd64909bcdb118'
end
- it "should accept a custom size" do
+ it 'should accept a custom size' do
allow(self).to receive(:request).and_return(double(:ssl? => false))
gravatar_icon(user_email, 64).should match(/\?s=64/)
end
- it "should use default size when size is wrong" do
+ it 'should use default size when size is wrong' do
allow(self).to receive(:request).and_return(double(:ssl? => false))
gravatar_icon(user_email, nil).should match(/\?s=40/)
end
- it "should be case insensitive" do
+ it 'should be case insensitive' do
allow(self).to receive(:request).and_return(double(:ssl? => false))
- gravatar_icon(user_email).should == gravatar_icon(user_email.upcase + " ")
+ gravatar_icon(user_email).should == gravatar_icon(user_email.upcase + ' ')
end
end
- describe "grouped_options_refs" do
+ describe 'grouped_options_refs' do
# Override Rails' grouped_options_for_select helper since HTML is harder to work with
def grouped_options_for_select(options, *args)
options
@@ -140,17 +162,17 @@ describe ApplicationHelper do
@project = create(:project)
end
- it "includes a list of branch names" do
+ it 'includes a list of branch names' do
options[0][0].should == 'Branches'
options[0][1].should include('master', 'feature')
end
- it "includes a list of tag names" do
+ it 'includes a list of tag names' do
options[1][0].should == 'Tags'
options[1][1].should include('v1.0.0','v1.1.0')
end
- it "includes a specific commit ref if defined" do
+ it 'includes a specific commit ref if defined' do
# Must be an instance variable
@ref = '2ed06dc41dbb5936af845b87d79e05bbf24c73b8'
@@ -158,26 +180,26 @@ describe ApplicationHelper do
options[2][1].should == [@ref]
end
- it "sorts tags in a natural order" do
+ it 'sorts tags in a natural order' do
# Stub repository.tag_names to make sure we get some valid testing data
- expect(@project.repository).to receive(:tag_names).and_return(["v1.0.9", "v1.0.10", "v2.0", "v3.1.4.2", "v1.0.9a"])
+ expect(@project.repository).to receive(:tag_names).and_return(['v1.0.9', 'v1.0.10', 'v2.0', 'v3.1.4.2', 'v1.0.9a'])
- options[1][1].should == ["v3.1.4.2", "v2.0", "v1.0.10", "v1.0.9a", "v1.0.9"]
+ options[1][1].should == ['v3.1.4.2', 'v2.0', 'v1.0.10', 'v1.0.9a', 'v1.0.9']
end
end
- describe "user_color_scheme_class" do
- context "with current_user is nil" do
- it "should return a string" do
+ describe 'user_color_scheme_class' do
+ context 'with current_user is nil' do
+ it 'should return a string' do
allow(self).to receive(:current_user).and_return(nil)
user_color_scheme_class.should be_kind_of(String)
end
end
- context "with a current_user" do
+ context 'with a current_user' do
(1..5).each do |color_scheme_id|
context "with color_scheme_id == #{color_scheme_id}" do
- it "should return a string" do
+ it 'should return a string' do
current_user = double(:color_scheme_id => color_scheme_id)
allow(self).to receive(:current_user).and_return(current_user)
user_color_scheme_class.should be_kind_of(String)
@@ -187,43 +209,43 @@ describe ApplicationHelper do
end
end
- describe "simple_sanitize" do
+ describe 'simple_sanitize' do
let(:a_tag) { '<a href="#">Foo</a>' }
- it "allows the a tag" do
+ it 'allows the a tag' do
simple_sanitize(a_tag).should == a_tag
end
- it "allows the span tag" do
+ it 'allows the span tag' do
input = '<span class="foo">Bar</span>'
simple_sanitize(input).should == input
end
- it "disallows other tags" do
+ it 'disallows other tags' do
input = "<strike><b>#{a_tag}</b></strike>"
simple_sanitize(input).should == a_tag
end
end
- describe "link_to" do
+ describe 'link_to' do
- it "should not include rel=nofollow for internal links" do
- expect(link_to("Home", root_path)).to eq("<a href=\"/\">Home</a>")
+ it 'should not include rel=nofollow for internal links' do
+ expect(link_to('Home', root_path)).to eq("<a href=\"/\">Home</a>")
end
- it "should include rel=nofollow for external links" do
- expect(link_to("Example", "http://www.example.com")).to eq("<a href=\"http://www.example.com\" rel=\"nofollow\">Example</a>")
+ it 'should include rel=nofollow for external links' do
+ expect(link_to('Example', 'http://www.example.com')).to eq("<a href=\"http://www.example.com\" rel=\"nofollow\">Example</a>")
end
- it "should include re=nofollow for external links and honor existing html_options" do
+ it 'should include re=nofollow for external links and honor existing html_options' do
expect(
- link_to("Example", "http://www.example.com", class: "toggle", data: {toggle: "dropdown"})
+ link_to('Example', 'http://www.example.com', class: 'toggle', data: {toggle: 'dropdown'})
).to eq("<a class=\"toggle\" data-toggle=\"dropdown\" href=\"http://www.example.com\" rel=\"nofollow\">Example</a>")
end
- it "should include rel=nofollow for external links and preserver other rel values" do
+ it 'should include rel=nofollow for external links and preserver other rel values' do
expect(
- link_to("Example", "http://www.example.com", rel: "noreferrer")
+ link_to('Example', 'http://www.example.com', rel: 'noreferrer')
).to eq("<a href=\"http://www.example.com\" rel=\"noreferrer nofollow\">Example</a>")
end
end
diff --git a/spec/helpers/gitlab_markdown_helper_spec.rb b/spec/helpers/gitlab_markdown_helper_spec.rb
index 86ba801ce07..d633287b2a9 100644
--- a/spec/helpers/gitlab_markdown_helper_spec.rb
+++ b/spec/helpers/gitlab_markdown_helper_spec.rb
@@ -23,6 +23,7 @@ describe GitlabMarkdownHelper do
@project = project
@ref = 'markdown'
@repository = project.repository
+ @request.host = Gitlab.config.gitlab.host
end
describe "#gfm" do
@@ -296,10 +297,13 @@ describe GitlabMarkdownHelper do
let(:reference) { "JIRA-#{issue.iid}" }
before do
- issue_tracker_config = { "jira" => { "title" => "JIRA tracker", "issues_url" => "http://jira.example/browse/:id" } }
- Gitlab.config.stub(:issues_tracker).and_return(issue_tracker_config)
- @project.stub(:issues_tracker).and_return("jira")
- @project.stub(:issues_tracker_id).and_return("JIRA")
+ jira = @project.create_jira_service if @project.jira_service.nil?
+ properties = {"title"=>"JIRA tracker", "project_url"=>"http://jira.example/issues/?jql=project=A", "issues_url"=>"http://jira.example/browse/:id", "new_issue_url"=>"http://jira.example/secure/CreateIssue.jspa"}
+ jira.update_attributes(properties: properties, active: true)
+ end
+
+ after do
+ @project.jira_service.destroy! unless @project.jira_service.nil?
end
it "should link using a valid id" do
diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb
index 9c95bc044f3..c82729a52e2 100644
--- a/spec/helpers/issues_helper_spec.rb
+++ b/spec/helpers/issues_helper_spec.rb
@@ -24,7 +24,7 @@ describe IssuesHelper do
end
describe :url_for_project_issues do
- let(:project_url) { Gitlab.config.issues_tracker.redmine.project_url}
+ let(:project_url) { ext_project.external_issue_tracker.project_url }
let(:ext_expected) do
project_url.gsub(':project_id', ext_project.id.to_s)
.gsub(':issues_tracker_id', ext_project.issues_tracker_id.to_s)
@@ -54,17 +54,16 @@ describe IssuesHelper do
Gitlab.config.stub(:issues_tracker).and_return(nil)
end
- it "should return path to internal tracker" do
- url_for_project_issues.should match(polymorphic_path([@project]))
+ it "should return path to external tracker" do
+ url_for_project_issues.should match(ext_expected)
end
end
end
describe :url_for_issue do
- let(:issue_id) { 3 }
- let(:issues_url) { Gitlab.config.issues_tracker.redmine.issues_url}
+ let(:issues_url) { ext_project.external_issue_tracker.issues_url}
let(:ext_expected) do
- issues_url.gsub(':id', issue_id.to_s)
+ issues_url.gsub(':id', issue.iid.to_s)
.gsub(':project_id', ext_project.id.to_s)
.gsub(':issues_tracker_id', ext_project.issues_tracker_id.to_s)
end
@@ -78,7 +77,7 @@ describe IssuesHelper do
it "should return path to external tracker" do
@project = ext_project
- url_for_issue(issue_id).should match(ext_expected)
+ url_for_issue(issue.iid).should match(ext_expected)
end
it "should return empty string if project nil" do
@@ -93,14 +92,14 @@ describe IssuesHelper do
Gitlab.config.stub(:issues_tracker).and_return(nil)
end
- it "should return internal path" do
- url_for_issue(issue.iid).should match(polymorphic_path([@project, issue]))
+ it "should return external path" do
+ url_for_issue(issue.iid).should match(ext_expected)
end
end
end
describe :url_for_new_issue do
- let(:issues_url) { Gitlab.config.issues_tracker.redmine.new_issue_url}
+ let(:issues_url) { ext_project.external_issue_tracker.new_issue_url }
let(:ext_expected) do
issues_url.gsub(':project_id', ext_project.id.to_s)
.gsub(':issues_tracker_id', ext_project.issues_tracker_id.to_s)
@@ -131,7 +130,7 @@ describe IssuesHelper do
end
it "should return internal path" do
- url_for_new_issue.should match(new_project_issue_path(@project))
+ url_for_new_issue.should match(ext_expected)
end
end
end
diff --git a/spec/helpers/notifications_helper_spec.rb b/spec/helpers/notifications_helper_spec.rb
index 31ecdacf28e..dcc3318e4f9 100644
--- a/spec/helpers/notifications_helper_spec.rb
+++ b/spec/helpers/notifications_helper_spec.rb
@@ -1,6 +1,9 @@
require 'spec_helper'
describe NotificationsHelper do
+ include FontAwesome::Rails::IconHelper
+ include IconsHelper
+
describe 'notification_icon' do
let(:notification) { double(disabled?: false, participating?: false, watch?: false) }
diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb
index 2146b0b1383..281d4862199 100644
--- a/spec/helpers/projects_helper_spec.rb
+++ b/spec/helpers/projects_helper_spec.rb
@@ -1,32 +1,11 @@
require 'spec_helper'
describe ProjectsHelper do
- describe '#project_issues_trackers' do
- it "returns the correct issues trackers available" do
- project_issues_trackers.should ==
- "<option value=\"redmine\">Redmine</option>\n" \
- "<option value=\"gitlab\">GitLab</option>"
- end
-
- it "returns the correct issues trackers available with current tracker 'gitlab' selected" do
- project_issues_trackers('gitlab').should ==
- "<option value=\"redmine\">Redmine</option>\n" \
- "<option selected=\"selected\" value=\"gitlab\">GitLab</option>"
- end
-
- it "returns the correct issues trackers available with current tracker 'redmine' selected" do
- project_issues_trackers('redmine').should ==
- "<option selected=\"selected\" value=\"redmine\">Redmine</option>\n" \
- "<option value=\"gitlab\">GitLab</option>"
- end
- end
-
describe "#project_status_css_class" do
it "returns appropriate class" do
project_status_css_class("started").should == "active"
project_status_css_class("failed").should == "danger"
project_status_css_class("finished").should == "success"
end
-
end
end
diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb
new file mode 100644
index 00000000000..867455daf23
--- /dev/null
+++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb
@@ -0,0 +1,84 @@
+require 'spec_helper'
+
+describe Gitlab::ClosingIssueExtractor do
+ let(:project) { create(:project) }
+ let(:issue) { create(:issue, project: project) }
+ let(:iid1) { issue.iid }
+
+ describe :closed_by_message_in_project do
+ context 'with a single reference' do
+ it do
+ message = "Awesome commit (Closes ##{iid1})"
+ subject.closed_by_message_in_project(message, project).should == [issue]
+ end
+
+ it do
+ message = "Awesome commit (closes ##{iid1})"
+ subject.closed_by_message_in_project(message, project).should == [issue]
+ end
+
+ it do
+ message = "Closed ##{iid1}"
+ subject.closed_by_message_in_project(message, project).should == [issue]
+ end
+
+ it do
+ message = "closed ##{iid1}"
+ subject.closed_by_message_in_project(message, project).should == [issue]
+ end
+
+ it do
+ message = "Awesome commit (fixes ##{iid1})"
+ subject.closed_by_message_in_project(message, project).should == [issue]
+ end
+
+ it do
+ message = "Awesome commit (fix ##{iid1})"
+ subject.closed_by_message_in_project(message, project).should == [issue]
+ end
+ end
+
+ context 'with multiple references' do
+ let(:other_issue) { create(:issue, project: project) }
+ let(:third_issue) { create(:issue, project: project) }
+ let(:iid2) { other_issue.iid }
+ let(:iid3) { third_issue.iid }
+
+ it 'fetches issues in single line message' do
+ message = "Closes ##{iid1} and fix ##{iid2}"
+
+ subject.closed_by_message_in_project(message, project).
+ should == [issue, other_issue]
+ end
+
+ it 'fetches comma-separated issues references in single line message' do
+ message = "Closes ##{iid1}, closes ##{iid2}"
+
+ subject.closed_by_message_in_project(message, project).
+ should == [issue, other_issue]
+ end
+
+ it 'fetches comma-separated issues numbers in single line message' do
+ message = "Closes ##{iid1}, ##{iid2} and ##{iid3}"
+
+ subject.closed_by_message_in_project(message, project).
+ should == [issue, other_issue, third_issue]
+ end
+
+ it 'fetches issues in multi-line message' do
+ message = "Awesome commit (closes ##{iid1})\nAlso fixes ##{iid2}"
+
+ subject.closed_by_message_in_project(message, project).
+ should == [issue, other_issue]
+ end
+
+ it 'fetches issues in hybrid message' do
+ message = "Awesome commit (closes ##{iid1})\n"\
+ "Also fixing issues ##{iid2}, ##{iid3} and #4"
+
+ subject.closed_by_message_in_project(message, project).
+ should == [issue, other_issue, third_issue]
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ldap/user_spec.rb b/spec/lib/gitlab/ldap/user_spec.rb
index f73884e6441..63ffc21ba3b 100644
--- a/spec/lib/gitlab/ldap/user_spec.rb
+++ b/spec/lib/gitlab/ldap/user_spec.rb
@@ -13,6 +13,23 @@ describe Gitlab::LDAP::User do
double(uid: 'my-uid', provider: 'ldapmain', info: double(info))
end
+ describe :changed? do
+ it "marks existing ldap user as changed" do
+ existing_user = create(:omniauth_user, extern_uid: 'my-uid', provider: 'ldapmain')
+ expect(gl_user.changed?).to be_true
+ end
+
+ it "marks existing non-ldap user if the email matches as changed" do
+ existing_user = create(:user, email: 'john@example.com')
+ expect(gl_user.changed?).to be_true
+ end
+
+ it "dont marks existing ldap user as changed" do
+ existing_user = create(:omniauth_user, email: 'john@example.com', extern_uid: 'my-uid', provider: 'ldapmain')
+ expect(gl_user.changed?).to be_false
+ end
+ end
+
describe :find_or_create do
it "finds the user if already existing" do
existing_user = create(:omniauth_user, extern_uid: 'my-uid', provider: 'ldapmain')
diff --git a/spec/lib/gitlab/reference_extractor_spec.rb b/spec/lib/gitlab/reference_extractor_spec.rb
index 23867df39dd..5f45df4e8c3 100644
--- a/spec/lib/gitlab/reference_extractor_spec.rb
+++ b/spec/lib/gitlab/reference_extractor_spec.rb
@@ -12,7 +12,6 @@ describe Gitlab::ReferenceExtractor do
end
it 'extracts JIRA issue references' do
- Gitlab.config.gitlab.stub(:issues_tracker).and_return('jira')
subject.analyze('this one talks about issue JIRA-1234', nil)
subject.issues.should == [{ project: nil, id: 'JIRA-1234' }]
end
diff --git a/spec/lib/repository_cache_spec.rb b/spec/lib/repository_cache_spec.rb
new file mode 100644
index 00000000000..af399f3a731
--- /dev/null
+++ b/spec/lib/repository_cache_spec.rb
@@ -0,0 +1,34 @@
+require 'rspec'
+require_relative '../../lib/repository_cache'
+
+describe RepositoryCache do
+ let(:backend) { double('backend').as_null_object }
+ let(:cache) { RepositoryCache.new('example', backend) }
+
+ describe '#cache_key' do
+ it 'includes the namespace' do
+ expect(cache.cache_key(:foo)).to eq 'foo:example'
+ end
+ end
+
+ describe '#expire' do
+ it 'expires the given key from the cache' do
+ cache.expire(:foo)
+ expect(backend).to have_received(:delete).with('foo:example')
+ end
+ end
+
+ describe '#fetch' do
+ it 'fetches the given key from the cache' do
+ cache.fetch(:bar)
+ expect(backend).to have_received(:fetch).with('bar:example')
+ end
+
+ it 'accepts a block' do
+ p = -> {}
+
+ cache.fetch(:baz, &p)
+ expect(backend).to have_received(:fetch).with('baz:example', &p)
+ end
+ end
+end
diff --git a/spec/lib/votes_spec.rb b/spec/lib/votes_spec.rb
index a3c353d5eab..a88a10d927f 100644
--- a/spec/lib/votes_spec.rb
+++ b/spec/lib/votes_spec.rb
@@ -20,11 +20,17 @@ describe Issue, 'Votes' do
issue.upvotes.should == 1
end
- it "should recognize multiple +1 notes" do
- add_note "+1 This is awesome"
- add_note "+1 I want this"
+ it 'should recognize multiple +1 notes' do
+ add_note '+1 This is awesome', create(:user)
+ add_note '+1 I want this', create(:user)
issue.upvotes.should == 2
end
+
+ it 'should not count 2 +1 votes from the same user' do
+ add_note '+1 This is awesome'
+ add_note '+1 I want this'
+ issue.upvotes.should == 1
+ end
end
describe "#downvotes" do
@@ -45,8 +51,8 @@ describe Issue, 'Votes' do
end
it "should recognize multiple -1 notes" do
- add_note "-1 This is bad"
- add_note "-1 Away with this"
+ add_note('-1 This is bad', create(:user))
+ add_note('-1 Away with this', create(:user))
issue.downvotes.should == 2
end
end
@@ -73,11 +79,17 @@ describe Issue, 'Votes' do
end
it "should recognize multiple notes" do
- add_note "+1 This is awesome"
- add_note "-1 This is bad"
- add_note "+1 I want this"
+ add_note('+1 This is awesome', create(:user))
+ add_note('-1 This is bad', create(:user))
+ add_note('+1 I want this', create(:user))
issue.votes_count.should == 3
end
+
+ it 'should not count 2 -1 votes from the same user' do
+ add_note '-1 This is suspicious'
+ add_note '-1 This is bad'
+ issue.votes_count.should == 1
+ end
end
describe "#upvotes_in_percent" do
@@ -90,17 +102,17 @@ describe Issue, 'Votes' do
issue.upvotes_in_percent.should == 100
end
- it "should count multiple +1 notes as 100%" do
- add_note "+1 This is awesome"
- add_note "+1 I want this"
+ it 'should count multiple +1 notes as 100%' do
+ add_note('+1 This is awesome', create(:user))
+ add_note('+1 I want this', create(:user))
issue.upvotes_in_percent.should == 100
end
- it "should count fractions for multiple +1 and -1 notes correctly" do
- add_note "+1 This is awesome"
- add_note "+1 I want this"
- add_note "-1 This is bad"
- add_note "+1 me too"
+ it 'should count fractions for multiple +1 and -1 notes correctly' do
+ add_note('+1 This is awesome', create(:user))
+ add_note('+1 I want this', create(:user))
+ add_note('-1 This is bad', create(:user))
+ add_note('+1 me too', create(:user))
issue.upvotes_in_percent.should == 75
end
end
@@ -115,22 +127,59 @@ describe Issue, 'Votes' do
issue.downvotes_in_percent.should == 100
end
- it "should count multiple -1 notes as 100%" do
- add_note "-1 This is bad"
- add_note "-1 Away with this"
+ it 'should count multiple -1 notes as 100%' do
+ add_note('-1 This is bad', create(:user))
+ add_note('-1 Away with this', create(:user))
issue.downvotes_in_percent.should == 100
end
- it "should count fractions for multiple +1 and -1 notes correctly" do
- add_note "+1 This is awesome"
- add_note "+1 I want this"
- add_note "-1 This is bad"
- add_note "+1 me too"
+ it 'should count fractions for multiple +1 and -1 notes correctly' do
+ add_note('+1 This is awesome', create(:user))
+ add_note('+1 I want this', create(:user))
+ add_note('-1 This is bad', create(:user))
+ add_note('+1 me too', create(:user))
issue.downvotes_in_percent.should == 25
end
end
- def add_note(text)
- issue.notes << create(:note, note: text, project: issue.project)
+ describe '#filter_superceded_votes' do
+
+ it 'should count a users vote only once amongst multiple votes' do
+ add_note('-1 This needs work before I will accept it')
+ add_note('+1 I want this', create(:user))
+ add_note('+1 This is is awesome', create(:user))
+ add_note('+1 this looks good now')
+ add_note('+1 This is awesome', create(:user))
+ add_note('+1 me too', create(:user))
+ issue.downvotes.should == 0
+ issue.upvotes.should == 5
+ end
+
+ it 'should count each users vote only once' do
+ add_note '-1 This needs work before it will be accepted'
+ add_note '+1 I like this'
+ add_note '+1 I still like this'
+ add_note '+1 I really like this'
+ add_note '+1 Give me this now!!!!'
+ issue.downvotes.should == 0
+ issue.upvotes.should == 1
+ end
+
+ it 'should count a users vote only once without caring about comments' do
+ add_note '-1 This needs work before it will be accepted'
+ add_note 'Comment 1'
+ add_note 'Another comment'
+ add_note '+1 vote'
+ add_note 'final comment'
+ issue.downvotes.should == 0
+ issue.upvotes.should == 1
+ end
+
+ end
+
+ def add_note(text, author = issue.author)
+ created_at = Time.now - 1.hour + Note.count.seconds
+ issue.notes << create(:note, note: text, project: issue.project,
+ author_id: author.id, created_at: created_at)
end
end
diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb
index 039775dddda..1723eba9ec3 100644
--- a/spec/models/application_setting_spec.rb
+++ b/spec/models/application_setting_spec.rb
@@ -1,3 +1,18 @@
+# == Schema Information
+#
+# Table name: application_settings
+#
+# 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)
+#
+
require 'spec_helper'
describe ApplicationSetting, models: true do
diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb
index a6ec44da4be..7a2a7a4ce9b 100644
--- a/spec/models/commit_spec.rb
+++ b/spec/models/commit_spec.rb
@@ -57,16 +57,12 @@ eos
let(:other_issue) { create :issue, project: other_project }
it 'detects issues that this commit is marked as closing' do
- stub_const('Gitlab::ClosingIssueExtractor::ISSUE_CLOSING_REGEX',
- /Fixes #\d+/)
commit.stub(safe_message: "Fixes ##{issue.iid}")
commit.closes_issues(project).should == [issue]
end
it 'does not detect issues from other projects' do
ext_ref = "#{other_project.path_with_namespace}##{other_issue.iid}"
- stub_const('Gitlab::ClosingIssueExtractor::ISSUE_CLOSING_REGEX',
- /^([Cc]loses|[Ff]ixes)/)
commit.stub(safe_message: "Fixes #{ext_ref}")
commit.closes_issues(project).should be_empty
end
diff --git a/spec/models/project_hook_spec.rb b/spec/models/hooks/project_hook_spec.rb
index 4e0d50d7f3f..4e0d50d7f3f 100644
--- a/spec/models/project_hook_spec.rb
+++ b/spec/models/hooks/project_hook_spec.rb
diff --git a/spec/models/service_hook_spec.rb b/spec/models/hooks/service_hook_spec.rb
index 6ec82438dfe..6ec82438dfe 100644
--- a/spec/models/service_hook_spec.rb
+++ b/spec/models/hooks/service_hook_spec.rb
diff --git a/spec/models/system_hook_spec.rb b/spec/models/hooks/system_hook_spec.rb
index 4ab5261dc9d..8deb732de9c 100644
--- a/spec/models/system_hook_spec.rb
+++ b/spec/models/hooks/system_hook_spec.rb
@@ -61,5 +61,40 @@ describe SystemHook do
project.project_members.destroy_all
WebMock.should have_requested(:post, @system_hook.url).with(body: /user_remove_from_team/).once
end
+
+ it 'group create hook' do
+ create(:group)
+ WebMock.should have_requested(:post, @system_hook.url).with(
+ body: /group_create/
+ ).once
+ end
+
+ it 'group destroy hook' do
+ group = create(:group)
+ group.destroy
+ WebMock.should have_requested(:post, @system_hook.url).with(
+ body: /group_destroy/
+ ).once
+ end
+
+ it 'group member create hook' do
+ group = create(:group)
+ user = create(:user)
+ group.add_user(user, Gitlab::Access::MASTER)
+ WebMock.should have_requested(:post, @system_hook.url).with(
+ body: /user_add_to_group/
+ ).once
+ end
+
+ it 'group member destroy hook' do
+ group = create(:group)
+ user = create(:user)
+ group.add_user(user, Gitlab::Access::MASTER)
+ group.group_members.destroy_all
+ WebMock.should have_requested(:post, @system_hook.url).with(
+ body: /user_remove_from_group/
+ ).once
+ end
+
end
end
diff --git a/spec/models/web_hook_spec.rb b/spec/models/hooks/web_hook_spec.rb
index e9c04ee89cb..e9c04ee89cb 100644
--- a/spec/models/web_hook_spec.rb
+++ b/spec/models/hooks/web_hook_spec.rb
diff --git a/spec/models/group_member_spec.rb b/spec/models/members/group_member_spec.rb
index 38657de6793..38657de6793 100644
--- a/spec/models/group_member_spec.rb
+++ b/spec/models/members/group_member_spec.rb
diff --git a/spec/models/project_member_spec.rb b/spec/models/members/project_member_spec.rb
index 9b5f89b6d7d..9b5f89b6d7d 100644
--- a/spec/models/project_member_spec.rb
+++ b/spec/models/members/project_member_spec.rb
diff --git a/spec/models/members_spec.rb b/spec/models/members_spec.rb
index 6866c4794c2..cea653ec285 100644
--- a/spec/models/members_spec.rb
+++ b/spec/models/members_spec.rb
@@ -10,7 +10,7 @@ describe Member do
it { should validate_presence_of(:user) }
it { should validate_presence_of(:source) }
- it { should ensure_inclusion_of(:access_level).in_array(Gitlab::Access.values) }
+ it { should validate_inclusion_of(:access_level).in_array(Gitlab::Access.values) }
end
describe "Delegate methods" do
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 7b0d261d72f..9585cf09768 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -18,6 +18,7 @@
# iid :integer
# description :text
# position :integer default(0)
+# locked_at :datetime
#
require 'spec_helper'
diff --git a/spec/models/assembla_service_spec.rb b/spec/models/project_services/assembla_service_spec.rb
index 005dd41fea9..005dd41fea9 100644
--- a/spec/models/assembla_service_spec.rb
+++ b/spec/models/project_services/assembla_service_spec.rb
diff --git a/spec/models/buildbox_service_spec.rb b/spec/models/project_services/buildbox_service_spec.rb
index 1d9ca51be16..1d9ca51be16 100644
--- a/spec/models/buildbox_service_spec.rb
+++ b/spec/models/project_services/buildbox_service_spec.rb
diff --git a/spec/models/flowdock_service_spec.rb b/spec/models/project_services/flowdock_service_spec.rb
index ac156719b43..ac156719b43 100644
--- a/spec/models/flowdock_service_spec.rb
+++ b/spec/models/project_services/flowdock_service_spec.rb
diff --git a/spec/models/gemnasium_service_spec.rb b/spec/models/project_services/gemnasium_service_spec.rb
index 2c560c11dac..2c560c11dac 100644
--- a/spec/models/gemnasium_service_spec.rb
+++ b/spec/models/project_services/gemnasium_service_spec.rb
diff --git a/spec/models/gitlab_ci_service_spec.rb b/spec/models/project_services/gitlab_ci_service_spec.rb
index 83277058fbb..83277058fbb 100644
--- a/spec/models/gitlab_ci_service_spec.rb
+++ b/spec/models/project_services/gitlab_ci_service_spec.rb
diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb
new file mode 100644
index 00000000000..99ca04eff6e
--- /dev/null
+++ b/spec/models/project_services/jira_service_spec.rb
@@ -0,0 +1,97 @@
+# == 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
+#
+
+require 'spec_helper'
+
+describe JiraService do
+ describe "Associations" do
+ it { should belong_to :project }
+ it { should have_one :service_hook }
+ end
+
+ describe "Validations" do
+ context "active" do
+ before do
+ subject.active = true
+ end
+
+ it { should validate_presence_of :project_url }
+ it { should validate_presence_of :issues_url }
+ it { should validate_presence_of :new_issue_url }
+ end
+ end
+
+ describe 'description and title' do
+ let(:project) { create(:project) }
+
+ context 'when it is not set' do
+ before do
+ @service = project.create_jira_service(active: true)
+ end
+
+ after do
+ @service.destroy!
+ end
+
+ it 'should be initialized' do
+ expect(@service.title).to eq('JIRA')
+ expect(@service.description).to eq("Jira issue tracker")
+ end
+ end
+
+ context 'when it is set' do
+ before do
+ properties = { 'title' => 'Jira One', 'description' => 'Jira One issue tracker' }
+ @service = project.create_jira_service(active: true, properties: properties)
+ end
+
+ after do
+ @service.destroy!
+ end
+
+ it "should be correct" do
+ expect(@service.title).to eq('Jira One')
+ expect(@service.description).to eq('Jira One issue tracker')
+ end
+ end
+ end
+
+ describe 'project and issue urls' do
+ let(:project) { create(:project) }
+
+ context 'when gitlab.yml was initialized' do
+ before do
+ settings = { "jira" => {
+ "title" => "Jira",
+ "project_url" => "http://jira.sample/projects/project_a",
+ "issues_url" => "http://jira.sample/issues/:id",
+ "new_issue_url" => "http://jira.sample/projects/project_a/issues/new"
+ }
+ }
+ Gitlab.config.stub(:issues_tracker).and_return(settings)
+ @service = project.create_jira_service(active: true)
+ end
+
+ after do
+ @service.destroy!
+ end
+
+ it 'should be prepopulated with the settings' do
+ expect(@service.properties[:project_url]).to eq('http://jira.sample/projects/project_a')
+ expect(@service.properties[:issues_url]).to eq("http://jira.sample/issues/:id")
+ expect(@service.properties[:new_issue_url]).to eq("http://jira.sample/projects/project_a/issues/new")
+ end
+ end
+ end
+end
diff --git a/spec/models/pushover_service_spec.rb b/spec/models/project_services/pushover_service_spec.rb
index f2813d66c7d..f2813d66c7d 100644
--- a/spec/models/pushover_service_spec.rb
+++ b/spec/models/project_services/pushover_service_spec.rb
diff --git a/spec/models/slack_message_spec.rb b/spec/models/project_services/slack_message_spec.rb
index c530fad619b..c530fad619b 100644
--- a/spec/models/slack_message_spec.rb
+++ b/spec/models/project_services/slack_message_spec.rb
diff --git a/spec/models/slack_service_spec.rb b/spec/models/project_services/slack_service_spec.rb
index 34594072409..34594072409 100644
--- a/spec/models/slack_service_spec.rb
+++ b/spec/models/project_services/slack_service_spec.rb
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 70a15cac1a8..4669a9fd87d 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -24,12 +24,15 @@
# import_status :string(255)
# repository_size :float default(0.0)
# star_count :integer default(0), not null
+# import_type :string(255)
+# import_source :string(255)
+# avatar :string(255)
#
require 'spec_helper'
describe Project do
- describe "Associations" do
+ describe 'Associations' do
it { should belong_to(:group) }
it { should belong_to(:namespace) }
it { should belong_to(:creator).class_name('User') }
@@ -50,10 +53,10 @@ describe Project do
it { should have_one(:pushover_service).dependent(:destroy) }
end
- describe "Mass assignment" do
+ describe 'Mass assignment' do
end
- describe "Validation" do
+ describe 'Validation' do
let!(:project) { create(:project) }
it { should validate_presence_of(:name) }
@@ -68,7 +71,7 @@ describe Project do
it { should ensure_length_of(:issues_tracker_id).is_within(0..255) }
it { should validate_presence_of(:namespace) }
- it "should not allow new projects beyond user limits" do
+ it 'should not allow new projects beyond user limits' do
project2 = build(:project)
project2.stub(:creator).and_return(double(can_create_project?: false, projects_limit: 0).as_null_object)
project2.should_not be_valid
@@ -76,7 +79,7 @@ describe Project do
end
end
- describe "Respond to" do
+ describe 'Respond to' do
it { should respond_to(:url_to_repo) }
it { should respond_to(:repo_exists?) }
it { should respond_to(:satellite) }
@@ -87,27 +90,27 @@ describe Project do
it { should respond_to(:path_with_namespace) }
end
- it "should return valid url to repo" do
- project = Project.new(path: "somewhere")
- project.url_to_repo.should == Gitlab.config.gitlab_shell.ssh_path_prefix + "somewhere.git"
+ it 'should return valid url to repo' do
+ project = Project.new(path: 'somewhere')
+ project.url_to_repo.should == Gitlab.config.gitlab_shell.ssh_path_prefix + 'somewhere.git'
end
- it "returns the full web URL for this repo" do
- project = Project.new(path: "somewhere")
+ it 'returns the full web URL for this repo' do
+ project = Project.new(path: 'somewhere')
project.web_url.should == "#{Gitlab.config.gitlab.url}/somewhere"
end
- it "returns the web URL without the protocol for this repo" do
- project = Project.new(path: "somewhere")
- project.web_url_without_protocol.should == "#{Gitlab.config.gitlab.url.split("://")[1]}/somewhere"
+ it 'returns the web URL without the protocol for this repo' do
+ project = Project.new(path: 'somewhere')
+ project.web_url_without_protocol.should == "#{Gitlab.config.gitlab.url.split('://')[1]}/somewhere"
end
- describe "last_activity methods" do
+ describe 'last_activity methods' do
let(:project) { create(:project) }
let(:last_event) { double(created_at: Time.now) }
- describe "last_activity" do
- it "should alias last_activity to last_event" do
+ describe 'last_activity' do
+ it 'should alias last_activity to last_event' do
project.stub(last_event: last_event)
project.last_activity.should == last_event
end
@@ -132,13 +135,13 @@ describe Project do
let(:prev_commit_id) { merge_request.commits.last.id }
let(:commit_id) { merge_request.commits.first.id }
- it "should close merge request if last commit from source branch was pushed to target branch" do
+ it 'should close merge request if last commit from source branch was pushed to target branch' do
project.update_merge_requests(prev_commit_id, commit_id, "refs/heads/#{merge_request.target_branch}", key.user)
merge_request.reload
merge_request.merged?.should be_true
end
- it "should update merge request commits with new one if pushed to source branch" do
+ it 'should update merge request commits with new one if pushed to source branch' do
project.update_merge_requests(prev_commit_id, commit_id, "refs/heads/#{merge_request.source_branch}", key.user)
merge_request.reload
merge_request.last_commit.id.should == commit_id
@@ -164,14 +167,14 @@ describe Project do
@project = create(:project, name: 'gitlabhq', namespace: @group)
end
- it { @project.to_param.should == "gitlab/gitlabhq" }
+ it { @project.to_param.should == 'gitlab/gitlabhq' }
end
end
describe :repository do
let(:project) { create(:project) }
- it "should return valid repo" do
+ it 'should return valid repo' do
project.repository.should be_kind_of(Repository)
end
end
@@ -182,29 +185,29 @@ describe Project do
let(:not_existed_issue) { create(:issue) }
let(:ext_project) { create(:redmine_project) }
- it "should be true or if used internal tracker and issue exists" do
+ it 'should be true or if used internal tracker and issue exists' do
project.issue_exists?(existed_issue.iid).should be_true
end
- it "should be false or if used internal tracker and issue not exists" do
+ it 'should be false or if used internal tracker and issue not exists' do
project.issue_exists?(not_existed_issue.iid).should be_false
end
- it "should always be true if used other tracker" do
+ it 'should always be true if used other tracker' do
ext_project.issue_exists?(rand(100)).should be_true
end
end
- describe :used_default_issues_tracker? do
+ describe :default_issues_tracker? do
let(:project) { create(:project) }
let(:ext_project) { create(:redmine_project) }
it "should be true if used internal tracker" do
- project.used_default_issues_tracker?.should be_true
+ project.default_issues_tracker?.should be_true
end
it "should be false if used other tracker" do
- ext_project.used_default_issues_tracker?.should be_false
+ ext_project.default_issues_tracker?.should be_false
end
end
@@ -212,15 +215,15 @@ describe Project do
let(:project) { create(:project) }
let(:ext_project) { create(:redmine_project) }
- it "should be true for projects with external issues tracker if issues enabled" do
+ it 'should be true for projects with external issues tracker if issues enabled' do
ext_project.can_have_issues_tracker_id?.should be_true
end
- it "should be false for projects with internal issue tracker if issues enabled" do
+ it 'should be false for projects with internal issue tracker if issues enabled' do
project.can_have_issues_tracker_id?.should be_false
end
- it "should be always false if issues disabled" do
+ it 'should be always false if issues disabled' do
project.issues_enabled = false
ext_project.issues_enabled = false
@@ -308,4 +311,18 @@ describe Project do
expect(project.star_count).to eq(0)
end
end
+
+ describe :avatar_type do
+ let(:project) { create(:project) }
+
+ it 'should be true if avatar is image' do
+ project.update_attribute(:avatar, 'uploads/avatar.png')
+ project.avatar_type.should be_true
+ end
+
+ it 'should be false if avatar is html page' do
+ project.update_attribute(:avatar, 'uploads/avatar.html')
+ project.avatar_type.should == ['only images allowed']
+ end
+ end
end
diff --git a/spec/models/protected_branch_spec.rb b/spec/models/protected_branch_spec.rb
index af48c2c6d9e..b0f57e8a206 100644
--- a/spec/models/protected_branch_spec.rb
+++ b/spec/models/protected_branch_spec.rb
@@ -2,11 +2,12 @@
#
# Table name: protected_branches
#
-# id :integer not null, primary key
-# project_id :integer not null
-# name :string(255) not null
-# created_at :datetime
-# updated_at :datetime
+# id :integer not null, primary key
+# project_id :integer not null
+# name :string(255) not null
+# created_at :datetime
+# updated_at :datetime
+# developers_can_push :boolean default(FALSE), not null
#
require 'spec_helper'
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 8be7f733a5b..83341e516a5 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -26,8 +26,6 @@
# bio :string(255)
# failed_attempts :integer default(0)
# locked_at :datetime
-# extern_uid :string(255)
-# provider :string(255)
# username :string(255)
# can_create_group :boolean default(TRUE), not null
# can_create_team :boolean default(TRUE), not null
@@ -36,7 +34,6 @@
# 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
@@ -44,6 +41,8 @@
# 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)
#
require 'spec_helper'
diff --git a/spec/requests/api/milestones_spec.rb b/spec/requests/api/milestones_spec.rb
index f0619a1c801..647033309bd 100644
--- a/spec/requests/api/milestones_spec.rb
+++ b/spec/requests/api/milestones_spec.rb
@@ -8,48 +8,48 @@ describe API::API, api: true do
before { project.team << [user, :developer] }
- describe "GET /projects/:id/milestones" do
- it "should return project milestones" do
+ describe 'GET /projects/:id/milestones' do
+ it 'should return project milestones' do
get api("/projects/#{project.id}/milestones", user)
response.status.should == 200
json_response.should be_an Array
json_response.first['title'].should == milestone.title
end
- it "should return a 401 error if user not authenticated" do
+ it 'should return a 401 error if user not authenticated' do
get api("/projects/#{project.id}/milestones")
response.status.should == 401
end
end
- describe "GET /projects/:id/milestones/:milestone_id" do
- it "should return a project milestone by id" do
+ describe 'GET /projects/:id/milestones/:milestone_id' do
+ it 'should return a project milestone by id' do
get api("/projects/#{project.id}/milestones/#{milestone.id}", user)
response.status.should == 200
json_response['title'].should == milestone.title
json_response['iid'].should == milestone.iid
end
- it "should return 401 error if user not authenticated" do
+ it 'should return 401 error if user not authenticated' do
get api("/projects/#{project.id}/milestones/#{milestone.id}")
response.status.should == 401
end
- it "should return a 404 error if milestone id not found" do
+ it 'should return a 404 error if milestone id not found' do
get api("/projects/#{project.id}/milestones/1234", user)
response.status.should == 404
end
end
- describe "POST /projects/:id/milestones" do
- it "should create a new project milestone" do
+ describe 'POST /projects/:id/milestones' do
+ it 'should create a new project milestone' do
post api("/projects/#{project.id}/milestones", user), title: 'new milestone'
response.status.should == 201
json_response['title'].should == 'new milestone'
json_response['description'].should be_nil
end
- it "should create a new project milestone with description and due date" do
+ it 'should create a new project milestone with description and due date' do
post api("/projects/#{project.id}/milestones", user),
title: 'new milestone', description: 'release', due_date: '2013-03-02'
response.status.should == 201
@@ -57,29 +57,29 @@ describe API::API, api: true do
json_response['due_date'].should == '2013-03-02'
end
- it "should return a 400 error if title is missing" do
+ it 'should return a 400 error if title is missing' do
post api("/projects/#{project.id}/milestones", user)
response.status.should == 400
end
end
- describe "PUT /projects/:id/milestones/:milestone_id" do
- it "should update a project milestone" do
+ describe 'PUT /projects/:id/milestones/:milestone_id' do
+ it 'should update a project milestone' do
put api("/projects/#{project.id}/milestones/#{milestone.id}", user),
title: 'updated title'
response.status.should == 200
json_response['title'].should == 'updated title'
end
- it "should return a 404 error if milestone id not found" do
+ it 'should return a 404 error if milestone id not found' do
put api("/projects/#{project.id}/milestones/1234", user),
title: 'updated title'
response.status.should == 404
end
end
- describe "PUT /projects/:id/milestones/:milestone_id to close milestone" do
- it "should update a project milestone" do
+ describe 'PUT /projects/:id/milestones/:milestone_id to close milestone' do
+ it 'should update a project milestone' do
put api("/projects/#{project.id}/milestones/#{milestone.id}", user),
state_event: 'close'
response.status.should == 200
@@ -88,12 +88,29 @@ describe API::API, api: true do
end
end
- describe "PUT /projects/:id/milestones/:milestone_id to test observer on close" do
- it "should create an activity event when an milestone is closed" do
+ describe 'PUT /projects/:id/milestones/:milestone_id to test observer on close' do
+ it 'should create an activity event when an milestone is closed' do
Event.should_receive(:create)
put api("/projects/#{project.id}/milestones/#{milestone.id}", user),
state_event: 'close'
end
end
+
+ describe 'GET /projects/:id/milestones/:milestone_id/issues' do
+ before do
+ milestone.issues << create(:issue)
+ end
+ it 'should return project issues for a particular milestone' do
+ get api("/projects/#{project.id}/milestones/#{milestone.id}/issues", user)
+ response.status.should == 200
+ json_response.should be_an Array
+ json_response.first['milestone']['title'].should == milestone.title
+ end
+
+ it 'should return a 401 error if user not authenticated' do
+ get api("/projects/#{project.id}/milestones/#{milestone.id}/issues")
+ response.status.should == 401
+ end
+ end
end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 3098b0f77f9..dc410107410 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
require 'spec_helper'
describe API::API, api: true do
@@ -12,43 +13,67 @@ describe API::API, api: true do
let(:snippet) { create(:project_snippet, author: user, project: project, title: 'example') }
let(:project_member) { create(:project_member, user: user, project: project, access_level: ProjectMember::MASTER) }
let(:project_member2) { create(:project_member, user: user3, project: project, access_level: ProjectMember::DEVELOPER) }
+ let(:user4) { create(:user) }
+ let(:project3) do
+ create(:project,
+ name: 'second_project',
+ path: 'second_project',
+ creator_id: user.id,
+ namespace: user.namespace,
+ merge_requests_enabled: false,
+ issues_enabled: false, wiki_enabled: false,
+ snippets_enabled: false, visibility_level: 0)
+ end
+ let(:project_member3) do
+ create(:project_member,
+ user: user4,
+ project: project3,
+ access_level: ProjectMember::MASTER)
+ end
+ let(:project4) do
+ create(:project,
+ name: 'third_project',
+ path: 'third_project',
+ creator_id: user4.id,
+ namespace: user4.namespace)
+ end
- describe "GET /projects" do
+ describe 'GET /projects' do
before { project }
- context "when unauthenticated" do
- it "should return authentication error" do
- get api("/projects")
+ context 'when unauthenticated' do
+ it 'should return authentication error' do
+ get api('/projects')
response.status.should == 401
end
end
- context "when authenticated" do
- it "should return an array of projects" do
- get api("/projects", user)
+ context 'when authenticated' do
+ it 'should return an array of projects' do
+ get api('/projects', user)
response.status.should == 200
json_response.should be_an Array
json_response.first['name'].should == project.name
json_response.first['owner']['username'].should == user.username
end
- context "and using search" do
- it "should return searched project" do
- get api("/projects", user), { search: project.name }
+ context 'and using search' do
+ it 'should return searched project' do
+ get api('/projects', user), { search: project.name }
response.status.should eq(200)
json_response.should be_an Array
json_response.length.should eq(1)
end
end
- context "and using sorting" do
+ context 'and using sorting' do
before do
project2
project3
end
- it "should return the correct order when sorted by id" do
- get api("/projects", user), { order_by: 'id', sort: 'desc'}
+ it 'should return the correct order when sorted by id' do
+ get api('/projects', user), { order_by: 'id', sort: 'desc'}
response.status.should eq(200)
json_response.should be_an Array
json_response.first['id'].should eq(project3.id)
@@ -57,26 +82,26 @@ describe API::API, api: true do
end
end
- describe "GET /projects/all" do
+ describe 'GET /projects/all' do
before { project }
- context "when unauthenticated" do
- it "should return authentication error" do
- get api("/projects/all")
+ context 'when unauthenticated' do
+ it 'should return authentication error' do
+ get api('/projects/all')
response.status.should == 401
end
end
- context "when authenticated as regular user" do
- it "should return authentication error" do
- get api("/projects/all", user)
+ context 'when authenticated as regular user' do
+ it 'should return authentication error' do
+ get api('/projects/all', user)
response.status.should == 403
end
end
- context "when authenticated as admin" do
- it "should return an array of all projects" do
- get api("/projects/all", admin)
+ context 'when authenticated as admin' do
+ it 'should return an array of all projects' do
+ get api('/projects/all', admin)
response.status.should == 200
json_response.should be_an Array
project_name = project.name
@@ -92,59 +117,59 @@ describe API::API, api: true do
end
end
- describe "POST /projects" do
- context "maximum number of projects reached" do
+ describe 'POST /projects' do
+ context 'maximum number of projects reached' do
before do
(1..user2.projects_limit).each do |project|
- post api("/projects", user2), name: "foo#{project}"
+ post api('/projects', user2), name: "foo#{project}"
end
end
- it "should not create new project" do
+ it 'should not create new project' do
expect {
- post api("/projects", user2), name: 'foo'
+ post api('/projects', user2), name: 'foo'
}.to change {Project.count}.by(0)
end
end
- it "should create new project without path" do
- expect { post api("/projects", user), name: 'foo' }.to change {Project.count}.by(1)
+ it 'should create new project without path' do
+ expect { post api('/projects', user), name: 'foo' }.to change {Project.count}.by(1)
end
- it "should not create new project without name" do
- expect { post api("/projects", user) }.to_not change {Project.count}
+ it 'should not create new project without name' do
+ expect { post api('/projects', user) }.to_not change {Project.count}
end
- it "should return a 400 error if name not given" do
- post api("/projects", user)
+ it 'should return a 400 error if name not given' do
+ post api('/projects', user)
response.status.should == 400
end
- it "should create last project before reaching project limit" do
- (1..user2.projects_limit-1).each { |p| post api("/projects", user2), name: "foo#{p}" }
- post api("/projects", user2), name: "foo"
+ it 'should create last project before reaching project limit' do
+ (1..user2.projects_limit-1).each { |p| post api('/projects', user2), name: "foo#{p}" }
+ post api('/projects', user2), name: 'foo'
response.status.should == 201
end
- it "should respond with 201 on success" do
- post api("/projects", user), name: 'foo'
+ it 'should respond with 201 on success' do
+ post api('/projects', user), name: 'foo'
response.status.should == 201
end
- it "should respond with 400 if name is not given" do
- post api("/projects", user)
+ it 'should respond with 400 if name is not given' do
+ post api('/projects', user)
response.status.should == 400
end
- it "should return a 403 error if project limit reached" do
+ it 'should return a 403 error if project limit reached' do
(1..user.projects_limit).each do |p|
- post api("/projects", user), name: "foo#{p}"
+ post api('/projects', user), name: "foo#{p}"
end
- post api("/projects", user), name: 'bar'
+ post api('/projects', user), name: 'bar'
response.status.should == 403
end
- it "should assign attributes to project" do
+ it 'should assign attributes to project' do
project = attributes_for(:project, {
path: 'camelCasePath',
description: Faker::Lorem.sentence,
@@ -153,69 +178,69 @@ describe API::API, api: true do
wiki_enabled: false
})
- post api("/projects", user), project
+ post api('/projects', user), project
project.each_pair do |k,v|
json_response[k.to_s].should == v
end
end
- it "should set a project as public" do
+ it 'should set a project as public' do
project = attributes_for(:project, :public)
- post api("/projects", user), project
+ post api('/projects', user), project
json_response['public'].should be_true
json_response['visibility_level'].should == Gitlab::VisibilityLevel::PUBLIC
end
- it "should set a project as public using :public" do
+ it 'should set a project as public using :public' do
project = attributes_for(:project, { public: true })
- post api("/projects", user), project
+ post api('/projects', user), project
json_response['public'].should be_true
json_response['visibility_level'].should == Gitlab::VisibilityLevel::PUBLIC
end
- it "should set a project as internal" do
+ it 'should set a project as internal' do
project = attributes_for(:project, :internal)
- post api("/projects", user), project
+ post api('/projects', user), project
json_response['public'].should be_false
json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL
end
- it "should set a project as internal overriding :public" do
+ it 'should set a project as internal overriding :public' do
project = attributes_for(:project, :internal, { public: true })
- post api("/projects", user), project
+ post api('/projects', user), project
json_response['public'].should be_false
json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL
end
- it "should set a project as private" do
+ it 'should set a project as private' do
project = attributes_for(:project, :private)
- post api("/projects", user), project
+ post api('/projects', user), project
json_response['public'].should be_false
json_response['visibility_level'].should == Gitlab::VisibilityLevel::PRIVATE
end
- it "should set a project as private using :public" do
+ it 'should set a project as private using :public' do
project = attributes_for(:project, { public: false })
- post api("/projects", user), project
+ post api('/projects', user), project
json_response['public'].should be_false
json_response['visibility_level'].should == Gitlab::VisibilityLevel::PRIVATE
end
end
- describe "POST /projects/user/:id" do
+ describe 'POST /projects/user/:id' do
before { project }
before { admin }
- it "should create new project without path" do
+ it 'should create new project without path' do
expect { post api("/projects/user/#{user.id}", admin), name: 'foo' }.to change {Project.count}.by(1)
end
- it "should not create new project without name" do
+ it 'should not create new project without name' do
expect { post api("/projects/user/#{user.id}", admin) }.to_not change {Project.count}
end
- it "should respond with 201 on success" do
+ it 'should respond with 201 on success' do
post api("/projects/user/#{user.id}", admin), name: 'foo'
response.status.should == 201
end
@@ -235,7 +260,7 @@ describe API::API, api: true do
]
end
- it "should assign attributes to project" do
+ it 'should assign attributes to project' do
project = attributes_for(:project, {
description: Faker::Lorem.sentence,
issues_enabled: false,
@@ -251,42 +276,42 @@ describe API::API, api: true do
end
end
- it "should set a project as public" do
+ it 'should set a project as public' do
project = attributes_for(:project, :public)
post api("/projects/user/#{user.id}", admin), project
json_response['public'].should be_true
json_response['visibility_level'].should == Gitlab::VisibilityLevel::PUBLIC
end
- it "should set a project as public using :public" do
+ it 'should set a project as public using :public' do
project = attributes_for(:project, { public: true })
post api("/projects/user/#{user.id}", admin), project
json_response['public'].should be_true
json_response['visibility_level'].should == Gitlab::VisibilityLevel::PUBLIC
end
- it "should set a project as internal" do
+ it 'should set a project as internal' do
project = attributes_for(:project, :internal)
post api("/projects/user/#{user.id}", admin), project
json_response['public'].should be_false
json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL
end
- it "should set a project as internal overriding :public" do
+ it 'should set a project as internal overriding :public' do
project = attributes_for(:project, :internal, { public: true })
post api("/projects/user/#{user.id}", admin), project
json_response['public'].should be_false
json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL
end
- it "should set a project as private" do
+ it 'should set a project as private' do
project = attributes_for(:project, :private)
post api("/projects/user/#{user.id}", admin), project
json_response['public'].should be_false
json_response['visibility_level'].should == Gitlab::VisibilityLevel::PRIVATE
end
- it "should set a project as private using :public" do
+ it 'should set a project as private using :public' do
project = attributes_for(:project, { public: false })
post api("/projects/user/#{user.id}", admin), project
json_response['public'].should be_false
@@ -294,30 +319,30 @@ describe API::API, api: true do
end
end
- describe "GET /projects/:id" do
+ describe 'GET /projects/:id' do
before { project }
before { project_member }
- it "should return a project by id" do
+ it 'should return a project by id' do
get api("/projects/#{project.id}", user)
response.status.should == 200
json_response['name'].should == project.name
json_response['owner']['username'].should == user.username
end
- it "should return a project by path name" do
+ it 'should return a project by path name' do
get api("/projects/#{project.id}", user)
response.status.should == 200
json_response['name'].should == project.name
end
- it "should return a 404 error if not found" do
- get api("/projects/42", user)
+ it 'should return a 404 error if not found' do
+ get api('/projects/42', user)
response.status.should == 404
json_response['message'].should == '404 Project Not Found'
end
- it "should return a 404 error if user is not a member" do
+ it 'should return a 404 error if user is not a member' do
other_user = create(:user)
get api("/projects/#{project.id}", other_user)
response.status.should == 404
@@ -331,8 +356,8 @@ describe API::API, api: true do
end
it { response.status.should == 200 }
- it { json_response['permissions']["project_access"]["access_level"].should == Gitlab::Access::MASTER }
- it { json_response['permissions']["group_access"].should be_nil }
+ it { json_response['permissions']['project_access']['access_level'].should == Gitlab::Access::MASTER }
+ it { json_response['permissions']['group_access'].should be_nil }
end
context 'group project' do
@@ -343,16 +368,16 @@ describe API::API, api: true do
end
it { response.status.should == 200 }
- it { json_response['permissions']["project_access"].should be_nil }
- it { json_response['permissions']["group_access"]["access_level"].should == Gitlab::Access::OWNER }
+ it { json_response['permissions']['project_access'].should be_nil }
+ it { json_response['permissions']['group_access']['access_level'].should == Gitlab::Access::OWNER }
end
end
end
- describe "GET /projects/:id/events" do
+ describe 'GET /projects/:id/events' do
before { project_member }
- it "should return a project events" do
+ it 'should return a project events' do
get api("/projects/#{project.id}/events", user)
response.status.should == 200
json_event = json_response.first
@@ -362,23 +387,23 @@ describe API::API, api: true do
json_event['author_username'].should == user.username
end
- it "should return a 404 error if not found" do
- get api("/projects/42/events", user)
+ it 'should return a 404 error if not found' do
+ get api('/projects/42/events', user)
response.status.should == 404
json_response['message'].should == '404 Project Not Found'
end
- it "should return a 404 error if user is not a member" do
+ it 'should return a 404 error if user is not a member' do
other_user = create(:user)
get api("/projects/#{project.id}/events", other_user)
response.status.should == 404
end
end
- describe "GET /projects/:id/snippets" do
+ describe 'GET /projects/:id/snippets' do
before { snippet }
- it "should return an array of project snippets" do
+ it 'should return an array of project snippets' do
get api("/projects/#{project.id}/snippets", user)
response.status.should == 200
json_response.should be_an Array
@@ -386,48 +411,48 @@ describe API::API, api: true do
end
end
- describe "GET /projects/:id/snippets/:snippet_id" do
- it "should return a project snippet" do
+ describe 'GET /projects/:id/snippets/:snippet_id' do
+ it 'should return a project snippet' do
get api("/projects/#{project.id}/snippets/#{snippet.id}", user)
response.status.should == 200
json_response['title'].should == snippet.title
end
- it "should return a 404 error if snippet id not found" do
+ it 'should return a 404 error if snippet id not found' do
get api("/projects/#{project.id}/snippets/1234", user)
response.status.should == 404
end
end
- describe "POST /projects/:id/snippets" do
- it "should create a new project snippet" do
+ describe 'POST /projects/:id/snippets' do
+ it 'should create a new project snippet' do
post api("/projects/#{project.id}/snippets", user),
title: 'api test', file_name: 'sample.rb', code: 'test'
response.status.should == 201
json_response['title'].should == 'api test'
end
- it "should return a 400 error if title is not given" do
+ it 'should return a 400 error if title is not given' do
post api("/projects/#{project.id}/snippets", user),
file_name: 'sample.rb', code: 'test'
response.status.should == 400
end
- it "should return a 400 error if file_name not given" do
+ it 'should return a 400 error if file_name not given' do
post api("/projects/#{project.id}/snippets", user),
title: 'api test', code: 'test'
response.status.should == 400
end
- it "should return a 400 error if code not given" do
+ it 'should return a 400 error if code not given' do
post api("/projects/#{project.id}/snippets", user),
title: 'api test', file_name: 'sample.rb'
response.status.should == 400
end
end
- describe "PUT /projects/:id/snippets/:shippet_id" do
- it "should update an existing project snippet" do
+ describe 'PUT /projects/:id/snippets/:shippet_id' do
+ it 'should update an existing project snippet' do
put api("/projects/#{project.id}/snippets/#{snippet.id}", user),
code: 'updated code'
response.status.should == 200
@@ -435,7 +460,7 @@ describe API::API, api: true do
snippet.reload.content.should == 'updated code'
end
- it "should update an existing project snippet with new title" do
+ it 'should update an existing project snippet with new title' do
put api("/projects/#{project.id}/snippets/#{snippet.id}", user),
title: 'other api test'
response.status.should == 200
@@ -443,10 +468,10 @@ describe API::API, api: true do
end
end
- describe "DELETE /projects/:id/snippets/:snippet_id" do
+ describe 'DELETE /projects/:id/snippets/:snippet_id' do
before { snippet }
- it "should delete existing project snippet" do
+ it 'should delete existing project snippet' do
expect {
delete api("/projects/#{project.id}/snippets/#{snippet.id}", user)
}.to change { Snippet.count }.by(-1)
@@ -459,13 +484,13 @@ describe API::API, api: true do
end
end
- describe "GET /projects/:id/snippets/:snippet_id/raw" do
- it "should get a raw project snippet" do
+ describe 'GET /projects/:id/snippets/:snippet_id/raw' do
+ it 'should get a raw project snippet' do
get api("/projects/#{project.id}/snippets/#{snippet.id}/raw", user)
response.status.should == 200
end
- it "should return a 404 error if raw project snippet not found" do
+ it 'should return a 404 error if raw project snippet not found' do
get api("/projects/#{project.id}/snippets/5555/raw", user)
response.status.should == 404
end
@@ -475,10 +500,10 @@ describe API::API, api: true do
let(:deploy_keys_project) { create(:deploy_keys_project, project: project) }
let(:deploy_key) { deploy_keys_project.deploy_key }
- describe "GET /projects/:id/keys" do
+ describe 'GET /projects/:id/keys' do
before { deploy_key }
- it "should return array of ssh keys" do
+ it 'should return array of ssh keys' do
get api("/projects/#{project.id}/keys", user)
response.status.should == 200
json_response.should be_an Array
@@ -486,22 +511,22 @@ describe API::API, api: true do
end
end
- describe "GET /projects/:id/keys/:key_id" do
- it "should return a single key" do
+ describe 'GET /projects/:id/keys/:key_id' do
+ it 'should return a single key' do
get api("/projects/#{project.id}/keys/#{deploy_key.id}", user)
response.status.should == 200
json_response['title'].should == deploy_key.title
end
- it "should return 404 Not Found with invalid ID" do
+ it 'should return 404 Not Found with invalid ID' do
get api("/projects/#{project.id}/keys/404", user)
response.status.should == 404
end
end
- describe "POST /projects/:id/keys" do
- it "should not create an invalid ssh key" do
- post api("/projects/#{project.id}/keys", user), { title: "invalid key" }
+ describe 'POST /projects/:id/keys' do
+ it 'should not create an invalid ssh key' do
+ post api("/projects/#{project.id}/keys", user), { title: 'invalid key' }
response.status.should == 400
json_response['message']['key'].should == [
'can\'t be blank',
@@ -519,7 +544,7 @@ describe API::API, api: true do
]
end
- it "should create new ssh key" do
+ it 'should create new ssh key' do
key_attrs = attributes_for :key
expect {
post api("/projects/#{project.id}/keys", user), key_attrs
@@ -527,16 +552,16 @@ describe API::API, api: true do
end
end
- describe "DELETE /projects/:id/keys/:key_id" do
+ describe 'DELETE /projects/:id/keys/:key_id' do
before { deploy_key }
- it "should delete existing key" do
+ it 'should delete existing key' do
expect {
delete api("/projects/#{project.id}/keys/#{deploy_key.id}", user)
}.to change{ project.deploy_keys.count }.by(-1)
end
- it "should return 404 Not Found with invalid ID" do
+ it 'should return 404 Not Found with invalid ID' do
delete api("/projects/#{project.id}/keys/404", user)
response.status.should == 404
end
@@ -547,7 +572,7 @@ describe API::API, api: true do
let(:project_fork_target) { create(:project) }
let(:project_fork_source) { create(:project, :public) }
- describe "POST /projects/:id/fork/:forked_from_id" do
+ describe 'POST /projects/:id/fork/:forked_from_id' do
let(:new_project_fork_source) { create(:project, :public) }
it "shouldn't available for non admin users" do
@@ -555,7 +580,7 @@ describe API::API, api: true do
response.status.should == 403
end
- it "should allow project to be forked from an existing project" do
+ it 'should allow project to be forked from an existing project' do
project_fork_target.forked?.should_not be_true
post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin)
response.status.should == 201
@@ -565,12 +590,12 @@ describe API::API, api: true do
project_fork_target.forked?.should be_true
end
- it "should fail if forked_from project which does not exist" do
+ it 'should fail if forked_from project which does not exist' do
post api("/projects/#{project_fork_target.id}/fork/9999", admin)
response.status.should == 404
end
- it "should fail with 409 if already forked" do
+ it 'should fail with 409 if already forked' do
post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin)
project_fork_target.reload
project_fork_target.forked_from_project.id.should == project_fork_source.id
@@ -582,14 +607,14 @@ describe API::API, api: true do
end
end
- describe "DELETE /projects/:id/fork" do
+ describe 'DELETE /projects/:id/fork' do
it "shouldn't available for non admin users" do
delete api("/projects/#{project_fork_target.id}/fork", user)
response.status.should == 403
end
- it "should make forked project unforked" do
+ it 'should make forked project unforked' do
post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin)
project_fork_target.reload
project_fork_target.forked_from_project.should_not be_nil
@@ -601,7 +626,7 @@ describe API::API, api: true do
project_fork_target.forked?.should_not be_true
end
- it "should be idempotent if not forked" do
+ it 'should be idempotent if not forked' do
project_fork_target.forked_from_project.should be_nil
delete api("/projects/#{project_fork_target.id}/fork", admin)
response.status.should == 200
@@ -610,7 +635,7 @@ describe API::API, api: true do
end
end
- describe "GET /projects/search/:query" do
+ describe 'GET /projects/search/:query' do
let!(:query) { 'query'}
let!(:search) { create(:empty_project, name: query, creator_id: user.id, namespace: user.namespace) }
let!(:pre) { create(:empty_project, name: "pre_#{query}", creator_id: user.id, namespace: user.namespace) }
@@ -622,15 +647,15 @@ describe API::API, api: true do
let!(:public) { create(:empty_project, :public, name: "public #{query}") }
let!(:unfound_public) { create(:empty_project, :public, name: 'unfound public') }
- context "when unauthenticated" do
- it "should return authentication error" do
+ context 'when unauthenticated' do
+ it 'should return authentication error' do
get api("/projects/search/#{query}")
response.status.should == 401
end
end
- context "when authenticated" do
- it "should return an array of projects" do
+ context 'when authenticated' do
+ it 'should return an array of projects' do
get api("/projects/search/#{query}",user)
response.status.should == 200
json_response.should be_an Array
@@ -639,8 +664,8 @@ describe API::API, api: true do
end
end
- context "when authenticated as a different user" do
- it "should return matching public projects" do
+ context 'when authenticated as a different user' do
+ it 'should return matching public projects' do
get api("/projects/search/#{query}", user2)
response.status.should == 200
json_response.should be_an Array
@@ -650,9 +675,121 @@ describe API::API, api: true do
end
end
- describe "DELETE /projects/:id" do
- context "when authenticated as user" do
- it "should remove project" do
+ describe 'PUT /projects/:id̈́' do
+ before { project }
+ before { user }
+ before { user3 }
+ before { user4 }
+ before { project3 }
+ before { project4 }
+ before { project_member3 }
+ before { project_member2 }
+
+ context 'when unauthenticated' do
+ it 'should return authentication error' do
+ project_param = { name: 'bar' }
+ put api("/projects/#{project.id}"), project_param
+ response.status.should == 401
+ end
+ end
+
+ context 'when authenticated as project owner' do
+ it 'should update name' do
+ project_param = { name: 'bar' }
+ put api("/projects/#{project.id}", user), project_param
+ response.status.should == 200
+ project_param.each_pair do |k, v|
+ json_response[k.to_s].should == v
+ end
+ end
+
+ it 'should update visibility_level' do
+ project_param = { visibility_level: 20 }
+ put api("/projects/#{project3.id}", user), project_param
+ response.status.should == 200
+ project_param.each_pair do |k, v|
+ json_response[k.to_s].should == v
+ end
+ end
+
+ it 'should not update name to existing name' do
+ project_param = { name: project3.name }
+ put api("/projects/#{project.id}", user), project_param
+ response.status.should == 400
+ json_response['message']['name'].should == ['has already been taken']
+ end
+
+ it 'should update path & name to existing path & name in different namespace' do
+ project_param = { path: project4.path, name: project4.name }
+ put api("/projects/#{project3.id}", user), project_param
+ response.status.should == 200
+ project_param.each_pair do |k, v|
+ json_response[k.to_s].should == v
+ end
+ end
+ end
+
+ context 'when authenticated as project master' do
+ it 'should update path' do
+ project_param = { path: 'bar' }
+ put api("/projects/#{project3.id}", user4), project_param
+ response.status.should == 200
+ project_param.each_pair do |k, v|
+ json_response[k.to_s].should == v
+ end
+ end
+
+ it 'should update other attributes' do
+ project_param = { issues_enabled: true,
+ wiki_enabled: true,
+ snippets_enabled: true,
+ merge_requests_enabled: true,
+ description: 'new description' }
+
+ put api("/projects/#{project3.id}", user4), project_param
+ response.status.should == 200
+ project_param.each_pair do |k, v|
+ json_response[k.to_s].should == v
+ end
+ end
+
+ it 'should not update path to existing path' do
+ project_param = { path: project.path }
+ put api("/projects/#{project3.id}", user4), project_param
+ response.status.should == 400
+ json_response['message']['path'].should == ['has already been taken']
+ end
+
+ it 'should not update name' do
+ project_param = { name: 'bar' }
+ put api("/projects/#{project3.id}", user4), project_param
+ response.status.should == 403
+ end
+
+ it 'should not update visibility_level' do
+ project_param = { visibility_level: 20 }
+ put api("/projects/#{project3.id}", user4), project_param
+ response.status.should == 403
+ end
+ end
+
+ context 'when authenticated as project developer' do
+ it 'should not update other attributes' do
+ project_param = { path: 'bar',
+ issues_enabled: true,
+ wiki_enabled: true,
+ snippets_enabled: true,
+ merge_requests_enabled: true,
+ description: 'new description' }
+ put api("/projects/#{project.id}", user3), project_param
+ response.status.should == 403
+ end
+ end
+ end
+
+ describe 'DELETE /projects/:id' do
+ context 'when authenticated as user' do
+ it 'should remove project' do
expect(GitlabShellWorker).to(
receive(:perform_async).with(:remove_repository,
/#{project.path_with_namespace}/)
@@ -662,32 +799,32 @@ describe API::API, api: true do
response.status.should == 200
end
- it "should not remove a project if not an owner" do
+ it 'should not remove a project if not an owner' do
user3 = create(:user)
project.team << [user3, :developer]
delete api("/projects/#{project.id}", user3)
response.status.should == 403
end
- it "should not remove a non existing project" do
- delete api("/projects/1328", user)
+ it 'should not remove a non existing project' do
+ delete api('/projects/1328', user)
response.status.should == 404
end
- it "should not remove a project not attached to user" do
+ it 'should not remove a project not attached to user' do
delete api("/projects/#{project.id}", user2)
response.status.should == 404
end
end
- context "when authenticated as admin" do
- it "should remove any existing project" do
+ context 'when authenticated as admin' do
+ it 'should remove any existing project' do
delete api("/projects/#{project.id}", admin)
response.status.should == 200
end
- it "should not remove a non existing project" do
- delete api("/projects/1328", admin)
+ it 'should not remove a non existing project' do
+ delete api('/projects/1328', admin)
response.status.should == 404
end
end
diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb
index beae71c02d9..5518d2df566 100644
--- a/spec/requests/api/repositories_spec.rb
+++ b/spec/requests/api/repositories_spec.rb
@@ -101,6 +101,14 @@ describe API::API, api: true do
json_response.first['type'].should == 'tree'
json_response.first['mode'].should == '040000'
end
+
+ it 'should return a 404 for unknown ref' do
+ get api("/projects/#{project.id}/repository/tree?ref_name=foo", user)
+ response.status.should == 404
+
+ json_response.should be_an Object
+ json_response['message'] == '404 Tree Not Found'
+ end
end
context "unauthorized user" do
@@ -145,6 +153,14 @@ describe API::API, api: true do
get api("/projects/#{project.id}/repository/raw_blobs/#{sample_blob.oid}", user)
response.status.should == 200
end
+
+ it 'should return a 404 for unknown blob' do
+ get api("/projects/#{project.id}/repository/raw_blobs/123456", user)
+ response.status.should == 404
+
+ json_response.should be_an Object
+ json_response['message'] == '404 Blob Not Found'
+ end
end
describe "GET /projects/:id/repository/archive(.:format)?:sha" do
diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb
index f149f3f62a9..e36b266a1ff 100644
--- a/spec/routing/project_routing_spec.rb
+++ b/spec/routing/project_routing_spec.rb
@@ -12,43 +12,43 @@ require 'spec_helper'
# Examples
#
# # Default behavior
-# it_behaves_like "RESTful project resources" do
+# it_behaves_like 'RESTful project resources' do
# let(:controller) { 'issues' }
# end
#
# # Customizing actions
-# it_behaves_like "RESTful project resources" do
+# it_behaves_like 'RESTful project resources' do
# let(:actions) { [:index] }
# let(:controller) { 'issues' }
# end
-shared_examples "RESTful project resources" do
+shared_examples 'RESTful project resources' do
let(:actions) { [:index, :create, :new, :edit, :show, :update, :destroy] }
- it "to #index" do
+ it 'to #index' do
get("/gitlab/gitlabhq/#{controller}").should route_to("projects/#{controller}#index", project_id: 'gitlab/gitlabhq') if actions.include?(:index)
end
- it "to #create" do
+ it 'to #create' do
post("/gitlab/gitlabhq/#{controller}").should route_to("projects/#{controller}#create", project_id: 'gitlab/gitlabhq') if actions.include?(:create)
end
- it "to #new" do
+ it 'to #new' do
get("/gitlab/gitlabhq/#{controller}/new").should route_to("projects/#{controller}#new", project_id: 'gitlab/gitlabhq') if actions.include?(:new)
end
- it "to #edit" do
+ it 'to #edit' do
get("/gitlab/gitlabhq/#{controller}/1/edit").should route_to("projects/#{controller}#edit", project_id: 'gitlab/gitlabhq', id: '1') if actions.include?(:edit)
end
- it "to #show" do
+ it 'to #show' do
get("/gitlab/gitlabhq/#{controller}/1").should route_to("projects/#{controller}#show", project_id: 'gitlab/gitlabhq', id: '1') if actions.include?(:show)
end
- it "to #update" do
+ it 'to #update' do
put("/gitlab/gitlabhq/#{controller}/1").should route_to("projects/#{controller}#update", project_id: 'gitlab/gitlabhq', id: '1') if actions.include?(:update)
end
- it "to #destroy" do
+ it 'to #destroy' do
delete("/gitlab/gitlabhq/#{controller}/1").should route_to("projects/#{controller}#destroy", project_id: 'gitlab/gitlabhq', id: '1') if actions.include?(:destroy)
end
end
@@ -61,33 +61,33 @@ end
# PUT /:id(.:format) projects#update
# DELETE /:id(.:format) projects#destroy
# markdown_preview_project GET /:id/markdown_preview(.:format) projects#markdown_preview
-describe ProjectsController, "routing" do
- it "to #create" do
- post("/projects").should route_to('projects#create')
+describe ProjectsController, 'routing' do
+ it 'to #create' do
+ post('/projects').should route_to('projects#create')
end
- it "to #new" do
- get("/projects/new").should route_to('projects#new')
+ it 'to #new' do
+ get('/projects/new').should route_to('projects#new')
end
- it "to #edit" do
- get("/gitlab/gitlabhq/edit").should route_to('projects#edit', id: 'gitlab/gitlabhq')
+ it 'to #edit' do
+ get('/gitlab/gitlabhq/edit').should route_to('projects#edit', id: 'gitlab/gitlabhq')
end
- it "to #autocomplete_sources" do
- get('/gitlab/gitlabhq/autocomplete_sources').should route_to('projects#autocomplete_sources', id: "gitlab/gitlabhq")
+ it 'to #autocomplete_sources' do
+ get('/gitlab/gitlabhq/autocomplete_sources').should route_to('projects#autocomplete_sources', id: 'gitlab/gitlabhq')
end
- it "to #show" do
- get("/gitlab/gitlabhq").should route_to('projects#show', id: 'gitlab/gitlabhq')
+ it 'to #show' do
+ get('/gitlab/gitlabhq').should route_to('projects#show', id: 'gitlab/gitlabhq')
end
- it "to #update" do
- put("/gitlab/gitlabhq").should route_to('projects#update', id: 'gitlab/gitlabhq')
+ it 'to #update' do
+ put('/gitlab/gitlabhq').should route_to('projects#update', id: 'gitlab/gitlabhq')
end
- it "to #destroy" do
- delete("/gitlab/gitlabhq").should route_to('projects#destroy', id: 'gitlab/gitlabhq')
+ it 'to #destroy' do
+ delete('/gitlab/gitlabhq').should route_to('projects#destroy', id: 'gitlab/gitlabhq')
end
it 'to #markdown_preview' do
@@ -103,16 +103,16 @@ end
# edit_project_wiki GET /:project_id/wikis/:id/edit(.:format) projects/wikis#edit
# project_wiki GET /:project_id/wikis/:id(.:format) projects/wikis#show
# DELETE /:project_id/wikis/:id(.:format) projects/wikis#destroy
-describe Projects::WikisController, "routing" do
- it "to #pages" do
- get("/gitlab/gitlabhq/wikis/pages").should route_to('projects/wikis#pages', project_id: 'gitlab/gitlabhq')
+describe Projects::WikisController, 'routing' do
+ it 'to #pages' do
+ get('/gitlab/gitlabhq/wikis/pages').should route_to('projects/wikis#pages', project_id: 'gitlab/gitlabhq')
end
- it "to #history" do
- get("/gitlab/gitlabhq/wikis/1/history").should route_to('projects/wikis#history', project_id: 'gitlab/gitlabhq', id: '1')
+ it 'to #history' do
+ get('/gitlab/gitlabhq/wikis/1/history').should route_to('projects/wikis#history', project_id: 'gitlab/gitlabhq', id: '1')
end
- it_behaves_like "RESTful project resources" do
+ it_behaves_like 'RESTful project resources' do
let(:actions) { [:create, :edit, :show, :destroy] }
let(:controller) { 'wikis' }
end
@@ -122,45 +122,45 @@ end
# tags_project_repository GET /:project_id/repository/tags(.:format) projects/repositories#tags
# archive_project_repository GET /:project_id/repository/archive(.:format) projects/repositories#archive
# edit_project_repository GET /:project_id/repository/edit(.:format) projects/repositories#edit
-describe Projects::RepositoriesController, "routing" do
- it "to #archive" do
- get("/gitlab/gitlabhq/repository/archive").should route_to('projects/repositories#archive', project_id: 'gitlab/gitlabhq')
+describe Projects::RepositoriesController, 'routing' do
+ it 'to #archive' do
+ get('/gitlab/gitlabhq/repository/archive').should route_to('projects/repositories#archive', project_id: 'gitlab/gitlabhq')
end
- it "to #archive format:zip" do
- get("/gitlab/gitlabhq/repository/archive.zip").should route_to('projects/repositories#archive', project_id: 'gitlab/gitlabhq', format: 'zip')
+ it 'to #archive format:zip' do
+ get('/gitlab/gitlabhq/repository/archive.zip').should route_to('projects/repositories#archive', project_id: 'gitlab/gitlabhq', format: 'zip')
end
- it "to #archive format:tar.bz2" do
- get("/gitlab/gitlabhq/repository/archive.tar.bz2").should route_to('projects/repositories#archive', project_id: 'gitlab/gitlabhq', format: 'tar.bz2')
+ it 'to #archive format:tar.bz2' do
+ get('/gitlab/gitlabhq/repository/archive.tar.bz2').should route_to('projects/repositories#archive', project_id: 'gitlab/gitlabhq', format: 'tar.bz2')
end
- it "to #show" do
- get("/gitlab/gitlabhq/repository").should route_to('projects/repositories#show', project_id: 'gitlab/gitlabhq')
+ it 'to #show' do
+ get('/gitlab/gitlabhq/repository').should route_to('projects/repositories#show', project_id: 'gitlab/gitlabhq')
end
end
-describe Projects::BranchesController, "routing" do
- it "to #branches" do
- get("/gitlab/gitlabhq/branches").should route_to('projects/branches#index', project_id: 'gitlab/gitlabhq')
- delete("/gitlab/gitlabhq/branches/feature%2345").should route_to('projects/branches#destroy', project_id: 'gitlab/gitlabhq', id: 'feature#45')
- delete("/gitlab/gitlabhq/branches/feature%2B45").should route_to('projects/branches#destroy', project_id: 'gitlab/gitlabhq', id: 'feature+45')
- delete("/gitlab/gitlabhq/branches/feature@45").should route_to('projects/branches#destroy', project_id: 'gitlab/gitlabhq', id: 'feature@45')
- delete("/gitlab/gitlabhq/branches/feature%2345/foo/bar/baz").should route_to('projects/branches#destroy', project_id: 'gitlab/gitlabhq', id: 'feature#45/foo/bar/baz')
- delete("/gitlab/gitlabhq/branches/feature%2B45/foo/bar/baz").should route_to('projects/branches#destroy', project_id: 'gitlab/gitlabhq', id: 'feature+45/foo/bar/baz')
- delete("/gitlab/gitlabhq/branches/feature@45/foo/bar/baz").should route_to('projects/branches#destroy', project_id: 'gitlab/gitlabhq', id: 'feature@45/foo/bar/baz')
+describe Projects::BranchesController, 'routing' do
+ it 'to #branches' do
+ get('/gitlab/gitlabhq/branches').should route_to('projects/branches#index', project_id: 'gitlab/gitlabhq')
+ delete('/gitlab/gitlabhq/branches/feature%2345').should route_to('projects/branches#destroy', project_id: 'gitlab/gitlabhq', id: 'feature#45')
+ delete('/gitlab/gitlabhq/branches/feature%2B45').should route_to('projects/branches#destroy', project_id: 'gitlab/gitlabhq', id: 'feature+45')
+ delete('/gitlab/gitlabhq/branches/feature@45').should route_to('projects/branches#destroy', project_id: 'gitlab/gitlabhq', id: 'feature@45')
+ delete('/gitlab/gitlabhq/branches/feature%2345/foo/bar/baz').should route_to('projects/branches#destroy', project_id: 'gitlab/gitlabhq', id: 'feature#45/foo/bar/baz')
+ delete('/gitlab/gitlabhq/branches/feature%2B45/foo/bar/baz').should route_to('projects/branches#destroy', project_id: 'gitlab/gitlabhq', id: 'feature+45/foo/bar/baz')
+ delete('/gitlab/gitlabhq/branches/feature@45/foo/bar/baz').should route_to('projects/branches#destroy', project_id: 'gitlab/gitlabhq', id: 'feature@45/foo/bar/baz')
end
end
-describe Projects::TagsController, "routing" do
- it "to #tags" do
- get("/gitlab/gitlabhq/tags").should route_to('projects/tags#index', project_id: 'gitlab/gitlabhq')
- delete("/gitlab/gitlabhq/tags/feature%2345").should route_to('projects/tags#destroy', project_id: 'gitlab/gitlabhq', id: 'feature#45')
- delete("/gitlab/gitlabhq/tags/feature%2B45").should route_to('projects/tags#destroy', project_id: 'gitlab/gitlabhq', id: 'feature+45')
- delete("/gitlab/gitlabhq/tags/feature@45").should route_to('projects/tags#destroy', project_id: 'gitlab/gitlabhq', id: 'feature@45')
- delete("/gitlab/gitlabhq/tags/feature%2345/foo/bar/baz").should route_to('projects/tags#destroy', project_id: 'gitlab/gitlabhq', id: 'feature#45/foo/bar/baz')
- delete("/gitlab/gitlabhq/tags/feature%2B45/foo/bar/baz").should route_to('projects/tags#destroy', project_id: 'gitlab/gitlabhq', id: 'feature+45/foo/bar/baz')
- delete("/gitlab/gitlabhq/tags/feature@45/foo/bar/baz").should route_to('projects/tags#destroy', project_id: 'gitlab/gitlabhq', id: 'feature@45/foo/bar/baz')
+describe Projects::TagsController, 'routing' do
+ it 'to #tags' do
+ get('/gitlab/gitlabhq/tags').should route_to('projects/tags#index', project_id: 'gitlab/gitlabhq')
+ delete('/gitlab/gitlabhq/tags/feature%2345').should route_to('projects/tags#destroy', project_id: 'gitlab/gitlabhq', id: 'feature#45')
+ delete('/gitlab/gitlabhq/tags/feature%2B45').should route_to('projects/tags#destroy', project_id: 'gitlab/gitlabhq', id: 'feature+45')
+ delete('/gitlab/gitlabhq/tags/feature@45').should route_to('projects/tags#destroy', project_id: 'gitlab/gitlabhq', id: 'feature@45')
+ delete('/gitlab/gitlabhq/tags/feature%2345/foo/bar/baz').should route_to('projects/tags#destroy', project_id: 'gitlab/gitlabhq', id: 'feature#45/foo/bar/baz')
+ delete('/gitlab/gitlabhq/tags/feature%2B45/foo/bar/baz').should route_to('projects/tags#destroy', project_id: 'gitlab/gitlabhq', id: 'feature+45/foo/bar/baz')
+ delete('/gitlab/gitlabhq/tags/feature@45/foo/bar/baz').should route_to('projects/tags#destroy', project_id: 'gitlab/gitlabhq', id: 'feature@45/foo/bar/baz')
end
end
@@ -172,8 +172,8 @@ end
# project_deploy_key GET /:project_id/deploy_keys/:id(.:format) deploy_keys#show
# PUT /:project_id/deploy_keys/:id(.:format) deploy_keys#update
# DELETE /:project_id/deploy_keys/:id(.:format) deploy_keys#destroy
-describe Projects::DeployKeysController, "routing" do
- it_behaves_like "RESTful project resources" do
+describe Projects::DeployKeysController, 'routing' do
+ it_behaves_like 'RESTful project resources' do
let(:controller) { 'deploy_keys' }
end
end
@@ -181,8 +181,8 @@ end
# project_protected_branches GET /:project_id/protected_branches(.:format) protected_branches#index
# POST /:project_id/protected_branches(.:format) protected_branches#create
# project_protected_branch DELETE /:project_id/protected_branches/:id(.:format) protected_branches#destroy
-describe Projects::ProtectedBranchesController, "routing" do
- it_behaves_like "RESTful project resources" do
+describe Projects::ProtectedBranchesController, 'routing' do
+ it_behaves_like 'RESTful project resources' do
let(:actions) { [:index, :create, :destroy] }
let(:controller) { 'protected_branches' }
end
@@ -191,21 +191,21 @@ end
# switch_project_refs GET /:project_id/refs/switch(.:format) refs#switch
# logs_tree_project_ref GET /:project_id/refs/:id/logs_tree(.:format) refs#logs_tree
# logs_file_project_ref GET /:project_id/refs/:id/logs_tree/:path(.:format) refs#logs_tree
-describe Projects::RefsController, "routing" do
- it "to #switch" do
- get("/gitlab/gitlabhq/refs/switch").should route_to('projects/refs#switch', project_id: 'gitlab/gitlabhq')
- end
-
- it "to #logs_tree" do
- get("/gitlab/gitlabhq/refs/stable/logs_tree").should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'stable')
- get("/gitlab/gitlabhq/refs/feature%2345/logs_tree").should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'feature#45')
- get("/gitlab/gitlabhq/refs/feature%2B45/logs_tree").should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'feature+45')
- get("/gitlab/gitlabhq/refs/feature@45/logs_tree").should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'feature@45')
- get("/gitlab/gitlabhq/refs/stable/logs_tree/foo/bar/baz").should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'stable', path: 'foo/bar/baz')
- get("/gitlab/gitlabhq/refs/feature%2345/logs_tree/foo/bar/baz").should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'feature#45', path: 'foo/bar/baz')
- get("/gitlab/gitlabhq/refs/feature%2B45/logs_tree/foo/bar/baz").should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'feature+45', path: 'foo/bar/baz')
- get("/gitlab/gitlabhq/refs/feature@45/logs_tree/foo/bar/baz").should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'feature@45', path: 'foo/bar/baz')
- get("/gitlab/gitlabhq/refs/stable/logs_tree/files.scss").should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'stable', path: 'files.scss')
+describe Projects::RefsController, 'routing' do
+ it 'to #switch' do
+ get('/gitlab/gitlabhq/refs/switch').should route_to('projects/refs#switch', project_id: 'gitlab/gitlabhq')
+ end
+
+ it 'to #logs_tree' do
+ get('/gitlab/gitlabhq/refs/stable/logs_tree').should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'stable')
+ get('/gitlab/gitlabhq/refs/feature%2345/logs_tree').should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'feature#45')
+ get('/gitlab/gitlabhq/refs/feature%2B45/logs_tree').should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'feature+45')
+ get('/gitlab/gitlabhq/refs/feature@45/logs_tree').should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'feature@45')
+ get('/gitlab/gitlabhq/refs/stable/logs_tree/foo/bar/baz').should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'stable', path: 'foo/bar/baz')
+ get('/gitlab/gitlabhq/refs/feature%2345/logs_tree/foo/bar/baz').should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'feature#45', path: 'foo/bar/baz')
+ get('/gitlab/gitlabhq/refs/feature%2B45/logs_tree/foo/bar/baz').should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'feature+45', path: 'foo/bar/baz')
+ get('/gitlab/gitlabhq/refs/feature@45/logs_tree/foo/bar/baz').should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'feature@45', path: 'foo/bar/baz')
+ get('/gitlab/gitlabhq/refs/stable/logs_tree/files.scss').should route_to('projects/refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'stable', path: 'files.scss')
end
end
@@ -221,36 +221,36 @@ end
# project_merge_request GET /:project_id/merge_requests/:id(.:format) projects/merge_requests#show
# PUT /:project_id/merge_requests/:id(.:format) projects/merge_requests#update
# DELETE /:project_id/merge_requests/:id(.:format) projects/merge_requests#destroy
-describe Projects::MergeRequestsController, "routing" do
- it "to #diffs" do
- get("/gitlab/gitlabhq/merge_requests/1/diffs").should route_to('projects/merge_requests#diffs', project_id: 'gitlab/gitlabhq', id: '1')
+describe Projects::MergeRequestsController, 'routing' do
+ it 'to #diffs' do
+ get('/gitlab/gitlabhq/merge_requests/1/diffs').should route_to('projects/merge_requests#diffs', project_id: 'gitlab/gitlabhq', id: '1')
end
- it "to #automerge" do
+ it 'to #automerge' do
post('/gitlab/gitlabhq/merge_requests/1/automerge').should route_to(
'projects/merge_requests#automerge',
project_id: 'gitlab/gitlabhq', id: '1'
)
end
- it "to #automerge_check" do
- get("/gitlab/gitlabhq/merge_requests/1/automerge_check").should route_to('projects/merge_requests#automerge_check', project_id: 'gitlab/gitlabhq', id: '1')
+ it 'to #automerge_check' do
+ get('/gitlab/gitlabhq/merge_requests/1/automerge_check').should route_to('projects/merge_requests#automerge_check', project_id: 'gitlab/gitlabhq', id: '1')
end
- it "to #branch_from" do
- get("/gitlab/gitlabhq/merge_requests/branch_from").should route_to('projects/merge_requests#branch_from', project_id: 'gitlab/gitlabhq')
+ it 'to #branch_from' do
+ get('/gitlab/gitlabhq/merge_requests/branch_from').should route_to('projects/merge_requests#branch_from', project_id: 'gitlab/gitlabhq')
end
- it "to #branch_to" do
- get("/gitlab/gitlabhq/merge_requests/branch_to").should route_to('projects/merge_requests#branch_to', project_id: 'gitlab/gitlabhq')
+ it 'to #branch_to' do
+ get('/gitlab/gitlabhq/merge_requests/branch_to').should route_to('projects/merge_requests#branch_to', project_id: 'gitlab/gitlabhq')
end
- it "to #show" do
- get("/gitlab/gitlabhq/merge_requests/1.diff").should route_to('projects/merge_requests#show', project_id: 'gitlab/gitlabhq', id: '1', format: 'diff')
- get("/gitlab/gitlabhq/merge_requests/1.patch").should route_to('projects/merge_requests#show', project_id: 'gitlab/gitlabhq', id: '1', format: 'patch')
+ it 'to #show' do
+ get('/gitlab/gitlabhq/merge_requests/1.diff').should route_to('projects/merge_requests#show', project_id: 'gitlab/gitlabhq', id: '1', format: 'diff')
+ get('/gitlab/gitlabhq/merge_requests/1.patch').should route_to('projects/merge_requests#show', project_id: 'gitlab/gitlabhq', id: '1', format: 'patch')
end
- it_behaves_like "RESTful project resources" do
+ it_behaves_like 'RESTful project resources' do
let(:controller) { 'merge_requests' }
let(:actions) { [:index, :create, :new, :edit, :show, :update] }
end
@@ -264,37 +264,37 @@ end
# project_snippet GET /:project_id/snippets/:id(.:format) snippets#show
# PUT /:project_id/snippets/:id(.:format) snippets#update
# DELETE /:project_id/snippets/:id(.:format) snippets#destroy
-describe SnippetsController, "routing" do
- it "to #raw" do
- get("/gitlab/gitlabhq/snippets/1/raw").should route_to('projects/snippets#raw', project_id: 'gitlab/gitlabhq', id: '1')
+describe SnippetsController, 'routing' do
+ it 'to #raw' do
+ get('/gitlab/gitlabhq/snippets/1/raw').should route_to('projects/snippets#raw', project_id: 'gitlab/gitlabhq', id: '1')
end
- it "to #index" do
- get("/gitlab/gitlabhq/snippets").should route_to("projects/snippets#index", project_id: 'gitlab/gitlabhq')
+ it 'to #index' do
+ get('/gitlab/gitlabhq/snippets').should route_to('projects/snippets#index', project_id: 'gitlab/gitlabhq')
end
- it "to #create" do
- post("/gitlab/gitlabhq/snippets").should route_to("projects/snippets#create", project_id: 'gitlab/gitlabhq')
+ it 'to #create' do
+ post('/gitlab/gitlabhq/snippets').should route_to('projects/snippets#create', project_id: 'gitlab/gitlabhq')
end
- it "to #new" do
- get("/gitlab/gitlabhq/snippets/new").should route_to("projects/snippets#new", project_id: 'gitlab/gitlabhq')
+ it 'to #new' do
+ get('/gitlab/gitlabhq/snippets/new').should route_to('projects/snippets#new', project_id: 'gitlab/gitlabhq')
end
- it "to #edit" do
- get("/gitlab/gitlabhq/snippets/1/edit").should route_to("projects/snippets#edit", project_id: 'gitlab/gitlabhq', id: '1')
+ it 'to #edit' do
+ get('/gitlab/gitlabhq/snippets/1/edit').should route_to('projects/snippets#edit', project_id: 'gitlab/gitlabhq', id: '1')
end
- it "to #show" do
- get("/gitlab/gitlabhq/snippets/1").should route_to("projects/snippets#show", project_id: 'gitlab/gitlabhq', id: '1')
+ it 'to #show' do
+ get('/gitlab/gitlabhq/snippets/1').should route_to('projects/snippets#show', project_id: 'gitlab/gitlabhq', id: '1')
end
- it "to #update" do
- put("/gitlab/gitlabhq/snippets/1").should route_to("projects/snippets#update", project_id: 'gitlab/gitlabhq', id: '1')
+ it 'to #update' do
+ put('/gitlab/gitlabhq/snippets/1').should route_to('projects/snippets#update', project_id: 'gitlab/gitlabhq', id: '1')
end
- it "to #destroy" do
- delete("/gitlab/gitlabhq/snippets/1").should route_to("projects/snippets#destroy", project_id: 'gitlab/gitlabhq', id: '1')
+ it 'to #destroy' do
+ delete('/gitlab/gitlabhq/snippets/1').should route_to('projects/snippets#destroy', project_id: 'gitlab/gitlabhq', id: '1')
end
end
@@ -302,24 +302,24 @@ end
# project_hooks GET /:project_id/hooks(.:format) hooks#index
# POST /:project_id/hooks(.:format) hooks#create
# project_hook DELETE /:project_id/hooks/:id(.:format) hooks#destroy
-describe Projects::HooksController, "routing" do
- it "to #test" do
- get("/gitlab/gitlabhq/hooks/1/test").should route_to('projects/hooks#test', project_id: 'gitlab/gitlabhq', id: '1')
+describe Projects::HooksController, 'routing' do
+ it 'to #test' do
+ get('/gitlab/gitlabhq/hooks/1/test').should route_to('projects/hooks#test', project_id: 'gitlab/gitlabhq', id: '1')
end
- it_behaves_like "RESTful project resources" do
+ it_behaves_like 'RESTful project resources' do
let(:actions) { [:index, :create, :destroy] }
let(:controller) { 'hooks' }
end
end
# project_commit GET /:project_id/commit/:id(.:format) commit#show {id: /[[:alnum:]]{6,40}/, project_id: /[^\/]+/}
-describe Projects::CommitController, "routing" do
- it "to #show" do
- get("/gitlab/gitlabhq/commit/4246fb").should route_to('projects/commit#show', project_id: 'gitlab/gitlabhq', id: '4246fb')
- get("/gitlab/gitlabhq/commit/4246fb.diff").should route_to('projects/commit#show', project_id: 'gitlab/gitlabhq', id: '4246fb', format: 'diff')
- get("/gitlab/gitlabhq/commit/4246fb.patch").should route_to('projects/commit#show', project_id: 'gitlab/gitlabhq', id: '4246fb', format: 'patch')
- get("/gitlab/gitlabhq/commit/4246fbd13872934f72a8fd0d6fb1317b47b59cb5").should route_to('projects/commit#show', project_id: 'gitlab/gitlabhq', id: '4246fbd13872934f72a8fd0d6fb1317b47b59cb5')
+describe Projects::CommitController, 'routing' do
+ it 'to #show' do
+ get('/gitlab/gitlabhq/commit/4246fb').should route_to('projects/commit#show', project_id: 'gitlab/gitlabhq', id: '4246fb')
+ get('/gitlab/gitlabhq/commit/4246fb.diff').should route_to('projects/commit#show', project_id: 'gitlab/gitlabhq', id: '4246fb', format: 'diff')
+ get('/gitlab/gitlabhq/commit/4246fb.patch').should route_to('projects/commit#show', project_id: 'gitlab/gitlabhq', id: '4246fb', format: 'patch')
+ get('/gitlab/gitlabhq/commit/4246fbd13872934f72a8fd0d6fb1317b47b59cb5').should route_to('projects/commit#show', project_id: 'gitlab/gitlabhq', id: '4246fbd13872934f72a8fd0d6fb1317b47b59cb5')
end
end
@@ -327,14 +327,14 @@ end
# project_commits GET /:project_id/commits(.:format) commits#index
# POST /:project_id/commits(.:format) commits#create
# project_commit GET /:project_id/commits/:id(.:format) commits#show
-describe Projects::CommitsController, "routing" do
- it_behaves_like "RESTful project resources" do
+describe Projects::CommitsController, 'routing' do
+ it_behaves_like 'RESTful project resources' do
let(:actions) { [:show] }
let(:controller) { 'commits' }
end
- it "to #show" do
- get("/gitlab/gitlabhq/commits/master.atom").should route_to('projects/commits#show', project_id: 'gitlab/gitlabhq', id: "master", format: "atom")
+ it 'to #show' do
+ get('/gitlab/gitlabhq/commits/master.atom').should route_to('projects/commits#show', project_id: 'gitlab/gitlabhq', id: 'master', format: 'atom')
end
end
@@ -345,8 +345,8 @@ end
# project_team_member GET /:project_id/team_members/:id(.:format) team_members#show
# PUT /:project_id/team_members/:id(.:format) team_members#update
# DELETE /:project_id/team_members/:id(.:format) team_members#destroy
-describe Projects::TeamMembersController, "routing" do
- it_behaves_like "RESTful project resources" do
+describe Projects::TeamMembersController, 'routing' do
+ it_behaves_like 'RESTful project resources' do
let(:actions) { [:new, :create, :update, :destroy] }
let(:controller) { 'team_members' }
end
@@ -359,17 +359,17 @@ end
# project_milestone GET /:project_id/milestones/:id(.:format) milestones#show
# PUT /:project_id/milestones/:id(.:format) milestones#update
# DELETE /:project_id/milestones/:id(.:format) milestones#destroy
-describe Projects::MilestonesController, "routing" do
- it_behaves_like "RESTful project resources" do
+describe Projects::MilestonesController, 'routing' do
+ it_behaves_like 'RESTful project resources' do
let(:controller) { 'milestones' }
let(:actions) { [:index, :create, :new, :edit, :show, :update] }
end
end
# project_labels GET /:project_id/labels(.:format) labels#index
-describe Projects::LabelsController, "routing" do
- it "to #index" do
- get("/gitlab/gitlabhq/labels").should route_to('projects/labels#index', project_id: 'gitlab/gitlabhq')
+describe Projects::LabelsController, 'routing' do
+ it 'to #index' do
+ get('/gitlab/gitlabhq/labels').should route_to('projects/labels#index', project_id: 'gitlab/gitlabhq')
end
end
@@ -383,12 +383,12 @@ end
# project_issue GET /:project_id/issues/:id(.:format) issues#show
# PUT /:project_id/issues/:id(.:format) issues#update
# DELETE /:project_id/issues/:id(.:format) issues#destroy
-describe Projects::IssuesController, "routing" do
- it "to #bulk_update" do
- post("/gitlab/gitlabhq/issues/bulk_update").should route_to('projects/issues#bulk_update', project_id: 'gitlab/gitlabhq')
+describe Projects::IssuesController, 'routing' do
+ it 'to #bulk_update' do
+ post('/gitlab/gitlabhq/issues/bulk_update').should route_to('projects/issues#bulk_update', project_id: 'gitlab/gitlabhq')
end
- it_behaves_like "RESTful project resources" do
+ it_behaves_like 'RESTful project resources' do
let(:controller) { 'issues' }
let(:actions) { [:index, :create, :new, :edit, :show, :update] }
end
@@ -397,54 +397,50 @@ end
# project_notes GET /:project_id/notes(.:format) notes#index
# POST /:project_id/notes(.:format) notes#create
# project_note DELETE /:project_id/notes/:id(.:format) notes#destroy
-describe Projects::NotesController, "routing" do
- it_behaves_like "RESTful project resources" do
+describe Projects::NotesController, 'routing' do
+ it_behaves_like 'RESTful project resources' do
let(:actions) { [:index, :create, :destroy] }
let(:controller) { 'notes' }
end
end
# project_blame GET /:project_id/blame/:id(.:format) blame#show {id: /.+/, project_id: /[^\/]+/}
-describe Projects::BlameController, "routing" do
- it "to #show" do
- get("/gitlab/gitlabhq/blame/master/app/models/project.rb").should route_to('projects/blame#show', project_id: 'gitlab/gitlabhq', id: 'master/app/models/project.rb')
- get("/gitlab/gitlabhq/blame/master/files.scss").should route_to('projects/blame#show', project_id: 'gitlab/gitlabhq', id: 'master/files.scss')
+describe Projects::BlameController, 'routing' do
+ it 'to #show' do
+ get('/gitlab/gitlabhq/blame/master/app/models/project.rb').should route_to('projects/blame#show', project_id: 'gitlab/gitlabhq', id: 'master/app/models/project.rb')
+ get('/gitlab/gitlabhq/blame/master/files.scss').should route_to('projects/blame#show', project_id: 'gitlab/gitlabhq', id: 'master/files.scss')
end
end
# project_blob GET /:project_id/blob/:id(.:format) blob#show {id: /.+/, project_id: /[^\/]+/}
-describe Projects::BlobController, "routing" do
- it "to #show" do
- get("/gitlab/gitlabhq/blob/master/app/models/project.rb").should route_to('projects/blob#show', project_id: 'gitlab/gitlabhq', id: 'master/app/models/project.rb')
- get("/gitlab/gitlabhq/blob/master/app/models/compare.rb").should route_to('projects/blob#show', project_id: 'gitlab/gitlabhq', id: 'master/app/models/compare.rb')
- get("/gitlab/gitlabhq/blob/master/app/models/diff.js").should route_to('projects/blob#show', project_id: 'gitlab/gitlabhq', id: 'master/app/models/diff.js')
- get("/gitlab/gitlabhq/blob/master/files.scss").should route_to('projects/blob#show', project_id: 'gitlab/gitlabhq', id: 'master/files.scss')
+describe Projects::BlobController, 'routing' do
+ it 'to #show' do
+ get('/gitlab/gitlabhq/blob/master/app/models/project.rb').should route_to('projects/blob#show', project_id: 'gitlab/gitlabhq', id: 'master/app/models/project.rb')
+ get('/gitlab/gitlabhq/blob/master/app/models/compare.rb').should route_to('projects/blob#show', project_id: 'gitlab/gitlabhq', id: 'master/app/models/compare.rb')
+ get('/gitlab/gitlabhq/blob/master/app/models/diff.js').should route_to('projects/blob#show', project_id: 'gitlab/gitlabhq', id: 'master/app/models/diff.js')
+ get('/gitlab/gitlabhq/blob/master/files.scss').should route_to('projects/blob#show', project_id: 'gitlab/gitlabhq', id: 'master/files.scss')
end
end
# project_tree GET /:project_id/tree/:id(.:format) tree#show {id: /.+/, project_id: /[^\/]+/}
-describe Projects::TreeController, "routing" do
- it "to #show" do
- get("/gitlab/gitlabhq/tree/master/app/models/project.rb").should route_to('projects/tree#show', project_id: 'gitlab/gitlabhq', id: 'master/app/models/project.rb')
- get("/gitlab/gitlabhq/tree/master/files.scss").should route_to('projects/tree#show', project_id: 'gitlab/gitlabhq', id: 'master/files.scss')
+describe Projects::TreeController, 'routing' do
+ it 'to #show' do
+ get('/gitlab/gitlabhq/tree/master/app/models/project.rb').should route_to('projects/tree#show', project_id: 'gitlab/gitlabhq', id: 'master/app/models/project.rb')
+ get('/gitlab/gitlabhq/tree/master/files.scss').should route_to('projects/tree#show', project_id: 'gitlab/gitlabhq', id: 'master/files.scss')
end
end
-describe Projects::EditTreeController, 'routing' do
- it 'to #show' do
+describe Projects::BlobController, 'routing' do
+ it 'to #edit' do
get('/gitlab/gitlabhq/edit/master/app/models/project.rb').should(
- route_to('projects/edit_tree#show',
+ route_to('projects/blob#edit',
project_id: 'gitlab/gitlabhq',
id: 'master/app/models/project.rb'))
- get('/gitlab/gitlabhq/edit/master/app/models/project.rb/preview').should(
- route_to('projects/edit_tree#show',
- project_id: 'gitlab/gitlabhq',
- id: 'master/app/models/project.rb/preview'))
end
it 'to #preview' do
- post('/gitlab/gitlabhq/edit/master/app/models/project.rb/preview').should(
- route_to('projects/edit_tree#preview',
+ post('/gitlab/gitlabhq/preview/master/app/models/project.rb').should(
+ route_to('projects/blob#preview',
project_id: 'gitlab/gitlabhq',
id: 'master/app/models/project.rb'))
end
@@ -453,40 +449,48 @@ end
# project_compare_index GET /:project_id/compare(.:format) compare#index {id: /[^\/]+/, project_id: /[^\/]+/}
# POST /:project_id/compare(.:format) compare#create {id: /[^\/]+/, project_id: /[^\/]+/}
# project_compare /:project_id/compare/:from...:to(.:format) compare#show {from: /.+/, to: /.+/, id: /[^\/]+/, project_id: /[^\/]+/}
-describe Projects::CompareController, "routing" do
- it "to #index" do
- get("/gitlab/gitlabhq/compare").should route_to('projects/compare#index', project_id: 'gitlab/gitlabhq')
+describe Projects::CompareController, 'routing' do
+ it 'to #index' do
+ get('/gitlab/gitlabhq/compare').should route_to('projects/compare#index', project_id: 'gitlab/gitlabhq')
end
- it "to #compare" do
- post("/gitlab/gitlabhq/compare").should route_to('projects/compare#create', project_id: 'gitlab/gitlabhq')
+ it 'to #compare' do
+ post('/gitlab/gitlabhq/compare').should route_to('projects/compare#create', project_id: 'gitlab/gitlabhq')
end
- it "to #show" do
- get("/gitlab/gitlabhq/compare/master...stable").should route_to('projects/compare#show', project_id: 'gitlab/gitlabhq', from: 'master', to: 'stable')
- get("/gitlab/gitlabhq/compare/issue/1234...stable").should route_to('projects/compare#show', project_id: 'gitlab/gitlabhq', from: 'issue/1234', to: 'stable')
+ it 'to #show' do
+ get('/gitlab/gitlabhq/compare/master...stable').should route_to('projects/compare#show', project_id: 'gitlab/gitlabhq', from: 'master', to: 'stable')
+ get('/gitlab/gitlabhq/compare/issue/1234...stable').should route_to('projects/compare#show', project_id: 'gitlab/gitlabhq', from: 'issue/1234', to: 'stable')
end
end
-describe Projects::NetworkController, "routing" do
- it "to #show" do
- get("/gitlab/gitlabhq/network/master").should route_to('projects/network#show', project_id: 'gitlab/gitlabhq', id: 'master')
- get("/gitlab/gitlabhq/network/master.json").should route_to('projects/network#show', project_id: 'gitlab/gitlabhq', id: 'master', format: "json")
+describe Projects::NetworkController, 'routing' do
+ it 'to #show' do
+ get('/gitlab/gitlabhq/network/master').should route_to('projects/network#show', project_id: 'gitlab/gitlabhq', id: 'master')
+ get('/gitlab/gitlabhq/network/master.json').should route_to('projects/network#show', project_id: 'gitlab/gitlabhq', id: 'master', format: 'json')
end
end
-describe Projects::GraphsController, "routing" do
- it "to #show" do
- get("/gitlab/gitlabhq/graphs/master").should route_to('projects/graphs#show', project_id: 'gitlab/gitlabhq', id: 'master')
+describe Projects::GraphsController, 'routing' do
+ it 'to #show' do
+ get('/gitlab/gitlabhq/graphs/master').should route_to('projects/graphs#show', project_id: 'gitlab/gitlabhq', id: 'master')
end
end
-describe Projects::ForksController, "routing" do
- it "to #new" do
- get("/gitlab/gitlabhq/fork/new").should route_to("projects/forks#new", project_id: 'gitlab/gitlabhq')
+describe Projects::ForksController, 'routing' do
+ it 'to #new' do
+ get('/gitlab/gitlabhq/fork/new').should route_to('projects/forks#new', project_id: 'gitlab/gitlabhq')
+ end
+
+ it 'to #create' do
+ post('/gitlab/gitlabhq/fork').should route_to('projects/forks#create', project_id: 'gitlab/gitlabhq')
end
+end
- it "to #create" do
- post("/gitlab/gitlabhq/fork").should route_to("projects/forks#create", project_id: 'gitlab/gitlabhq')
+# project_avatar DELETE /project/avatar(.:format) projects/avatars#destroy
+describe Projects::AvatarsController, 'routing' do
+ it 'to #destroy' do
+ delete('/gitlab/gitlabhq/avatar').should route_to(
+ 'projects/avatars#destroy', project_id: 'gitlab/gitlabhq')
end
end
diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb
index 347560414e7..36030577835 100644
--- a/spec/services/issues/update_service_spec.rb
+++ b/spec/services/issues/update_service_spec.rb
@@ -22,6 +22,7 @@ describe Issues::UpdateService do
}
@issue = Issues::UpdateService.new(project, user, opts).execute(issue)
+ @issue.reload
end
it { @issue.should be_valid }
diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb
index c8f40f48bab..0e60baae2c4 100644
--- a/spec/services/merge_requests/update_service_spec.rb
+++ b/spec/services/merge_requests/update_service_spec.rb
@@ -21,12 +21,14 @@ describe MergeRequests::UpdateService do
state_event: 'close'
}
end
+
let(:service) { MergeRequests::UpdateService.new(project, user, opts) }
before do
service.stub(:execute_hooks)
@merge_request = service.execute(merge_request)
+ @merge_request.reload
end
it { @merge_request.should be_valid }
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index e305536f7ee..2ba1e3372b9 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -187,7 +187,7 @@ describe NotificationService do
end
describe 'Issues' do
- let(:issue) { create :issue, assignee: create(:user) }
+ let(:issue) { create :issue, assignee: create(:user), description: 'cc @participant' }
before do
build_team(issue.project)
@@ -197,6 +197,7 @@ describe NotificationService do
it do
should_email(issue.assignee_id)
should_email(@u_watcher.id)
+ should_email(@u_participant_mentioned.id)
should_not_email(@u_mentioned.id)
should_not_email(@u_participating.id)
should_not_email(@u_disabled.id)
@@ -222,6 +223,7 @@ describe NotificationService do
it 'should email new assignee' do
should_email(issue.assignee_id)
should_email(@u_watcher.id)
+ should_email(@u_participant_mentioned.id)
should_not_email(@u_participating.id)
should_not_email(@u_disabled.id)
@@ -242,6 +244,7 @@ describe NotificationService do
should_email(issue.assignee_id)
should_email(issue.author_id)
should_email(@u_watcher.id)
+ should_email(@u_participant_mentioned.id)
should_not_email(@u_participating.id)
should_not_email(@u_disabled.id)
@@ -262,6 +265,7 @@ describe NotificationService do
should_email(issue.assignee_id)
should_email(issue.author_id)
should_email(@u_watcher.id)
+ should_email(@u_participant_mentioned.id)
should_not_email(@u_participating.id)
should_not_email(@u_disabled.id)
@@ -404,6 +408,7 @@ describe NotificationService do
def build_team(project)
@u_watcher = create(:user, notification_level: Notification::N_WATCH)
@u_participating = create(:user, notification_level: Notification::N_PARTICIPATING)
+ @u_participant_mentioned = create(:user, username: 'participant', notification_level: Notification::N_PARTICIPATING)
@u_disabled = create(:user, notification_level: Notification::N_DISABLED)
@u_mentioned = create(:user, username: 'mention', notification_level: Notification::N_MENTION)
@u_committer = create(:user, username: 'committer')
diff --git a/spec/services/system_hooks_service_spec.rb b/spec/services/system_hooks_service_spec.rb
index 573446d3a19..a45e9d0575c 100644
--- a/spec/services/system_hooks_service_spec.rb
+++ b/spec/services/system_hooks_service_spec.rb
@@ -5,6 +5,8 @@ describe SystemHooksService do
let (:project) { create :project }
let (:project_member) { create :project_member }
let (:key) { create(:key, user: user) }
+ let (:group) { create(:group) }
+ let (:group_member) { create(:group_member) }
context 'event data' do
it { event_data(user, :create).should include(:event_name, :name, :created_at, :email, :user_id) }
@@ -15,6 +17,31 @@ describe SystemHooksService do
it { event_data(project_member, :destroy).should include(:event_name, :created_at, :project_name, :project_path, :project_id, :user_name, :user_email, :access_level, :project_visibility) }
it { event_data(key, :create).should include(:username, :key, :id) }
it { event_data(key, :destroy).should include(:username, :key, :id) }
+
+ it do
+ event_data(group, :create).should include(
+ :event_name, :name, :created_at, :path, :group_id, :owner_name,
+ :owner_email
+ )
+ end
+ it do
+ event_data(group, :destroy).should include(
+ :event_name, :name, :created_at, :path, :group_id, :owner_name,
+ :owner_email
+ )
+ end
+ it do
+ event_data(group_member, :create).should include(
+ :event_name, :created_at, :group_name, :group_path, :group_id, :user_id,
+ :user_name, :user_email, :group_access
+ )
+ end
+ it do
+ event_data(group_member, :destroy).should include(
+ :event_name, :created_at, :group_name, :group_path, :group_id, :user_id,
+ :user_name, :user_email, :group_access
+ )
+ end
end
context 'event names' do
@@ -26,6 +53,10 @@ describe SystemHooksService do
it { event_name(project_member, :destroy).should eq "user_remove_from_team" }
it { event_name(key, :create).should eq 'key_create' }
it { event_name(key, :destroy).should eq 'key_destroy' }
+ it { event_name(group, :create).should eq 'group_create' }
+ it { event_name(group, :destroy).should eq 'group_destroy' }
+ it { event_name(group_member, :create).should eq 'user_add_to_group' }
+ it { event_name(group_member, :destroy).should eq 'user_remove_from_group' }
end
def event_data(*args)