summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--CHANGELOG5
-rw-r--r--app/assets/javascripts/dispatcher.js.coffee1
-rw-r--r--app/assets/stylesheets/generic/typography.scss7
-rw-r--r--app/controllers/admin/application_controller.rb2
-rw-r--r--app/controllers/application_controller.rb1
-rw-r--r--app/controllers/dashboard/application_controller.rb3
-rw-r--r--app/controllers/dashboard/groups_controller.rb2
-rw-r--r--app/controllers/dashboard/milestones_controller.rb2
-rw-r--r--app/controllers/dashboard/projects_controller.rb2
-rw-r--r--app/controllers/dashboard_controller.rb6
-rw-r--r--app/controllers/explore/application_controller.rb3
-rw-r--r--app/controllers/explore/groups_controller.rb4
-rw-r--r--app/controllers/explore/projects_controller.rb4
-rw-r--r--app/controllers/groups/application_controller.rb9
-rw-r--r--app/controllers/groups/avatars_controller.rb2
-rw-r--r--app/controllers/groups/group_members_controller.rb2
-rw-r--r--app/controllers/groups/milestones_controller.rb4
-rw-r--r--app/controllers/groups_controller.rb11
-rw-r--r--app/controllers/help_controller.rb10
-rw-r--r--app/controllers/import/google_code_controller.rb1
-rw-r--r--app/controllers/invites_controller.rb2
-rw-r--r--app/controllers/oauth/applications_controller.rb5
-rw-r--r--app/controllers/oauth/authorizations_controller.rb3
-rw-r--r--app/controllers/oauth/authorized_applications_controller.rb4
-rw-r--r--app/controllers/profiles/accounts_controller.rb4
-rw-r--r--app/controllers/profiles/application_controller.rb3
-rw-r--r--app/controllers/profiles/avatars_controller.rb4
-rw-r--r--app/controllers/profiles/emails_controller.rb4
-rw-r--r--app/controllers/profiles/keys_controller.rb3
-rw-r--r--app/controllers/profiles/notifications_controller.rb4
-rw-r--r--app/controllers/profiles/passwords_controller.rb13
-rw-r--r--app/controllers/profiles_controller.rb4
-rw-r--r--app/controllers/projects/application_controller.rb10
-rw-r--r--app/controllers/projects/avatars_controller.rb2
-rw-r--r--app/controllers/projects/forks_controller.rb1
-rw-r--r--app/controllers/projects/uploads_controller.rb2
-rw-r--r--app/controllers/projects_controller.rb27
-rw-r--r--app/controllers/search_controller.rb2
-rw-r--r--app/controllers/snippets_controller.rb14
-rw-r--r--app/controllers/users_controller.rb12
-rw-r--r--app/helpers/gitlab_markdown_helper.rb7
-rw-r--r--app/helpers/groups_helper.rb18
-rw-r--r--app/helpers/issues_helper.rb13
-rw-r--r--app/helpers/page_layout_helper.rb26
-rw-r--r--app/helpers/projects_helper.rb40
-rw-r--r--app/mailers/devise_mailer.rb4
-rw-r--r--app/models/external_issue.rb4
-rw-r--r--app/models/project.rb16
-rw-r--r--app/views/admin/application_settings/show.html.haml1
-rw-r--r--app/views/admin/applications/edit.html.haml3
-rw-r--r--app/views/admin/applications/index.html.haml1
-rw-r--r--app/views/admin/applications/new.html.haml3
-rw-r--r--app/views/admin/applications/show.html.haml1
-rw-r--r--app/views/admin/background_jobs/show.html.haml1
-rw-r--r--app/views/admin/broadcast_messages/index.html.haml1
-rw-r--r--app/views/admin/deploy_keys/index.html.haml1
-rw-r--r--app/views/admin/deploy_keys/new.html.haml1
-rw-r--r--app/views/admin/deploy_keys/show.html.haml1
-rw-r--r--app/views/admin/groups/edit.html.haml1
-rw-r--r--app/views/admin/groups/index.html.haml1
-rw-r--r--app/views/admin/groups/new.html.haml1
-rw-r--r--app/views/admin/groups/show.html.haml1
-rw-r--r--app/views/admin/hooks/index.html.haml1
-rw-r--r--app/views/admin/keys/show.html.haml1
-rw-r--r--app/views/admin/logs/show.html.haml1
-rw-r--r--app/views/admin/projects/index.html.haml1
-rw-r--r--app/views/admin/projects/show.html.haml1
-rw-r--r--app/views/admin/services/edit.html.haml1
-rw-r--r--app/views/admin/services/index.html.haml1
-rw-r--r--app/views/admin/users/edit.html.haml1
-rw-r--r--app/views/admin/users/index.html.haml1
-rw-r--r--app/views/admin/users/new.html.haml1
-rw-r--r--app/views/admin/users/show.html.haml1
-rw-r--r--app/views/dashboard/groups/index.html.haml1
-rw-r--r--app/views/dashboard/issues.html.haml1
-rw-r--r--app/views/dashboard/merge_requests.html.haml1
-rw-r--r--app/views/dashboard/milestones/index.html.haml1
-rw-r--r--app/views/dashboard/milestones/show.html.haml1
-rw-r--r--app/views/dashboard/projects/starred.html.haml1
-rw-r--r--app/views/devise/registrations/new.html.haml3
-rw-r--r--app/views/devise/sessions/new.html.haml1
-rw-r--r--app/views/doorkeeper/applications/edit.html.haml3
-rw-r--r--app/views/doorkeeper/applications/index.html.haml3
-rw-r--r--app/views/doorkeeper/applications/show.html.haml1
-rw-r--r--app/views/errors/access_denied.html.haml1
-rw-r--r--app/views/errors/encoding.html.haml1
-rw-r--r--app/views/errors/git_not_found.html.haml1
-rw-r--r--app/views/errors/not_found.html.haml1
-rw-r--r--app/views/errors/omniauth_error.html.haml1
-rw-r--r--app/views/explore/groups/index.html.haml1
-rw-r--r--app/views/explore/projects/index.html.haml1
-rw-r--r--app/views/explore/projects/starred.html.haml1
-rw-r--r--app/views/explore/projects/trending.html.haml1
-rw-r--r--app/views/groups/_settings_nav.html.haml11
-rw-r--r--app/views/groups/edit.html.haml1
-rw-r--r--app/views/groups/group_members/index.html.haml1
-rw-r--r--app/views/groups/issues.html.haml1
-rw-r--r--app/views/groups/merge_requests.html.haml1
-rw-r--r--app/views/groups/milestones/index.html.haml1
-rw-r--r--app/views/groups/milestones/show.html.haml1
-rw-r--r--app/views/groups/new.html.haml2
-rw-r--r--app/views/groups/projects.html.haml1
-rw-r--r--app/views/help/show.html.haml1
-rw-r--r--app/views/help/ui.html.haml1
-rw-r--r--app/views/import/bitbucket/status.html.haml1
-rw-r--r--app/views/import/github/status.html.haml1
-rw-r--r--app/views/import/gitlab/status.html.haml1
-rw-r--r--app/views/import/gitorious/status.html.haml1
-rw-r--r--app/views/import/google_code/new.html.haml1
-rw-r--r--app/views/import/google_code/new_user_map.html.haml1
-rw-r--r--app/views/import/google_code/status.html.haml39
-rw-r--r--app/views/invites/show.html.haml1
-rw-r--r--app/views/layouts/_head.html.haml5
-rw-r--r--app/views/layouts/_page.html.haml4
-rw-r--r--app/views/layouts/admin.html.haml11
-rw-r--r--app/views/layouts/application.html.haml12
-rw-r--r--app/views/layouts/dashboard.html.haml5
-rw-r--r--app/views/layouts/errors.html.haml2
-rw-r--r--app/views/layouts/explore.html.haml15
-rw-r--r--app/views/layouts/group.html.haml11
-rw-r--r--app/views/layouts/help.html.haml4
-rw-r--r--app/views/layouts/nav/_group.html.haml13
-rw-r--r--app/views/layouts/nav/_project.html.haml156
-rw-r--r--app/views/layouts/nav/_project_settings.html.haml41
-rw-r--r--app/views/layouts/nav/_snippets.html.haml11
-rw-r--r--app/views/layouts/navless.html.haml7
-rw-r--r--app/views/layouts/profile.html.haml11
-rw-r--r--app/views/layouts/project.html.haml8
-rw-r--r--app/views/layouts/project_settings.html.haml12
-rw-r--r--app/views/layouts/projects.html.haml7
-rw-r--r--app/views/layouts/public_group.html.haml6
-rw-r--r--app/views/layouts/public_projects.html.haml6
-rw-r--r--app/views/layouts/public_users.html.haml6
-rw-r--r--app/views/layouts/search.html.haml10
-rw-r--r--app/views/layouts/snippets.html.haml11
-rw-r--r--app/views/profiles/accounts/show.html.haml1
-rw-r--r--app/views/profiles/applications.html.haml1
-rw-r--r--app/views/profiles/design.html.haml1
-rw-r--r--app/views/profiles/emails/index.html.haml1
-rw-r--r--app/views/profiles/history.html.haml1
-rw-r--r--app/views/profiles/keys/index.html.haml1
-rw-r--r--app/views/profiles/keys/new.html.haml1
-rw-r--r--app/views/profiles/keys/show.html.haml1
-rw-r--r--app/views/profiles/notifications/show.html.haml1
-rw-r--r--app/views/profiles/passwords/edit.html.haml1
-rw-r--r--app/views/profiles/passwords/new.html.haml2
-rw-r--r--app/views/profiles/show.html.haml1
-rw-r--r--app/views/projects/_settings_nav.html.haml31
-rw-r--r--app/views/projects/blame/show.html.haml1
-rw-r--r--app/views/projects/blob/edit.html.haml1
-rw-r--r--app/views/projects/blob/new.html.haml1
-rw-r--r--app/views/projects/blob/show.html.haml1
-rw-r--r--app/views/projects/branches/index.html.haml1
-rw-r--r--app/views/projects/branches/new.html.haml1
-rw-r--r--app/views/projects/commit/show.html.haml1
-rw-r--r--app/views/projects/commits/show.html.haml1
-rw-r--r--app/views/projects/compare/index.html.haml1
-rw-r--r--app/views/projects/compare/show.html.haml1
-rw-r--r--app/views/projects/deploy_keys/index.html.haml1
-rw-r--r--app/views/projects/deploy_keys/new.html.haml1
-rw-r--r--app/views/projects/deploy_keys/show.html.haml1
-rw-r--r--app/views/projects/forks/error.html.haml1
-rw-r--r--app/views/projects/forks/new.html.haml1
-rw-r--r--app/views/projects/graphs/commits.html.haml1
-rw-r--r--app/views/projects/graphs/show.html.haml1
-rw-r--r--app/views/projects/hooks/index.html.haml1
-rw-r--r--app/views/projects/imports/new.html.haml1
-rw-r--r--app/views/projects/imports/show.html.haml1
-rw-r--r--app/views/projects/issues/edit.html.haml1
-rw-r--r--app/views/projects/issues/index.html.haml1
-rw-r--r--app/views/projects/issues/new.html.haml1
-rw-r--r--app/views/projects/issues/show.html.haml1
-rw-r--r--app/views/projects/labels/edit.html.haml1
-rw-r--r--app/views/projects/labels/index.html.haml1
-rw-r--r--app/views/projects/labels/new.html.haml1
-rw-r--r--app/views/projects/merge_requests/_show.html.haml1
-rw-r--r--app/views/projects/merge_requests/edit.html.haml1
-rw-r--r--app/views/projects/merge_requests/index.html.haml1
-rw-r--r--app/views/projects/merge_requests/invalid.html.haml1
-rw-r--r--app/views/projects/merge_requests/new.html.haml1
-rw-r--r--app/views/projects/milestones/edit.html.haml1
-rw-r--r--app/views/projects/milestones/index.html.haml1
-rw-r--r--app/views/projects/milestones/new.html.haml1
-rw-r--r--app/views/projects/milestones/show.html.haml1
-rw-r--r--app/views/projects/network/show.html.haml1
-rw-r--r--app/views/projects/new.html.haml2
-rw-r--r--app/views/projects/project_members/import.html.haml1
-rw-r--r--app/views/projects/project_members/index.html.haml1
-rw-r--r--app/views/projects/protected_branches/index.html.haml1
-rw-r--r--app/views/projects/services/edit.html.haml1
-rw-r--r--app/views/projects/services/index.html.haml1
-rw-r--r--app/views/projects/snippets/edit.html.haml1
-rw-r--r--app/views/projects/snippets/index.html.haml1
-rw-r--r--app/views/projects/snippets/new.html.haml1
-rw-r--r--app/views/projects/snippets/show.html.haml1
-rw-r--r--app/views/projects/tags/index.html.haml1
-rw-r--r--app/views/projects/tags/new.html.haml1
-rw-r--r--app/views/projects/tree/show.html.haml1
-rw-r--r--app/views/projects/wikis/edit.html.haml1
-rw-r--r--app/views/projects/wikis/empty.html.haml1
-rw-r--r--app/views/projects/wikis/git_access.html.haml1
-rw-r--r--app/views/projects/wikis/history.html.haml1
-rw-r--r--app/views/projects/wikis/pages.html.haml1
-rw-r--r--app/views/projects/wikis/show.html.haml1
-rw-r--r--app/views/search/show.html.haml1
-rw-r--r--app/views/snippets/current_user_index.html.haml1
-rw-r--r--app/views/snippets/edit.html.haml1
-rw-r--r--app/views/snippets/index.html.haml1
-rw-r--r--app/views/snippets/new.html.haml1
-rw-r--r--app/views/snippets/show.html.haml1
-rw-r--r--app/views/snippets/user_index.html.haml1
-rw-r--r--app/views/users/show.html.haml3
-rw-r--r--config/initializers/devise.rb7
-rw-r--r--db/migrate/20150425164648_add_missing_unique_indices.acts_as_taggable_on_engine.rb20
-rw-r--r--db/migrate/20150425164649_add_taggings_counter_cache_to_tags.acts_as_taggable_on_engine.rb15
-rw-r--r--db/migrate/20150425164650_add_missing_taggable_index.acts_as_taggable_on_engine.rb10
-rw-r--r--db/migrate/20150425164651_change_collation_for_tag_names.acts_as_taggable_on_engine.rb10
-rw-r--r--db/schema.rb7
-rw-r--r--doc/api/README.md1
-rw-r--r--doc/update/upgrader.md6
-rw-r--r--features/project/project.feature7
-rw-r--r--features/steps/project/project.rb8
-rw-r--r--features/steps/shared/markdown.rb8
-rw-r--r--lib/gitlab/google_code_import/client.rb4
-rw-r--r--lib/gitlab/markdown.rb69
-rw-r--r--lib/gitlab/markdown/autolink_filter.rb100
-rw-r--r--lib/gitlab/markdown/issue_reference_filter.rb17
-rw-r--r--lib/gitlab/markdown/merge_request_reference_filter.rb1
-rw-r--r--lib/gitlab/markdown/sanitization_filter.rb38
-rw-r--r--lib/gitlab/markdown/table_of_contents_filter.rb62
-rw-r--r--lib/gitlab/theme.rb2
-rw-r--r--lib/redcarpet/render/gitlab_html.rb39
-rw-r--r--spec/controllers/import/google_code_controller_spec.rb13
-rw-r--r--spec/factories/projects.rb20
-rw-r--r--spec/features/markdown_spec.rb391
-rw-r--r--spec/fixtures/markdown.md.erb178
-rw-r--r--spec/helpers/gitlab_markdown_helper_spec.rb107
-rw-r--r--spec/helpers/issues_helper_spec.rb18
-rw-r--r--spec/lib/gitlab/google_code_import/client_spec.rb1
-rw-r--r--spec/lib/gitlab/markdown/autolink_filter_spec.rb106
-rw-r--r--spec/lib/gitlab/markdown/external_issue_reference_filter_spec.rb19
-rw-r--r--spec/lib/gitlab/markdown/issue_reference_filter_spec.rb8
-rw-r--r--spec/lib/gitlab/markdown/sanitization_filter_spec.rb81
-rw-r--r--spec/lib/gitlab/markdown/table_of_contents_filter_spec.rb101
-rw-r--r--spec/mailers/notify_spec.rb3
-rw-r--r--spec/models/project_spec.rb61
-rw-r--r--spec/support/test_env.rb3
248 files changed, 1749 insertions, 734 deletions
diff --git a/.gitignore b/.gitignore
index 7a7b5c93936..3e30fb8cf77 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,6 +37,6 @@ public/assets/
public/uploads.*
public/uploads/
rails_best_practices_output.html
-tags
+/tags
tmp/
vendor/bundle/*
diff --git a/CHANGELOG b/CHANGELOG
index 3af83ddc256..be0c005fa06 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,7 +1,9 @@
Please view this file on the master branch, on stable branches it's out of date.
v 7.11.0 (unreleased)
+ - Make Reply-To config apply to change e-mail confirmation and other Devise notifications (Stan Hu)
- Don't allow a merge request to be merged when its title starts with "WIP".
+ - Add a page title to every page.
- Get Gitorious importer to work again.
- Fix clone URL field and X11 Primary selection (Dmitry Medvinsky)
- Ignore invalid lines in .gitmodules
@@ -11,6 +13,7 @@ v 7.11.0 (unreleased)
- Fix "Revspec not found" errors when viewing diffs in a forked project with submodules (Stan Hu)
-
- Fix broken file browsing with relative submodule in personal projects (Stan Hu)
+ - Fix DB error when trying to tag a repository (Stan Hu)
- Add "Reply quoting selected text" shortcut key (`r`)
- Fix bug causing `@whatever` inside an issue's first code block to be picked up as a user mention.
- Fix bug causing `@whatever` inside an inline code snippet (backtick-style) to be picked up as a user mention.
@@ -21,7 +24,7 @@ v 7.11.0 (unreleased)
- Include commit comments in MR from a forked project.
- Fix adding new group members from admin area
- Add default project and snippet visibility settings to the admin web UI.
- -
+ - Show incompatible projects in Google Code import status (Stan Hu)
- Fix bug where commit data would not appear in some subdirectories (Stan Hu)
- Fix bug where Slack service channel was not saved in admin template settings. (Stan Hu)
- Move snippets UI to fluid layout
diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee
index 9aee3b281f3..06787ddf874 100644
--- a/app/assets/javascripts/dispatcher.js.coffee
+++ b/app/assets/javascripts/dispatcher.js.coffee
@@ -8,7 +8,6 @@ class Dispatcher
initPageScripts: ->
page = $('body').attr('data-page')
- project_id = $('body').attr('data-project-id')
unless page
return false
diff --git a/app/assets/stylesheets/generic/typography.scss b/app/assets/stylesheets/generic/typography.scss
index 80190424c1b..36a9a540747 100644
--- a/app/assets/stylesheets/generic/typography.scss
+++ b/app/assets/stylesheets/generic/typography.scss
@@ -35,7 +35,12 @@ pre {
/* Link to current header. */
h1, h2, h3, h4, h5, h6 {
position: relative;
- &:hover > :last-child {
+
+ a.anchor {
+ display: none;
+ }
+
+ &:hover > a.anchor {
$size: 16px;
position: absolute;
right: 100%;
diff --git a/app/controllers/admin/application_controller.rb b/app/controllers/admin/application_controller.rb
index fe5456f820c..56e24386463 100644
--- a/app/controllers/admin/application_controller.rb
+++ b/app/controllers/admin/application_controller.rb
@@ -2,8 +2,8 @@
#
# Automatically sets the layout and ensures an administrator is logged in
class Admin::ApplicationController < ApplicationController
- layout 'admin'
before_action :authenticate_admin!
+ layout 'admin'
def authenticate_admin!
return render_404 unless current_user.is_admin?
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 69fd7901832..c9b34eac4b0 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -3,6 +3,7 @@ require 'gon'
class ApplicationController < ActionController::Base
include Gitlab::CurrentSettings
include GitlabRoutingHelper
+ include PageLayoutHelper
PER_PAGE = 20
diff --git a/app/controllers/dashboard/application_controller.rb b/app/controllers/dashboard/application_controller.rb
new file mode 100644
index 00000000000..962ea38d6c9
--- /dev/null
+++ b/app/controllers/dashboard/application_controller.rb
@@ -0,0 +1,3 @@
+class Dashboard::ApplicationController < ApplicationController
+ layout 'dashboard'
+end
diff --git a/app/controllers/dashboard/groups_controller.rb b/app/controllers/dashboard/groups_controller.rb
index ed14f4e1f3b..3bc94ff2187 100644
--- a/app/controllers/dashboard/groups_controller.rb
+++ b/app/controllers/dashboard/groups_controller.rb
@@ -1,4 +1,4 @@
-class Dashboard::GroupsController < ApplicationController
+class Dashboard::GroupsController < Dashboard::ApplicationController
def index
@group_members = current_user.group_members.page(params[:page]).per(PER_PAGE)
end
diff --git a/app/controllers/dashboard/milestones_controller.rb b/app/controllers/dashboard/milestones_controller.rb
index 33227e7f1d8..53896d4f2c7 100644
--- a/app/controllers/dashboard/milestones_controller.rb
+++ b/app/controllers/dashboard/milestones_controller.rb
@@ -1,4 +1,4 @@
-class Dashboard::MilestonesController < ApplicationController
+class Dashboard::MilestonesController < Dashboard::ApplicationController
before_action :load_projects
def index
diff --git a/app/controllers/dashboard/projects_controller.rb b/app/controllers/dashboard/projects_controller.rb
index 426bc615415..da96171e885 100644
--- a/app/controllers/dashboard/projects_controller.rb
+++ b/app/controllers/dashboard/projects_controller.rb
@@ -1,4 +1,4 @@
-class Dashboard::ProjectsController < ApplicationController
+class Dashboard::ProjectsController < Dashboard::ApplicationController
before_action :event_filter
def starred
diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb
index 40b5de1295a..17ddde68f93 100644
--- a/app/controllers/dashboard_controller.rb
+++ b/app/controllers/dashboard_controller.rb
@@ -1,8 +1,8 @@
-class DashboardController < ApplicationController
- respond_to :html
-
+class DashboardController < Dashboard::ApplicationController
before_action :load_projects, except: [:projects]
before_action :event_filter, only: :show
+
+ respond_to :html
def show
@projects = @projects.includes(:namespace)
diff --git a/app/controllers/explore/application_controller.rb b/app/controllers/explore/application_controller.rb
new file mode 100644
index 00000000000..4b275033d26
--- /dev/null
+++ b/app/controllers/explore/application_controller.rb
@@ -0,0 +1,3 @@
+class Explore::ApplicationController < ApplicationController
+ layout 'explore'
+end
diff --git a/app/controllers/explore/groups_controller.rb b/app/controllers/explore/groups_controller.rb
index a7250b799f3..55cda0cff17 100644
--- a/app/controllers/explore/groups_controller.rb
+++ b/app/controllers/explore/groups_controller.rb
@@ -1,9 +1,7 @@
-class Explore::GroupsController < ApplicationController
+class Explore::GroupsController < Explore::ApplicationController
skip_before_action :authenticate_user!,
:reject_blocked, :set_current_user_for_observers
- layout "explore"
-
def index
@groups = GroupsFinder.new.execute(current_user)
@groups = @groups.search(params[:search]) if params[:search].present?
diff --git a/app/controllers/explore/projects_controller.rb b/app/controllers/explore/projects_controller.rb
index b1b0a2514dc..e9bcb44f6b3 100644
--- a/app/controllers/explore/projects_controller.rb
+++ b/app/controllers/explore/projects_controller.rb
@@ -1,9 +1,7 @@
-class Explore::ProjectsController < ApplicationController
+class Explore::ProjectsController < Explore::ApplicationController
skip_before_action :authenticate_user!,
:reject_blocked
- layout 'explore'
-
def index
@projects = ProjectsFinder.new.execute(current_user)
@tags = @projects.tags_on(:tags)
diff --git a/app/controllers/groups/application_controller.rb b/app/controllers/groups/application_controller.rb
index 469a6813ee2..4df9d1b7533 100644
--- a/app/controllers/groups/application_controller.rb
+++ b/app/controllers/groups/application_controller.rb
@@ -1,4 +1,5 @@
class Groups::ApplicationController < ApplicationController
+ layout 'group'
private
@@ -17,12 +18,4 @@ class Groups::ApplicationController < ApplicationController
return render_404
end
end
-
- def determine_layout
- if current_user
- 'group'
- else
- 'public_group'
- end
- end
end
diff --git a/app/controllers/groups/avatars_controller.rb b/app/controllers/groups/avatars_controller.rb
index 38071410f40..6aa64222f77 100644
--- a/app/controllers/groups/avatars_controller.rb
+++ b/app/controllers/groups/avatars_controller.rb
@@ -1,6 +1,4 @@
class Groups::AvatarsController < ApplicationController
- layout "profile"
-
def destroy
@group = Group.find_by(path: params[:group_id])
@group.remove_avatar!
diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb
index 5648a652e8e..a11c554a2af 100644
--- a/app/controllers/groups/group_members_controller.rb
+++ b/app/controllers/groups/group_members_controller.rb
@@ -6,8 +6,6 @@ class Groups::GroupMembersController < Groups::ApplicationController
before_action :authorize_read_group!
before_action :authorize_admin_group!, except: [:index, :leave]
- layout :determine_layout
-
def index
@project = @group.projects.find(params[:project_id]) if params[:project_id]
@members = @group.group_members
diff --git a/app/controllers/groups/milestones_controller.rb b/app/controllers/groups/milestones_controller.rb
index 41564b04a92..669f7f3126d 100644
--- a/app/controllers/groups/milestones_controller.rb
+++ b/app/controllers/groups/milestones_controller.rb
@@ -1,6 +1,4 @@
-class Groups::MilestonesController < ApplicationController
- layout 'group'
-
+class Groups::MilestonesController < Groups::ApplicationController
before_action :authorize_group_milestone!, only: :update
def index
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index 294af0b1704..34f0b257db3 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -11,7 +11,6 @@ class GroupsController < Groups::ApplicationController
# Load group projects
before_action :load_projects, except: [:new, :create, :projects, :edit, :update]
before_action :event_filter, only: :show
- before_action :set_title, only: [:new, :create]
layout :determine_layout
@@ -119,17 +118,11 @@ class GroupsController < Groups::ApplicationController
end
end
- def set_title
- @title = 'New Group'
- end
-
def determine_layout
if [:new, :create].include?(action_name.to_sym)
- 'navless'
- elsif current_user
- 'group'
+ 'application'
else
- 'public_group'
+ 'group'
end
end
diff --git a/app/controllers/help_controller.rb b/app/controllers/help_controller.rb
index 35ece5b270b..8a45dc8860d 100644
--- a/app/controllers/help_controller.rb
+++ b/app/controllers/help_controller.rb
@@ -1,14 +1,16 @@
class HelpController < ApplicationController
+ layout 'help'
+
def index
end
def show
- category = clean_path_info(path_params[:category])
- file = path_params[:file]
+ @category = clean_path_info(path_params[:category])
+ @file = path_params[:file]
respond_to do |format|
format.any(:markdown, :md, :html) do
- path = Rails.root.join('doc', category, "#{file}.md")
+ path = Rails.root.join('doc', @category, "#{@file}.md")
if File.exist?(path)
@markdown = File.read(path)
@@ -22,7 +24,7 @@ class HelpController < ApplicationController
# Allow access to images in the doc folder
format.any(:png, :gif, :jpeg) do
- path = Rails.root.join('doc', category, "#{file}.#{params[:format]}")
+ path = Rails.root.join('doc', @category, "#{@file}.#{params[:format]}")
if File.exist?(path)
send_file(path, disposition: 'inline')
diff --git a/app/controllers/import/google_code_controller.rb b/app/controllers/import/google_code_controller.rb
index 5adf6ed7853..4aa6d28c9a8 100644
--- a/app/controllers/import/google_code_controller.rb
+++ b/app/controllers/import/google_code_controller.rb
@@ -72,6 +72,7 @@ class Import::GoogleCodeController < Import::BaseController
end
@repos = client.repos
+ @incompatible_repos = client.incompatible_repos
@already_added_projects = current_user.created_projects.where(import_type: "google_code")
already_added_projects_names = @already_added_projects.pluck(:import_source)
diff --git a/app/controllers/invites_controller.rb b/app/controllers/invites_controller.rb
index a29c03395f4..eb3c8233530 100644
--- a/app/controllers/invites_controller.rb
+++ b/app/controllers/invites_controller.rb
@@ -4,8 +4,6 @@ class InvitesController < ApplicationController
respond_to :html
- layout 'navless'
-
def show
end
diff --git a/app/controllers/oauth/applications_controller.rb b/app/controllers/oauth/applications_controller.rb
index ea256de2c3e..507b8290a2b 100644
--- a/app/controllers/oauth/applications_controller.rb
+++ b/app/controllers/oauth/applications_controller.rb
@@ -1,6 +1,9 @@
class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
+ include PageLayoutHelper
+
before_action :authenticate_user!
- layout "profile"
+
+ layout 'profile'
def index
head :forbidden and return
diff --git a/app/controllers/oauth/authorizations_controller.rb b/app/controllers/oauth/authorizations_controller.rb
index 6d3c1a320db..24025d8c723 100644
--- a/app/controllers/oauth/authorizations_controller.rb
+++ b/app/controllers/oauth/authorizations_controller.rb
@@ -1,6 +1,7 @@
class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
before_action :authenticate_resource_owner!
- layout "profile"
+
+ layout 'profile'
def new
if pre_auth.authorizable?
diff --git a/app/controllers/oauth/authorized_applications_controller.rb b/app/controllers/oauth/authorized_applications_controller.rb
index 0b27ce7da72..3ab6def511c 100644
--- a/app/controllers/oauth/authorized_applications_controller.rb
+++ b/app/controllers/oauth/authorized_applications_controller.rb
@@ -1,5 +1,7 @@
class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicationsController
- layout "profile"
+ include PageLayoutHelper
+
+ layout 'profile'
def destroy
Doorkeeper::AccessToken.revoke_all_for(params[:id], current_resource_owner)
diff --git a/app/controllers/profiles/accounts_controller.rb b/app/controllers/profiles/accounts_controller.rb
index 9bd34fe2261..175afbf8425 100644
--- a/app/controllers/profiles/accounts_controller.rb
+++ b/app/controllers/profiles/accounts_controller.rb
@@ -1,6 +1,4 @@
-class Profiles::AccountsController < ApplicationController
- layout "profile"
-
+class Profiles::AccountsController < Profiles::ApplicationController
def show
@user = current_user
end
diff --git a/app/controllers/profiles/application_controller.rb b/app/controllers/profiles/application_controller.rb
new file mode 100644
index 00000000000..c8be288b9a0
--- /dev/null
+++ b/app/controllers/profiles/application_controller.rb
@@ -0,0 +1,3 @@
+class Profiles::ApplicationController < ApplicationController
+ layout 'profile'
+end
diff --git a/app/controllers/profiles/avatars_controller.rb b/app/controllers/profiles/avatars_controller.rb
index 57f3bbf0627..f193adb46b4 100644
--- a/app/controllers/profiles/avatars_controller.rb
+++ b/app/controllers/profiles/avatars_controller.rb
@@ -1,6 +1,4 @@
-class Profiles::AvatarsController < ApplicationController
- layout "profile"
-
+class Profiles::AvatarsController < Profiles::ApplicationController
def destroy
@user = current_user
@user.remove_avatar!
diff --git a/app/controllers/profiles/emails_controller.rb b/app/controllers/profiles/emails_controller.rb
index 954c98c0d9f..3e904700de5 100644
--- a/app/controllers/profiles/emails_controller.rb
+++ b/app/controllers/profiles/emails_controller.rb
@@ -1,6 +1,4 @@
-class Profiles::EmailsController < ApplicationController
- layout "profile"
-
+class Profiles::EmailsController < Profiles::ApplicationController
def index
@primary = current_user.email
@public_email = current_user.public_email
diff --git a/app/controllers/profiles/keys_controller.rb b/app/controllers/profiles/keys_controller.rb
index b0a5a631c63..f3224148fda 100644
--- a/app/controllers/profiles/keys_controller.rb
+++ b/app/controllers/profiles/keys_controller.rb
@@ -1,5 +1,4 @@
-class Profiles::KeysController < ApplicationController
- layout "profile"
+class Profiles::KeysController < Profiles::ApplicationController
skip_before_action :authenticate_user!, only: [:get_keys]
def index
diff --git a/app/controllers/profiles/notifications_controller.rb b/app/controllers/profiles/notifications_controller.rb
index 3fdcbbab61b..22423651c17 100644
--- a/app/controllers/profiles/notifications_controller.rb
+++ b/app/controllers/profiles/notifications_controller.rb
@@ -1,6 +1,4 @@
-class Profiles::NotificationsController < ApplicationController
- layout 'profile'
-
+class Profiles::NotificationsController < Profiles::ApplicationController
def show
@user = current_user
@notification = current_user.notification
diff --git a/app/controllers/profiles/passwords_controller.rb b/app/controllers/profiles/passwords_controller.rb
index b719a7fe9a9..c780e0983f9 100644
--- a/app/controllers/profiles/passwords_controller.rb
+++ b/app/controllers/profiles/passwords_controller.rb
@@ -1,12 +1,11 @@
-class Profiles::PasswordsController < ApplicationController
- layout :determine_layout
-
+class Profiles::PasswordsController < Profiles::ApplicationController
skip_before_action :check_password_expiration, only: [:new, :create]
before_action :set_user
- before_action :set_title
before_action :authorize_change_password!
+ layout :determine_layout
+
def new
end
@@ -66,13 +65,9 @@ class Profiles::PasswordsController < ApplicationController
@user = current_user
end
- def set_title
- @title = "New password"
- end
-
def determine_layout
if [:new, :create].include?(action_name.to_sym)
- 'navless'
+ 'application'
else
'profile'
end
diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb
index eb001e8d739..f4366c18e7b 100644
--- a/app/controllers/profiles_controller.rb
+++ b/app/controllers/profiles_controller.rb
@@ -1,12 +1,10 @@
-class ProfilesController < ApplicationController
+class ProfilesController < Profiles::ApplicationController
include ActionView::Helpers::SanitizeHelper
before_action :user
before_action :authorize_change_username!, only: :update_username
skip_before_action :require_email, only: [:show, :update]
- layout 'profile'
-
def show
end
diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb
index f7a28e920d1..ee88d49b400 100644
--- a/app/controllers/projects/application_controller.rb
+++ b/app/controllers/projects/application_controller.rb
@@ -1,7 +1,7 @@
class Projects::ApplicationController < ApplicationController
before_action :project
before_action :repository
- layout :determine_layout
+ layout 'project'
def authenticate_user!
# Restrict access to Projects area only
@@ -17,14 +17,6 @@ class Projects::ApplicationController < ApplicationController
super
end
- def determine_layout
- if current_user
- 'projects'
- else
- 'public_projects'
- end
- end
-
def require_branch_head
unless @repository.branch_names.include?(@ref)
redirect_to(
diff --git a/app/controllers/projects/avatars_controller.rb b/app/controllers/projects/avatars_controller.rb
index 22a12c4b9ae..9c3763d5934 100644
--- a/app/controllers/projects/avatars_controller.rb
+++ b/app/controllers/projects/avatars_controller.rb
@@ -1,6 +1,4 @@
class Projects::AvatarsController < Projects::ApplicationController
- layout 'project'
-
before_action :project
def show
diff --git a/app/controllers/projects/forks_controller.rb b/app/controllers/projects/forks_controller.rb
index 01a079d2e65..9e72597ea87 100644
--- a/app/controllers/projects/forks_controller.rb
+++ b/app/controllers/projects/forks_controller.rb
@@ -18,7 +18,6 @@ class Projects::ForksController < Projects::ApplicationController
notice: 'Project was successfully forked.'
)
else
- @title = 'Fork project'
render :error
end
end
diff --git a/app/controllers/projects/uploads_controller.rb b/app/controllers/projects/uploads_controller.rb
index e2d0b0d9459..71ecc20dd95 100644
--- a/app/controllers/projects/uploads_controller.rb
+++ b/app/controllers/projects/uploads_controller.rb
@@ -1,6 +1,4 @@
class Projects::UploadsController < Projects::ApplicationController
- layout 'project'
-
skip_before_action :authenticate_user!, :reject_blocked!, :project,
:repository, if: -> { action_name == 'show' && image? }
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 883e5865a21..dc430351551 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -6,17 +6,16 @@ class ProjectsController < ApplicationController
# Authorize
before_action :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer, :archive, :unarchive]
- before_action :set_title, only: [:new, :create]
before_action :event_filter, only: :show
- layout 'navless', only: [:new, :create, :fork]
+ layout :determine_layout
def new
@project = Project.new
end
def edit
- render 'edit', layout: 'project_settings'
+ render 'edit'
end
def create
@@ -46,7 +45,7 @@ class ProjectsController < ApplicationController
end
format.js
else
- format.html { render 'edit', layout: 'project_settings' }
+ format.html { render 'edit' }
format.js
end
end
@@ -72,13 +71,13 @@ class ProjectsController < ApplicationController
format.html do
if @project.repository_exists?
if @project.empty_repo?
- render 'projects/empty', layout: user_layout
+ render 'projects/empty'
else
@last_push = current_user.recent_push(@project.id) if current_user
- render :show, layout: user_layout
+ render :show
end
else
- render 'projects/no_repo', layout: user_layout
+ render 'projects/no_repo'
end
end
@@ -160,12 +159,14 @@ class ProjectsController < ApplicationController
private
- def set_title
- @title = 'New Project'
- end
-
- def user_layout
- current_user ? 'projects' : 'public_projects'
+ def determine_layout
+ if [:new, :create].include?(action_name.to_sym)
+ 'application'
+ elsif [:edit, :update].include?(action_name.to_sym)
+ 'project_settings'
+ else
+ 'project'
+ end
end
def load_events
diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb
index ad9e9e8487e..4e2ea6c5710 100644
--- a/app/controllers/search_controller.rb
+++ b/app/controllers/search_controller.rb
@@ -1,6 +1,8 @@
class SearchController < ApplicationController
include SearchHelper
+ layout 'search'
+
def show
return if params[:search].nil? || params[:search].blank?
diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb
index a5259466cb8..cf672c5c093 100644
--- a/app/controllers/snippets_controller.rb
+++ b/app/controllers/snippets_controller.rb
@@ -7,14 +7,11 @@ class SnippetsController < ApplicationController
# Allow destroy snippet
before_action :authorize_admin_snippet!, only: [:destroy]
- before_action :set_title
-
skip_before_action :authenticate_user!, only: [:index, :user_index, :show, :raw]
+ layout 'snippets'
respond_to :html
- layout :determine_layout
-
def index
if params[:username].present?
@user = User.find_by(username: params[:username])
@@ -98,16 +95,7 @@ class SnippetsController < ApplicationController
return render_404 unless can?(current_user, :admin_personal_snippet, @snippet)
end
- def set_title
- @title = 'Snippets'
- @title_url = snippets_path
- end
-
def snippet_params
params.require(:personal_snippet).permit(:title, :content, :file_name, :private, :visibility_level)
end
-
- def determine_layout
- current_user ? 'snippets' : 'public_users'
- end
end
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 460cc868b35..2bb5c338cf6 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -1,7 +1,6 @@
class UsersController < ApplicationController
skip_before_action :authenticate_user!
before_action :set_user
- layout :determine_layout
def show
@contributed_projects = contributed_projects.joined(@user).
@@ -13,9 +12,6 @@ class UsersController < ApplicationController
# Collect only groups common for both users
@groups = @user.groups & GroupsFinder.new.execute(current_user)
- @title = @user.name
- @title_url = user_path(@user)
-
respond_to do |format|
format.html
@@ -51,14 +47,6 @@ class UsersController < ApplicationController
render 'calendar_activities', layout: false
end
- def determine_layout
- if current_user
- 'navless'
- else
- 'public_users'
- end
- end
-
private
def set_user
diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb
index bc234500000..24263a0f619 100644
--- a/app/helpers/gitlab_markdown_helper.rb
+++ b/app/helpers/gitlab_markdown_helper.rb
@@ -34,10 +34,8 @@ module GitlabMarkdownHelper
# see https://github.com/vmg/redcarpet#darling-i-packed-you-a-couple-renderers-for-lunch
rend = Redcarpet::Render::GitlabHTML.new(self, user_color_scheme_class, {
- with_toc_data: true,
- safe_links_only: true,
- # Handled further down the line by HTML::Pipeline::SanitizationFilter
- escape_html: false
+ # Handled further down the line by Gitlab::Markdown::SanitizationFilter
+ escape_html: false
}.merge(options))
# see https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use
@@ -45,7 +43,6 @@ module GitlabMarkdownHelper
no_intra_emphasis: true,
tables: true,
fenced_code_blocks: true,
- autolink: true,
strikethrough: true,
lax_spacing: true,
space_after_headers: true,
diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb
index add0a776a63..3569ac2af63 100644
--- a/app/helpers/groups_helper.rb
+++ b/app/helpers/groups_helper.rb
@@ -19,24 +19,6 @@ module GroupsHelper
end
end
- def group_head_title
- title = @group.name
-
- title = if current_action?(:issues)
- "Issues - " + title
- elsif current_action?(:merge_requests)
- "Merge requests - " + title
- elsif current_action?(:members)
- "Members - " + title
- elsif current_action?(:edit)
- "Settings - " + title
- else
- title
- end
-
- title
- end
-
def group_settings_page?
if current_controller?('groups')
current_action?('edit') || current_action?('projects')
diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb
index c3b4731dff3..36d3f371c1b 100644
--- a/app/helpers/issues_helper.rb
+++ b/app/helpers/issues_helper.rb
@@ -43,17 +43,6 @@ module IssuesHelper
end
end
- def title_for_issue(issue_iid, project = @project)
- return '' if project.nil?
-
- if project.default_issues_tracker?
- issue = project.issues.where(iid: issue_iid).first
- return issue.title if issue
- end
-
- ''
- end
-
def issue_timestamp(issue)
# Shows the created at time and the updated at time if different
ts = "#{time_ago_with_tooltip(issue.created_at, 'bottom', 'note_created_ago')}"
@@ -110,5 +99,5 @@ module IssuesHelper
end
# Required for Gitlab::Markdown::IssueReferenceFilter
- module_function :url_for_issue, :title_for_issue
+ module_function :url_for_issue
end
diff --git a/app/helpers/page_layout_helper.rb b/app/helpers/page_layout_helper.rb
new file mode 100644
index 00000000000..01b6a63552c
--- /dev/null
+++ b/app/helpers/page_layout_helper.rb
@@ -0,0 +1,26 @@
+module PageLayoutHelper
+ def page_title(*titles)
+ @page_title ||= []
+
+ @page_title.push(*titles.compact) if titles.any?
+
+ @page_title.join(" | ")
+ end
+
+ def header_title(title = nil, title_url = nil)
+ if title
+ @header_title = title
+ @header_title_url = title_url
+ else
+ @header_title_url ? link_to(@header_title, @header_title_url) : @header_title
+ end
+ end
+
+ def sidebar(name = nil)
+ if name
+ @sidebar = name
+ else
+ @sidebar
+ end
+ end
+end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index c2a7732e6f0..96d2606f1a1 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -192,46 +192,6 @@ module ProjectsHelper
'unknown'
end
- def project_head_title
- title = @project.name_with_namespace
-
- title = if current_controller?(:tree)
- "#{@project.path}\/#{@path} at #{@ref} - " + title
- elsif current_controller?(:issues)
- if current_action?(:show)
- "Issue ##{@issue.iid} - #{@issue.title} - " + title
- else
- "Issues - " + title
- end
- elsif current_controller?(:blob)
- 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)
- if current_action?(:show)
- "Merge request ##{@merge_request.iid} - " + title
- else
- "Merge requests - " + title
- end
- elsif current_controller?(:wikis)
- "Wiki - " + title
- elsif current_controller?(:network)
- "Network graph - " + title
- elsif current_controller?(:graphs)
- "Graphs - " + title
- else
- title
- end
-
- title
- end
-
def default_url_to_repo(project = nil)
project = project || @project
current_user ? project.url_to_repo : project.http_url_to_repo
diff --git a/app/mailers/devise_mailer.rb b/app/mailers/devise_mailer.rb
new file mode 100644
index 00000000000..5395fe16414
--- /dev/null
+++ b/app/mailers/devise_mailer.rb
@@ -0,0 +1,4 @@
+class DeviseMailer < Devise::Mailer
+ default from: "GitLab <#{Gitlab.config.gitlab.email_from}>"
+ default reply_to: Gitlab.config.gitlab.email_reply_to
+end
diff --git a/app/models/external_issue.rb b/app/models/external_issue.rb
index 50efcb32f1b..85fdb12bfdc 100644
--- a/app/models/external_issue.rb
+++ b/app/models/external_issue.rb
@@ -15,6 +15,10 @@ class ExternalIssue
@issue_identifier.to_s
end
+ def title
+ "External Issue #{self}"
+ end
+
def ==(other)
other.is_a?(self.class) && (to_s == other.to_s)
end
diff --git a/app/models/project.rb b/app/models/project.rb
index 397232e98d8..e866681aab9 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -329,14 +329,18 @@ class Project < ActiveRecord::Base
self.id
end
- def issue_exists?(issue_id)
+ def get_issue(issue_id)
if default_issues_tracker?
- self.issues.where(iid: issue_id).first.present?
+ issues.find_by(iid: issue_id)
else
- true
+ ExternalIssue.new(issue_id, self)
end
end
+ def issue_exists?(issue_id)
+ get_issue(issue_id)
+ end
+
def default_issue_tracker
gitlab_issue_tracker_service || create_gitlab_issue_tracker_service
end
@@ -350,11 +354,7 @@ class Project < ActiveRecord::Base
end
def default_issues_tracker?
- if external_issue_tracker
- false
- else
- true
- end
+ !external_issue_tracker
end
def external_issues_trackers
diff --git a/app/views/admin/application_settings/show.html.haml b/app/views/admin/application_settings/show.html.haml
index 39b66647a5a..1632dd8affa 100644
--- a/app/views/admin/application_settings/show.html.haml
+++ b/app/views/admin/application_settings/show.html.haml
@@ -1,3 +1,4 @@
+- page_title "Settings"
%h3.page-title Application settings
%hr
= render 'form'
diff --git a/app/views/admin/applications/edit.html.haml b/app/views/admin/applications/edit.html.haml
index e408ae2f29d..c596866bde2 100644
--- a/app/views/admin/applications/edit.html.haml
+++ b/app/views/admin/applications/edit.html.haml
@@ -1,3 +1,4 @@
+- page_title "Edit", @application.name, "Applications"
%h3.page-title Edit application
- @url = admin_application_path(@application)
-= render 'form', application: @application \ No newline at end of file
+= render 'form', application: @application
diff --git a/app/views/admin/applications/index.html.haml b/app/views/admin/applications/index.html.haml
index d550278710e..fc921a966f3 100644
--- a/app/views/admin/applications/index.html.haml
+++ b/app/views/admin/applications/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Applications"
%h3.page-title
System OAuth applications
%p.light
diff --git a/app/views/admin/applications/new.html.haml b/app/views/admin/applications/new.html.haml
index 7c62425f19c..6310d89bd6b 100644
--- a/app/views/admin/applications/new.html.haml
+++ b/app/views/admin/applications/new.html.haml
@@ -1,3 +1,4 @@
+- page_title "New Application"
%h3.page-title New application
- @url = admin_applications_path
-= render 'form', application: @application \ No newline at end of file
+= render 'form', application: @application
diff --git a/app/views/admin/applications/show.html.haml b/app/views/admin/applications/show.html.haml
index 2abe390ce13..0ea2ffeda99 100644
--- a/app/views/admin/applications/show.html.haml
+++ b/app/views/admin/applications/show.html.haml
@@ -1,3 +1,4 @@
+- page_title @application.name, "Applications"
%h3.page-title
Application: #{@application.name}
diff --git a/app/views/admin/background_jobs/show.html.haml b/app/views/admin/background_jobs/show.html.haml
index 4ef8e878a7f..3a01e115109 100644
--- a/app/views/admin/background_jobs/show.html.haml
+++ b/app/views/admin/background_jobs/show.html.haml
@@ -1,3 +1,4 @@
+- page_title "Background Jobs"
%h3.page-title Background Jobs
%p.light GitLab uses #{link_to "sidekiq", "http://sidekiq.org/"} library for async job processing
diff --git a/app/views/admin/broadcast_messages/index.html.haml b/app/views/admin/broadcast_messages/index.html.haml
index 7e29311bf42..267c9a52921 100644
--- a/app/views/admin/broadcast_messages/index.html.haml
+++ b/app/views/admin/broadcast_messages/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Broadcast Messages"
%h3.page-title
Broadcast Messages
%p.light
diff --git a/app/views/admin/deploy_keys/index.html.haml b/app/views/admin/deploy_keys/index.html.haml
index 2ae83ab95f7..367d25cd6a1 100644
--- a/app/views/admin/deploy_keys/index.html.haml
+++ b/app/views/admin/deploy_keys/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Deploy Keys"
.panel.panel-default
.panel-heading
Public deploy keys (#{@deploy_keys.count})
diff --git a/app/views/admin/deploy_keys/new.html.haml b/app/views/admin/deploy_keys/new.html.haml
index c00049424c5..5b46b3222a9 100644
--- a/app/views/admin/deploy_keys/new.html.haml
+++ b/app/views/admin/deploy_keys/new.html.haml
@@ -1,3 +1,4 @@
+- page_title "New Deploy Key"
%h3.page-title New public deploy key
%hr
diff --git a/app/views/admin/deploy_keys/show.html.haml b/app/views/admin/deploy_keys/show.html.haml
index cfa2adf92ee..ea361ca4bdb 100644
--- a/app/views/admin/deploy_keys/show.html.haml
+++ b/app/views/admin/deploy_keys/show.html.haml
@@ -1,3 +1,4 @@
+- page_title @deploy_key.title, "Deploy Keys"
.row
.col-md-4
.panel.panel-default
diff --git a/app/views/admin/groups/edit.html.haml b/app/views/admin/groups/edit.html.haml
index 824e51c1cf1..eb09a6328ed 100644
--- a/app/views/admin/groups/edit.html.haml
+++ b/app/views/admin/groups/edit.html.haml
@@ -1,3 +1,4 @@
+- page_title "Edit", @group.name, "Groups"
%h3.page-title Edit group: #{@group.name}
%hr
= render 'form'
diff --git a/app/views/admin/groups/index.html.haml b/app/views/admin/groups/index.html.haml
index 4c53ff55708..e00b23ad99f 100644
--- a/app/views/admin/groups/index.html.haml
+++ b/app/views/admin/groups/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Groups"
%h3.page-title
Groups (#{@groups.total_count})
= link_to 'New Group', new_admin_group_path, class: "btn btn-new pull-right"
diff --git a/app/views/admin/groups/new.html.haml b/app/views/admin/groups/new.html.haml
index f46f45c5514..c81ee552ac3 100644
--- a/app/views/admin/groups/new.html.haml
+++ b/app/views/admin/groups/new.html.haml
@@ -1,3 +1,4 @@
+- page_title "New Group"
%h3.page-title New group
%hr
= render 'form'
diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml
index 427f38018b0..187314872de 100644
--- a/app/views/admin/groups/show.html.haml
+++ b/app/views/admin/groups/show.html.haml
@@ -1,3 +1,4 @@
+- page_title @group.name, "Groups"
%h3.page-title
Group: #{@group.name}
diff --git a/app/views/admin/hooks/index.html.haml b/app/views/admin/hooks/index.html.haml
index 7a9dc113f2a..e74e1e85f41 100644
--- a/app/views/admin/hooks/index.html.haml
+++ b/app/views/admin/hooks/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "System Hooks"
%h3.page-title
System hooks
diff --git a/app/views/admin/keys/show.html.haml b/app/views/admin/keys/show.html.haml
index 5b23027b3ab..9ee77c77398 100644
--- a/app/views/admin/keys/show.html.haml
+++ b/app/views/admin/keys/show.html.haml
@@ -1 +1,2 @@
+- page_title @key.title, "Keys"
= render "profiles/keys/key_details", admin: true
diff --git a/app/views/admin/logs/show.html.haml b/app/views/admin/logs/show.html.haml
index 384c6ee9af5..1484baa78e0 100644
--- a/app/views/admin/logs/show.html.haml
+++ b/app/views/admin/logs/show.html.haml
@@ -1,3 +1,4 @@
+- page_title "Logs"
- loggers = [Gitlab::GitLogger, Gitlab::AppLogger,
Gitlab::ProductionLogger, Gitlab::SidekiqLogger]
%ul.nav.nav-tabs.log-tabs
diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml
index 0a13791029d..f43d46356fa 100644
--- a/app/views/admin/projects/index.html.haml
+++ b/app/views/admin/projects/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Projects"
= render 'shared/show_aside'
.row
diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml
index 78684c692c7..4c2865ac3f2 100644
--- a/app/views/admin/projects/show.html.haml
+++ b/app/views/admin/projects/show.html.haml
@@ -1,3 +1,4 @@
+- page_title @project.name_with_namespace, "Projects"
%h3.page-title
Project: #{@project.name_with_namespace}
= link_to edit_project_path(@project), class: "btn pull-right" do
diff --git a/app/views/admin/services/edit.html.haml b/app/views/admin/services/edit.html.haml
index bcc5832792f..53d970e33c1 100644
--- a/app/views/admin/services/edit.html.haml
+++ b/app/views/admin/services/edit.html.haml
@@ -1 +1,2 @@
+- page_title @service.title, "Service Templates"
= render 'form'
diff --git a/app/views/admin/services/index.html.haml b/app/views/admin/services/index.html.haml
index 0093fb97765..e2377291142 100644
--- a/app/views/admin/services/index.html.haml
+++ b/app/views/admin/services/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Service Templates"
%h3.page-title Service templates
%p.light Service template allows you to set default values for project services
diff --git a/app/views/admin/users/edit.html.haml b/app/views/admin/users/edit.html.haml
index d71d8189c51..a8837d74dd9 100644
--- a/app/views/admin/users/edit.html.haml
+++ b/app/views/admin/users/edit.html.haml
@@ -1,3 +1,4 @@
+- page_title "Edit", @user.name, "Users"
%h3.page-title
Edit user: #{@user.name}
.back-link
diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml
index 23a9d769639..fe648470233 100644
--- a/app/views/admin/users/index.html.haml
+++ b/app/views/admin/users/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Users"
= render 'shared/show_aside'
.row
diff --git a/app/views/admin/users/new.html.haml b/app/views/admin/users/new.html.haml
index 8fbb757f424..bfc36ed7373 100644
--- a/app/views/admin/users/new.html.haml
+++ b/app/views/admin/users/new.html.haml
@@ -1,3 +1,4 @@
+- page_title "New User"
%h3.page-title
New user
%hr
diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml
index 3524f04c5ed..7fc85206109 100644
--- a/app/views/admin/users/show.html.haml
+++ b/app/views/admin/users/show.html.haml
@@ -1,3 +1,4 @@
+- page_title @user.name, "Users"
%h3.page-title
User:
= @user.name
diff --git a/app/views/dashboard/groups/index.html.haml b/app/views/dashboard/groups/index.html.haml
index 0cb7f764fab..5ecd53cff84 100644
--- a/app/views/dashboard/groups/index.html.haml
+++ b/app/views/dashboard/groups/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Groups"
%h3.page-title
Group Membership
- if current_user.can_create_group?
diff --git a/app/views/dashboard/issues.html.haml b/app/views/dashboard/issues.html.haml
index 62cc80a30dc..dfdf0d68c8f 100644
--- a/app/views/dashboard/issues.html.haml
+++ b/app/views/dashboard/issues.html.haml
@@ -1,3 +1,4 @@
+- page_title "Issues"
= content_for :meta_tags do
- if current_user
= auto_discovery_link_tag(:atom, issues_dashboard_url(format: :atom, private_token: current_user.private_token), title: "#{current_user.name} issues")
diff --git a/app/views/dashboard/merge_requests.html.haml b/app/views/dashboard/merge_requests.html.haml
index 97a42461b4e..a7e1b08a0a4 100644
--- a/app/views/dashboard/merge_requests.html.haml
+++ b/app/views/dashboard/merge_requests.html.haml
@@ -1,3 +1,4 @@
+- page_title "Merge Requests"
%h3.page-title
Merge Requests
diff --git a/app/views/dashboard/milestones/index.html.haml b/app/views/dashboard/milestones/index.html.haml
index 9944c0df815..9a9a5e139a4 100644
--- a/app/views/dashboard/milestones/index.html.haml
+++ b/app/views/dashboard/milestones/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Milestones"
%h3.page-title
Milestones
%span.pull-right #{@dashboard_milestones.count} milestones
diff --git a/app/views/dashboard/milestones/show.html.haml b/app/views/dashboard/milestones/show.html.haml
index 57cce9ab749..24f0bcb60d5 100644
--- a/app/views/dashboard/milestones/show.html.haml
+++ b/app/views/dashboard/milestones/show.html.haml
@@ -1,3 +1,4 @@
+- page_title @dashboard_milestone.title, "Milestones"
%h4.page-title
.issue-box{ class: "issue-box-#{@dashboard_milestone.closed? ? 'closed' : 'open'}" }
- if @dashboard_milestone.closed?
diff --git a/app/views/dashboard/projects/starred.html.haml b/app/views/dashboard/projects/starred.html.haml
index 67943f2267b..8aaa0a7f071 100644
--- a/app/views/dashboard/projects/starred.html.haml
+++ b/app/views/dashboard/projects/starred.html.haml
@@ -1,3 +1,4 @@
+- page_title "Starred Projects"
- if @projects.any?
= render 'shared/show_aside'
diff --git a/app/views/devise/registrations/new.html.haml b/app/views/devise/registrations/new.html.haml
index d3e37f7494c..42cfbbf84f2 100644
--- a/app/views/devise/registrations/new.html.haml
+++ b/app/views/devise/registrations/new.html.haml
@@ -1,3 +1,4 @@
+- page_title "Sign up"
= render 'devise/shared/signup_box'
-= render 'devise/shared/sign_in_link' \ No newline at end of file
+= render 'devise/shared/sign_in_link'
diff --git a/app/views/devise/sessions/new.html.haml b/app/views/devise/sessions/new.html.haml
index 89e4e229ac0..dbc8eda6196 100644
--- a/app/views/devise/sessions/new.html.haml
+++ b/app/views/devise/sessions/new.html.haml
@@ -1,3 +1,4 @@
+- page_title "Sign in"
%div
- if signin_enabled? || ldap_enabled?
= render 'devise/shared/signin_box'
diff --git a/app/views/doorkeeper/applications/edit.html.haml b/app/views/doorkeeper/applications/edit.html.haml
index 61584eb9c49..fb6aa30acee 100644
--- a/app/views/doorkeeper/applications/edit.html.haml
+++ b/app/views/doorkeeper/applications/edit.html.haml
@@ -1,2 +1,3 @@
+- page_title "Edit", @application.name, "Applications"
%h3.page-title Edit application
-= render 'form', application: @application \ No newline at end of file
+= render 'form', application: @application
diff --git a/app/views/doorkeeper/applications/index.html.haml b/app/views/doorkeeper/applications/index.html.haml
index e5be4b4bcac..3b0b19107ca 100644
--- a/app/views/doorkeeper/applications/index.html.haml
+++ b/app/views/doorkeeper/applications/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Applications"
%h3.page-title Your applications
%p= link_to 'New Application', new_oauth_application_path, class: 'btn btn-success'
%table.table.table-striped
@@ -13,4 +14,4 @@
%td= link_to application.name, oauth_application_path(application)
%td= application.redirect_uri
%td= link_to 'Edit', edit_oauth_application_path(application), class: 'btn btn-link'
- %td= render 'delete_form', application: application \ No newline at end of file
+ %td= render 'delete_form', application: application
diff --git a/app/views/doorkeeper/applications/show.html.haml b/app/views/doorkeeper/applications/show.html.haml
index 82e78b4af13..80340aca54c 100644
--- a/app/views/doorkeeper/applications/show.html.haml
+++ b/app/views/doorkeeper/applications/show.html.haml
@@ -1,3 +1,4 @@
+- page_title @application.name, "Applications"
%h3.page-title
Application: #{@application.name}
diff --git a/app/views/errors/access_denied.html.haml b/app/views/errors/access_denied.html.haml
index a1d8664c4ce..012e9857642 100644
--- a/app/views/errors/access_denied.html.haml
+++ b/app/views/errors/access_denied.html.haml
@@ -1,3 +1,4 @@
+- page_title "Access Denied"
%h1 403
%h3 Access Denied
%hr
diff --git a/app/views/errors/encoding.html.haml b/app/views/errors/encoding.html.haml
index 64c7451a8da..90cfbebfcc6 100644
--- a/app/views/errors/encoding.html.haml
+++ b/app/views/errors/encoding.html.haml
@@ -1,3 +1,4 @@
+- page_title "Encoding Error"
%h1 500
%h3 Encoding Error
%hr
diff --git a/app/views/errors/git_not_found.html.haml b/app/views/errors/git_not_found.html.haml
index 189e53bca55..ff5d4cc1506 100644
--- a/app/views/errors/git_not_found.html.haml
+++ b/app/views/errors/git_not_found.html.haml
@@ -1,3 +1,4 @@
+- page_title "Git Resource Not Found"
%h1 404
%h3 Git Resource Not found
%hr
diff --git a/app/views/errors/not_found.html.haml b/app/views/errors/not_found.html.haml
index 7bf88f592cf..3756b98ebb2 100644
--- a/app/views/errors/not_found.html.haml
+++ b/app/views/errors/not_found.html.haml
@@ -1,3 +1,4 @@
+- page_title "Not Found"
%h1 404
%h3 The resource you were looking for doesn't exist.
%hr
diff --git a/app/views/errors/omniauth_error.html.haml b/app/views/errors/omniauth_error.html.haml
index f3c8221a9d9..3e70e98a24c 100644
--- a/app/views/errors/omniauth_error.html.haml
+++ b/app/views/errors/omniauth_error.html.haml
@@ -1,3 +1,4 @@
+- page_title "Auth Error"
%h1 422
%h3 Sign-in using #{@provider} auth failed
%hr
diff --git a/app/views/explore/groups/index.html.haml b/app/views/explore/groups/index.html.haml
index 2ea6cb18655..c05d45e0100 100644
--- a/app/views/explore/groups/index.html.haml
+++ b/app/views/explore/groups/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Groups"
.clearfix
.pull-left
= form_tag explore_groups_path, method: :get, class: 'form-inline form-tiny' do |f|
diff --git a/app/views/explore/projects/index.html.haml b/app/views/explore/projects/index.html.haml
index 5086b58cd03..ba2276f51ce 100644
--- a/app/views/explore/projects/index.html.haml
+++ b/app/views/explore/projects/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Projects"
.clearfix
= render 'filter'
diff --git a/app/views/explore/projects/starred.html.haml b/app/views/explore/projects/starred.html.haml
index 420f0693756..b5d146b1f2f 100644
--- a/app/views/explore/projects/starred.html.haml
+++ b/app/views/explore/projects/starred.html.haml
@@ -1,3 +1,4 @@
+- page_title "Starred Projects"
.explore-trending-block
%p.lead
%i.fa.fa-star
diff --git a/app/views/explore/projects/trending.html.haml b/app/views/explore/projects/trending.html.haml
index 18749ac00ae..5ae2653fede 100644
--- a/app/views/explore/projects/trending.html.haml
+++ b/app/views/explore/projects/trending.html.haml
@@ -1,3 +1,4 @@
+- page_title "Trending Projects"
.explore-title
%h3
Explore GitLab
diff --git a/app/views/groups/_settings_nav.html.haml b/app/views/groups/_settings_nav.html.haml
deleted file mode 100644
index f93caf90076..00000000000
--- a/app/views/groups/_settings_nav.html.haml
+++ /dev/null
@@ -1,11 +0,0 @@
-%ul.sidebar-subnav
- = nav_link(path: 'groups#edit') do
- = link_to edit_group_path(@group), title: 'Group', data: {placement: 'right'} do
- = icon('pencil-square-o')
- %span
- Group
- = nav_link(path: 'groups#projects') do
- = link_to projects_group_path(@group), title: 'Projects', data: {placement: 'right'} do
- = icon('folder')
- %span
- Projects
diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml
index 49e7180bf98..85179d4c4a2 100644
--- a/app/views/groups/edit.html.haml
+++ b/app/views/groups/edit.html.haml
@@ -1,3 +1,4 @@
+- page_title "Settings"
.panel.panel-default
.panel-heading
%strong= @group.name
diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml
index c0c9cd170ad..903ca877218 100644
--- a/app/views/groups/group_members/index.html.haml
+++ b/app/views/groups/group_members/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Members"
- show_roles = should_user_see_group_roles?(current_user, @group)
%h3.page-title
diff --git a/app/views/groups/issues.html.haml b/app/views/groups/issues.html.haml
index cf0da2da466..6a3da6adacf 100644
--- a/app/views/groups/issues.html.haml
+++ b/app/views/groups/issues.html.haml
@@ -1,3 +1,4 @@
+- page_title "Issues"
= content_for :meta_tags do
- if current_user
= auto_discovery_link_tag(:atom, issues_group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} issues")
diff --git a/app/views/groups/merge_requests.html.haml b/app/views/groups/merge_requests.html.haml
index 1ad74905636..268f33d5761 100644
--- a/app/views/groups/merge_requests.html.haml
+++ b/app/views/groups/merge_requests.html.haml
@@ -1,3 +1,4 @@
+- page_title "Merge Requests"
%h3.page-title
Merge Requests
diff --git a/app/views/groups/milestones/index.html.haml b/app/views/groups/milestones/index.html.haml
index 008d5a6bd22..385222fa5b7 100644
--- a/app/views/groups/milestones/index.html.haml
+++ b/app/views/groups/milestones/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Milestones"
%h3.page-title
Milestones
%span.pull-right #{@group_milestones.count} milestones
diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml
index fb32f2caa4c..6c41cd6b9e4 100644
--- a/app/views/groups/milestones/show.html.haml
+++ b/app/views/groups/milestones/show.html.haml
@@ -1,3 +1,4 @@
+- page_title @group_milestone.title, "Milestones"
%h4.page-title
.issue-box{ class: "issue-box-#{@group_milestone.closed? ? 'closed' : 'open'}" }
- if @group_milestone.closed?
diff --git a/app/views/groups/new.html.haml b/app/views/groups/new.html.haml
index 6e17cdaef6f..edb882bea19 100644
--- a/app/views/groups/new.html.haml
+++ b/app/views/groups/new.html.haml
@@ -1,3 +1,5 @@
+- page_title 'New Group'
+- header_title 'New Group'
= form_for @group, html: { class: 'group-form form-horizontal' } do |f|
- if @group.errors.any?
.alert.alert-danger
diff --git a/app/views/groups/projects.html.haml b/app/views/groups/projects.html.haml
index 0d547984cc9..6b7efa83dea 100644
--- a/app/views/groups/projects.html.haml
+++ b/app/views/groups/projects.html.haml
@@ -1,3 +1,4 @@
+- page_title "Projects"
.panel.panel-default
.panel-heading
%strong= @group.name
diff --git a/app/views/help/show.html.haml b/app/views/help/show.html.haml
index cc1be6a717a..8551496b98a 100644
--- a/app/views/help/show.html.haml
+++ b/app/views/help/show.html.haml
@@ -1,2 +1,3 @@
+- page_title @file.humanize, *@category.split("/").reverse.map(&:humanize)
.documentation.wiki
= markdown @markdown.gsub('$your_email', current_user.email)
diff --git a/app/views/help/ui.html.haml b/app/views/help/ui.html.haml
index 246a6c1bdfd..7c89457ace3 100644
--- a/app/views/help/ui.html.haml
+++ b/app/views/help/ui.html.haml
@@ -1,3 +1,4 @@
+- page_title "UI Development Kit", "Help"
- lorem = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed fermentum nisi sapien, non consequat lectus aliquam ultrices. Suspendisse sodales est euismod nunc condimentum, a consectetur diam ornare."
.gitlab-ui-dev-kit
diff --git a/app/views/import/bitbucket/status.html.haml b/app/views/import/bitbucket/status.html.haml
index 4e49bbbc7fa..9d2858e4e72 100644
--- a/app/views/import/bitbucket/status.html.haml
+++ b/app/views/import/bitbucket/status.html.haml
@@ -1,3 +1,4 @@
+- page_title "Bitbucket import"
%h3.page-title
%i.fa.fa-bitbucket
Import projects from Bitbucket
diff --git a/app/views/import/github/status.html.haml b/app/views/import/github/status.html.haml
index f0bc3e6b1ac..ef552498239 100644
--- a/app/views/import/github/status.html.haml
+++ b/app/views/import/github/status.html.haml
@@ -1,3 +1,4 @@
+- page_title "GitHub import"
%h3.page-title
%i.fa.fa-github
Import projects from GitHub
diff --git a/app/views/import/gitlab/status.html.haml b/app/views/import/gitlab/status.html.haml
index 33b0a21acf3..727f3c7e7fa 100644
--- a/app/views/import/gitlab/status.html.haml
+++ b/app/views/import/gitlab/status.html.haml
@@ -1,3 +1,4 @@
+- page_title "GitLab.com import"
%h3.page-title
%i.fa.fa-heart
Import projects from GitLab.com
diff --git a/app/views/import/gitorious/status.html.haml b/app/views/import/gitorious/status.html.haml
index 78c5e957be0..bff7ee7c85d 100644
--- a/app/views/import/gitorious/status.html.haml
+++ b/app/views/import/gitorious/status.html.haml
@@ -1,3 +1,4 @@
+- page_title "Gitorious import"
%h3.page-title
%i.icon-gitorious.icon-gitorious-big
Import projects from Gitorious.org
diff --git a/app/views/import/google_code/new.html.haml b/app/views/import/google_code/new.html.haml
index ce78fec205f..9c64e0a009f 100644
--- a/app/views/import/google_code/new.html.haml
+++ b/app/views/import/google_code/new.html.haml
@@ -1,3 +1,4 @@
+- page_title "Google Code import"
%h3.page-title
%i.fa.fa-google
Import projects from Google Code
diff --git a/app/views/import/google_code/new_user_map.html.haml b/app/views/import/google_code/new_user_map.html.haml
index 9c6824ecad7..e53ebda7dc1 100644
--- a/app/views/import/google_code/new_user_map.html.haml
+++ b/app/views/import/google_code/new_user_map.html.haml
@@ -1,3 +1,4 @@
+- page_title "User map", "Google Code import"
%h3.page-title
%i.fa.fa-google
Import projects from Google Code
diff --git a/app/views/import/google_code/status.html.haml b/app/views/import/google_code/status.html.haml
index 2013b8c03c6..e8ec79e72f7 100644
--- a/app/views/import/google_code/status.html.haml
+++ b/app/views/import/google_code/status.html.haml
@@ -1,16 +1,21 @@
+- page_title "Google Code import"
%h3.page-title
%i.fa.fa-google
Import projects from Google Code
-%p.light
- Select projects you want to import.
-%p.light
- Optionally, you can
- = link_to "customize", new_user_map_import_google_code_path
- how Google Code email addresses and usernames are imported into GitLab.
-%hr
-%p
- = button_tag 'Import all projects', class: "btn btn-success js-import-all"
+- if @repos.any?
+ %p.light
+ Select projects you want to import.
+ %p.light
+ Optionally, you can
+ = link_to "customize", new_user_map_import_google_code_path
+ how Google Code email addresses and usernames are imported into GitLab.
+ %hr
+ %p
+ - if @incompatible_repos.any?
+ = button_tag 'Import all compatible projects', class: "btn btn-success js-import-all"
+ - else
+ = button_tag 'Import all projects', class: "btn btn-success js-import-all"
%table.table.import-jobs
%thead
@@ -44,6 +49,22 @@
= "#{current_user.username}/#{repo.name}"
%td.import-actions.job-status
= button_tag "Import", class: "btn js-add-to-import"
+ - @incompatible_repos.each do |repo|
+ %tr{id: "repo_#{repo.id}"}
+ %td
+ = link_to repo.name, "https://code.google.com/p/#{repo.name}", target: "_blank"
+ %td.import-target
+ %td.import-actions-job-status
+ = label_tag "Incompatible Project", nil, class: "label label-danger"
+
+- if @incompatible_repos.any?
+ %p
+ One or more of your Google Code projects cannot be imported into GitLab
+ directly because they use Subversion or Mercurial for version control,
+ rather than Git. Please convert them to Git on Google Code, and go
+ through the
+ = link_to "import flow", new_import_google_code_path
+ again.
:coffeescript
new ImporterStatus("#{jobs_import_google_code_path}", "#{import_google_code_path}")
diff --git a/app/views/invites/show.html.haml b/app/views/invites/show.html.haml
index ab0ecffe4d2..2fd4859c1c6 100644
--- a/app/views/invites/show.html.haml
+++ b/app/views/invites/show.html.haml
@@ -1,3 +1,4 @@
+- page_title "Invitation"
%h3.page-title Invitation
%p
diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml
index f72882d883e..b1a57d9824e 100644
--- a/app/views/layouts/_head.html.haml
+++ b/app/views/layouts/_head.html.haml
@@ -1,11 +1,10 @@
+- page_title "GitLab"
%head
%meta{charset: "utf-8"}
%meta{'http-equiv' => 'X-UA-Compatible', content: 'IE=edge'}
%meta{content: "GitLab Community Edition", name: "description"}
- %title
- = "#{title} | " if defined?(title)
- GitLab
+ %title= page_title
= favicon_link_tag 'favicon.ico'
= stylesheet_link_tag "application", :media => "all"
diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml
index 0fa2ec9824d..5c55bdb5465 100644
--- a/app/views/layouts/_page.html.haml
+++ b/app/views/layouts/_page.html.haml
@@ -1,8 +1,8 @@
.page-with-sidebar{ class: nav_sidebar_class }
= render "layouts/broadcast"
.sidebar-wrapper
- - if defined?(sidebar)
- = render(sidebar)
+ - if defined?(sidebar) && sidebar
+ = render "layouts/nav/#{sidebar}"
- elsif current_user
= render 'layouts/nav/dashboard'
.collapse-nav
diff --git a/app/views/layouts/admin.html.haml b/app/views/layouts/admin.html.haml
index ab84e87c300..1c738719bd8 100644
--- a/app/views/layouts/admin.html.haml
+++ b/app/views/layouts/admin.html.haml
@@ -1,6 +1,5 @@
-!!! 5
-%html{ lang: "en"}
- = render "layouts/head", title: "Admin area"
- %body{class: "#{app_theme} admin", :'data-page' => body_data_page}
- = render "layouts/head_panel", title: link_to("Admin area", admin_root_path)
- = render 'layouts/page', sidebar: 'layouts/nav/admin'
+- page_title "Admin area"
+- header_title "Admin area", admin_root_path
+- sidebar "admin"
+
+= render template: "layouts/application"
diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml
index 6bd8ac4adb8..a97feeb1ecd 100644
--- a/app/views/layouts/application.html.haml
+++ b/app/views/layouts/application.html.haml
@@ -1,6 +1,10 @@
!!! 5
%html{ lang: "en"}
- = render "layouts/head", title: "Dashboard"
- %body{class: "#{app_theme} application", :'data-page' => body_data_page }
- = render "layouts/head_panel", title: link_to("Dashboard", root_path)
- = render 'layouts/page', sidebar: 'layouts/nav/dashboard'
+ = render "layouts/head"
+ %body{class: "#{app_theme}", :'data-page' => body_data_page}
+ - if current_user
+ = render "layouts/head_panel", title: header_title
+ - else
+ = render "layouts/public_head_panel", title: header_title
+
+ = render 'layouts/page', sidebar: sidebar
diff --git a/app/views/layouts/dashboard.html.haml b/app/views/layouts/dashboard.html.haml
new file mode 100644
index 00000000000..c72eca10bf4
--- /dev/null
+++ b/app/views/layouts/dashboard.html.haml
@@ -0,0 +1,5 @@
+- page_title "Dashboard"
+- header_title "Dashboard", root_path
+- sidebar "dashboard"
+
+= render template: "layouts/application"
diff --git a/app/views/layouts/errors.html.haml b/app/views/layouts/errors.html.haml
index e51fd4cb820..aa0f3f0a819 100644
--- a/app/views/layouts/errors.html.haml
+++ b/app/views/layouts/errors.html.haml
@@ -1,6 +1,6 @@
!!! 5
%html{ lang: "en"}
- = render "layouts/head", title: "Error"
+ = render "layouts/head"
%body{class: "#{app_theme} application"}
= render "layouts/head_panel", title: "" if current_user
.container.navless-container
diff --git a/app/views/layouts/explore.html.haml b/app/views/layouts/explore.html.haml
index ca79382324e..56bb92a536e 100644
--- a/app/views/layouts/explore.html.haml
+++ b/app/views/layouts/explore.html.haml
@@ -1,12 +1,5 @@
-- page_title = 'Explore GitLab'
-!!! 5
-%html{ lang: "en"}
- = render "layouts/head", title: page_title
- %body{class: "#{app_theme} application", :'data-page' => body_data_page}
- = render "layouts/broadcast"
- - if current_user
- = render "layouts/head_panel", title: link_to(page_title, explore_root_path)
- - else
- = render "layouts/public_head_panel", title: link_to(page_title, explore_root_path)
+- page_title "Explore"
+- header_title "Explore GitLab", explore_root_path
+- sidebar "explore"
- = render 'layouts/page', sidebar: 'layouts/nav/explore'
+= render template: "layouts/application"
diff --git a/app/views/layouts/group.html.haml b/app/views/layouts/group.html.haml
index f4a6bee15f6..5edc03129d2 100644
--- a/app/views/layouts/group.html.haml
+++ b/app/views/layouts/group.html.haml
@@ -1,6 +1,5 @@
-!!! 5
-%html{ lang: "en"}
- = render "layouts/head", title: group_head_title
- %body{class: "#{app_theme} application", :'data-page' => body_data_page}
- = render "layouts/head_panel", title: link_to(@group.name, group_path(@group))
- = render 'layouts/page', sidebar: 'layouts/nav/group'
+- page_title @group.name
+- header_title @group.name, group_path(@group)
+- sidebar "group"
+
+= render template: "layouts/application"
diff --git a/app/views/layouts/help.html.haml b/app/views/layouts/help.html.haml
new file mode 100644
index 00000000000..224b24befbe
--- /dev/null
+++ b/app/views/layouts/help.html.haml
@@ -0,0 +1,4 @@
+- page_title "Help"
+- header_title "Help", help_path
+
+= render template: "layouts/application"
diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml
index 74a8526dbd7..62f0579d48b 100644
--- a/app/views/layouts/nav/_group.html.haml
+++ b/app/views/layouts/nav/_group.html.haml
@@ -39,4 +39,15 @@
= icon ('angle-down fw')
- if group_settings_page?
- = render 'groups/settings_nav'
+ %ul.sidebar-subnav
+ = nav_link(path: 'groups#edit') do
+ = link_to edit_group_path(@group), title: 'Group', data: {placement: 'right'} do
+ = icon('pencil-square-o')
+ %span
+ Group
+ = nav_link(path: 'groups#projects') do
+ = link_to projects_group_path(@group), title: 'Projects', data: {placement: 'right'} do
+ = icon('folder')
+ %span
+ Projects
+
diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml
index 01b3d70194f..172f5197b24 100644
--- a/app/views/layouts/nav/_project.html.haml
+++ b/app/views/layouts/nav/_project.html.haml
@@ -1,97 +1,85 @@
%ul.project-navigation.nav.nav-sidebar
- - if @project_settings_nav
- = nav_link do
- = link_to project_path(@project), title: 'Back to project', data: {placement: 'right'} do
- = icon('caret-square-o-left fw')
+ = nav_link(path: 'projects#show', html_options: {class: 'home'}) do
+ = link_to project_path(@project), title: 'Project', class: 'shortcuts-project', data: {placement: 'right'} do
+ = icon('dashboard fw')
+ %span
+ Project
+ - if project_nav_tab? :files
+ = nav_link(controller: %w(tree blob blame edit_tree new_tree)) do
+ = link_to namespace_project_tree_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Files', class: 'shortcuts-tree', data: {placement: 'right'} do
+ = icon('files-o fw')
%span
- Back to project
+ Files
- %li.separate-item
-
- = render 'projects/settings_nav'
-
- - else
- = nav_link(path: 'projects#show', html_options: {class: 'home'}) do
- = link_to project_path(@project), title: 'Project', class: 'shortcuts-project', data: {placement: 'right'} do
- = icon('dashboard fw')
+ - if project_nav_tab? :commits
+ = nav_link(controller: %w(commit commits compare repositories tags branches)) do
+ = link_to namespace_project_commits_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Commits', class: 'shortcuts-commits', data: {placement: 'right'} do
+ = icon('history fw')
%span
- Project
- - if project_nav_tab? :files
- = nav_link(controller: %w(tree blob blame edit_tree new_tree)) do
- = link_to namespace_project_tree_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Files', class: 'shortcuts-tree', data: {placement: 'right'} do
- = icon('files-o fw')
- %span
- Files
-
- - if project_nav_tab? :commits
- = nav_link(controller: %w(commit commits compare repositories tags branches)) do
- = link_to namespace_project_commits_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Commits', class: 'shortcuts-commits', data: {placement: 'right'} do
- = icon('history fw')
- %span
- Commits
+ Commits
- - if project_nav_tab? :network
- = nav_link(controller: %w(network)) do
- = link_to namespace_project_network_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Network', class: 'shortcuts-network', data: {placement: 'right'} do
- = icon('code-fork fw')
- %span
- Network
+ - if project_nav_tab? :network
+ = nav_link(controller: %w(network)) do
+ = link_to namespace_project_network_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Network', class: 'shortcuts-network', data: {placement: 'right'} do
+ = icon('code-fork fw')
+ %span
+ Network
- - if project_nav_tab? :graphs
- = nav_link(controller: %w(graphs)) do
- = link_to namespace_project_graph_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Graphs', class: 'shortcuts-graphs', data: {placement: 'right'} do
- = icon('area-chart fw')
- %span
- Graphs
+ - if project_nav_tab? :graphs
+ = nav_link(controller: %w(graphs)) do
+ = link_to namespace_project_graph_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Graphs', class: 'shortcuts-graphs', data: {placement: 'right'} do
+ = icon('area-chart fw')
+ %span
+ Graphs
- - if project_nav_tab? :milestones
- = nav_link(controller: :milestones) do
- = link_to namespace_project_milestones_path(@project.namespace, @project), title: 'Milestones', data: {placement: 'right'} do
- = icon('clock-o fw')
- %span
- Milestones
+ - if project_nav_tab? :milestones
+ = nav_link(controller: :milestones) do
+ = link_to namespace_project_milestones_path(@project.namespace, @project), title: 'Milestones', data: {placement: 'right'} do
+ = icon('clock-o fw')
+ %span
+ Milestones
- - if project_nav_tab? :issues
- = nav_link(controller: :issues) do
- = link_to url_for_project_issues(@project, only_path: true), title: 'Issues', class: 'shortcuts-issues', data: {placement: 'right'} do
- = icon('exclamation-circle fw')
- %span
- Issues
- - if @project.default_issues_tracker?
- %span.count.issue_counter= @project.issues.opened.count
+ - if project_nav_tab? :issues
+ = nav_link(controller: :issues) do
+ = link_to url_for_project_issues(@project, only_path: true), title: 'Issues', class: 'shortcuts-issues', data: {placement: 'right'} do
+ = icon('exclamation-circle fw')
+ %span
+ Issues
+ - 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 namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests', class: 'shortcuts-merge_requests', data: {placement: 'right'} do
- = icon('tasks fw')
- %span
- Merge Requests
- %span.count.merge_counter= @project.merge_requests.opened.count
+ - if project_nav_tab? :merge_requests
+ = nav_link(controller: :merge_requests) do
+ = link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests', class: 'shortcuts-merge_requests', data: {placement: 'right'} do
+ = icon('tasks fw')
+ %span
+ Merge Requests
+ %span.count.merge_counter= @project.merge_requests.opened.count
- - if project_nav_tab? :labels
- = nav_link(controller: :labels) do
- = link_to namespace_project_labels_path(@project.namespace, @project), title: 'Labels', data: {placement: 'right'} do
- = icon('tags fw')
- %span
- Labels
+ - if project_nav_tab? :labels
+ = nav_link(controller: :labels) do
+ = link_to namespace_project_labels_path(@project.namespace, @project), title: 'Labels', data: {placement: 'right'} do
+ = icon('tags fw')
+ %span
+ Labels
- - if project_nav_tab? :wiki
- = nav_link(controller: :wikis) do
- = link_to get_project_wiki_path(@project), title: 'Wiki', class: 'shortcuts-wiki', data: {placement: 'right'} do
- = icon('book fw')
- %span
- Wiki
+ - if project_nav_tab? :wiki
+ = nav_link(controller: :wikis) do
+ = link_to get_project_wiki_path(@project), title: 'Wiki', class: 'shortcuts-wiki', data: {placement: 'right'} do
+ = icon('book fw')
+ %span
+ Wiki
- - if project_nav_tab? :snippets
- = nav_link(controller: :snippets) do
- = link_to namespace_project_snippets_path(@project.namespace, @project), title: 'Snippets', class: 'shortcuts-snippets', data: {placement: 'right'} do
- = icon('file-text-o fw')
- %span
- Snippets
+ - if project_nav_tab? :snippets
+ = nav_link(controller: :snippets) do
+ = link_to namespace_project_snippets_path(@project.namespace, @project), title: 'Snippets', class: 'shortcuts-snippets', data: {placement: 'right'} do
+ = icon('file-text-o fw')
+ %span
+ Snippets
- - if project_nav_tab? :settings
- = nav_link(html_options: {class: "#{project_tab_class} separate-item"}) do
- = link_to edit_project_path(@project), title: 'Settings', class: 'stat-tab tab no-highlight', data: {placement: 'right'} do
- = icon('cogs fw')
- %span
- Settings
+ - if project_nav_tab? :settings
+ = nav_link(html_options: {class: "#{project_tab_class} separate-item"}) do
+ = link_to edit_project_path(@project), title: 'Settings', class: 'stat-tab tab no-highlight', data: {placement: 'right'} do
+ = icon('cogs fw')
+ %span
+ Settings
diff --git a/app/views/layouts/nav/_project_settings.html.haml b/app/views/layouts/nav/_project_settings.html.haml
new file mode 100644
index 00000000000..21260302a09
--- /dev/null
+++ b/app/views/layouts/nav/_project_settings.html.haml
@@ -0,0 +1,41 @@
+%ul.project-navigation.nav.nav-sidebar
+ = nav_link do
+ = link_to project_path(@project), title: 'Back to project', data: {placement: 'right'} do
+ = icon('caret-square-o-left fw')
+ %span
+ Back to project
+
+ %li.separate-item
+
+ %ul.project-settings-nav.sidebar-subnav
+ = nav_link(path: 'projects#edit') do
+ = link_to edit_project_path(@project), title: 'Project', class: 'stat-tab tab', data: {placement: 'right'} do
+ = icon('pencil-square-o')
+ %span
+ Project
+ = nav_link(controller: [:project_members, :teams]) do
+ = link_to namespace_project_project_members_path(@project.namespace, @project), title: 'Members', class: 'team-tab tab', data: {placement: 'right'} do
+ = icon('users')
+ %span
+ Members
+ = nav_link(controller: :deploy_keys) do
+ = link_to namespace_project_deploy_keys_path(@project.namespace, @project), title: 'Deploy Keys', data: {placement: 'right'} do
+ = icon('key')
+ %span
+ Deploy Keys
+ = nav_link(controller: :hooks) do
+ = link_to namespace_project_hooks_path(@project.namespace, @project), title: 'Web Hooks', data: {placement: 'right'} do
+ = icon('link')
+ %span
+ Web Hooks
+ = nav_link(controller: :services) do
+ = link_to namespace_project_services_path(@project.namespace, @project), title: 'Services', data: {placement: 'right'} do
+ = icon('cogs')
+ %span
+ Services
+ = nav_link(controller: :protected_branches) do
+ = link_to namespace_project_protected_branches_path(@project.namespace, @project), title: 'Protected Branches', data: {placement: 'right'} do
+ = icon('lock')
+ %span
+ Protected branches
+
diff --git a/app/views/layouts/nav/_snippets.html.haml b/app/views/layouts/nav/_snippets.html.haml
index 0de3a9e5bb7..458b76a2c99 100644
--- a/app/views/layouts/nav/_snippets.html.haml
+++ b/app/views/layouts/nav/_snippets.html.haml
@@ -1,9 +1,10 @@
%ul.nav.nav-sidebar
- = nav_link(path: user_snippets_path(current_user), html_options: {class: 'home'}) do
- = link_to user_snippets_path(current_user), title: 'Your snippets', data: {placement: 'right'} do
- = icon('dashboard fw')
- %span
- Your Snippets
+ - if current_user
+ = nav_link(path: user_snippets_path(current_user), html_options: {class: 'home'}) do
+ = link_to user_snippets_path(current_user), title: 'Your snippets', data: {placement: 'right'} do
+ = icon('dashboard fw')
+ %span
+ Your Snippets
= nav_link(path: snippets_path) do
= link_to snippets_path, title: 'Discover snippets', data: {placement: 'right'} do
= icon('globe fw')
diff --git a/app/views/layouts/navless.html.haml b/app/views/layouts/navless.html.haml
deleted file mode 100644
index 10a0fcea2f8..00000000000
--- a/app/views/layouts/navless.html.haml
+++ /dev/null
@@ -1,7 +0,0 @@
-!!! 5
-%html{ lang: "en"}
- = render "layouts/head", title: @title
- %body{class: "#{app_theme} application", :'data-page' => body_data_page}
- = render "layouts/broadcast"
- = render "layouts/head_panel", title: defined?(@title_url) ? link_to(@title, @title_url) : @title
- = render 'layouts/page'
diff --git a/app/views/layouts/profile.html.haml b/app/views/layouts/profile.html.haml
index 2b5be7fc372..9799b4cc4d7 100644
--- a/app/views/layouts/profile.html.haml
+++ b/app/views/layouts/profile.html.haml
@@ -1,6 +1,5 @@
-!!! 5
-%html{ lang: "en"}
- = render "layouts/head", title: "Profile"
- %body{class: "#{app_theme} profile", :'data-page' => body_data_page}
- = render "layouts/head_panel", title: link_to("Profile", profile_path)
- = render 'layouts/page', sidebar: 'layouts/nav/profile'
+- page_title "Profile"
+- header_title "Profile", profile_path
+- sidebar "profile"
+
+= render template: "layouts/application"
diff --git a/app/views/layouts/project.html.haml b/app/views/layouts/project.html.haml
new file mode 100644
index 00000000000..4aeb9d397d2
--- /dev/null
+++ b/app/views/layouts/project.html.haml
@@ -0,0 +1,8 @@
+- page_title @project.name_with_namespace
+- header_title project_title(@project)
+- sidebar "project" unless sidebar
+
+- content_for :embedded_scripts do
+ = render "layouts/init_auto_complete" if current_user
+
+= render template: "layouts/application"
diff --git a/app/views/layouts/project_settings.html.haml b/app/views/layouts/project_settings.html.haml
index 0a0039dec16..43401668334 100644
--- a/app/views/layouts/project_settings.html.haml
+++ b/app/views/layouts/project_settings.html.haml
@@ -1,8 +1,4 @@
-!!! 5
-%html{ lang: "en"}
- = render "layouts/head", title: @project.name_with_namespace
- %body{class: "#{app_theme} project", :'data-page' => body_data_page, :'data-project-id' => @project.id }
- = render "layouts/head_panel", title: project_title(@project)
- = render "layouts/init_auto_complete"
- - @project_settings_nav = true
- = render 'layouts/page', sidebar: 'layouts/nav/project'
+- page_title "Settings"
+- sidebar "project_settings"
+
+= render template: "layouts/project"
diff --git a/app/views/layouts/projects.html.haml b/app/views/layouts/projects.html.haml
deleted file mode 100644
index dde0964f47f..00000000000
--- a/app/views/layouts/projects.html.haml
+++ /dev/null
@@ -1,7 +0,0 @@
-!!! 5
-%html{ lang: "en"}
- = render "layouts/head", title: project_head_title
- %body{class: "#{app_theme} project", :'data-page' => body_data_page, :'data-project-id' => @project.id }
- = 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
deleted file mode 100644
index b9b1d03e08e..00000000000
--- a/app/views/layouts/public_group.html.haml
+++ /dev/null
@@ -1,6 +0,0 @@
-!!! 5
-%html{ lang: "en"}
- = render "layouts/head", title: group_head_title
- %body{class: "#{app_theme} application", :'data-page' => body_data_page}
- = render "layouts/public_head_panel", title: link_to(@group.name, group_path(@group))
- = 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
deleted file mode 100644
index 04fa7c84e73..00000000000
--- a/app/views/layouts/public_projects.html.haml
+++ /dev/null
@@ -1,6 +0,0 @@
-!!! 5
-%html{ lang: "en"}
- = render "layouts/head", title: @project.name_with_namespace
- %body{class: "#{app_theme} application", :'data-page' => body_data_page}
- = 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
deleted file mode 100644
index 71c16bd1684..00000000000
--- a/app/views/layouts/public_users.html.haml
+++ /dev/null
@@ -1,6 +0,0 @@
-!!! 5
-%html{ lang: "en"}
- = render "layouts/head", title: @title
- %body{class: "#{app_theme} application", :'data-page' => body_data_page}
- = render "layouts/public_head_panel", title: defined?(@title_url) ? link_to(@title, @title_url) : @title
- = render 'layouts/page'
diff --git a/app/views/layouts/search.html.haml b/app/views/layouts/search.html.haml
index 4b526686be4..fd4c7ad21a7 100644
--- a/app/views/layouts/search.html.haml
+++ b/app/views/layouts/search.html.haml
@@ -1,6 +1,4 @@
-!!! 5
-%html{ lang: "en"}
- = render "layouts/head", title: "Search"
- %body{class: "#{app_theme} application", :'data-page' => body_data_page}
- = render "layouts/head_panel", title: link_to("Search", search_path)
- = render 'layouts/page'
+- page_title "Search"
+- header_title "Search", search_path
+
+= render template: "layouts/application"
diff --git a/app/views/layouts/snippets.html.haml b/app/views/layouts/snippets.html.haml
index fbd29eb23a7..9b0f40073ab 100644
--- a/app/views/layouts/snippets.html.haml
+++ b/app/views/layouts/snippets.html.haml
@@ -1,6 +1,5 @@
-!!! 5
-%html{ lang: "en"}
- = render "layouts/head", title: "Dashboard"
- %body{class: "#{app_theme} application", :'data-page' => body_data_page }
- = render "layouts/head_panel", title: link_to("Snippets", snippets_path)
- = render 'layouts/page', sidebar: 'layouts/nav/snippets'
+- page_title 'Snippets'
+- header_title 'Snippets', snippets_path
+- sidebar "snippets"
+
+= render template: "layouts/application"
diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml
index 5bffb4acc1d..1c3a3d68aca 100644
--- a/app/views/profiles/accounts/show.html.haml
+++ b/app/views/profiles/accounts/show.html.haml
@@ -1,3 +1,4 @@
+- page_title "Account"
- if current_user.ldap_user?
.alert.alert-info
Some options are unavailable for LDAP accounts
diff --git a/app/views/profiles/applications.html.haml b/app/views/profiles/applications.html.haml
index 97e98948f36..c4f6f59624b 100644
--- a/app/views/profiles/applications.html.haml
+++ b/app/views/profiles/applications.html.haml
@@ -1,3 +1,4 @@
+- page_title "Applications"
%h3.page-title
Application Settings
%p.light
diff --git a/app/views/profiles/design.html.haml b/app/views/profiles/design.html.haml
index 646576c3164..af284f60409 100644
--- a/app/views/profiles/design.html.haml
+++ b/app/views/profiles/design.html.haml
@@ -1,3 +1,4 @@
+- page_title "Design"
%h3.page-title
Design Settings
%p.light
diff --git a/app/views/profiles/emails/index.html.haml b/app/views/profiles/emails/index.html.haml
index 09f290429ea..c17e01425d8 100644
--- a/app/views/profiles/emails/index.html.haml
+++ b/app/views/profiles/emails/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Emails"
%h3.page-title
Email Settings
%p.light
diff --git a/app/views/profiles/history.html.haml b/app/views/profiles/history.html.haml
index b1ab433f48f..b414fb69f4e 100644
--- a/app/views/profiles/history.html.haml
+++ b/app/views/profiles/history.html.haml
@@ -1,3 +1,4 @@
+- page_title "History"
%h3.page-title
Your Account History
%p.light
diff --git a/app/views/profiles/keys/index.html.haml b/app/views/profiles/keys/index.html.haml
index 0904c50c88b..e3af0d4e189 100644
--- a/app/views/profiles/keys/index.html.haml
+++ b/app/views/profiles/keys/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "SSH Keys"
%h3.page-title
SSH Keys Settings
.pull-right
diff --git a/app/views/profiles/keys/new.html.haml b/app/views/profiles/keys/new.html.haml
index ccec716d0c6..2bf207a3221 100644
--- a/app/views/profiles/keys/new.html.haml
+++ b/app/views/profiles/keys/new.html.haml
@@ -1,3 +1,4 @@
+- page_title "Add SSH Keys"
%h3.page-title Add an SSH Key
%p.light
Paste your public key here. Read more about how to generate a key on #{link_to "the SSH help page", help_page_path("ssh", "README")}.
diff --git a/app/views/profiles/keys/show.html.haml b/app/views/profiles/keys/show.html.haml
index cfd53298962..89f6f01581a 100644
--- a/app/views/profiles/keys/show.html.haml
+++ b/app/views/profiles/keys/show.html.haml
@@ -1 +1,2 @@
+- page_title @key.title, "SSH Keys"
= render "key_details"
diff --git a/app/views/profiles/notifications/show.html.haml b/app/views/profiles/notifications/show.html.haml
index 273e72f8a4d..a74d97dac3b 100644
--- a/app/views/profiles/notifications/show.html.haml
+++ b/app/views/profiles/notifications/show.html.haml
@@ -1,3 +1,4 @@
+- page_title "Notifications"
%h3.page-title
Notifications Settings
%p.light
diff --git a/app/views/profiles/passwords/edit.html.haml b/app/views/profiles/passwords/edit.html.haml
index 4b04b113e89..21dabbdfe2c 100644
--- a/app/views/profiles/passwords/edit.html.haml
+++ b/app/views/profiles/passwords/edit.html.haml
@@ -1,3 +1,4 @@
+- page_title "Password"
%h3.page-title Password Settings
%p.light
- if @user.password_automatically_set?
diff --git a/app/views/profiles/passwords/new.html.haml b/app/views/profiles/passwords/new.html.haml
index 8bed6e0dbee..9c6204963e0 100644
--- a/app/views/profiles/passwords/new.html.haml
+++ b/app/views/profiles/passwords/new.html.haml
@@ -1,3 +1,5 @@
+- page_title "New Password"
+- header_title "New Password"
%h3.page-title Setup new password
%hr
= form_for @user, url: profile_password_path, method: :post, html: { class: 'form-horizontal '} do |f|
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index 6c745e69e40..29c30905117 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -1,3 +1,4 @@
+- page_title "Settings"
%h3.page-title
Profile Settings
%p.light
diff --git a/app/views/projects/_settings_nav.html.haml b/app/views/projects/_settings_nav.html.haml
deleted file mode 100644
index f8b74809b76..00000000000
--- a/app/views/projects/_settings_nav.html.haml
+++ /dev/null
@@ -1,31 +0,0 @@
-%ul.project-settings-nav.sidebar-subnav
- = nav_link(path: 'projects#edit') do
- = link_to edit_project_path(@project), title: 'Project', class: 'stat-tab tab', data: {placement: 'right'} do
- = icon('pencil-square-o')
- %span
- Project
- = nav_link(controller: [:project_members, :teams]) do
- = link_to namespace_project_project_members_path(@project.namespace, @project), title: 'Members', class: 'team-tab tab', data: {placement: 'right'} do
- = icon('users')
- %span
- Members
- = nav_link(controller: :deploy_keys) do
- = link_to namespace_project_deploy_keys_path(@project.namespace, @project), title: 'Deploy Keys', data: {placement: 'right'} do
- = icon('key')
- %span
- Deploy Keys
- = nav_link(controller: :hooks) do
- = link_to namespace_project_hooks_path(@project.namespace, @project), title: 'Web Hooks', data: {placement: 'right'} do
- = icon('link')
- %span
- Web Hooks
- = nav_link(controller: :services) do
- = link_to namespace_project_services_path(@project.namespace, @project), title: 'Services', data: {placement: 'right'} do
- = icon('cogs')
- %span
- Services
- = nav_link(controller: :protected_branches) do
- = link_to namespace_project_protected_branches_path(@project.namespace, @project), title: 'Protected Branches', data: {placement: 'right'} do
- = icon('lock')
- %span
- Protected branches
diff --git a/app/views/projects/blame/show.html.haml b/app/views/projects/blame/show.html.haml
index 89dd68d6471..462f5b7afb0 100644
--- a/app/views/projects/blame/show.html.haml
+++ b/app/views/projects/blame/show.html.haml
@@ -1,3 +1,4 @@
+- page_title "Blame", @blob.path, @ref
%h3.page-title Blame view
#tree-holder.tree-holder
diff --git a/app/views/projects/blob/edit.html.haml b/app/views/projects/blob/edit.html.haml
index 1f61a0b940c..e78181f8801 100644
--- a/app/views/projects/blob/edit.html.haml
+++ b/app/views/projects/blob/edit.html.haml
@@ -1,3 +1,4 @@
+- page_title "Edit", @blob.path, @ref
.file-editor
%ul.nav.nav-tabs.js-edit-mode
%li.active
diff --git a/app/views/projects/blob/new.html.haml b/app/views/projects/blob/new.html.haml
index d78a01f6422..9b1d03b820e 100644
--- a/app/views/projects/blob/new.html.haml
+++ b/app/views/projects/blob/new.html.haml
@@ -1,3 +1,4 @@
+- page_title "New File", @ref
%h3.page-title New file
.file-editor
= form_tag(namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post, class: 'form-horizontal form-new-file') do
diff --git a/app/views/projects/blob/show.html.haml b/app/views/projects/blob/show.html.haml
index 69167654c39..a1d464bac59 100644
--- a/app/views/projects/blob/show.html.haml
+++ b/app/views/projects/blob/show.html.haml
@@ -1,3 +1,4 @@
+- page_title @blob.path, @ref
%div.tree-ref-holder
= render 'shared/ref_switcher', destination: 'blob', path: @path
diff --git a/app/views/projects/branches/index.html.haml b/app/views/projects/branches/index.html.haml
index a313ffcf272..80acc937908 100644
--- a/app/views/projects/branches/index.html.haml
+++ b/app/views/projects/branches/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Branches"
= render "projects/commits/head"
%h3.page-title
Branches
diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml
index e5fcb98c68c..cac5dc91afd 100644
--- a/app/views/projects/branches/new.html.haml
+++ b/app/views/projects/branches/new.html.haml
@@ -1,3 +1,4 @@
+- page_title "New Branch"
- if @error
.alert.alert-danger
%button{ type: "button", class: "close", "data-dismiss" => "alert"} &times;
diff --git a/app/views/projects/commit/show.html.haml b/app/views/projects/commit/show.html.haml
index fc721067ed4..fc91f71e8d2 100644
--- a/app/views/projects/commit/show.html.haml
+++ b/app/views/projects/commit/show.html.haml
@@ -1,3 +1,4 @@
+- page_title "#{@commit.title} (#{@commit.short_id})", "Commits"
= render "commit_box"
= render "projects/diffs/diffs", diffs: @diffs, project: @project
= render "projects/notes/notes_with_form"
diff --git a/app/views/projects/commits/show.html.haml b/app/views/projects/commits/show.html.haml
index fb1012deb74..c8531b090a6 100644
--- a/app/views/projects/commits/show.html.haml
+++ b/app/views/projects/commits/show.html.haml
@@ -1,3 +1,4 @@
+- page_title "Commits", @ref
= content_for :meta_tags do
- if current_user
= auto_discovery_link_tag(:atom, namespace_project_commits_url(@project.namespace, @project, @ref, format: :atom, private_token: current_user.private_token), title: "#{@project.name}:#{@ref} commits")
diff --git a/app/views/projects/compare/index.html.haml b/app/views/projects/compare/index.html.haml
index 4745bfbeaaf..d1e579a2ede 100644
--- a/app/views/projects/compare/index.html.haml
+++ b/app/views/projects/compare/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Compare"
= render "projects/commits/head"
%h3.page-title
diff --git a/app/views/projects/compare/show.html.haml b/app/views/projects/compare/show.html.haml
index 214b5bd337b..3670dd5c13b 100644
--- a/app/views/projects/compare/show.html.haml
+++ b/app/views/projects/compare/show.html.haml
@@ -1,3 +1,4 @@
+- page_title "#{params[:from]}...#{params[:to]}"
= render "projects/commits/head"
%h3.page-title
diff --git a/app/views/projects/deploy_keys/index.html.haml b/app/views/projects/deploy_keys/index.html.haml
index 472a13a8524..2e9c5dc08c8 100644
--- a/app/views/projects/deploy_keys/index.html.haml
+++ b/app/views/projects/deploy_keys/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Deploy Keys"
%h3.page-title
Deploy keys allow read-only access to the repository
diff --git a/app/views/projects/deploy_keys/new.html.haml b/app/views/projects/deploy_keys/new.html.haml
index 186d6b58972..01c810aee18 100644
--- a/app/views/projects/deploy_keys/new.html.haml
+++ b/app/views/projects/deploy_keys/new.html.haml
@@ -1,3 +1,4 @@
+- page_title "New Deploy Key"
%h3.page-title New Deploy key
%hr
diff --git a/app/views/projects/deploy_keys/show.html.haml b/app/views/projects/deploy_keys/show.html.haml
index 405b5bcd0d3..7d44652af72 100644
--- a/app/views/projects/deploy_keys/show.html.haml
+++ b/app/views/projects/deploy_keys/show.html.haml
@@ -1,3 +1,4 @@
+- page_title @key.title, "Deploy Keys"
%h3.page-title
Deploy key:
= @key.title
diff --git a/app/views/projects/forks/error.html.haml b/app/views/projects/forks/error.html.haml
index 8eb4f795971..3d0ab5b85d6 100644
--- a/app/views/projects/forks/error.html.haml
+++ b/app/views/projects/forks/error.html.haml
@@ -1,3 +1,4 @@
+- page_title "Fork project"
- if @forked_project && !@forked_project.saved?
.alert.alert-danger.alert-block
%h4
diff --git a/app/views/projects/forks/new.html.haml b/app/views/projects/forks/new.html.haml
index 5a6c46f3208..b7a2ed68e25 100644
--- a/app/views/projects/forks/new.html.haml
+++ b/app/views/projects/forks/new.html.haml
@@ -1,3 +1,4 @@
+- page_title "Fork project"
%h3.page-title Fork project
%p.lead
Click to fork the project to a user or group
diff --git a/app/views/projects/graphs/commits.html.haml b/app/views/projects/graphs/commits.html.haml
index 78b4c1923dd..254a76e108b 100644
--- a/app/views/projects/graphs/commits.html.haml
+++ b/app/views/projects/graphs/commits.html.haml
@@ -1,3 +1,4 @@
+- page_title "Commit statistics"
= render 'head'
%p.lead
diff --git a/app/views/projects/graphs/show.html.haml b/app/views/projects/graphs/show.html.haml
index e3d5094ddc5..3a8dc89f84c 100644
--- a/app/views/projects/graphs/show.html.haml
+++ b/app/views/projects/graphs/show.html.haml
@@ -1,3 +1,4 @@
+- page_title "Contributor statistics"
= render 'head'
.loading-graph
.center
diff --git a/app/views/projects/hooks/index.html.haml b/app/views/projects/hooks/index.html.haml
index bbaddba31b9..808c03148f4 100644
--- a/app/views/projects/hooks/index.html.haml
+++ b/app/views/projects/hooks/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Web Hooks"
%h3.page-title
Web hooks
diff --git a/app/views/projects/imports/new.html.haml b/app/views/projects/imports/new.html.haml
index 934b6b8c017..f8f2e192e29 100644
--- a/app/views/projects/imports/new.html.haml
+++ b/app/views/projects/imports/new.html.haml
@@ -1,3 +1,4 @@
+- page_title "Import repository"
%h3.page-title
- if @project.import_failed?
Import failed. Retry?
diff --git a/app/views/projects/imports/show.html.haml b/app/views/projects/imports/show.html.haml
index 2d1fdafed24..39fe0fc1c4f 100644
--- a/app/views/projects/imports/show.html.haml
+++ b/app/views/projects/imports/show.html.haml
@@ -1,3 +1,4 @@
+- page_title "Import in progress"
.save-project-loader
.center
%h2
diff --git a/app/views/projects/issues/edit.html.haml b/app/views/projects/issues/edit.html.haml
index b1bc3ba0eba..53b6f0879c9 100644
--- a/app/views/projects/issues/edit.html.haml
+++ b/app/views/projects/issues/edit.html.haml
@@ -1 +1,2 @@
+- page_title "Edit", "#{@issue.title} (##{@issue.iid})", "Issues"
= render "form"
diff --git a/app/views/projects/issues/index.html.haml b/app/views/projects/issues/index.html.haml
index c2522816f3b..709ea1f7897 100644
--- a/app/views/projects/issues/index.html.haml
+++ b/app/views/projects/issues/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Issues"
= content_for :meta_tags do
- if current_user
= auto_discovery_link_tag(:atom, namespace_project_issues_url(@project.namespace, @project, :atom, private_token: current_user.private_token), title: "#{@project.name} issues")
diff --git a/app/views/projects/issues/new.html.haml b/app/views/projects/issues/new.html.haml
index b1bc3ba0eba..da6edd5c2d2 100644
--- a/app/views/projects/issues/new.html.haml
+++ b/app/views/projects/issues/new.html.haml
@@ -1 +1,2 @@
+- page_title "New Issue"
= render "form"
diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml
index bd28d8a1db2..81478dfe568 100644
--- a/app/views/projects/issues/show.html.haml
+++ b/app/views/projects/issues/show.html.haml
@@ -1,3 +1,4 @@
+- page_title "#{@issue.title} (##{@issue.iid})", "Issues"
.issue
.issue-details
%h4.page-title
diff --git a/app/views/projects/labels/edit.html.haml b/app/views/projects/labels/edit.html.haml
index e003d1dfe7f..645402667fd 100644
--- a/app/views/projects/labels/edit.html.haml
+++ b/app/views/projects/labels/edit.html.haml
@@ -1,3 +1,4 @@
+- page_title "Edit", @label.name, "Labels"
%h3
Edit label
%span.light #{@label.name}
diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml
index 0700e72d39c..7d19415a7f4 100644
--- a/app/views/projects/labels/index.html.haml
+++ b/app/views/projects/labels/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Labels"
- if can? current_user, :admin_label, @project
= link_to new_namespace_project_label_path(@project.namespace, @project), class: "pull-right btn btn-new" do
New label
diff --git a/app/views/projects/labels/new.html.haml b/app/views/projects/labels/new.html.haml
index 0683ed5d4fb..b3ef17025c3 100644
--- a/app/views/projects/labels/new.html.haml
+++ b/app/views/projects/labels/new.html.haml
@@ -1,3 +1,4 @@
+- page_title "New Label"
%h3 New label
.back-link
= link_to namespace_project_labels_path(@project.namespace, @project) do
diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml
index 45dd410dd15..c2f5cdacae7 100644
--- a/app/views/projects/merge_requests/_show.html.haml
+++ b/app/views/projects/merge_requests/_show.html.haml
@@ -1,3 +1,4 @@
+- page_title "#{@merge_request.title} (##{@merge_request.iid})", "Merge Requests"
.merge-request{'data-url' => merge_request_path(@merge_request)}
.merge-request-details
= render "projects/merge_requests/show/mr_title"
diff --git a/app/views/projects/merge_requests/edit.html.haml b/app/views/projects/merge_requests/edit.html.haml
index 839c63986ab..7e5cb07f249 100644
--- a/app/views/projects/merge_requests/edit.html.haml
+++ b/app/views/projects/merge_requests/edit.html.haml
@@ -1,3 +1,4 @@
+- page_title "Edit", "#{@merge_request.title} (##{@merge_request.iid})", "Merge Requests"
%h3.page-title
= "Edit merge request ##{@merge_request.iid}"
%hr
diff --git a/app/views/projects/merge_requests/index.html.haml b/app/views/projects/merge_requests/index.html.haml
index d7992bdd19e..ab845a7e719 100644
--- a/app/views/projects/merge_requests/index.html.haml
+++ b/app/views/projects/merge_requests/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Merge Requests"
.append-bottom-10
.pull-right
= render 'shared/issuable_search_form', path: namespace_project_merge_requests_path(@project.namespace, @project)
diff --git a/app/views/projects/merge_requests/invalid.html.haml b/app/views/projects/merge_requests/invalid.html.haml
index b9c466657de..15bd4e2fafd 100644
--- a/app/views/projects/merge_requests/invalid.html.haml
+++ b/app/views/projects/merge_requests/invalid.html.haml
@@ -1,3 +1,4 @@
+- page_title "#{@merge_request.title} (##{@merge_request.iid})", "Merge Requests"
.merge-request
= render "projects/merge_requests/show/mr_title"
= render "projects/merge_requests/show/mr_box"
diff --git a/app/views/projects/merge_requests/new.html.haml b/app/views/projects/merge_requests/new.html.haml
index 4756903d0e0..b038a640f67 100644
--- a/app/views/projects/merge_requests/new.html.haml
+++ b/app/views/projects/merge_requests/new.html.haml
@@ -1,3 +1,4 @@
+- page_title "New Merge Request"
- if @merge_request.can_be_created
= render 'new_submit'
- else
diff --git a/app/views/projects/milestones/edit.html.haml b/app/views/projects/milestones/edit.html.haml
index b1bc3ba0eba..c09815a212a 100644
--- a/app/views/projects/milestones/edit.html.haml
+++ b/app/views/projects/milestones/edit.html.haml
@@ -1 +1,2 @@
+- page_title "Edit", @milestone.title, "Milestones"
= render "form"
diff --git a/app/views/projects/milestones/index.html.haml b/app/views/projects/milestones/index.html.haml
index d3eab8d6d75..995eecd7830 100644
--- a/app/views/projects/milestones/index.html.haml
+++ b/app/views/projects/milestones/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Milestones"
.pull-right
- if can? current_user, :admin_milestone, @project
= link_to new_namespace_project_milestone_path(@project.namespace, @project), class: "pull-right btn btn-new", title: "New Milestone" do
diff --git a/app/views/projects/milestones/new.html.haml b/app/views/projects/milestones/new.html.haml
index b1bc3ba0eba..47149dfea41 100644
--- a/app/views/projects/milestones/new.html.haml
+++ b/app/views/projects/milestones/new.html.haml
@@ -1 +1,2 @@
+- page_title "New Milestone"
= render "form"
diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml
index 25cc0030965..bba2b8764ac 100644
--- a/app/views/projects/milestones/show.html.haml
+++ b/app/views/projects/milestones/show.html.haml
@@ -1,3 +1,4 @@
+- page_title @milestone.title, "Milestones"
%h4.page-title
.issue-box{ class: issue_box_class(@milestone) }
- if @milestone.closed?
diff --git a/app/views/projects/network/show.html.haml b/app/views/projects/network/show.html.haml
index c36bad1e94b..c67a7d256a8 100644
--- a/app/views/projects/network/show.html.haml
+++ b/app/views/projects/network/show.html.haml
@@ -1,3 +1,4 @@
+- page_title "Network", @ref
= render "head"
.project-network
.controls
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index 47c69f89a97..e56d8615132 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -1,3 +1,5 @@
+- page_title 'New Project'
+- header_title 'New Project'
.project-edit-container
.project-edit-errors
= render 'projects/errors'
diff --git a/app/views/projects/project_members/import.html.haml b/app/views/projects/project_members/import.html.haml
index 293754cd0c0..6914543f6da 100644
--- a/app/views/projects/project_members/import.html.haml
+++ b/app/views/projects/project_members/import.html.haml
@@ -1,3 +1,4 @@
+- page_title "Import members"
%h3.page-title
Import members from another project
%p.light
diff --git a/app/views/projects/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml
index 36a6f6a1554..6edb92acd4d 100644
--- a/app/views/projects/project_members/index.html.haml
+++ b/app/views/projects/project_members/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Members"
%h3.page-title
Users with access to this project
diff --git a/app/views/projects/protected_branches/index.html.haml b/app/views/projects/protected_branches/index.html.haml
index a3464c0e5e1..52b3a50c1e6 100644
--- a/app/views/projects/protected_branches/index.html.haml
+++ b/app/views/projects/protected_branches/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Protected branches"
%h3.page-title Protected branches
%p.light Keep stable branches secure and force developers to use Merge Requests
%hr
diff --git a/app/views/projects/services/edit.html.haml b/app/views/projects/services/edit.html.haml
index bcc5832792f..50ed78286d2 100644
--- a/app/views/projects/services/edit.html.haml
+++ b/app/views/projects/services/edit.html.haml
@@ -1 +1,2 @@
+- page_title @service.title, "Services"
= render 'form'
diff --git a/app/views/projects/services/index.html.haml b/app/views/projects/services/index.html.haml
index 0d3ccb6bb83..1065def693b 100644
--- a/app/views/projects/services/index.html.haml
+++ b/app/views/projects/services/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Services"
%h3.page-title Project services
%p.light Project services allow you to integrate GitLab with other applications
diff --git a/app/views/projects/snippets/edit.html.haml b/app/views/projects/snippets/edit.html.haml
index 7baddebde45..945f0084dff 100644
--- a/app/views/projects/snippets/edit.html.haml
+++ b/app/views/projects/snippets/edit.html.haml
@@ -1,3 +1,4 @@
+- page_title "Edit", @snippet.title, "Snippets"
%h3.page-title
Edit snippet
%hr
diff --git a/app/views/projects/snippets/index.html.haml b/app/views/projects/snippets/index.html.haml
index e2d8ec673a1..da9401bd8c1 100644
--- a/app/views/projects/snippets/index.html.haml
+++ b/app/views/projects/snippets/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Snippets"
%h3.page-title
Snippets
- if can? current_user, :write_project_snippet, @project
diff --git a/app/views/projects/snippets/new.html.haml b/app/views/projects/snippets/new.html.haml
index 5efe662665e..e38d95c45e7 100644
--- a/app/views/projects/snippets/new.html.haml
+++ b/app/views/projects/snippets/new.html.haml
@@ -1,3 +1,4 @@
+- page_title "New Snippets"
%h3.page-title
New snippet
%hr
diff --git a/app/views/projects/snippets/show.html.haml b/app/views/projects/snippets/show.html.haml
index d19689a1056..5725d804df3 100644
--- a/app/views/projects/snippets/show.html.haml
+++ b/app/views/projects/snippets/show.html.haml
@@ -1,3 +1,4 @@
+- page_title @snippet.title, "Snippets"
%h3.page-title
= @snippet.title
diff --git a/app/views/projects/tags/index.html.haml b/app/views/projects/tags/index.html.haml
index f1bc2bc9a2b..d4652a47cba 100644
--- a/app/views/projects/tags/index.html.haml
+++ b/app/views/projects/tags/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Tags"
= render "projects/commits/head"
%h3.page-title
diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml
index 655044438d5..172fafdeeff 100644
--- a/app/views/projects/tags/new.html.haml
+++ b/app/views/projects/tags/new.html.haml
@@ -1,3 +1,4 @@
+- page_title "New Tag"
- if @error
.alert.alert-danger
%button{ type: "button", class: "close", "data-dismiss" => "alert"} &times;
diff --git a/app/views/projects/tree/show.html.haml b/app/views/projects/tree/show.html.haml
index a8a580944e1..72916cad182 100644
--- a/app/views/projects/tree/show.html.haml
+++ b/app/views/projects/tree/show.html.haml
@@ -1,3 +1,4 @@
+- page_title @path.presence || "Files", @ref
= content_for :meta_tags do
- if current_user
= auto_discovery_link_tag(:atom, namespace_project_commits_url(@project.namespace, @project, @ref, format: :atom, private_token: current_user.private_token), title: "#{@project.name}:#{@ref} commits")
diff --git a/app/views/projects/wikis/edit.html.haml b/app/views/projects/wikis/edit.html.haml
index 566850cb78d..3f1dce1050c 100644
--- a/app/views/projects/wikis/edit.html.haml
+++ b/app/views/projects/wikis/edit.html.haml
@@ -1,3 +1,4 @@
+- page_title "Edit", @page.title, "Wiki"
= render 'nav'
.pull-right
= render 'main_links'
diff --git a/app/views/projects/wikis/empty.html.haml b/app/views/projects/wikis/empty.html.haml
index 48058124f97..ead99412406 100644
--- a/app/views/projects/wikis/empty.html.haml
+++ b/app/views/projects/wikis/empty.html.haml
@@ -1,3 +1,4 @@
+- page_title "Wiki"
%h3.page-title Empty page
%hr
.error_message
diff --git a/app/views/projects/wikis/git_access.html.haml b/app/views/projects/wikis/git_access.html.haml
index 365edb524f4..825f2a161c4 100644
--- a/app/views/projects/wikis/git_access.html.haml
+++ b/app/views/projects/wikis/git_access.html.haml
@@ -1,3 +1,4 @@
+- page_title "Git Access", "Wiki"
= render 'nav'
.row
.col-sm-6
diff --git a/app/views/projects/wikis/history.html.haml b/app/views/projects/wikis/history.html.haml
index 91291f753f7..673ec2d20e5 100644
--- a/app/views/projects/wikis/history.html.haml
+++ b/app/views/projects/wikis/history.html.haml
@@ -1,3 +1,4 @@
+- page_title "History", @page.title, "Wiki"
= render 'nav'
%h3.page-title
%span.light History for
diff --git a/app/views/projects/wikis/pages.html.haml b/app/views/projects/wikis/pages.html.haml
index ee233d9086f..890ff1aed73 100644
--- a/app/views/projects/wikis/pages.html.haml
+++ b/app/views/projects/wikis/pages.html.haml
@@ -1,3 +1,4 @@
+- page_title "All Pages", "Wiki"
= render 'nav'
%h3.page-title
All Pages
diff --git a/app/views/projects/wikis/show.html.haml b/app/views/projects/wikis/show.html.haml
index a6263e93f67..83cd4c66672 100644
--- a/app/views/projects/wikis/show.html.haml
+++ b/app/views/projects/wikis/show.html.haml
@@ -1,3 +1,4 @@
+- page_title @page.title, "Wiki"
= render 'nav'
%h3.page-title
= @page.title
diff --git a/app/views/search/show.html.haml b/app/views/search/show.html.haml
index e9f2711be2a..60f9e9ac9de 100644
--- a/app/views/search/show.html.haml
+++ b/app/views/search/show.html.haml
@@ -1,3 +1,4 @@
+- page_title @search_term
= render 'search/form'
%hr
- if @search_term
diff --git a/app/views/snippets/current_user_index.html.haml b/app/views/snippets/current_user_index.html.haml
index 6bb2237a759..0718f743828 100644
--- a/app/views/snippets/current_user_index.html.haml
+++ b/app/views/snippets/current_user_index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Your Snippets"
%h3.page-title
Your Snippets
.pull-right
diff --git a/app/views/snippets/edit.html.haml b/app/views/snippets/edit.html.haml
index 30aa174edfb..1a380035661 100644
--- a/app/views/snippets/edit.html.haml
+++ b/app/views/snippets/edit.html.haml
@@ -1,3 +1,4 @@
+- page_title "Edit", @snippet.title, "Snippets"
%h3.page-title
Edit snippet
%hr
diff --git a/app/views/snippets/index.html.haml b/app/views/snippets/index.html.haml
index 108dd0cca3e..e9bb6a908d3 100644
--- a/app/views/snippets/index.html.haml
+++ b/app/views/snippets/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Public Snippets"
%h3.page-title
Public snippets
diff --git a/app/views/snippets/new.html.haml b/app/views/snippets/new.html.haml
index 77cfd9af335..a74d5e792ad 100644
--- a/app/views/snippets/new.html.haml
+++ b/app/views/snippets/new.html.haml
@@ -1,3 +1,4 @@
+- page_title "New Snippet"
%h3.page-title
New snippet
%hr
diff --git a/app/views/snippets/show.html.haml b/app/views/snippets/show.html.haml
index 5204fb9a907..70a95abde6f 100644
--- a/app/views/snippets/show.html.haml
+++ b/app/views/snippets/show.html.haml
@@ -1,3 +1,4 @@
+- page_title @snippet.title, "Snippets"
%h3.page-title
= @snippet.title
diff --git a/app/views/snippets/user_index.html.haml b/app/views/snippets/user_index.html.haml
index df524cd18b0..23700eb39da 100644
--- a/app/views/snippets/user_index.html.haml
+++ b/app/views/snippets/user_index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Snippets", @user.name
%h3.page-title
= image_tag avatar_icon(@user.email), class: "avatar s24"
= @user.name
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index 0576c6a11b9..6ed45fedfa2 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -1,3 +1,6 @@
+- page_title @user.name
+- header_title @user.name, user_path(@user)
+
= content_for :meta_tags do
= auto_discovery_link_tag(:atom, user_url(@user, format: :atom), title: "#{@user.name} activity")
diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb
index 9dce495106f..8f8c4169740 100644
--- a/config/initializers/devise.rb
+++ b/config/initializers/devise.rb
@@ -2,13 +2,8 @@
# four configuration values can also be set straight in your models.
Devise.setup do |config|
# ==> Mailer Configuration
- # Configure the e-mail address which will be shown in Devise::Mailer,
- # note that it will be overwritten if you use your own mailer class with default "from" parameter.
- config.mailer_sender = "GitLab <#{Gitlab.config.gitlab.email_from}>"
-
-
# Configure the class responsible to send e-mails.
- # config.mailer = "Devise::Mailer"
+ config.mailer = "DeviseMailer"
# ==> ORM configuration
# Load and configure the ORM. Supports :active_record (default) and
diff --git a/db/migrate/20150425164648_add_missing_unique_indices.acts_as_taggable_on_engine.rb b/db/migrate/20150425164648_add_missing_unique_indices.acts_as_taggable_on_engine.rb
new file mode 100644
index 00000000000..4ca676f6c72
--- /dev/null
+++ b/db/migrate/20150425164648_add_missing_unique_indices.acts_as_taggable_on_engine.rb
@@ -0,0 +1,20 @@
+# This migration comes from acts_as_taggable_on_engine (originally 2)
+class AddMissingUniqueIndices < ActiveRecord::Migration
+ def self.up
+ add_index :tags, :name, unique: true
+
+ remove_index :taggings, :tag_id
+ remove_index :taggings, [:taggable_id, :taggable_type, :context]
+ add_index :taggings,
+ [:tag_id, :taggable_id, :taggable_type, :context, :tagger_id, :tagger_type],
+ unique: true, name: 'taggings_idx'
+ end
+
+ def self.down
+ remove_index :tags, :name
+
+ remove_index :taggings, name: 'taggings_idx'
+ add_index :taggings, :tag_id
+ add_index :taggings, [:taggable_id, :taggable_type, :context]
+ end
+end
diff --git a/db/migrate/20150425164649_add_taggings_counter_cache_to_tags.acts_as_taggable_on_engine.rb b/db/migrate/20150425164649_add_taggings_counter_cache_to_tags.acts_as_taggable_on_engine.rb
new file mode 100644
index 00000000000..8edb5080781
--- /dev/null
+++ b/db/migrate/20150425164649_add_taggings_counter_cache_to_tags.acts_as_taggable_on_engine.rb
@@ -0,0 +1,15 @@
+# This migration comes from acts_as_taggable_on_engine (originally 3)
+class AddTaggingsCounterCacheToTags < ActiveRecord::Migration
+ def self.up
+ add_column :tags, :taggings_count, :integer, default: 0
+
+ ActsAsTaggableOn::Tag.reset_column_information
+ ActsAsTaggableOn::Tag.find_each do |tag|
+ ActsAsTaggableOn::Tag.reset_counters(tag.id, :taggings)
+ end
+ end
+
+ def self.down
+ remove_column :tags, :taggings_count
+ end
+end
diff --git a/db/migrate/20150425164650_add_missing_taggable_index.acts_as_taggable_on_engine.rb b/db/migrate/20150425164650_add_missing_taggable_index.acts_as_taggable_on_engine.rb
new file mode 100644
index 00000000000..71f2d7f4330
--- /dev/null
+++ b/db/migrate/20150425164650_add_missing_taggable_index.acts_as_taggable_on_engine.rb
@@ -0,0 +1,10 @@
+# This migration comes from acts_as_taggable_on_engine (originally 4)
+class AddMissingTaggableIndex < ActiveRecord::Migration
+ def self.up
+ add_index :taggings, [:taggable_id, :taggable_type, :context]
+ end
+
+ def self.down
+ remove_index :taggings, [:taggable_id, :taggable_type, :context]
+ end
+end
diff --git a/db/migrate/20150425164651_change_collation_for_tag_names.acts_as_taggable_on_engine.rb b/db/migrate/20150425164651_change_collation_for_tag_names.acts_as_taggable_on_engine.rb
new file mode 100644
index 00000000000..bfb06bc7cda
--- /dev/null
+++ b/db/migrate/20150425164651_change_collation_for_tag_names.acts_as_taggable_on_engine.rb
@@ -0,0 +1,10 @@
+# This migration comes from acts_as_taggable_on_engine (originally 5)
+# This migration is added to circumvent issue #623 and have special characters
+# work properly
+class ChangeCollationForTagNames < ActiveRecord::Migration
+ def up
+ if ActsAsTaggableOn::Utils.using_mysql?
+ execute("ALTER TABLE tags MODIFY name varchar(255) CHARACTER SET utf8 COLLATE utf8_bin;")
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index a2ed9efc933..02882186fc8 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -433,13 +433,16 @@ ActiveRecord::Schema.define(version: 20150429002313) do
t.datetime "created_at"
end
- add_index "taggings", ["tag_id"], name: "index_taggings_on_tag_id", using: :btree
+ add_index "taggings", ["tag_id", "taggable_id", "taggable_type", "context", "tagger_id", "tagger_type"], name: "taggings_idx", unique: true, using: :btree
add_index "taggings", ["taggable_id", "taggable_type", "context"], name: "index_taggings_on_taggable_id_and_taggable_type_and_context", using: :btree
create_table "tags", force: true do |t|
- t.string "name"
+ t.string "name"
+ t.integer "taggings_count", default: 0
end
+ add_index "tags", ["name"], name: "index_tags_on_name", unique: true, using: :btree
+
create_table "users", force: true do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
diff --git a/doc/api/README.md b/doc/api/README.md
index dec530d0b81..f6757b0a6aa 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -6,6 +6,7 @@
- [Session](session.md)
- [Projects](projects.md)
- [Project Snippets](project_snippets.md)
+- [Services](services.md)
- [Repositories](repositories.md)
- [Repository Files](repository_files.md)
- [Commits](commits.md)
diff --git a/doc/update/upgrader.md b/doc/update/upgrader.md
index e32984c4e68..2cca46c86a3 100644
--- a/doc/update/upgrader.md
+++ b/doc/update/upgrader.md
@@ -27,10 +27,10 @@ If you have local changes to your GitLab repository the script will stash them a
Note: GitLab 7.9 adds `nodejs` as a dependency. GitLab 7.6 adds `libkrb5-dev` as a dependency (installed by default on Ubuntu and OSX). GitLab 7.2 adds `pkg-config` and `cmake` as dependency. Please check the dependencies in the [installation guide.](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md#1-packages-dependencies)
cd /home/git/gitlab
- sudo -u git -H ruby -Ilib -e 'require "gitlab/upgrader"' -e 'class Gitlab::Upgrader' -e 'def latest_version_raw' -e '"v7.10.0"' -e 'end' -e 'end' -e 'Gitlab::Upgrader.new.execute'
+ sudo -u git -H ruby -Ilib -e 'require "gitlab/upgrader"' -e 'class Gitlab::Upgrader' -e 'def latest_version_raw' -e '"v7.10.1"' -e 'end' -e 'end' -e 'Gitlab::Upgrader.new.execute'
# to perform a non-interactive install (no user input required) you can add -y
- # sudo -u git -H ruby -Ilib -e 'require "gitlab/upgrader"' -e 'class Gitlab::Upgrader' -e 'def latest_version_raw' -e '"v7.10.0"' -e 'end' -e 'end' -e 'Gitlab::Upgrader.new.execute' -- -y
+ # sudo -u git -H ruby -Ilib -e 'require "gitlab/upgrader"' -e 'class Gitlab::Upgrader' -e 'def latest_version_raw' -e '"v7.10.1"' -e 'end' -e 'end' -e 'Gitlab::Upgrader.new.execute' -- -y
## 3. Start application
@@ -65,7 +65,7 @@ Here is a one line command with step 1 to 5 for the next time you upgrade:
cd /home/git/gitlab; \
sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production; \
sudo service gitlab stop; \
- sudo -u git -H ruby -Ilib -e 'require "gitlab/upgrader"' -e 'class Gitlab::Upgrader' -e 'def latest_version_raw' -e '"v7.10.0"' -e 'end' -e 'end' -e 'Gitlab::Upgrader.new.execute' -- -y; \
+ sudo -u git -H ruby -Ilib -e 'require "gitlab/upgrader"' -e 'class Gitlab::Upgrader' -e 'def latest_version_raw' -e '"v7.10.1"' -e 'end' -e 'end' -e 'Gitlab::Upgrader.new.execute' -- -y; \
cd /home/git/gitlab-shell; \
sudo -u git -H git fetch; \
sudo -u git -H git checkout v`cat /home/git/gitlab/GITLAB_SHELL_VERSION`; \
diff --git a/features/project/project.feature b/features/project/project.feature
index 3e1fd54bee8..ae28312a69a 100644
--- a/features/project/project.feature
+++ b/features/project/project.feature
@@ -55,3 +55,10 @@ Feature: Project
Then I should see project "Forum" README
And I visit project "Shop" page
Then I should see project "Shop" README
+
+ Scenario: I tag a project
+ When I visit edit project "Shop" page
+ Then I should see project settings
+ And I add project tags
+ And I save project
+ Then I should see project tags
diff --git a/features/steps/project/project.rb b/features/steps/project/project.rb
index d39c8e7d2db..f14396bcfff 100644
--- a/features/steps/project/project.rb
+++ b/features/steps/project/project.rb
@@ -94,4 +94,12 @@ class Spinach::Features::Project < Spinach::FeatureSteps
page.should have_link 'README.md'
page.should have_content 'testme'
end
+
+ step 'I add project tags' do
+ fill_in 'Tags', with: 'tag1, tag2'
+ end
+
+ step 'I should see project tags' do
+ expect(find_field('Tags').value).to eq 'tag1, tag2'
+ end
end
diff --git a/features/steps/shared/markdown.rb b/features/steps/shared/markdown.rb
index e71700880cd..a7231c47d14 100644
--- a/features/steps/shared/markdown.rb
+++ b/features/steps/shared/markdown.rb
@@ -2,8 +2,12 @@ module SharedMarkdown
include Spinach::DSL
def header_should_have_correct_id_and_link(level, text, id, parent = ".wiki")
- find(:css, "#{parent} h#{level}##{id}").text.should == text
- find(:css, "#{parent} h#{level}##{id} > :last-child")[:href].should =~ /##{id}$/
+ node = find("#{parent} h#{level} a##{id}")
+ node[:href].should == "##{id}"
+
+ # Work around a weird Capybara behavior where calling `parent` on a node
+ # returns the whole document, not the node's actual parent element
+ find(:xpath, "#{node.path}/..").text.should == text
end
def create_taskable(type, title)
diff --git a/lib/gitlab/google_code_import/client.rb b/lib/gitlab/google_code_import/client.rb
index 02f31e45f88..890bd9a3554 100644
--- a/lib/gitlab/google_code_import/client.rb
+++ b/lib/gitlab/google_code_import/client.rb
@@ -21,6 +21,10 @@ module Gitlab
@repos ||= raw_data["projects"].map { |raw_repo| GoogleCodeImport::Repository.new(raw_repo) }.select(&:git?)
end
+ def incompatible_repos
+ @incompatible_repos ||= raw_data["projects"].map { |raw_repo| GoogleCodeImport::Repository.new(raw_repo) }.reject(&:git?)
+ end
+
def repo(id)
repos.find { |repo| repo.id == id }
end
diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb
index 44779d7fdd8..8348e28d0f5 100644
--- a/lib/gitlab/markdown.rb
+++ b/lib/gitlab/markdown.rb
@@ -3,33 +3,10 @@ require 'html/pipeline'
module Gitlab
# Custom parser for GitLab-flavored Markdown
#
- # It replaces references in the text with links to the appropriate items in
- # GitLab.
- #
- # Supported reference formats are:
- # * @foo for team members
- # * #123 for issues
- # * JIRA-123 for Jira issues
- # * !123 for merge requests
- # * $123 for snippets
- # * 1c002d for specific commit
- # * 1c002d...35cfb2 for commit ranges (comparisons)
- #
- # It also parses Emoji codes to insert images. See
- # http://www.emoji-cheat-sheet.com/ for a list of the supported icons.
- #
- # Examples
- #
- # >> gfm("Hey @david, can you fix this?")
- # => "Hey <a href="/u/david">@david</a>, can you fix this?"
- #
- # >> gfm("Commit 35d5f7c closes #1234")
- # => "Commit <a href="/gitlab/commits/35d5f7c">35d5f7c</a> closes <a href="/gitlab/issues/1234">#1234</a>"
- #
- # >> gfm(":trollface:")
- # => "<img alt=\":trollface:\" class=\"emoji\" src=\"/images/trollface.png" title=\":trollface:\" />
+ # See the files in `lib/gitlab/markdown/` for specific processing information.
module Markdown
# Provide autoload paths for filters to prevent a circular dependency error
+ autoload :AutolinkFilter, 'gitlab/markdown/autolink_filter'
autoload :CommitRangeReferenceFilter, 'gitlab/markdown/commit_range_reference_filter'
autoload :CommitReferenceFilter, 'gitlab/markdown/commit_reference_filter'
autoload :EmojiFilter, 'gitlab/markdown/emoji_filter'
@@ -37,7 +14,9 @@ module Gitlab
autoload :IssueReferenceFilter, 'gitlab/markdown/issue_reference_filter'
autoload :LabelReferenceFilter, 'gitlab/markdown/label_reference_filter'
autoload :MergeRequestReferenceFilter, 'gitlab/markdown/merge_request_reference_filter'
+ autoload :SanitizationFilter, 'gitlab/markdown/sanitization_filter'
autoload :SnippetReferenceFilter, 'gitlab/markdown/snippet_reference_filter'
+ autoload :TableOfContentsFilter, 'gitlab/markdown/table_of_contents_filter'
autoload :UserReferenceFilter, 'gitlab/markdown/user_reference_filter'
# Public: Parse the provided text with GitLab-Flavored Markdown
@@ -74,13 +53,13 @@ module Gitlab
pipeline = HTML::Pipeline.new(filters)
context = {
- # SanitizationFilter
- whitelist: sanitization_whitelist,
-
# EmojiFilter
asset_root: Gitlab.config.gitlab.url,
asset_host: Gitlab::Application.config.asset_host,
+ # TableOfContentsFilter
+ no_header_anchors: options[:no_header_anchors],
+
# ReferenceFilter
current_user: current_user,
only_path: options[:reference_only_path],
@@ -111,12 +90,14 @@ module Gitlab
# SanitizationFilter should come first so that all generated reference HTML
# goes through untouched.
#
- # See https://gitlab.com/gitlab-org/html-pipeline-gitlab for more filters
+ # See https://github.com/jch/html-pipeline#filters for more filters.
def filters
[
- HTML::Pipeline::SanitizationFilter,
+ Gitlab::Markdown::SanitizationFilter,
Gitlab::Markdown::EmojiFilter,
+ Gitlab::Markdown::TableOfContentsFilter,
+ Gitlab::Markdown::AutolinkFilter,
Gitlab::Markdown::UserReferenceFilter,
Gitlab::Markdown::IssueReferenceFilter,
@@ -125,36 +106,10 @@ module Gitlab
Gitlab::Markdown::SnippetReferenceFilter,
Gitlab::Markdown::CommitRangeReferenceFilter,
Gitlab::Markdown::CommitReferenceFilter,
- Gitlab::Markdown::LabelReferenceFilter,
+ Gitlab::Markdown::LabelReferenceFilter
]
end
- # Customize the SanitizationFilter whitelist
- #
- # - Allow `class` and `id` attributes on all elements
- # - Allow `span` elements
- # - Remove `rel` attributes from `a` elements
- # - Remove `a` nodes with `javascript:` in the `href` attribute
- def sanitization_whitelist
- whitelist = HTML::Pipeline::SanitizationFilter::WHITELIST
- whitelist[:attributes][:all].push('class', 'id')
- whitelist[:elements].push('span')
-
- fix_anchors = lambda do |env|
- name, node = env[:node_name], env[:node]
- if name == 'a'
- node.remove_attribute('rel')
- if node['href'] && node['href'].match('javascript:')
- node.remove_attribute('href')
- end
- end
- end
-
- whitelist[:transformers].push(fix_anchors)
-
- whitelist
- end
-
# Turn list items that start with "[ ]" into HTML checkbox inputs.
def parse_tasks(text)
li_tag = '<li class="task-list-item">'
diff --git a/lib/gitlab/markdown/autolink_filter.rb b/lib/gitlab/markdown/autolink_filter.rb
new file mode 100644
index 00000000000..4e14a048cfb
--- /dev/null
+++ b/lib/gitlab/markdown/autolink_filter.rb
@@ -0,0 +1,100 @@
+require 'html/pipeline/filter'
+require 'uri'
+
+module Gitlab
+ module Markdown
+ # HTML Filter for auto-linking URLs in HTML.
+ #
+ # Based on HTML::Pipeline::AutolinkFilter
+ #
+ # Context options:
+ # :autolink - Boolean, skips all processing done by this filter when false
+ # :link_attr - Hash of attributes for the generated links
+ #
+ class AutolinkFilter < HTML::Pipeline::Filter
+ include ActionView::Helpers::TagHelper
+
+ # Pattern to match text that should be autolinked.
+ #
+ # A URI scheme begins with a letter and may contain letters, numbers,
+ # plus, period and hyphen. Schemes are case-insensitive but we're being
+ # picky here and allowing only lowercase for autolinks.
+ #
+ # See http://en.wikipedia.org/wiki/URI_scheme
+ #
+ # The negative lookbehind ensures that users can paste a URL followed by a
+ # period or comma for punctuation without those characters being included
+ # in the generated link.
+ #
+ # Rubular: http://rubular.com/r/cxjPyZc7Sb
+ LINK_PATTERN = %r{([a-z][a-z0-9\+\.-]+://\S+)(?<!,|\.)}
+
+ # Text matching LINK_PATTERN inside these elements will not be linked
+ IGNORE_PARENTS = %w(a code kbd pre script style).to_set
+
+ def call
+ return doc if context[:autolink] == false
+
+ rinku_parse
+ text_parse
+ end
+
+ private
+
+ # Run the text through Rinku as a first pass
+ #
+ # This will quickly autolink http(s) and ftp links.
+ #
+ # `@doc` will be re-parsed with the HTML String from Rinku.
+ def rinku_parse
+ # Convert the options from a Hash to a String that Rinku expects
+ options = tag_options(link_options)
+
+ # NOTE: We don't parse email links because it will erroneously match
+ # external Commit and CommitRange references.
+ #
+ # The final argument tells Rinku to link short URLs that don't include a
+ # period (e.g., http://localhost:3000/)
+ rinku = Rinku.auto_link(html, :urls, options, IGNORE_PARENTS.to_a, 1)
+
+ # Rinku returns a String, so parse it back to a Nokogiri::XML::Document
+ # for further processing.
+ @doc = parse_html(rinku)
+ end
+
+ # Autolinks any text matching LINK_PATTERN that Rinku didn't already
+ # replace
+ def text_parse
+ search_text_nodes(doc).each do |node|
+ content = node.to_html
+
+ next if has_ancestor?(node, IGNORE_PARENTS)
+ next unless content.match(LINK_PATTERN)
+
+ # If Rinku didn't link this, there's probably a good reason, so we'll
+ # skip it too
+ next if content.start_with?(*%w(http https ftp))
+
+ html = autolink_filter(content)
+
+ next if html == content
+
+ node.replace(html)
+ end
+
+ doc
+ end
+
+ def autolink_filter(text)
+ text.gsub(LINK_PATTERN) do |match|
+ options = link_options.merge(href: match)
+ content_tag(:a, match, options)
+ end
+ end
+
+ def link_options
+ @link_options ||= context[:link_attr] || {}
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/markdown/issue_reference_filter.rb b/lib/gitlab/markdown/issue_reference_filter.rb
index 4b360369d37..1e885615163 100644
--- a/lib/gitlab/markdown/issue_reference_filter.rb
+++ b/lib/gitlab/markdown/issue_reference_filter.rb
@@ -44,21 +44,20 @@ module Gitlab
# Returns a String with `#123` references replaced with links. All links
# have `gfm` and `gfm-issue` class names attached for styling.
def issue_link_filter(text)
- self.class.references_in(text) do |match, issue, project_ref|
+ self.class.references_in(text) do |match, id, project_ref|
project = self.project_from_ref(project_ref)
- if project && project.issue_exists?(issue)
- # FIXME (rspeicher): Law of Demeter
- push_result(:issue, project.issues.where(iid: issue).first)
+ if project && issue = project.get_issue(id)
+ push_result(:issue, issue)
- url = url_for_issue(issue, project, only_path: context[:only_path])
+ url = url_for_issue(id, project, only_path: context[:only_path])
- title = escape_once("Issue: #{title_for_issue(issue, project)}")
+ title = escape_once("Issue: #{issue.title}")
klass = reference_class(:issue)
%(<a href="#{url}"
title="#{title}"
- class="#{klass}">#{project_ref}##{issue}</a>)
+ class="#{klass}">#{project_ref}##{id}</a>)
else
match
end
@@ -68,10 +67,6 @@ module Gitlab
def url_for_issue(*args)
IssuesHelper.url_for_issue(*args)
end
-
- def title_for_issue(*args)
- IssuesHelper.title_for_issue(*args)
- end
end
end
end
diff --git a/lib/gitlab/markdown/merge_request_reference_filter.rb b/lib/gitlab/markdown/merge_request_reference_filter.rb
index 7c28fe112ef..740d72abb36 100644
--- a/lib/gitlab/markdown/merge_request_reference_filter.rb
+++ b/lib/gitlab/markdown/merge_request_reference_filter.rb
@@ -64,7 +64,6 @@ module Gitlab
end
end
- # TODO (rspeicher): Cleanup
def url_for_merge_request(mr, project)
h = Rails.application.routes.url_helpers
h.namespace_project_merge_request_url(project.namespace, project, mr,
diff --git a/lib/gitlab/markdown/sanitization_filter.rb b/lib/gitlab/markdown/sanitization_filter.rb
new file mode 100644
index 00000000000..9a154e0b2fe
--- /dev/null
+++ b/lib/gitlab/markdown/sanitization_filter.rb
@@ -0,0 +1,38 @@
+require 'html/pipeline/filter'
+require 'html/pipeline/sanitization_filter'
+
+module Gitlab
+ module Markdown
+ # Sanitize HTML
+ #
+ # Extends HTML::Pipeline::SanitizationFilter with a custom whitelist.
+ class SanitizationFilter < HTML::Pipeline::SanitizationFilter
+ def whitelist
+ whitelist = HTML::Pipeline::SanitizationFilter::WHITELIST
+
+ # Allow `class` and `id` on all elements
+ whitelist[:attributes][:all].push('class', 'id')
+
+ # Allow table alignment
+ whitelist[:attributes]['th'] = %w(style)
+ whitelist[:attributes]['td'] = %w(style)
+
+ # Allow span elements
+ whitelist[:elements].push('span')
+
+ # Remove `rel` attribute from `a` elements
+ whitelist[:transformers].push(remove_rel)
+
+ whitelist
+ end
+
+ def remove_rel
+ lambda do |env|
+ if env[:node_name] == 'a'
+ env[:node].remove_attribute('rel')
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/markdown/table_of_contents_filter.rb b/lib/gitlab/markdown/table_of_contents_filter.rb
new file mode 100644
index 00000000000..c97612dafb8
--- /dev/null
+++ b/lib/gitlab/markdown/table_of_contents_filter.rb
@@ -0,0 +1,62 @@
+require 'html/pipeline/filter'
+
+module Gitlab
+ module Markdown
+ # HTML filter that adds an anchor child element to all Headers in a
+ # document, so that they can be linked to.
+ #
+ # Generates the Table of Contents with links to each header. See Results.
+ #
+ # Based on HTML::Pipeline::TableOfContentsFilter.
+ #
+ # Context options:
+ # :no_header_anchors - Skips all processing done by this filter.
+ #
+ # Results:
+ # :toc - String containing Table of Contents data as a `ul` element with
+ # `li` child elements.
+ class TableOfContentsFilter < HTML::Pipeline::Filter
+ PUNCTUATION_REGEXP = /[^\p{Word}\- ]/u
+
+ def call
+ return doc if context[:no_header_anchors]
+
+ result[:toc] = ""
+
+ headers = Hash.new(0)
+
+ doc.css('h1, h2, h3, h4, h5, h6').each do |node|
+ text = node.text
+
+ id = text.downcase
+ id.gsub!(PUNCTUATION_REGEXP, '') # remove punctuation
+ id.gsub!(' ', '-') # replace spaces with dash
+ id.squeeze!(' -') # replace multiple spaces or dashes with one
+
+ uniq = (headers[id] > 0) ? "-#{headers[id]}" : ''
+ headers[id] += 1
+
+ if header_content = node.children.first
+ href = "#{id}#{uniq}"
+ push_toc(href, text)
+ header_content.add_previous_sibling(anchor_tag(href))
+ end
+ end
+
+ result[:toc] = %Q{<ul class="section-nav">\n#{result[:toc]}</ul>} unless result[:toc].empty?
+
+ doc
+ end
+
+ private
+
+ def anchor_tag(href)
+ %Q{<a id="#{href}" class="anchor" href="##{href}" aria-hidden="true"></a>}
+ end
+
+ def push_toc(href, text)
+ result[:toc] << %Q{<li><a href="##{href}">#{text}</a></li>\n}
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/theme.rb b/lib/gitlab/theme.rb
index f0e61aa2e81..e5a1f1b44d9 100644
--- a/lib/gitlab/theme.rb
+++ b/lib/gitlab/theme.rb
@@ -40,7 +40,7 @@ module Gitlab
end
# Convenience method to get a space-separated String of all the theme
- # classes that mighty be applied to the `body` element
+ # classes that might be applied to the `body` element
#
# Returns a String
def self.body_classes
diff --git a/lib/redcarpet/render/gitlab_html.rb b/lib/redcarpet/render/gitlab_html.rb
index 10efff2ae9f..bea66e6cdc1 100644
--- a/lib/redcarpet/render/gitlab_html.rb
+++ b/lib/redcarpet/render/gitlab_html.rb
@@ -1,5 +1,6 @@
-class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
+require 'active_support/core_ext/string/output_safety'
+class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
attr_reader :template
alias_method :h, :template
@@ -8,24 +9,12 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
@color_scheme = color_scheme
@project = @template.instance_variable_get("@project")
@options = options.dup
- super options
- end
- def preprocess(full_document)
- # Redcarpet doesn't allow SMB links when `safe_links_only` is enabled.
- # FTP links are allowed, so we trick Redcarpet.
- full_document.gsub("smb://", "ftp://smb:")
+ super(options)
end
- # If project has issue number 39, apostrophe will be linked in
- # regular text to the issue as Redcarpet will convert apostrophe to
- # #39;
- # We replace apostrophe with right single quote before Redcarpet
- # does the processing and put the apostrophe back in postprocessing.
- # This only influences regular text, code blocks are untouched.
def normal_text(text)
- return text unless text.present?
- text.gsub("'", "&rsquo;")
+ ERB::Util.html_escape_once(text)
end
# Stolen from Rugments::Plugins::Redcarpet as this module is not required
@@ -37,7 +26,7 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
# so we assume you're not using leading spaces that aren't tabs,
# and just replace them here.
if lexer.tag == 'make'
- code.gsub! /^ /, "\t"
+ code.gsub!(/^ /, "\t")
end
formatter = Rugments::Formatters::HTML.new(
@@ -46,27 +35,11 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
formatter.format(lexer.lex(code))
end
- def link(link, title, content)
- h.link_to_gfm(content, link, title: title)
- end
-
- def header(text, level)
- if @options[:no_header_anchors]
- "<h#{level}>#{text}</h#{level}>"
- else
- id = ActionController::Base.helpers.strip_tags(h.gfm(text)).downcase() \
- .gsub(/[^a-z0-9_-]/, '-').gsub(/-+/, '-').gsub(/^-/, '').gsub(/-$/, '')
- "<h#{level} id=\"#{id}\">#{text}<a href=\"\##{id}\"></a></h#{level}>"
- end
- end
-
def postprocess(full_document)
- full_document.gsub!("ftp://smb:", "smb://")
-
- full_document.gsub!("&rsquo;", "'")
unless @template.instance_variable_get("@project_wiki") || @project.nil?
full_document = h.create_relative_links(full_document)
end
+
h.gfm_with_options(full_document, @options)
end
end
diff --git a/spec/controllers/import/google_code_controller_spec.rb b/spec/controllers/import/google_code_controller_spec.rb
index 037cddb4600..78c0f5079cc 100644
--- a/spec/controllers/import/google_code_controller_spec.rb
+++ b/spec/controllers/import/google_code_controller_spec.rb
@@ -27,21 +27,34 @@ describe Import::GoogleCodeController do
it "assigns variables" do
@project = create(:project, import_type: 'google_code', creator_id: user.id)
controller.stub_chain(:client, :repos).and_return([@repo])
+ controller.stub_chain(:client, :incompatible_repos).and_return([])
get :status
expect(assigns(:already_added_projects)).to eq([@project])
expect(assigns(:repos)).to eq([@repo])
+ expect(assigns(:incompatible_repos)).to eq([])
end
it "does not show already added project" do
@project = create(:project, import_type: 'google_code', creator_id: user.id, import_source: 'vim')
controller.stub_chain(:client, :repos).and_return([@repo])
+ controller.stub_chain(:client, :incompatible_repos).and_return([])
get :status
expect(assigns(:already_added_projects)).to eq([@project])
expect(assigns(:repos)).to eq([])
end
+
+ it "does not show any invalid projects" do
+ controller.stub_chain(:client, :repos).and_return([])
+ controller.stub_chain(:client, :incompatible_repos).and_return([@repo])
+
+ get :status
+
+ expect(assigns(:repos)).to be_empty
+ expect(assigns(:incompatible_repos)).to eq([@repo])
+ end
end
end
diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb
index 57fa079d753..102678a1d74 100644
--- a/spec/factories/projects.rb
+++ b/spec/factories/projects.rb
@@ -94,10 +94,26 @@ FactoryGirl.define do
'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
+
+ factory :jira_project, parent: :project do
+ after :create do |project|
+ project.create_jira_service(
+ active: true,
+ 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'
+ }
+ )
+
+ project.issues_tracker = 'jira'
+ project.issues_tracker_id = 'project_name_in_jira'
+ end
+ end
end
diff --git a/spec/features/markdown_spec.rb b/spec/features/markdown_spec.rb
new file mode 100644
index 00000000000..3528200e12b
--- /dev/null
+++ b/spec/features/markdown_spec.rb
@@ -0,0 +1,391 @@
+require 'spec_helper'
+require 'erb'
+
+# This feature spec is intended to be a comprehensive exercising of all of
+# GitLab's non-standard Markdown parsing and the integration thereof.
+#
+# These tests should be very high-level. Anything low-level belongs in the specs
+# for the corresponding HTML::Pipeline filter or helper method.
+#
+# The idea is to pass a Markdown document through our entire processing stack.
+#
+# The process looks like this:
+#
+# Raw Markdown
+# -> `markdown` helper
+# -> Redcarpet::Render::GitlabHTML converts Markdown to HTML
+# -> Post-process HTML
+# -> `gfm_with_options` helper
+# -> HTML::Pipeline
+# -> Sanitize
+# -> Emoji
+# -> Table of Contents
+# -> Autolinks
+# -> Rinku (http, https, ftp)
+# -> Other schemes
+# -> References
+# -> `html_safe`
+# -> Template
+#
+# See the MarkdownFeature class for setup details.
+
+describe 'GitLab Markdown' do
+ include ActionView::Helpers::TagHelper
+ include ActionView::Helpers::UrlHelper
+ include Capybara::Node::Matchers
+ include GitlabMarkdownHelper
+
+ # `markdown` calls these two methods
+ def current_user
+ @feat.user
+ end
+
+ def user_color_scheme_class
+ :white
+ end
+
+ # Let's only parse this thing once
+ before(:all) do
+ @feat = MarkdownFeature.new
+
+ # `markdown` expects a `@project` variable
+ @project = @feat.project
+
+ @md = markdown(@feat.raw_markdown)
+ @doc = Nokogiri::HTML::DocumentFragment.parse(@md)
+ end
+
+ after(:all) do
+ @feat.teardown
+ end
+
+ # Given a header ID, goes to that element's parent (the header), then to its
+ # second sibling (the body).
+ def get_section(id)
+ @doc.at_css("##{id}").parent.next.next
+ end
+
+ # it 'writes to a file' do
+ # File.open(Rails.root.join('tmp/capybara/markdown_spec.html'), 'w') do |file|
+ # file.puts @md
+ # end
+ # end
+
+ describe 'Markdown' do
+ describe 'No Intra Emphasis' do
+ it 'does not parse emphasis inside of words' do
+ body = get_section('no-intra-emphasis')
+ expect(body.to_html).not_to match('foo<em>bar</em>baz')
+ end
+ end
+
+ describe 'Tables' do
+ it 'parses table Markdown' do
+ body = get_section('tables')
+ expect(body).to have_selector('th:contains("Header")')
+ expect(body).to have_selector('th:contains("Row")')
+ expect(body).to have_selector('th:contains("Example")')
+ end
+
+ it 'allows Markdown in tables' do
+ expect(@doc.at_css('td:contains("Baz")').children.to_html).
+ to eq '<strong>Baz</strong>'
+ end
+ end
+
+ describe 'Fenced Code Blocks' do
+ it 'parses fenced code blocks' do
+ expect(@doc).to have_selector('pre.code.highlight.white.c')
+ expect(@doc).to have_selector('pre.code.highlight.white.python')
+ end
+ end
+
+ describe 'Strikethrough' do
+ it 'parses strikethroughs' do
+ expect(@doc).to have_selector(%{del:contains("and this text doesn't")})
+ end
+ end
+
+ describe 'Superscript' do
+ it 'parses superscript' do
+ body = get_section('superscript')
+ expect(body.to_html).to match('1<sup>st</sup>')
+ expect(body.to_html).to match('2<sup>nd</sup>')
+ end
+ end
+ end
+
+ describe 'HTML::Pipeline' do
+ describe 'SanitizationFilter' do
+ it 'uses a permissive whitelist' do
+ expect(@doc).to have_selector('b#manual-b')
+ expect(@doc).to have_selector('em#manual-em')
+ expect(@doc).to have_selector("code#manual-code")
+ expect(@doc).to have_selector('kbd:contains("s")')
+ expect(@doc).to have_selector('strike:contains(Emoji)')
+ expect(@doc).to have_selector('img#manual-img')
+ expect(@doc).to have_selector('br#manual-br')
+ expect(@doc).to have_selector('hr#manual-hr')
+ end
+
+ it 'permits span elements' do
+ expect(@doc).to have_selector('span#span-class-light.light')
+ end
+
+ it 'permits table alignment' do
+ expect(@doc.at_css('th:contains("Header")')['style']).to eq 'text-align: center'
+ expect(@doc.at_css('th:contains("Row")')['style']).to eq 'text-align: right'
+ expect(@doc.at_css('th:contains("Example")')['style']).to eq 'text-align: left'
+
+ expect(@doc.at_css('td:contains("Foo")')['style']).to eq 'text-align: center'
+ expect(@doc.at_css('td:contains("Bar")')['style']).to eq 'text-align: right'
+ expect(@doc.at_css('td:contains("Baz")')['style']).to eq 'text-align: left'
+ end
+
+ it 'removes `rel` attribute from links' do
+ expect(@doc).to have_selector('a#a-rel-nofollow')
+ expect(@doc).not_to have_selector('a#a-rel-nofollow[rel]')
+ end
+
+ it "removes `href` from `a` elements if it's fishy" do
+ expect(@doc).to have_selector('a#a-href-javascript')
+ expect(@doc).not_to have_selector('a#a-href-javascript[href]')
+ end
+ end
+
+ describe 'Escaping' do
+ let(:table) { @doc.css('table').last.at_css('tbody') }
+
+ it 'escapes non-tag angle brackets' do
+ expect(table.at_xpath('.//tr[1]/td[3]').inner_html).to eq '1 &lt; 3 &amp; 5'
+ end
+ end
+
+ describe 'EmojiFilter' do
+ it 'parses Emoji' do
+ expect(@doc).to have_selector('img.emoji', count: 10)
+ end
+ end
+
+ describe 'TableOfContentsFilter' do
+ it 'creates anchors inside header elements' do
+ expect(@doc).to have_selector('h1 a#gitlab-markdown')
+ expect(@doc).to have_selector('h2 a#markdown')
+ expect(@doc).to have_selector('h3 a#autolinkfilter')
+ end
+ end
+
+ describe 'AutolinkFilter' do
+ let(:list) { get_section('autolinkfilter').parent.search('ul') }
+
+ def item(index)
+ list.at_css("li:nth-child(#{index})")
+ end
+
+ it 'autolinks http://' do
+ expect(item(1).children.first.name).to eq 'a'
+ expect(item(1).children.first['href']).to eq 'http://about.gitlab.com/'
+ end
+
+ it 'autolinks https://' do
+ expect(item(2).children.first.name).to eq 'a'
+ expect(item(2).children.first['href']).to eq 'https://google.com/'
+ end
+
+ it 'autolinks ftp://' do
+ expect(item(3).children.first.name).to eq 'a'
+ expect(item(3).children.first['href']).to eq 'ftp://ftp.us.debian.org/debian/'
+ end
+
+ it 'autolinks smb://' do
+ expect(item(4).children.first.name).to eq 'a'
+ expect(item(4).children.first['href']).to eq 'smb://foo/bar/baz'
+ end
+
+ it 'autolinks irc://' do
+ expect(item(5).children.first.name).to eq 'a'
+ expect(item(5).children.first['href']).to eq 'irc://irc.freenode.net/git'
+ end
+
+ it 'autolinks short, invalid URLs' do
+ expect(item(6).children.first.name).to eq 'a'
+ expect(item(6).children.first['href']).to eq 'http://localhost:3000'
+ end
+
+ %w(code a kbd).each do |elem|
+ it "ignores links inside '#{elem}' element" do
+ expect(@doc.at_css("#{elem}#autolink-#{elem}").child).to be_text
+ end
+ end
+ end
+
+ describe 'ReferenceFilter' do
+ it 'handles references in headers' do
+ header = @doc.at_css('#reference-filters-eg-1').parent
+
+ expect(header.css('a').size).to eq 2
+ end
+
+ it "handles references in Markdown" do
+ body = get_section('reference-filters-eg-1')
+ expect(body).to have_selector('em a.gfm-merge_request', count: 1)
+ end
+
+ it 'parses user references' do
+ body = get_section('userreferencefilter')
+ expect(body).to have_selector('a.gfm.gfm-project_member', count: 3)
+ end
+
+ it 'parses issue references' do
+ body = get_section('issuereferencefilter')
+ expect(body).to have_selector('a.gfm.gfm-issue', count: 2)
+ end
+
+ it 'parses merge request references' do
+ body = get_section('mergerequestreferencefilter')
+ expect(body).to have_selector('a.gfm.gfm-merge_request', count: 2)
+ end
+
+ it 'parses snippet references' do
+ body = get_section('snippetreferencefilter')
+ expect(body).to have_selector('a.gfm.gfm-snippet', count: 2)
+ end
+
+ it 'parses commit range references' do
+ body = get_section('commitrangereferencefilter')
+ expect(body).to have_selector('a.gfm.gfm-commit_range', count: 2)
+ end
+
+ it 'parses commit references' do
+ body = get_section('commitreferencefilter')
+ expect(body).to have_selector('a.gfm.gfm-commit', count: 2)
+ end
+
+ it 'parses label references' do
+ body = get_section('labelreferencefilter')
+ expect(body).to have_selector('a.gfm.gfm-label', count: 3)
+ end
+ end
+ end
+end
+
+# This is a helper class used by the GitLab Markdown feature spec
+#
+# Because the feature spec only cares about the output of the Markdown, and the
+# test setup and teardown and parsing is fairly expensive, we only want to do it
+# once. Unfortunately RSpec will not let you access `let`s in a `before(:all)`
+# block, so we fake it by encapsulating all the shared setup in this class.
+#
+# The class contains the raw Markup used in the test, dynamically substituting
+# real objects, created from factories and setup on-demand, when referenced in
+# the Markdown.
+class MarkdownFeature
+ include FactoryGirl::Syntax::Methods
+
+ def initialize
+ DatabaseCleaner.start
+ end
+
+ def teardown
+ DatabaseCleaner.clean
+ end
+
+ def user
+ @user ||= create(:user)
+ end
+
+ def group
+ unless @group
+ @group = create(:group)
+ @group.add_user(user, Gitlab::Access::DEVELOPER)
+ end
+
+ @group
+ end
+
+ # Direct references ----------------------------------------------------------
+
+ def project
+ @project ||= create(:project)
+ end
+
+ def issue
+ @issue ||= create(:issue, project: project)
+ end
+
+ def merge_request
+ @merge_request ||= create(:merge_request, :simple, source_project: project)
+ end
+
+ def snippet
+ @snippet ||= create(:project_snippet, project: project)
+ end
+
+ def commit
+ @commit ||= project.repository.commit
+ end
+
+ def commit_range
+ unless @commit_range
+ commit2 = project.repository.commit('HEAD~3')
+ @commit_range = CommitRange.new("#{commit.id}...#{commit2.id}")
+ end
+
+ @commit_range
+ end
+
+ def simple_label
+ @simple_label ||= create(:label, name: 'gfm', project: project)
+ end
+
+ def label
+ @label ||= create(:label, name: 'awaiting feedback', project: project)
+ end
+
+ # Cross-references -----------------------------------------------------------
+
+ def xproject
+ unless @xproject
+ namespace = create(:namespace, name: 'cross-reference')
+ @xproject = create(:project, namespace: namespace)
+ @xproject.team << [user, :developer]
+ end
+
+ @xproject
+ end
+
+ # Shortcut to "cross-reference/project"
+ def xref
+ xproject.path_with_namespace
+ end
+
+ def xissue
+ @xissue ||= create(:issue, project: xproject)
+ end
+
+ def xmerge_request
+ @xmerge_request ||= create(:merge_request, :simple, source_project: xproject)
+ end
+
+ def xsnippet
+ @xsnippet ||= create(:project_snippet, project: xproject)
+ end
+
+ def xcommit
+ @xcommit ||= xproject.repository.commit
+ end
+
+ def xcommit_range
+ unless @xcommit_range
+ xcommit2 = xproject.repository.commit('HEAD~2')
+ @xcommit_range = CommitRange.new("#{xcommit.id}...#{xcommit2.id}")
+ end
+
+ @xcommit_range
+ end
+
+ def raw_markdown
+ fixture = Rails.root.join('spec/fixtures/markdown.md.erb')
+ ERB.new(File.read(fixture)).result(binding)
+ end
+end
diff --git a/spec/fixtures/markdown.md.erb b/spec/fixtures/markdown.md.erb
new file mode 100644
index 00000000000..0c140758557
--- /dev/null
+++ b/spec/fixtures/markdown.md.erb
@@ -0,0 +1,178 @@
+# GitLab Markdown
+
+This document is intended to be a comprehensive example of custom GitLab
+Markdown usage. It will be parsed and then tested for accuracy. Let's get
+started.
+
+## Markdown
+
+GitLab uses [Redcarpet](http://git.io/ld_NVQ) to parse all Markdown into
+HTML.
+
+It has some special features. Let's try 'em out!
+
+### No Intra Emphasis
+
+This string should have no emphasis: foo_bar_baz
+
+### Tables
+
+| Header | Row | Example |
+| :------: | ---: | :------ |
+| Foo | Bar | **Baz** |
+
+### Fenced Code Blocks
+
+```c
+#include<stdio.h>
+
+main()
+{
+ printf("Hello World");
+
+}
+```
+
+```python
+print "Hello, World!"
+```
+
+### Strikethrough
+
+This text says this, ~~and this text doesn't~~.
+
+### Superscript
+
+This is my 1^(st) time using superscript in Markdown. Now this is my
+2^(nd).
+
+### Next step
+
+After the Markdown has been turned into HTML, it gets passed through...
+
+## HTML::Pipeline
+
+### SanitizationFilter
+
+GitLab uses <a href="http://git.io/vfW8a" class="sanitize" id="sanitize-link">HTML::Pipeline::SanitizationFilter</a>
+to sanitize the generated HTML, stripping dangerous or unwanted tags.
+
+Its default whitelist is pretty permissive. Check it:
+
+<b id="manual-b">This text is bold</b> and <em id="manual-em">this text is emphasized</em>.
+
+<code id="manual-code">echo "Hello, world!"</code>
+
+Press <kbd>s</kbd> to search.
+
+<strike>Emoji</strike> Plain old images! <img
+src="http://www.emoji-cheat-sheet.com/graphics/emojis/smile.png" width="20"
+height="20" id="manual-img" />
+
+Here comes a line break:
+
+<br id="manual-br" />
+
+And a horizontal rule:
+
+<hr id="manual-hr" />
+
+As permissive as it is, we've allowed even more stuff:
+
+<span class="light" id="span-class-light">Span elements</span>
+
+<a href="#" rel="nofollow" id="a-rel-nofollow">This is a link with a defined rel attribute, which should be removed</a>
+
+<a href="javascript:alert('Hi')" id="a-href-javascript">This is a link trying to be sneaky. It gets its link removed entirely.</a>
+
+### Escaping
+
+The problem with SanitizationFilter is that it can be too aggressive.
+
+| Input | Expected | Actual |
+| ----------- | ---------------- | --------- |
+| `1 < 3 & 5` | 1 &lt; 3 &amp; 5 | 1 < 3 & 5 |
+| `<foo>` | &lt;foo&gt; | <foo> |
+
+### EmojiFilter
+
+Because life would be :zzz: without Emoji, right? :rocket:
+
+Get ready for the Emoji :bomb:: :+1::-1::ok_hand::wave::v::raised_hand::muscle:
+
+### TableOfContentsFilter
+
+All headers in this document should be linkable. Try it.
+
+### AutolinkFilter
+
+These are all plain text that should get turned into links:
+
+- http://about.gitlab.com/
+- https://google.com/
+- ftp://ftp.us.debian.org/debian/
+- smb://foo/bar/baz
+- irc://irc.freenode.net/git
+- http://localhost:3000
+
+But it shouldn't autolink text inside certain tags:
+
+- <code id="autolink-code">http://about.gitlab.com/</code>
+- <a id="autolink-a">http://about.gitlab.com/</a>
+- <kbd id="autolink-kbd">http://about.gitlab.com/</kbd>
+
+### Reference Filters (e.g., #<%= issue.iid %>)
+
+References should be parseable even inside _!<%= merge_request.iid %>_ emphasis.
+
+#### UserReferenceFilter
+
+- All: @all
+- User: @<%= user.username %>
+- Group: @<%= group.name %>
+- Ignores invalid: @fake_user
+- Ignored in code: `@<%= user.username %>`
+- Ignored in links: [Link to @<%= user.username %>](#user-link)
+
+#### IssueReferenceFilter
+
+- Issue: #<%= issue.iid %>
+- Issue in another project: <%= xref %>#<%= xissue.iid %>
+- Ignored in code: `#<%= issue.iid %>`
+- Ignored in links: [Link to #<%= issue.iid %>](#issue-link)
+
+#### MergeRequestReferenceFilter
+
+- Merge request: !<%= merge_request.iid %>
+- Merge request in another project: <%= xref %>!<%= xmerge_request.iid %>
+- Ignored in code: `!<%= merge_request.iid %>`
+- Ignored in links: [Link to !<%= merge_request.iid %>](#merge-request-link)
+
+#### SnippetReferenceFilter
+
+- Snippet: $<%= snippet.id %>
+- Snippet in another project: <%= xref %>$<%= xsnippet.id %>
+- Ignored in code: `$<%= snippet.id %>`
+- Ignored in links: [Link to $<%= snippet.id %>](#snippet-link)
+
+#### CommitRangeReferenceFilter
+
+- Range: <%= commit_range %>
+- Range in another project: <%= xref %>@<%= xcommit_range %>
+- Ignored in code: `<%= commit_range %>`
+- Ignored in links: [Link to <%= commit_range %>](#commit-range-link)
+
+#### CommitReferenceFilter
+
+- Commit: <%= commit.id %>
+- Commit in another project: <%= xref %>@<%= xcommit.id %>
+- Ignored in code: `<%= commit.id %>`
+- Ignored in links: [Link to <%= commit.id %>](#commit-link)
+
+#### LabelReferenceFilter
+
+- Label by ID: ~<%= simple_label.id %>
+- Label by name: ~<%= simple_label.name %>
+- Label by name in quotes: ~"<%= label.name %>"
+- Ignored in code: `~<%= simple_label.name %>`
+- Ignored in links: [Link to ~<%= simple_label.id %>](#label-link)
diff --git a/spec/helpers/gitlab_markdown_helper_spec.rb b/spec/helpers/gitlab_markdown_helper_spec.rb
index e309dbb6a2f..b6be82e4109 100644
--- a/spec/helpers/gitlab_markdown_helper_spec.rb
+++ b/spec/helpers/gitlab_markdown_helper_spec.rb
@@ -107,8 +107,7 @@ describe GitlabMarkdownHelper do
end
it 'should not be confused by whitespace before bullets' do
- rendered_text_asterisk = markdown(@source_text_asterisk,
- parse_tasks: true)
+ rendered_text_asterisk = markdown(@source_text_asterisk, parse_tasks: true)
rendered_text_dash = markdown(@source_text_dash, parse_tasks: true)
expect(rendered_text_asterisk).to match(
@@ -207,78 +206,7 @@ describe GitlabMarkdownHelper do
end
describe "#markdown" do
- # TODO (rspeicher) - This block tests multiple different contexts. Break this up!
-
- it "should add ids and links to headers" do
- # Test every rule except nested tags.
- text = '..Ab_c-d. e..'
- id = 'ab_c-d-e'
- expect(markdown("# #{text}")).
- to match(%r{<h1 id="#{id}">#{text}<a href="[^"]*##{id}"></a></h1>})
- expect(markdown("# #{text}", {no_header_anchors:true})).
- to eq("<h1>#{text}</h1>")
-
- id = 'link-text'
- expect(markdown("# [link text](url) ![img alt](url)")).to match(
- %r{<h1 id="#{id}"><a href="[^"]*url">link text</a> <img[^>]*><a href="[^"]*##{id}"></a></h1>}
- )
- end
-
- # REFERENCES (PART TWO: THE REVENGE) ---------------------------------------
-
- it "should handle references in headers" do
- actual = "\n# Working around ##{issue.iid}\n## Apply !#{merge_request.iid}"
-
- expect(markdown(actual, no_header_anchors: true)).
- to match(%r{<h1[^<]*>Working around <a.+>##{issue.iid}</a></h1>})
- expect(markdown(actual, no_header_anchors: true)).
- to match(%r{<h2[^<]*>Apply <a.+>!#{merge_request.iid}</a></h2>})
- end
-
- it "should handle references in <em>" do
- actual = "Apply _!#{merge_request.iid}_ ASAP"
-
- expect(markdown(actual)).
- to match(%r{Apply <em><a.+>!#{merge_request.iid}</a></em>})
- end
-
- # CODE BLOCKS -------------------------------------------------------------
-
- it "should leave code blocks untouched" do
- allow(helper).to receive(:current_user).and_return(user)
- allow(helper).to receive(:user_color_scheme_class).and_return(:white)
-
- target_html = "<pre class=\"code highlight white plaintext\"><code>some code from $#{snippet.id}\nhere too\n</code></pre>\n"
-
- expect(markdown("\n some code from $#{snippet.id}\n here too\n")).
- to eq(target_html)
- expect(markdown("\n```\nsome code from $#{snippet.id}\nhere too\n```\n")).
- to eq(target_html)
- end
-
- it "should leave inline code untouched" do
- expect(markdown("Don't use `$#{snippet.id}` here.")).
- to eq "<p>Don't use <code>$#{snippet.id}</code> here.</p>\n"
- end
-
- # REF-LIKE AUTOLINKS? -----------------------------------------------------
- # Basically: Don't parse references inside `<a>` tags.
-
- it "should leave ref-like autolinks untouched" do
- expect(markdown("look at http://example.tld/#!#{merge_request.iid}")).to eq("<p>look at <a href=\"http://example.tld/#!#{merge_request.iid}\">http://example.tld/#!#{merge_request.iid}</a></p>\n")
- end
-
- it "should leave ref-like href of 'manual' links untouched" do
- expect(markdown("why not [inspect !#{merge_request.iid}](http://example.tld/#!#{merge_request.iid})")).to eq("<p>why not <a href=\"http://example.tld/#!#{merge_request.iid}\">inspect </a><a href=\"#{namespace_project_merge_request_path(project.namespace, project, merge_request)}\" title=\"Merge Request: #{merge_request.title}\" class=\"gfm gfm-merge_request\">!#{merge_request.iid}</a><a href=\"http://example.tld/#!#{merge_request.iid}\"></a></p>\n")
- end
-
- it "should leave ref-like src of images untouched" do
- expect(markdown("screen shot: ![some image](http://example.tld/#!#{merge_request.iid})")).to eq("<p>screen shot: <img src=\"http://example.tld/#!#{merge_request.iid}\" alt=\"some image\"></p>\n")
- end
-
- # RELATIVE URLS -----------------------------------------------------------
# TODO (rspeicher): These belong in a relative link filter spec
-
context 'relative links' do
context 'with a valid repository' do
before do
@@ -333,11 +261,6 @@ describe GitlabMarkdownHelper do
expected = ""
expect(markdown(actual)).to match(expected)
end
-
- it 'should allow whitelisted HTML tags from the user' do
- actual = '<dl><dt>Term</dt><dd>Definition</dd></dl>'
- expect(markdown(actual)).to match(actual)
- end
end
context 'with an empty repository' do
@@ -353,34 +276,6 @@ describe GitlabMarkdownHelper do
end
end
end
-
- # SANITIZATION ------------------------------------------------------------
- # TODO (rspeicher): These are testing SanitizationFilter, not `markdown`
-
- it 'should sanitize tags that are not whitelisted' do
- actual = '<textarea>no inputs allowed</textarea> <blink>no blinks</blink>'
- expected = 'no inputs allowed no blinks'
- expect(markdown(actual)).to match(expected)
- expect(markdown(actual)).not_to match('<.textarea>')
- expect(markdown(actual)).not_to match('<.blink>')
- end
-
- it 'should allow whitelisted tag attributes from the user' do
- actual = '<a class="custom">link text</a>'
- expect(markdown(actual)).to match(actual)
- end
-
- it 'should sanitize tag attributes that are not whitelisted' do
- actual = '<a href="http://example.com/bar.html" foo="bar">link text</a>'
- expected = '<a href="http://example.com/bar.html">link text</a>'
- expect(markdown(actual)).to match(expected)
- end
-
- it 'should sanitize javascript in attributes' do
- actual = %q(<a href="javascript:alert('foo')">link text</a>)
- expected = '<a>link text</a>'
- expect(markdown(actual)).to match(expected)
- end
end
describe '#render_wiki_content' do
diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb
index 54dd8d4aa64..c08ddb4cae1 100644
--- a/spec/helpers/issues_helper_spec.rb
+++ b/spec/helpers/issues_helper_spec.rb
@@ -5,24 +5,6 @@ describe IssuesHelper do
let(:issue) { create :issue, project: project }
let(:ext_project) { create :redmine_project }
- describe "title_for_issue" do
- it "should return issue title if used internal tracker" do
- @project = project
- expect(title_for_issue(issue.iid)).to eq issue.title
- end
-
- it "should always return empty string if used external tracker" do
- @project = ext_project
- expect(title_for_issue(rand(100))).to eq ""
- end
-
- it "should always return empty string if project nil" do
- @project = nil
-
- expect(title_for_issue(rand(100))).to eq ""
- end
- end
-
describe "url_for_project_issues" do
let(:project_url) { ext_project.external_issue_tracker.project_url }
let(:ext_expected) do
diff --git a/spec/lib/gitlab/google_code_import/client_spec.rb b/spec/lib/gitlab/google_code_import/client_spec.rb
index d2bf871daa8..a66b811e0fd 100644
--- a/spec/lib/gitlab/google_code_import/client_spec.rb
+++ b/spec/lib/gitlab/google_code_import/client_spec.rb
@@ -23,6 +23,7 @@ describe Gitlab::GoogleCodeImport::Client do
describe "#repos" do
it "returns only Git repositories" do
expect(subject.repos.length).to eq(1)
+ expect(subject.incompatible_repos.length).to eq(1)
end
end
diff --git a/spec/lib/gitlab/markdown/autolink_filter_spec.rb b/spec/lib/gitlab/markdown/autolink_filter_spec.rb
new file mode 100644
index 00000000000..0bbdc11a979
--- /dev/null
+++ b/spec/lib/gitlab/markdown/autolink_filter_spec.rb
@@ -0,0 +1,106 @@
+require 'spec_helper'
+
+module Gitlab::Markdown
+ describe AutolinkFilter do
+ let(:link) { 'http://about.gitlab.com/' }
+
+ def filter(html, options = {})
+ described_class.call(html, options)
+ end
+
+ it 'does nothing when :autolink is false' do
+ exp = act = link
+ expect(filter(act, autolink: false).to_html).to eq exp
+ end
+
+ it 'does nothing with non-link text' do
+ exp = act = 'This text contains no links to autolink'
+ expect(filter(act).to_html).to eq exp
+ end
+
+ context 'Rinku schemes' do
+ it 'autolinks http' do
+ doc = filter("See #{link}")
+ expect(doc.at_css('a').text).to eq link
+ expect(doc.at_css('a')['href']).to eq link
+ end
+
+ it 'autolinks https' do
+ link = 'https://google.com/'
+ doc = filter("See #{link}")
+
+ expect(doc.at_css('a').text).to eq link
+ expect(doc.at_css('a')['href']).to eq link
+ end
+
+ it 'autolinks ftp' do
+ link = 'ftp://ftp.us.debian.org/debian/'
+ doc = filter("See #{link}")
+
+ expect(doc.at_css('a').text).to eq link
+ expect(doc.at_css('a')['href']).to eq link
+ end
+
+ it 'autolinks short URLs' do
+ link = 'http://localhost:3000/'
+ doc = filter("See #{link}")
+
+ expect(doc.at_css('a').text).to eq link
+ expect(doc.at_css('a')['href']).to eq link
+ end
+
+ it 'accepts link_attr options' do
+ doc = filter("See #{link}", link_attr: {class: 'custom'})
+
+ expect(doc.at_css('a')['class']).to eq 'custom'
+ end
+
+ described_class::IGNORE_PARENTS.each do |elem|
+ it "ignores valid links contained inside '#{elem}' element" do
+ exp = act = "<#{elem}>See #{link}</#{elem}>"
+ expect(filter(act).to_html).to eq exp
+ end
+ end
+ end
+
+ context 'other schemes' do
+ let(:link) { 'foo://bar.baz/' }
+
+ it 'autolinks smb' do
+ link = 'smb:///Volumes/shared/foo.pdf'
+ doc = filter("See #{link}")
+
+ expect(doc.at_css('a').text).to eq link
+ expect(doc.at_css('a')['href']).to eq link
+ end
+
+ it 'autolinks irc' do
+ link = 'irc://irc.freenode.net/git'
+ doc = filter("See #{link}")
+
+ expect(doc.at_css('a').text).to eq link
+ expect(doc.at_css('a')['href']).to eq link
+ end
+
+ it 'does not include trailing punctuation' do
+ doc = filter("See #{link}.")
+ expect(doc.at_css('a').text).to eq link
+
+ doc = filter("See #{link}, ok?")
+ expect(doc.at_css('a').text).to eq link
+ end
+
+ it 'accepts link_attr options' do
+ doc = filter("See #{link}", link_attr: {class: 'custom'})
+ expect(doc.at_css('a')['class']).to eq 'custom'
+ end
+
+ described_class::IGNORE_PARENTS.each do |elem|
+ it "ignores valid links contained inside '#{elem}' element" do
+ exp = act = "<#{elem}>See #{link}</#{elem}>"
+ expect(filter(act).to_html).to eq exp
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/markdown/external_issue_reference_filter_spec.rb b/spec/lib/gitlab/markdown/external_issue_reference_filter_spec.rb
index 27e930ef7da..b19bc125b92 100644
--- a/spec/lib/gitlab/markdown/external_issue_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/external_issue_reference_filter_spec.rb
@@ -8,29 +8,12 @@ module Gitlab::Markdown
IssuesHelper
end
- let(:project) { create(:empty_project) }
+ let(:project) { create(:jira_project) }
let(:issue) { double('issue', iid: 123) }
context 'JIRA issue references' do
let(:reference) { "JIRA-#{issue.iid}" }
- before do
- jira = project.create_jira_service
-
- props = {
- '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: props, active: true)
- end
-
- after do
- project.jira_service.destroy
- end
-
it 'requires project context' do
expect { described_class.call('Issue JIRA-123', {}) }.
to raise_error(ArgumentError, /:project/)
diff --git a/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb b/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb
index 393bf32e196..08382b3e7e8 100644
--- a/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb
@@ -27,7 +27,7 @@ module Gitlab::Markdown
let(:reference) { "##{issue.iid}" }
it 'ignores valid references when using non-default tracker' do
- expect(project).to receive(:issue_exists?).with(issue.iid).and_return(false)
+ expect(project).to receive(:get_issue).with(issue.iid).and_return(nil)
exp = act = "Issue ##{issue.iid}"
expect(filter(act).to_html).to eq exp
@@ -48,7 +48,7 @@ module Gitlab::Markdown
it 'ignores invalid issue IDs' do
exp = act = "Fixed ##{issue.iid + 1}"
- expect(project).to receive(:issue_exists?).with(issue.iid + 1)
+ expect(project).to receive(:get_issue).with(issue.iid + 1).and_return(nil)
expect(filter(act).to_html).to eq exp
end
@@ -98,8 +98,8 @@ module Gitlab::Markdown
before { allow_cross_reference! }
it 'ignores valid references when cross-reference project uses external tracker' do
- expect_any_instance_of(Project).to receive(:issue_exists?).
- with(issue.iid).and_return(false)
+ expect_any_instance_of(Project).to receive(:get_issue).
+ with(issue.iid).and_return(nil)
exp = act = "Issue ##{issue.iid}"
expect(filter(act).to_html).to eq exp
diff --git a/spec/lib/gitlab/markdown/sanitization_filter_spec.rb b/spec/lib/gitlab/markdown/sanitization_filter_spec.rb
new file mode 100644
index 00000000000..ab909a68635
--- /dev/null
+++ b/spec/lib/gitlab/markdown/sanitization_filter_spec.rb
@@ -0,0 +1,81 @@
+require 'spec_helper'
+
+module Gitlab::Markdown
+ describe SanitizationFilter do
+ def filter(html, options = {})
+ described_class.call(html, options)
+ end
+
+ describe 'default whitelist' do
+ it 'sanitizes tags that are not whitelisted' do
+ act = %q{<textarea>no inputs</textarea> and <blink>no blinks</blink>}
+ exp = 'no inputs and no blinks'
+ expect(filter(act).to_html).to eq exp
+ end
+
+ it 'sanitizes tag attributes' do
+ act = %q{<a href="http://example.com/bar.html" onclick="bar">Text</a>}
+ exp = %q{<a href="http://example.com/bar.html">Text</a>}
+ expect(filter(act).to_html).to eq exp
+ end
+
+ it 'sanitizes javascript in attributes' do
+ act = %q(<a href="javascript:alert('foo')">Text</a>)
+ exp = '<a>Text</a>'
+ expect(filter(act).to_html).to eq exp
+ end
+
+ it 'allows whitelisted HTML tags from the user' do
+ exp = act = "<dl>\n<dt>Term</dt>\n<dd>Definition</dd>\n</dl>"
+ expect(filter(act).to_html).to eq exp
+ end
+ end
+
+ describe 'custom whitelist' do
+ it 'allows `class` attribute on any element' do
+ exp = act = %q{<strong class="foo">Strong</strong>}
+ expect(filter(act).to_html).to eq exp
+ end
+
+ it 'allows `id` attribute on any element' do
+ exp = act = %q{<em id="foo">Emphasis</em>}
+ expect(filter(act).to_html).to eq exp
+ end
+
+ it 'allows `style` attribute on table elements' do
+ html = <<-HTML.strip_heredoc
+ <table>
+ <tr><th style="text-align: center">Head</th></tr>
+ <tr><td style="text-align: right">Body</th></tr>
+ </table>
+ HTML
+
+ doc = filter(html)
+
+ expect(doc.at_css('th')['style']).to eq 'text-align: center'
+ expect(doc.at_css('td')['style']).to eq 'text-align: right'
+ end
+
+ it 'allows `span` elements' do
+ exp = act = %q{<span>Hello</span>}
+ expect(filter(act).to_html).to eq exp
+ end
+
+ it 'removes `rel` attribute from `a` elements' do
+ doc = filter(%q{<a href="#" rel="nofollow">Link</a>})
+
+ expect(doc.css('a').size).to eq 1
+ expect(doc.at_css('a')['href']).to eq '#'
+ expect(doc.at_css('a')['rel']).to be_nil
+ end
+
+ it 'removes script-like `href` attribute from `a` elements' do
+ html = %q{<a href="javascript:alert('Hi')">Hi</a>}
+ doc = filter(html)
+
+ expect(doc.css('a').size).to eq 1
+ expect(doc.at_css('a')['href']).to be_nil
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/markdown/table_of_contents_filter_spec.rb b/spec/lib/gitlab/markdown/table_of_contents_filter_spec.rb
new file mode 100644
index 00000000000..f383a5850d5
--- /dev/null
+++ b/spec/lib/gitlab/markdown/table_of_contents_filter_spec.rb
@@ -0,0 +1,101 @@
+# encoding: UTF-8
+
+require 'spec_helper'
+
+module Gitlab::Markdown
+ describe TableOfContentsFilter do
+ def filter(html, options = {})
+ described_class.call(html, options)
+ end
+
+ def header(level, text)
+ "<h#{level}>#{text}</h#{level}>\n"
+ end
+
+ it 'does nothing when :no_header_anchors is truthy' do
+ exp = act = header(1, 'Header')
+ expect(filter(act, no_header_anchors: 1).to_html).to eq exp
+ end
+
+ it 'does nothing with empty headers' do
+ exp = act = header(1, nil)
+ expect(filter(act).to_html).to eq exp
+ end
+
+ 1.upto(6) do |i|
+ it "processes h#{i} elements" do
+ html = header(i, "Header #{i}")
+ doc = filter(html)
+
+ expect(doc.css("h#{i} a").first.attr('id')).to eq "header-#{i}"
+ end
+ end
+
+ describe 'anchor tag' do
+ it 'has an `anchor` class' do
+ doc = filter(header(1, 'Header'))
+ expect(doc.css('h1 a').first.attr('class')).to eq 'anchor'
+ end
+
+ it 'links to the id' do
+ doc = filter(header(1, 'Header'))
+ expect(doc.css('h1 a').first.attr('href')).to eq '#header'
+ end
+
+ describe 'generated IDs' do
+ it 'translates spaces to dashes' do
+ doc = filter(header(1, 'This header has spaces in it'))
+ expect(doc.css('h1 a').first.attr('id')).to eq 'this-header-has-spaces-in-it'
+ end
+
+ it 'squeezes multiple spaces and dashes' do
+ doc = filter(header(1, 'This---header is poorly-formatted'))
+ expect(doc.css('h1 a').first.attr('id')).to eq 'this-header-is-poorly-formatted'
+ end
+
+ it 'removes punctuation' do
+ doc = filter(header(1, "This, header! is, filled. with @ punctuation?"))
+ expect(doc.css('h1 a').first.attr('id')).to eq 'this-header-is-filled-with-punctuation'
+ end
+
+ it 'appends a unique number to duplicates' do
+ doc = filter(header(1, 'One') + header(2, 'One'))
+
+ expect(doc.css('h1 a').first.attr('id')).to eq 'one'
+ expect(doc.css('h2 a').first.attr('id')).to eq 'one-1'
+ end
+
+ it 'supports Unicode' do
+ doc = filter(header(1, '한글'))
+ expect(doc.css('h1 a').first.attr('id')).to eq '한글'
+ expect(doc.css('h1 a').first.attr('href')).to eq '#한글'
+ end
+ end
+ end
+
+ describe 'result' do
+ def result(html)
+ HTML::Pipeline.new([described_class]).call(html)
+ end
+
+ let(:results) { result(header(1, 'Header 1') + header(2, 'Header 2')) }
+ let(:doc) { Nokogiri::XML::DocumentFragment.parse(results[:toc]) }
+
+ it 'is contained within a `ul` element' do
+ expect(doc.children.first.name).to eq 'ul'
+ expect(doc.children.first.attr('class')).to eq 'section-nav'
+ end
+
+ it 'contains an `li` element for each header' do
+ expect(doc.css('li').length).to eq 2
+
+ links = doc.css('li a')
+
+ expect(links.first.attr('href')).to eq '#header-1'
+ expect(links.first.text).to eq 'Header 1'
+ expect(links.last.attr('href')).to eq '#header-2'
+ expect(links.last.text).to eq 'Header 2'
+ end
+ end
+ end
+end
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index d28b4768545..dbcf7286e45 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -11,9 +11,8 @@ describe Notify do
let(:recipient) { create(:user, email: 'recipient@example.com') }
let(:project) { create(:project) }
- around(:each) { ActionMailer::Base.deliveries.clear }
-
before(:each) do
+ ActionMailer::Base.deliveries.clear
email = recipient.emails.create(email: "notifications@example.com")
recipient.update_attribute(:notification_email, email.email)
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 879a63dd9f9..37e21a90818 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -129,6 +129,48 @@ describe Project do
end
end
+ describe '#get_issue' do
+ let(:project) { create(:empty_project) }
+ let(:issue) { create(:issue, project: project) }
+
+ context 'with default issues tracker' do
+ it 'returns an issue' do
+ expect(project.get_issue(issue.iid)).to eq issue
+ end
+
+ it 'returns nil when no issue found' do
+ expect(project.get_issue(999)).to be_nil
+ end
+ end
+
+ context 'with external issues tracker' do
+ before do
+ allow(project).to receive(:default_issues_tracker?).and_return(false)
+ end
+
+ it 'returns an ExternalIssue' do
+ issue = project.get_issue('FOO-1234')
+ expect(issue).to be_kind_of(ExternalIssue)
+ expect(issue.iid).to eq 'FOO-1234'
+ expect(issue.project).to eq project
+ end
+ end
+ end
+
+ describe '#issue_exists?' do
+ let(:project) { create(:empty_project) }
+
+ it 'is truthy when issue exists' do
+ expect(project).to receive(:get_issue).and_return(double)
+ expect(project.issue_exists?(1)).to be_truthy
+ end
+
+ it 'is falsey when issue does not exist' do
+ expect(project).to receive(:get_issue).and_return(nil)
+ expect(project.issue_exists?(1)).to be_falsey
+ end
+ end
+
describe :update_merge_requests do
let(:project) { create(:project) }
let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
@@ -180,25 +222,6 @@ describe Project do
end
end
- describe :issue_exists? do
- let(:project) { create(:project) }
- let(:existed_issue) { create(:issue, project: project) }
- 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
- expect(project.issue_exists?(existed_issue.iid)).to be_truthy
- end
-
- it 'should be false or if used internal tracker and issue not exists' do
- expect(project.issue_exists?(not_existed_issue.iid)).to be_falsey
- end
-
- it 'should always be true if used other tracker' do
- expect(ext_project.issue_exists?(rand(100))).to be_truthy
- end
- end
-
describe :default_issues_tracker? do
let(:project) { create(:project) }
let(:ext_project) { create(:redmine_project) }
diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb
index d1844bd2b87..6d4a8067910 100644
--- a/spec/support/test_env.rb
+++ b/spec/support/test_env.rb
@@ -35,6 +35,7 @@ module TestEnv
# Create repository for FactoryGirl.create(:project)
setup_factory_repo
+
# Create repository for FactoryGirl.create(:forked_project_with_submodules)
setup_forked_repo
end
@@ -54,7 +55,7 @@ module TestEnv
tmp_test_path = Rails.root.join('tmp', 'tests', '**')
Dir[tmp_test_path].each do |entry|
- unless File.basename(entry) =~ /\Agitlab-(shell|test)\z/
+ unless File.basename(entry) =~ /\Agitlab-(shell|test|test-fork)\z/
FileUtils.rm_rf(entry)
end
end