Commit 0d0c539c authored by Robert Schilling's avatar Robert Schilling

Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce

parents 76f7bdcc b3890749
...@@ -37,6 +37,6 @@ public/assets/ ...@@ -37,6 +37,6 @@ public/assets/
public/uploads.* public/uploads.*
public/uploads/ public/uploads/
rails_best_practices_output.html rails_best_practices_output.html
tags /tags
tmp/ tmp/
vendor/bundle/* vendor/bundle/*
Please view this file on the master branch, on stable branches it's out of date. Please view this file on the master branch, on stable branches it's out of date.
v 7.11.0 (unreleased) 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". - 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. - Get Gitorious importer to work again.
- Fix clone URL field and X11 Primary selection (Dmitry Medvinsky) - Fix clone URL field and X11 Primary selection (Dmitry Medvinsky)
- Ignore invalid lines in .gitmodules - Ignore invalid lines in .gitmodules
...@@ -11,6 +13,7 @@ v 7.11.0 (unreleased) ...@@ -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 "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 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`) - 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 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. - 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) ...@@ -21,7 +24,7 @@ v 7.11.0 (unreleased)
- Include commit comments in MR from a forked project. - Include commit comments in MR from a forked project.
- Fix adding new group members from admin area - Fix adding new group members from admin area
- Add default project and snippet visibility settings to the admin web UI. - 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 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) - Fix bug where Slack service channel was not saved in admin template settings. (Stan Hu)
- Move snippets UI to fluid layout - Move snippets UI to fluid layout
......
...@@ -8,7 +8,6 @@ class Dispatcher ...@@ -8,7 +8,6 @@ class Dispatcher
initPageScripts: -> initPageScripts: ->
page = $('body').attr('data-page') page = $('body').attr('data-page')
project_id = $('body').attr('data-project-id')
unless page unless page
return false return false
......
...@@ -35,7 +35,12 @@ pre { ...@@ -35,7 +35,12 @@ pre {
/* Link to current header. */ /* Link to current header. */
h1, h2, h3, h4, h5, h6 { h1, h2, h3, h4, h5, h6 {
position: relative; position: relative;
&:hover > :last-child {
a.anchor {
display: none;
}
&:hover > a.anchor {
$size: 16px; $size: 16px;
position: absolute; position: absolute;
right: 100%; right: 100%;
......
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
# #
# Automatically sets the layout and ensures an administrator is logged in # Automatically sets the layout and ensures an administrator is logged in
class Admin::ApplicationController < ApplicationController class Admin::ApplicationController < ApplicationController
layout 'admin'
before_action :authenticate_admin! before_action :authenticate_admin!
layout 'admin'
def authenticate_admin! def authenticate_admin!
return render_404 unless current_user.is_admin? return render_404 unless current_user.is_admin?
......
...@@ -3,6 +3,7 @@ require 'gon' ...@@ -3,6 +3,7 @@ require 'gon'
class ApplicationController < ActionController::Base class ApplicationController < ActionController::Base
include Gitlab::CurrentSettings include Gitlab::CurrentSettings
include GitlabRoutingHelper include GitlabRoutingHelper
include PageLayoutHelper
PER_PAGE = 20 PER_PAGE = 20
......
class Dashboard::ApplicationController < ApplicationController
layout 'dashboard'
end
class Dashboard::GroupsController < ApplicationController class Dashboard::GroupsController < Dashboard::ApplicationController
def index def index
@group_members = current_user.group_members.page(params[:page]).per(PER_PAGE) @group_members = current_user.group_members.page(params[:page]).per(PER_PAGE)
end end
......
class Dashboard::MilestonesController < ApplicationController class Dashboard::MilestonesController < Dashboard::ApplicationController
before_action :load_projects before_action :load_projects
def index def index
......
class Dashboard::ProjectsController < ApplicationController class Dashboard::ProjectsController < Dashboard::ApplicationController
before_action :event_filter before_action :event_filter
def starred def starred
......
class DashboardController < ApplicationController class DashboardController < Dashboard::ApplicationController
respond_to :html
before_action :load_projects, except: [:projects] before_action :load_projects, except: [:projects]
before_action :event_filter, only: :show before_action :event_filter, only: :show
respond_to :html
def show def show
@projects = @projects.includes(:namespace) @projects = @projects.includes(:namespace)
@last_push = current_user.recent_push @last_push = current_user.recent_push
......
class Explore::ApplicationController < ApplicationController
layout 'explore'
end
class Explore::GroupsController < ApplicationController class Explore::GroupsController < Explore::ApplicationController
skip_before_action :authenticate_user!, skip_before_action :authenticate_user!,
:reject_blocked, :set_current_user_for_observers :reject_blocked, :set_current_user_for_observers
layout "explore"
def index def index
@groups = GroupsFinder.new.execute(current_user) @groups = GroupsFinder.new.execute(current_user)
@groups = @groups.search(params[:search]) if params[:search].present? @groups = @groups.search(params[:search]) if params[:search].present?
......
class Explore::ProjectsController < ApplicationController class Explore::ProjectsController < Explore::ApplicationController
skip_before_action :authenticate_user!, skip_before_action :authenticate_user!,
:reject_blocked :reject_blocked
layout 'explore'
def index def index
@projects = ProjectsFinder.new.execute(current_user) @projects = ProjectsFinder.new.execute(current_user)
@tags = @projects.tags_on(:tags) @tags = @projects.tags_on(:tags)
......
class Groups::ApplicationController < ApplicationController class Groups::ApplicationController < ApplicationController
layout 'group'
private private
...@@ -17,12 +18,4 @@ class Groups::ApplicationController < ApplicationController ...@@ -17,12 +18,4 @@ class Groups::ApplicationController < ApplicationController
return render_404 return render_404
end end
end end
def determine_layout
if current_user
'group'
else
'public_group'
end
end
end end
class Groups::AvatarsController < ApplicationController class Groups::AvatarsController < ApplicationController
layout "profile"
def destroy def destroy
@group = Group.find_by(path: params[:group_id]) @group = Group.find_by(path: params[:group_id])
@group.remove_avatar! @group.remove_avatar!
......
...@@ -6,8 +6,6 @@ class Groups::GroupMembersController < Groups::ApplicationController ...@@ -6,8 +6,6 @@ class Groups::GroupMembersController < Groups::ApplicationController
before_action :authorize_read_group! before_action :authorize_read_group!
before_action :authorize_admin_group!, except: [:index, :leave] before_action :authorize_admin_group!, except: [:index, :leave]
layout :determine_layout
def index def index
@project = @group.projects.find(params[:project_id]) if params[:project_id] @project = @group.projects.find(params[:project_id]) if params[:project_id]
@members = @group.group_members @members = @group.group_members
......
class Groups::MilestonesController < ApplicationController class Groups::MilestonesController < Groups::ApplicationController
layout 'group'
before_action :authorize_group_milestone!, only: :update before_action :authorize_group_milestone!, only: :update
def index def index
......
...@@ -11,7 +11,6 @@ class GroupsController < Groups::ApplicationController ...@@ -11,7 +11,6 @@ class GroupsController < Groups::ApplicationController
# Load group projects # Load group projects
before_action :load_projects, except: [:new, :create, :projects, :edit, :update] before_action :load_projects, except: [:new, :create, :projects, :edit, :update]
before_action :event_filter, only: :show before_action :event_filter, only: :show
before_action :set_title, only: [:new, :create]
layout :determine_layout layout :determine_layout
...@@ -119,17 +118,11 @@ class GroupsController < Groups::ApplicationController ...@@ -119,17 +118,11 @@ class GroupsController < Groups::ApplicationController
end end
end end
def set_title
@title = 'New Group'
end
def determine_layout def determine_layout
if [:new, :create].include?(action_name.to_sym) if [:new, :create].include?(action_name.to_sym)
'navless' 'application'
elsif current_user
'group'
else else
'public_group' 'group'
end end
end end
......
class HelpController < ApplicationController class HelpController < ApplicationController
layout 'help'
def index def index
end end
def show def show
category = clean_path_info(path_params[:category]) @category = clean_path_info(path_params[:category])
file = path_params[:file] @file = path_params[:file]
respond_to do |format| respond_to do |format|
format.any(:markdown, :md, :html) do 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) if File.exist?(path)
@markdown = File.read(path) @markdown = File.read(path)
...@@ -22,7 +24,7 @@ class HelpController < ApplicationController ...@@ -22,7 +24,7 @@ class HelpController < ApplicationController
# Allow access to images in the doc folder # Allow access to images in the doc folder
format.any(:png, :gif, :jpeg) do 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) if File.exist?(path)
send_file(path, disposition: 'inline') send_file(path, disposition: 'inline')
......
...@@ -72,6 +72,7 @@ class Import::GoogleCodeController < Import::BaseController ...@@ -72,6 +72,7 @@ class Import::GoogleCodeController < Import::BaseController
end end
@repos = client.repos @repos = client.repos
@incompatible_repos = client.incompatible_repos
@already_added_projects = current_user.created_projects.where(import_type: "google_code") @already_added_projects = current_user.created_projects.where(import_type: "google_code")
already_added_projects_names = @already_added_projects.pluck(:import_source) already_added_projects_names = @already_added_projects.pluck(:import_source)
......
...@@ -4,8 +4,6 @@ class InvitesController < ApplicationController ...@@ -4,8 +4,6 @@ class InvitesController < ApplicationController
respond_to :html respond_to :html
layout 'navless'
def show def show
end end
......
class Oauth::ApplicationsController < Doorkeeper::ApplicationsController class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
include PageLayoutHelper
before_action :authenticate_user! before_action :authenticate_user!
layout "profile"
layout 'profile'
def index def index
head :forbidden and return head :forbidden and return
......
class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
before_action :authenticate_resource_owner! before_action :authenticate_resource_owner!
layout "profile"
layout 'profile'
def new def new
if pre_auth.authorizable? if pre_auth.authorizable?
......
class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicationsController class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicationsController
layout "profile" include PageLayoutHelper
layout 'profile'
def destroy def destroy
Doorkeeper::AccessToken.revoke_all_for(params[:id], current_resource_owner) Doorkeeper::AccessToken.revoke_all_for(params[:id], current_resource_owner)
......
class Profiles::AccountsController < ApplicationController class Profiles::AccountsController < Profiles::ApplicationController
layout "profile"
def show def show
@user = current_user @user = current_user
end end
......
class Profiles::ApplicationController < ApplicationController
layout 'profile'
end
class Profiles::AvatarsController < ApplicationController class Profiles::AvatarsController < Profiles::ApplicationController
layout "profile"
def destroy def destroy
@user = current_user @user = current_user
@user.remove_avatar! @user.remove_avatar!
......
class Profiles::EmailsController < ApplicationController class Profiles::EmailsController < Profiles::ApplicationController
layout "profile"
def index def index
@primary = current_user.email @primary = current_user.email
@public_email = current_user.public_email @public_email = current_user.public_email
......
class Profiles::KeysController < ApplicationController class Profiles::KeysController < Profiles::ApplicationController
layout "profile"
skip_before_action :authenticate_user!, only: [:get_keys] skip_before_action :authenticate_user!, only: [:get_keys]
def index def index
......
class Profiles::NotificationsController < ApplicationController class Profiles::NotificationsController < Profiles::ApplicationController
layout 'profile'
def show def show
@user = current_user @user = current_user
@notification = current_user.notification @notification = current_user.notification
......
class Profiles::PasswordsController < ApplicationController class Profiles::PasswordsController < Profiles::ApplicationController
layout :determine_layout
skip_before_action :check_password_expiration, only: [:new, :create] skip_before_action :check_password_expiration, only: [:new, :create]
before_action :set_user before_action :set_user
before_action :set_title
before_action :authorize_change_password! before_action :authorize_change_password!
layout :determine_layout
def new def new
end end
...@@ -66,13 +65,9 @@ class Profiles::PasswordsController < ApplicationController ...@@ -66,13 +65,9 @@ class Profiles::PasswordsController < ApplicationController
@user = current_user @user = current_user
end end
def set_title
@title = "New password"
end
def determine_layout def determine_layout
if [:new, :create].include?(action_name.to_sym) if [:new, :create].include?(action_name.to_sym)
'navless' 'application'
else else
'profile' 'profile'
end end
......
class ProfilesController < ApplicationController class ProfilesController < Profiles::ApplicationController
include ActionView::Helpers::SanitizeHelper include ActionView::Helpers::SanitizeHelper
before_action :user before_action :user
before_action :authorize_change_username!, only: :update_username before_action :authorize_change_username!, only: :update_username
skip_before_action :require_email, only: [:show, :update] skip_before_action :require_email, only: [:show, :update]
layout 'profile'
def show def show
end end
......
class Projects::ApplicationController < ApplicationController class Projects::ApplicationController < ApplicationController
before_action :project before_action :project
before_action :repository before_action :repository
layout :determine_layout layout 'project'
def authenticate_user! def authenticate_user!
# Restrict access to Projects area only # Restrict access to Projects area only
...@@ -17,14 +17,6 @@ class Projects::ApplicationController < ApplicationController ...@@ -17,14 +17,6 @@ class Projects::ApplicationController < ApplicationController
super super
end end
def determine_layout
if current_user
'projects'
else
'public_projects'
end
end
def require_branch_head def require_branch_head
unless @repository.branch_names.include?(@ref) unless @repository.branch_names.include?(@ref)
redirect_to( redirect_to(
......
class Projects::AvatarsController < Projects::ApplicationController class Projects::AvatarsController < Projects::ApplicationController
layout 'project'
before_action :project before_action :project
def show def show
......
...@@ -18,7 +18,6 @@ class Projects::ForksController < Projects::ApplicationController ...@@ -18,7 +18,6 @@ class Projects::ForksController < Projects::ApplicationController
notice: 'Project was successfully forked.' notice: 'Project was successfully forked.'
) )
else else
@title = 'Fork project'
render :error render :error
end end
end end
......
class Projects::UploadsController < Projects::ApplicationController class Projects::UploadsController < Projects::ApplicationController
layout 'project'
skip_before_action :authenticate_user!, :reject_blocked!, :project, skip_before_action :authenticate_user!, :reject_blocked!, :project,
:repository, if: -> { action_name == 'show' && image? } :repository, if: -> { action_name == 'show' && image? }
......
...@@ -6,17 +6,16 @@ class ProjectsController < ApplicationController ...@@ -6,17 +6,16 @@ class ProjectsController < ApplicationController
# Authorize # Authorize
before_action :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer, :archive, :unarchive] 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 before_action :event_filter, only: :show
layout 'navless', only: [:new, :create, :fork] layout :determine_layout
def new def new
@project = Project.new @project = Project.new
end end
def edit def edit
render 'edit', layout: 'project_settings' render 'edit'
end end
def create def create
...@@ -46,7 +45,7 @@ class ProjectsController < ApplicationController ...@@ -46,7 +45,7 @@ class ProjectsController < ApplicationController
end end
format.js format.js
else else
format.html { render 'edit', layout: 'project_settings' } format.html { render 'edit' }
format.js format.js
end end
end end
...@@ -72,13 +71,13 @@ class ProjectsController < ApplicationController ...@@ -72,13 +71,13 @@ class ProjectsController < ApplicationController
format.html do format.html do
if @project.repository_exists? if @project.repository_exists?
if @project.empty_repo? if @project.empty_repo?
render 'projects/empty', layout: user_layout render 'projects/empty'
else else
@last_push = current_user.recent_push(@project.id) if current_user @last_push = current_user.recent_push(@project.id) if current_user
render :show, layout: user_layout render :show
end end
else else
render 'projects/no_repo', layout: user_layout render 'projects/no_repo'
end end
end end
...@@ -160,12 +159,14 @@ class ProjectsController < ApplicationController ...@@ -160,12 +159,14 @@ class ProjectsController < ApplicationController
private private
def set_title def determine_layout
@title = 'New Project' if [:new, :create].include?(action_name.to_sym)
'application'
elsif [:edit, :update].include?(action_name.to_sym)
'project_settings'
else
'project'
end end
def user_layout
current_user ? 'projects' : 'public_projects'
end end
def load_events def load_events
......
class SearchController < ApplicationController class SearchController < ApplicationController
include SearchHelper include SearchHelper
layout 'search'
def show def show
return if params[:search].nil? || params[:search].blank? return if params[:search].nil? || params[:search].blank?
......
...@@ -7,14 +7,11 @@ class SnippetsController < ApplicationController ...@@ -7,14 +7,11 @@ class SnippetsController < ApplicationController
# Allow destroy snippet # Allow destroy snippet
before_action :authorize_admin_snippet!, only: [:destroy] before_action :authorize_admin_snippet!, only: [:destroy]
before_action :set_title
skip_before_action :authenticate_user!, only: [:index, :user_index, :show, :raw] skip_before_action :authenticate_user!, only: [:index, :user_index, :show, :raw]
layout 'snippets'
respond_to :html respond_to :html
layout :determine_layout
def index def index
if params[:username].present? if params[:username].present?
@user = User.find_by(username: params[:username]) @user = User.find_by(username: params[:username])
...@@ -98,16 +95,7 @@ class SnippetsController < ApplicationController ...@@ -98,16 +95,7 @@ class SnippetsController < ApplicationController
return render_404 unless can?(current_user, :admin_personal_snippet, @snippet) return render_404 unless can?(current_user, :admin_personal_snippet, @snippet)
end end
def set_title
@title = 'Snippets'
@title_url = snippets_path
end
def snippet_params def snippet_params
params.require(:personal_snippet).permit(:title, :content, :file_name, :private, :visibility_level) params.require(:personal_snippet).permit(:title, :content, :file_name, :private, :visibility_level)
end end
def determine_layout
current_user ? 'snippets' : 'public_users'
end
end end
class UsersController < ApplicationController class UsersController < ApplicationController
skip_before_action :authenticate_user! skip_before_action :authenticate_user!
before_action :set_user before_action :set_user
layout :determine_layout
def show def show
@contributed_projects = contributed_projects.joined(@user). @contributed_projects = contributed_projects.joined(@user).
...@@ -13,9 +12,6 @@ class UsersController < ApplicationController ...@@ -13,9 +12,6 @@ class UsersController < ApplicationController
# Collect only groups common for both users # Collect only groups common for both users
@groups = @user.groups & GroupsFinder.new.execute(current_user) @groups = @user.groups & GroupsFinder.new.execute(current_user)
@title = @user.name
@title_url = user_path(@user)
respond_to do |format| respond_to do |format|
format.html format.html
...@@ -51,14 +47,6 @@ class UsersController < ApplicationController ...@@ -51,14 +47,6 @@ class UsersController < ApplicationController
render 'calendar_activities', layout: false render 'calendar_activities', layout: false
end end
def determine_layout
if current_user
'navless'
else
'public_users'
end
end
private private
def set_user def set_user
......
...@@ -34,9 +34,7 @@ module GitlabMarkdownHelper ...@@ -34,9 +34,7 @@ module GitlabMarkdownHelper
# see https://github.com/vmg/redcarpet#darling-i-packed-you-a-couple-renderers-for-lunch # 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, { rend = Redcarpet::Render::GitlabHTML.new(self, user_color_scheme_class, {
with_toc_data: true, # Handled further down the line by Gitlab::Markdown::SanitizationFilter
safe_links_only: true,
# Handled further down the line by HTML::Pipeline::SanitizationFilter
escape_html: false escape_html: false
}.merge(options)) }.merge(options))
...@@ -45,7 +43,6 @@ module GitlabMarkdownHelper ...@@ -45,7 +43,6 @@ module GitlabMarkdownHelper
no_intra_emphasis: true, no_intra_emphasis: true,
tables: true, tables: true,
fenced_code_blocks: true, fenced_code_blocks: true,
autolink: true,
strikethrough: true, strikethrough: true,
lax_spacing: true, lax_spacing: true,
space_after_headers: true, space_after_headers: true,
......
...@@ -19,24 +19,6 @@ module GroupsHelper ...@@ -19,24 +19,6 @@ module GroupsHelper
end end
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? def group_settings_page?
if current_controller?('groups') if current_controller?('groups')
current_action?('edit') || current_action?('projects') current_action?('edit') || current_action?('projects')
......
...@@ -43,17 +43,6 @@ module IssuesHelper ...@@ -43,17 +43,6 @@ module IssuesHelper
end end
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) def issue_timestamp(issue)
# Shows the created at time and the updated at time if different # Shows the created at time and the updated at time if different
ts = "#{time_ago_with_tooltip(issue.created_at, 'bottom', 'note_created_ago')}" ts = "#{time_ago_with_tooltip(issue.created_at, 'bottom', 'note_created_ago')}"
...@@ -110,5 +99,5 @@ module IssuesHelper ...@@ -110,5 +99,5 @@ module IssuesHelper
end end
# Required for Gitlab::Markdown::IssueReferenceFilter # Required for Gitlab::Markdown::IssueReferenceFilter
module_function :url_for_issue, :title_for_issue module_function :url_for_issue
end end
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
...@@ -192,46 +192,6 @@ module ProjectsHelper ...@@ -192,46 +192,6 @@ module ProjectsHelper
'unknown' 'unknown'
end 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) def default_url_to_repo(project = nil)
project = project || @project project = project || @project
current_user ? project.url_to_repo : project.http_url_to_repo current_user ? project.url_to_repo : project.http_url_to_repo
......
class DeviseMailer < Devise::Mailer
default from: "GitLab <#{Gitlab.config.gitlab.email_from}>"
default reply_to: Gitlab.config.gitlab.email_reply_to
end
...@@ -15,6 +15,10 @@ class ExternalIssue ...@@ -15,6 +15,10 @@ class ExternalIssue
@issue_identifier.to_s @issue_identifier.to_s
end end
def title
"External Issue #{self}"
end
def ==(other) def ==(other)
other.is_a?(self.class) && (to_s == other.to_s) other.is_a?(self.class) && (to_s == other.to_s)
end end
......
...@@ -329,12 +329,16 @@ class Project < ActiveRecord::Base ...@@ -329,12 +329,16 @@ class Project < ActiveRecord::Base
self.id self.id
end end
def issue_exists?(issue_id) def get_issue(issue_id)
if default_issues_tracker? if default_issues_tracker?
self.issues.where(iid: issue_id).first.present? issues.find_by(iid: issue_id)
else else
true ExternalIssue.new(issue_id, self)
end
end end
def issue_exists?(issue_id)
get_issue(issue_id)
end end
def default_issue_tracker def default_issue_tracker
...@@ -350,11 +354,7 @@ class Project < ActiveRecord::Base ...@@ -350,11 +354,7 @@ class Project < ActiveRecord::Base
end end
def default_issues_tracker? def default_issues_tracker?
if external_issue_tracker !external_issue_tracker
false
else
true
end
end end
def external_issues_trackers def external_issues_trackers
......
- page_title "Settings"
%h3.page-title Application settings %h3.page-title Application settings
%hr %hr
= render 'form' = render 'form'
- page_title "Edit", @application.name, "Applications"
%h3.page-title Edit application %h3.page-title Edit application
- @url = admin_application_path(@application) - @url = admin_application_path(@application)
= render 'form', application: @application = render 'form', application: @application
- page_title "Applications"
%h3.page-title %h3.page-title
System OAuth applications System OAuth applications
%p.light %p.light
......
- page_title "New Application"
%h3.page-title New application %h3.page-title New application
- @url = admin_applications_path - @url = admin_applications_path
= render 'form', application: @application = render 'form', application: @application
- page_title @application.name, "Applications"
%h3.page-title %h3.page-title
Application: #{@application.name} Application: #{@application.name}
......
- page_title "Background Jobs"
%h3.page-title Background Jobs %h3.page-title Background Jobs
%p.light GitLab uses #{link_to "sidekiq", "http://sidekiq.org/"} library for async job processing %p.light GitLab uses #{link_to "sidekiq", "http://sidekiq.org/"} library for async job processing
......
- page_title "Broadcast Messages"
%h3.page-title %h3.page-title
Broadcast Messages Broadcast Messages
%p.light %p.light
......
- page_title "Deploy Keys"
.panel.panel-default .panel.panel-default
.panel-heading .panel-heading
Public deploy keys (#{@deploy_keys.count}) Public deploy keys (#{@deploy_keys.count})
......
- page_title "New Deploy Key"
%h3.page-title New public deploy key %h3.page-title New public deploy key
%hr %hr
......
- page_title @deploy_key.title, "Deploy Keys"
.row .row
.col-md-4 .col-md-4
.panel.panel-default .panel.panel-default
......
- page_title "Edit", @group.name, "Groups"
%h3.page-title Edit group: #{@group.name} %h3.page-title Edit group: #{@group.name}
%hr %hr
= render 'form' = render 'form'
- page_title "Groups"
%h3.page-title %h3.page-title
Groups (#{@groups.total_count}) Groups (#{@groups.total_count})
= link_to 'New Group', new_admin_group_path, class: "btn btn-new pull-right" = link_to 'New Group', new_admin_group_path, class: "btn btn-new pull-right"
......
- page_title "New Group"
%h3.page-title New group %h3.page-title New group
%hr %hr
= render 'form' = render 'form'
- page_title @group.name, "Groups"
%h3.page-title %h3.page-title
Group: #{@group.name} Group: #{@group.name}
......
- page_title "System Hooks"
%h3.page-title %h3.page-title
System hooks System hooks
......
- page_title @key.title, "Keys"
= render "profiles/keys/key_details", admin: true = render "profiles/keys/key_details", admin: true
- page_title "Logs"
- loggers = [Gitlab::GitLogger, Gitlab::AppLogger, - loggers = [Gitlab::GitLogger, Gitlab::AppLogger,
Gitlab::ProductionLogger, Gitlab::SidekiqLogger] Gitlab::ProductionLogger, Gitlab::SidekiqLogger]
%ul.nav.nav-tabs.log-tabs %ul.nav.nav-tabs.log-tabs
......
- page_title "Projects"
= render 'shared/show_aside' = render 'shared/show_aside'
.row .row
......
- page_title @project.name_with_namespace, "Projects"
%h3.page-title %h3.page-title
Project: #{@project.name_with_namespace} Project: #{@project.name_with_namespace}
= link_to edit_project_path(@project), class: "btn pull-right" do = link_to edit_project_path(@project), class: "btn pull-right" do
......
- page_title @service.title, "Service Templates"
= render 'form' = render 'form'
- page_title "Service Templates"
%h3.page-title Service templates %h3.page-title Service templates
%p.light Service template allows you to set default values for project services %p.light Service template allows you to set default values for project services
......
- page_title "Edit", @user.name, "Users"
%h3.page-title %h3.page-title
Edit user: #{@user.name} Edit user: #{@user.name}
.back-link .back-link
......
- page_title "Users"
= render 'shared/show_aside' = render 'shared/show_aside'
.row .row
......
- page_title "New User"
%h3.page-title %h3.page-title
New user New user
%hr %hr
......
- page_title @user.name, "Users"
%h3.page-title %h3.page-title
User: User:
= @user.name = @user.name
......
- page_title "Groups"
%h3.page-title %h3.page-title
Group Membership Group Membership
- if current_user.can_create_group? - if current_user.can_create_group?
......
- page_title "Issues"
= content_for :meta_tags do = content_for :meta_tags do
- if current_user - if current_user
= auto_discovery_link_tag(:atom, issues_dashboard_url(format: :atom, private_token: current_user.private_token), title: "#{current_user.name} issues") = auto_discovery_link_tag(:atom, issues_dashboard_url(format: :atom, private_token: current_user.private_token), title: "#{current_user.name} issues")
......
- page_title "Merge Requests"
%h3.page-title %h3.page-title
Merge Requests Merge Requests
......
- page_title "Milestones"
%h3.page-title %h3.page-title
Milestones Milestones
%span.pull-right #{@dashboard_milestones.count} milestones %span.pull-right #{@dashboard_milestones.count} milestones
......
- page_title @dashboard_milestone.title, "Milestones"
%h4.page-title %h4.page-title
.issue-box{ class: "issue-box-#{@dashboard_milestone.closed? ? 'closed' : 'open'}" } .issue-box{ class: "issue-box-#{@dashboard_milestone.closed? ? 'closed' : 'open'}" }
- if @dashboard_milestone.closed? - if @dashboard_milestone.closed?
......
- page_title "Starred Projects"
- if @projects.any? - if @projects.any?
= render 'shared/show_aside' = render 'shared/show_aside'
......
- page_title "Sign up"
= render 'devise/shared/signup_box' = render 'devise/shared/signup_box'
= render 'devise/shared/sign_in_link' = render 'devise/shared/sign_in_link'
- page_title "Sign in"
%div %div
- if signin_enabled? || ldap_enabled? - if signin_enabled? || ldap_enabled?
= render 'devise/shared/signin_box' = render 'devise/shared/signin_box'
......
- page_title "Edit", @application.name, "Applications"
%h3.page-title Edit application %h3.page-title Edit application
= render 'form', application: @application = render 'form', application: @application
- page_title "Applications"
%h3.page-title Your applications %h3.page-title Your applications
%p= link_to 'New Application', new_oauth_application_path, class: 'btn btn-success' %p= link_to 'New Application', new_oauth_application_path, class: 'btn btn-success'
%table.table.table-striped %table.table.table-striped
......
- page_title @application.name, "Applications"
%h3.page-title %h3.page-title
Application: #{@application.name} Application: #{@application.name}
......
- page_title "Access Denied"
%h1 403 %h1 403
%h3 Access Denied %h3 Access Denied
%hr %hr
......
- page_title "Encoding Error"
%h1 500 %h1 500
%h3 Encoding Error %h3 Encoding Error
%hr %hr
......
- page_title "Git Resource Not Found"
%h1 404 %h1 404
%h3 Git Resource Not found %h3 Git Resource Not found
%hr %hr
......
- page_title "Not Found"
%h1 404 %h1 404
%h3 The resource you were looking for doesn't exist. %h3 The resource you were looking for doesn't exist.
%hr %hr
......
- page_title "Auth Error"
%h1 422 %h1 422
%h3 Sign-in using #{@provider} auth failed %h3 Sign-in using #{@provider} auth failed
%hr %hr
......
- page_title "Groups"
.clearfix .clearfix
.pull-left .pull-left
= form_tag explore_groups_path, method: :get, class: 'form-inline form-tiny' do |f| = form_tag explore_groups_path, method: :get, class: 'form-inline form-tiny' do |f|
......
- page_title "Projects"
.clearfix .clearfix
= render 'filter' = render 'filter'
......
- page_title "Starred Projects"
.explore-trending-block .explore-trending-block
%p.lead %p.lead
%i.fa.fa-star %i.fa.fa-star
......
- page_title "Trending Projects"
.explore-title .explore-title
%h3 %h3
Explore GitLab Explore GitLab
......
%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
- page_title "Settings"
.panel.panel-default .panel.panel-default
.panel-heading .panel-heading
%strong= @group.name %strong= @group.name
......
- page_title "Members"
- show_roles = should_user_see_group_roles?(current_user, @group) - show_roles = should_user_see_group_roles?(current_user, @group)
%h3.page-title %h3.page-title
......
- page_title "Issues"
= content_for :meta_tags do = content_for :meta_tags do
- if current_user - if current_user
= auto_discovery_link_tag(:atom, issues_group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} issues") = auto_discovery_link_tag(:atom, issues_group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} issues")
......
- page_title "Merge Requests"
%h3.page-title %h3.page-title
Merge Requests Merge Requests
......
- page_title "Milestones"
%h3.page-title %h3.page-title
Milestones Milestones
%span.pull-right #{@group_milestones.count} milestones %span.pull-right #{@group_milestones.count} milestones
......
- page_title @group_milestone.title, "Milestones"
%h4.page-title %h4.page-title
.issue-box{ class: "issue-box-#{@group_milestone.closed? ? 'closed' : 'open'}" } .issue-box{ class: "issue-box-#{@group_milestone.closed? ? 'closed' : 'open'}" }
- if @group_milestone.closed? - if @group_milestone.closed?
......
- page_title 'New Group'
- header_title 'New Group'
= form_for @group, html: { class: 'group-form form-horizontal' } do |f| = form_for @group, html: { class: 'group-form form-horizontal' } do |f|
- if @group.errors.any? - if @group.errors.any?
.alert.alert-danger .alert.alert-danger
......
- page_title "Projects"
.panel.panel-default .panel.panel-default
.panel-heading .panel-heading
%strong= @group.name %strong= @group.name
......
- page_title @file.humanize, *@category.split("/").reverse.map(&:humanize)
.documentation.wiki .documentation.wiki
= markdown @markdown.gsub('$your_email', current_user.email) = markdown @markdown.gsub('$your_email', current_user.email)
- 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." - 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 .gitlab-ui-dev-kit
......
- page_title "Bitbucket import"
%h3.page-title %h3.page-title
%i.fa.fa-bitbucket %i.fa.fa-bitbucket
Import projects from Bitbucket Import projects from Bitbucket
......
- page_title "GitHub import"
%h3.page-title %h3.page-title
%i.fa.fa-github %i.fa.fa-github
Import projects from GitHub Import projects from GitHub
......
- page_title "GitLab.com import"
%h3.page-title %h3.page-title
%i.fa.fa-heart %i.fa.fa-heart
Import projects from GitLab.com Import projects from GitLab.com
......
- page_title "Gitorious import"
%h3.page-title %h3.page-title
%i.icon-gitorious.icon-gitorious-big %i.icon-gitorious.icon-gitorious-big
Import projects from Gitorious.org Import projects from Gitorious.org
......
- page_title "Google Code import"
%h3.page-title %h3.page-title
%i.fa.fa-google %i.fa.fa-google
Import projects from Google Code Import projects from Google Code
......
- page_title "User map", "Google Code import"
%h3.page-title %h3.page-title
%i.fa.fa-google %i.fa.fa-google
Import projects from Google Code Import projects from Google Code
......
- page_title "Google Code import"
%h3.page-title %h3.page-title
%i.fa.fa-google %i.fa.fa-google
Import projects from Google Code Import projects from Google Code
%p.light - if @repos.any?
%p.light
Select projects you want to import. Select projects you want to import.
%p.light %p.light
Optionally, you can Optionally, you can
= link_to "customize", new_user_map_import_google_code_path = link_to "customize", new_user_map_import_google_code_path
how Google Code email addresses and usernames are imported into GitLab. how Google Code email addresses and usernames are imported into GitLab.
%hr %hr
%p %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" = button_tag 'Import all projects', class: "btn btn-success js-import-all"
%table.table.import-jobs %table.table.import-jobs
...@@ -44,6 +49,22 @@ ...@@ -44,6 +49,22 @@
= "#{current_user.username}/#{repo.name}" = "#{current_user.username}/#{repo.name}"
%td.import-actions.job-status %td.import-actions.job-status
= button_tag "Import", class: "btn js-add-to-import" = 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 :coffeescript
new ImporterStatus("#{jobs_import_google_code_path}", "#{import_google_code_path}") new ImporterStatus("#{jobs_import_google_code_path}", "#{import_google_code_path}")
- page_title "Invitation"
%h3.page-title Invitation %h3.page-title Invitation
%p %p
......
- page_title "GitLab"
%head %head
%meta{charset: "utf-8"} %meta{charset: "utf-8"}
%meta{'http-equiv' => 'X-UA-Compatible', content: 'IE=edge'} %meta{'http-equiv' => 'X-UA-Compatible', content: 'IE=edge'}
%meta{content: "GitLab Community Edition", name: "description"} %meta{content: "GitLab Community Edition", name: "description"}
%title %title= page_title
= "#{title} | " if defined?(title)
GitLab
= favicon_link_tag 'favicon.ico' = favicon_link_tag 'favicon.ico'
= stylesheet_link_tag "application", :media => "all" = stylesheet_link_tag "application", :media => "all"
......
.page-with-sidebar{ class: nav_sidebar_class } .page-with-sidebar{ class: nav_sidebar_class }
= render "layouts/broadcast" = render "layouts/broadcast"
.sidebar-wrapper .sidebar-wrapper
- if defined?(sidebar) - if defined?(sidebar) && sidebar
= render(sidebar) = render "layouts/nav/#{sidebar}"
- elsif current_user - elsif current_user
= render 'layouts/nav/dashboard' = render 'layouts/nav/dashboard'
.collapse-nav .collapse-nav
......
!!! 5 - page_title "Admin area"
%html{ lang: "en"} - header_title "Admin area", admin_root_path
= render "layouts/head", title: "Admin area" - sidebar "admin"
%body{class: "#{app_theme} admin", :'data-page' => body_data_page}
= render "layouts/head_panel", title: link_to("Admin area", admin_root_path) = render template: "layouts/application"
= render 'layouts/page', sidebar: 'layouts/nav/admin'
!!! 5 !!! 5
%html{ lang: "en"} %html{ lang: "en"}
= render "layouts/head", title: "Dashboard" = render "layouts/head"
%body{class: "#{app_theme} application", :'data-page' => body_data_page } %body{class: "#{app_theme}", :'data-page' => body_data_page}
= render "layouts/head_panel", title: link_to("Dashboard", root_path) - if current_user
= render 'layouts/page', sidebar: 'layouts/nav/dashboard' = render "layouts/head_panel", title: header_title
- else
= render "layouts/public_head_panel", title: header_title
= render 'layouts/page', sidebar: sidebar
- page_title "Dashboard"
- header_title "Dashboard", root_path
- sidebar "dashboard"
= render template: "layouts/application"
!!! 5 !!! 5
%html{ lang: "en"} %html{ lang: "en"}
= render "layouts/head", title: "Error" = render "layouts/head"
%body{class: "#{app_theme} application"} %body{class: "#{app_theme} application"}
= render "layouts/head_panel", title: "" if current_user = render "layouts/head_panel", title: "" if current_user
.container.navless-container .container.navless-container
......
- page_title = 'Explore GitLab' - page_title "Explore"
!!! 5 - header_title "Explore GitLab", explore_root_path
%html{ lang: "en"} - sidebar "explore"
= 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)
= render 'layouts/page', sidebar: 'layouts/nav/explore' = render template: "layouts/application"
!!! 5 - page_title @group.name
%html{ lang: "en"} - header_title @group.name, group_path(@group)
= render "layouts/head", title: group_head_title - sidebar "group"
%body{class: "#{app_theme} application", :'data-page' => body_data_page}
= render "layouts/head_panel", title: link_to(@group.name, group_path(@group)) = render template: "layouts/application"
= render 'layouts/page', sidebar: 'layouts/nav/group'
- page_title "Help"
- header_title "Help", help_path
= render template: "layouts/application"
...@@ -39,4 +39,15 @@ ...@@ -39,4 +39,15 @@
= icon ('angle-down fw') = icon ('angle-down fw')
- if group_settings_page? - 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
%ul.project-navigation.nav.nav-sidebar %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')
%span
Back to project
%li.separate-item
= render 'projects/settings_nav'
- else
= nav_link(path: 'projects#show', html_options: {class: 'home'}) do = nav_link(path: 'projects#show', html_options: {class: 'home'}) do
= link_to project_path(@project), title: 'Project', class: 'shortcuts-project', data: {placement: 'right'} do = link_to project_path(@project), title: 'Project', class: 'shortcuts-project', data: {placement: 'right'} do
= icon('dashboard fw') = icon('dashboard fw')
......
%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
%ul.nav.nav-sidebar %ul.nav.nav-sidebar
- if current_user
= nav_link(path: user_snippets_path(current_user), html_options: {class: 'home'}) do = 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 = link_to user_snippets_path(current_user), title: 'Your snippets', data: {placement: 'right'} do
= icon('dashboard fw') = icon('dashboard fw')
......
!!! 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'
!!! 5 - page_title "Profile"
%html{ lang: "en"} - header_title "Profile", profile_path
= render "layouts/head", title: "Profile" - sidebar "profile"
%body{class: "#{app_theme} profile", :'data-page' => body_data_page}
= render "layouts/head_panel", title: link_to("Profile", profile_path) = render template: "layouts/application"
= render 'layouts/page', sidebar: 'layouts/nav/profile'
- 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"
!!! 5 - page_title "Settings"
%html{ lang: "en"} - sidebar "project_settings"
= render "layouts/head", title: @project.name_with_namespace
%body{class: "#{app_theme} project", :'data-page' => body_data_page, :'data-project-id' => @project.id } = render template: "layouts/project"
= render "layouts/head_panel", title: project_title(@project)
= render "layouts/init_auto_complete"
- @project_settings_nav = true
= render 'layouts/page', sidebar: 'layouts/nav/project'
!!! 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'
!!! 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'
!!! 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'
!!! 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'
!!! 5 - page_title "Search"
%html{ lang: "en"} - header_title "Search", search_path
= render "layouts/head", title: "Search"
%body{class: "#{app_theme} application", :'data-page' => body_data_page} = render template: "layouts/application"
= render "layouts/head_panel", title: link_to("Search", search_path)
= render 'layouts/page'
!!! 5 - page_title 'Snippets'
%html{ lang: "en"} - header_title 'Snippets', snippets_path
= render "layouts/head", title: "Dashboard" - sidebar "snippets"
%body{class: "#{app_theme} application", :'data-page' => body_data_page }
= render "layouts/head_panel", title: link_to("Snippets", snippets_path) = render template: "layouts/application"
= render 'layouts/page', sidebar: 'layouts/nav/snippets'
- page_title "Account"
- if current_user.ldap_user? - if current_user.ldap_user?
.alert.alert-info .alert.alert-info
Some options are unavailable for LDAP accounts Some options are unavailable for LDAP accounts
......
- page_title "Applications"
%h3.page-title %h3.page-title
Application Settings Application Settings
%p.light %p.light
......
- page_title "Design"
%h3.page-title %h3.page-title
Design Settings Design Settings
%p.light %p.light
......
- page_title "Emails"
%h3.page-title %h3.page-title
Email Settings Email Settings
%p.light %p.light
......
- page_title "History"
%h3.page-title %h3.page-title
Your Account History Your Account History
%p.light %p.light
......
- page_title "SSH Keys"
%h3.page-title %h3.page-title
SSH Keys Settings SSH Keys Settings
.pull-right .pull-right
......
- page_title "Add SSH Keys"
%h3.page-title Add an SSH Key %h3.page-title Add an SSH Key
%p.light %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")}. 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")}.
......
- page_title @key.title, "SSH Keys"
= render "key_details" = render "key_details"
- page_title "Notifications"
%h3.page-title %h3.page-title
Notifications Settings Notifications Settings
%p.light %p.light
......
- page_title "Password"
%h3.page-title Password Settings %h3.page-title Password Settings
%p.light %p.light
- if @user.password_automatically_set? - if @user.password_automatically_set?
......
- page_title "New Password"
- header_title "New Password"
%h3.page-title Setup new password %h3.page-title Setup new password
%hr %hr
= form_for @user, url: profile_password_path, method: :post, html: { class: 'form-horizontal '} do |f| = form_for @user, url: profile_password_path, method: :post, html: { class: 'form-horizontal '} do |f|
......
- page_title "Settings"
%h3.page-title %h3.page-title
Profile Settings Profile Settings
%p.light %p.light
......
%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
- page_title "Blame", @blob.path, @ref
%h3.page-title Blame view %h3.page-title Blame view
#tree-holder.tree-holder #tree-holder.tree-holder
......
- page_title "Edit", @blob.path, @ref
.file-editor .file-editor
%ul.nav.nav-tabs.js-edit-mode %ul.nav.nav-tabs.js-edit-mode
%li.active %li.active
......
- page_title "New File", @ref
%h3.page-title New file %h3.page-title New file
.file-editor .file-editor
= form_tag(namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post, class: 'form-horizontal form-new-file') do = form_tag(namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post, class: 'form-horizontal form-new-file') do
......
- page_title @blob.path, @ref
%div.tree-ref-holder %div.tree-ref-holder
= render 'shared/ref_switcher', destination: 'blob', path: @path = render 'shared/ref_switcher', destination: 'blob', path: @path
......
- page_title "Branches"
= render "projects/commits/head" = render "projects/commits/head"
%h3.page-title %h3.page-title
Branches Branches
......
- page_title "New Branch"
- if @error - if @error
.alert.alert-danger .alert.alert-danger
%button{ type: "button", class: "close", "data-dismiss" => "alert"} &times; %button{ type: "button", class: "close", "data-dismiss" => "alert"} &times;
......
- page_title "#{@commit.title} (#{@commit.short_id})", "Commits"
= render "commit_box" = render "commit_box"
= render "projects/diffs/diffs", diffs: @diffs, project: @project = render "projects/diffs/diffs", diffs: @diffs, project: @project
= render "projects/notes/notes_with_form" = render "projects/notes/notes_with_form"
- page_title "Commits", @ref
= content_for :meta_tags do = content_for :meta_tags do
- if current_user - 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") = 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")
......
- page_title "Compare"
= render "projects/commits/head" = render "projects/commits/head"
%h3.page-title %h3.page-title
......
- page_title "#{params[:from]}...#{params[:to]}"
= render "projects/commits/head" = render "projects/commits/head"
%h3.page-title %h3.page-title
......
- page_title "Deploy Keys"
%h3.page-title %h3.page-title
Deploy keys allow read-only access to the repository Deploy keys allow read-only access to the repository
......
- page_title "New Deploy Key"
%h3.page-title New Deploy key %h3.page-title New Deploy key
%hr %hr
......
- page_title @key.title, "Deploy Keys"
%h3.page-title %h3.page-title
Deploy key: Deploy key:
= @key.title = @key.title
......
- page_title "Fork project"
- if @forked_project && !@forked_project.saved? - if @forked_project && !@forked_project.saved?
.alert.alert-danger.alert-block .alert.alert-danger.alert-block
%h4 %h4
......
- page_title "Fork project"
%h3.page-title Fork project %h3.page-title Fork project
%p.lead %p.lead
Click to fork the project to a user or group Click to fork the project to a user or group
......
- page_title "Commit statistics"
= render 'head' = render 'head'
%p.lead %p.lead
......
- page_title "Contributor statistics"
= render 'head' = render 'head'
.loading-graph .loading-graph
.center .center
......
- page_title "Web Hooks"
%h3.page-title %h3.page-title
Web hooks Web hooks
......
- page_title "Import repository"
%h3.page-title %h3.page-title
- if @project.import_failed? - if @project.import_failed?
Import failed. Retry? Import failed. Retry?
......
- page_title "Import in progress"
.save-project-loader .save-project-loader
.center .center
%h2 %h2
......
- page_title "Edit", "#{@issue.title} (##{@issue.iid})", "Issues"
= render "form" = render "form"
- page_title "Issues"
= content_for :meta_tags do = content_for :meta_tags do
- if current_user - 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") = auto_discovery_link_tag(:atom, namespace_project_issues_url(@project.namespace, @project, :atom, private_token: current_user.private_token), title: "#{@project.name} issues")
......
- page_title "New Issue"
= render "form" = render "form"
- page_title "#{@issue.title} (##{@issue.iid})", "Issues"
.issue .issue
.issue-details .issue-details
%h4.page-title %h4.page-title
......
- page_title "Edit", @label.name, "Labels"
%h3 %h3
Edit label Edit label
%span.light #{@label.name} %span.light #{@label.name}
......
- page_title "Labels"
- if can? current_user, :admin_label, @project - if can? current_user, :admin_label, @project
= link_to new_namespace_project_label_path(@project.namespace, @project), class: "pull-right btn btn-new" do = link_to new_namespace_project_label_path(@project.namespace, @project), class: "pull-right btn btn-new" do
New label New label
......
- page_title "New Label"
%h3 New label %h3 New label
.back-link .back-link
= link_to namespace_project_labels_path(@project.namespace, @project) do = link_to namespace_project_labels_path(@project.namespace, @project) do
......
- page_title "#{@merge_request.title} (##{@merge_request.iid})", "Merge Requests"
.merge-request{'data-url' => merge_request_path(@merge_request)} .merge-request{'data-url' => merge_request_path(@merge_request)}
.merge-request-details .merge-request-details
= render "projects/merge_requests/show/mr_title" = render "projects/merge_requests/show/mr_title"
......
- page_title "Edit", "#{@merge_request.title} (##{@merge_request.iid})", "Merge Requests"
%h3.page-title %h3.page-title
= "Edit merge request ##{@merge_request.iid}" = "Edit merge request ##{@merge_request.iid}"
%hr %hr
......
- page_title "Merge Requests"
.append-bottom-10 .append-bottom-10
.pull-right .pull-right
= render 'shared/issuable_search_form', path: namespace_project_merge_requests_path(@project.namespace, @project) = render 'shared/issuable_search_form', path: namespace_project_merge_requests_path(@project.namespace, @project)
......
- page_title "#{@merge_request.title} (##{@merge_request.iid})", "Merge Requests"
.merge-request .merge-request
= render "projects/merge_requests/show/mr_title" = render "projects/merge_requests/show/mr_title"
= render "projects/merge_requests/show/mr_box" = render "projects/merge_requests/show/mr_box"
......
- page_title "New Merge Request"
- if @merge_request.can_be_created - if @merge_request.can_be_created
= render 'new_submit' = render 'new_submit'
- else - else
......
- page_title "Edit", @milestone.title, "Milestones"
= render "form" = render "form"
- page_title "Milestones"
.pull-right .pull-right
- if can? current_user, :admin_milestone, @project - 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 = link_to new_namespace_project_milestone_path(@project.namespace, @project), class: "pull-right btn btn-new", title: "New Milestone" do
......
- page_title "New Milestone"
= render "form" = render "form"
- page_title @milestone.title, "Milestones"
%h4.page-title %h4.page-title
.issue-box{ class: issue_box_class(@milestone) } .issue-box{ class: issue_box_class(@milestone) }
- if @milestone.closed? - if @milestone.closed?
......
- page_title "Network", @ref
= render "head" = render "head"
.project-network .project-network
.controls .controls
......
- page_title 'New Project'
- header_title 'New Project'
.project-edit-container .project-edit-container
.project-edit-errors .project-edit-errors
= render 'projects/errors' = render 'projects/errors'
......
- page_title "Import members"
%h3.page-title %h3.page-title
Import members from another project Import members from another project
%p.light %p.light
......
- page_title "Members"
%h3.page-title %h3.page-title
Users with access to this project Users with access to this project
......
- page_title "Protected branches"
%h3.page-title Protected branches %h3.page-title Protected branches
%p.light Keep stable branches secure and force developers to use Merge Requests %p.light Keep stable branches secure and force developers to use Merge Requests
%hr %hr
......
- page_title @service.title, "Services"
= render 'form' = render 'form'
- page_title "Services"
%h3.page-title Project services %h3.page-title Project services
%p.light Project services allow you to integrate GitLab with other applications %p.light Project services allow you to integrate GitLab with other applications
......
- page_title "Edit", @snippet.title, "Snippets"
%h3.page-title %h3.page-title
Edit snippet Edit snippet
%hr %hr
......
- page_title "Snippets"
%h3.page-title %h3.page-title
Snippets Snippets
- if can? current_user, :write_project_snippet, @project - if can? current_user, :write_project_snippet, @project
......
- page_title "New Snippets"
%h3.page-title %h3.page-title
New snippet New snippet
%hr %hr
......
- page_title @snippet.title, "Snippets"
%h3.page-title %h3.page-title
= @snippet.title = @snippet.title
......
- page_title "Tags"
= render "projects/commits/head" = render "projects/commits/head"
%h3.page-title %h3.page-title
......
- page_title "New Tag"
- if @error - if @error
.alert.alert-danger .alert.alert-danger
%button{ type: "button", class: "close", "data-dismiss" => "alert"} &times; %button{ type: "button", class: "close", "data-dismiss" => "alert"} &times;
......
- page_title @path.presence || "Files", @ref
= content_for :meta_tags do = content_for :meta_tags do
- if current_user - 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") = 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")
......
- page_title "Edit", @page.title, "Wiki"
= render 'nav' = render 'nav'
.pull-right .pull-right
= render 'main_links' = render 'main_links'
......
- page_title "Wiki"
%h3.page-title Empty page %h3.page-title Empty page
%hr %hr
.error_message .error_message
......
- page_title "Git Access", "Wiki"
= render 'nav' = render 'nav'
.row .row
.col-sm-6 .col-sm-6
......
- page_title "History", @page.title, "Wiki"
= render 'nav' = render 'nav'
%h3.page-title %h3.page-title
%span.light History for %span.light History for
......
- page_title "All Pages", "Wiki"
= render 'nav' = render 'nav'
%h3.page-title %h3.page-title
All Pages All Pages
......
- page_title @page.title, "Wiki"
= render 'nav' = render 'nav'
%h3.page-title %h3.page-title
= @page.title = @page.title
......
- page_title @search_term
= render 'search/form' = render 'search/form'
%hr %hr
- if @search_term - if @search_term
......
- page_title "Your Snippets"
%h3.page-title %h3.page-title
Your Snippets Your Snippets
.pull-right .pull-right
......
- page_title "Edit", @snippet.title, "Snippets"
%h3.page-title %h3.page-title
Edit snippet Edit snippet
%hr %hr
......
- page_title "Public Snippets"
%h3.page-title %h3.page-title
Public snippets Public snippets
......
- page_title "New Snippet"
%h3.page-title %h3.page-title
New snippet New snippet
%hr %hr
......
- page_title @snippet.title, "Snippets"
%h3.page-title %h3.page-title
= @snippet.title = @snippet.title
......
- page_title "Snippets", @user.name
%h3.page-title %h3.page-title
= image_tag avatar_icon(@user.email), class: "avatar s24" = image_tag avatar_icon(@user.email), class: "avatar s24"
= @user.name = @user.name
......
- page_title @user.name
- header_title @user.name, user_path(@user)
= content_for :meta_tags do = content_for :meta_tags do
= auto_discovery_link_tag(:atom, user_url(@user, format: :atom), title: "#{@user.name} activity") = auto_discovery_link_tag(:atom, user_url(@user, format: :atom), title: "#{@user.name} activity")
......
...@@ -2,13 +2,8 @@ ...@@ -2,13 +2,8 @@
# four configuration values can also be set straight in your models. # four configuration values can also be set straight in your models.
Devise.setup do |config| Devise.setup do |config|
# ==> Mailer Configuration # ==> 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. # Configure the class responsible to send e-mails.
# config.mailer = "Devise::Mailer" config.mailer = "DeviseMailer"
# ==> ORM configuration # ==> ORM configuration
# Load and configure the ORM. Supports :active_record (default) and # Load and configure the ORM. Supports :active_record (default) and
......
# 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
# 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
# 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
# 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
...@@ -433,13 +433,16 @@ ActiveRecord::Schema.define(version: 20150429002313) do ...@@ -433,13 +433,16 @@ ActiveRecord::Schema.define(version: 20150429002313) do
t.datetime "created_at" t.datetime "created_at"
end 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 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| create_table "tags", force: true do |t|
t.string "name" t.string "name"
t.integer "taggings_count", default: 0
end end
add_index "tags", ["name"], name: "index_tags_on_name", unique: true, using: :btree
create_table "users", force: true do |t| create_table "users", force: true do |t|
t.string "email", default: "", null: false t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false t.string "encrypted_password", default: "", null: false
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
- [Session](session.md) - [Session](session.md)
- [Projects](projects.md) - [Projects](projects.md)
- [Project Snippets](project_snippets.md) - [Project Snippets](project_snippets.md)
- [Services](services.md)
- [Repositories](repositories.md) - [Repositories](repositories.md)
- [Repository Files](repository_files.md) - [Repository Files](repository_files.md)
- [Commits](commits.md) - [Commits](commits.md)
......
...@@ -27,10 +27,10 @@ If you have local changes to your GitLab repository the script will stash them a ...@@ -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) 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 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 # 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 ## 3. Start application
...@@ -65,7 +65,7 @@ Here is a one line command with step 1 to 5 for the next time you upgrade: ...@@ -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; \ cd /home/git/gitlab; \
sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production; \ sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production; \
sudo service gitlab stop; \ 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; \ cd /home/git/gitlab-shell; \
sudo -u git -H git fetch; \ sudo -u git -H git fetch; \
sudo -u git -H git checkout v`cat /home/git/gitlab/GITLAB_SHELL_VERSION`; \ sudo -u git -H git checkout v`cat /home/git/gitlab/GITLAB_SHELL_VERSION`; \
......
...@@ -55,3 +55,10 @@ Feature: Project ...@@ -55,3 +55,10 @@ Feature: Project
Then I should see project "Forum" README Then I should see project "Forum" README
And I visit project "Shop" page And I visit project "Shop" page
Then I should see project "Shop" README 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
...@@ -94,4 +94,12 @@ class Spinach::Features::Project < Spinach::FeatureSteps ...@@ -94,4 +94,12 @@ class Spinach::Features::Project < Spinach::FeatureSteps
page.should have_link 'README.md' page.should have_link 'README.md'
page.should have_content 'testme' page.should have_content 'testme'
end 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 end
...@@ -2,8 +2,12 @@ module SharedMarkdown ...@@ -2,8 +2,12 @@ module SharedMarkdown
include Spinach::DSL include Spinach::DSL
def header_should_have_correct_id_and_link(level, text, id, parent = ".wiki") def header_should_have_correct_id_and_link(level, text, id, parent = ".wiki")
find(:css, "#{parent} h#{level}##{id}").text.should == text node = find("#{parent} h#{level} a##{id}")
find(:css, "#{parent} h#{level}##{id} > :last-child")[:href].should =~ /##{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 end
def create_taskable(type, title) def create_taskable(type, title)
......
...@@ -21,6 +21,10 @@ module Gitlab ...@@ -21,6 +21,10 @@ module Gitlab
@repos ||= raw_data["projects"].map { |raw_repo| GoogleCodeImport::Repository.new(raw_repo) }.select(&:git?) @repos ||= raw_data["projects"].map { |raw_repo| GoogleCodeImport::Repository.new(raw_repo) }.select(&:git?)
end end
def incompatible_repos
@incompatible_repos ||= raw_data["projects"].map { |raw_repo| GoogleCodeImport::Repository.new(raw_repo) }.reject(&:git?)
end
def repo(id) def repo(id)
repos.find { |repo| repo.id == id } repos.find { |repo| repo.id == id }
end end
......
...@@ -3,33 +3,10 @@ require 'html/pipeline' ...@@ -3,33 +3,10 @@ require 'html/pipeline'
module Gitlab module Gitlab
# Custom parser for GitLab-flavored Markdown # Custom parser for GitLab-flavored Markdown
# #
# It replaces references in the text with links to the appropriate items in # See the files in `lib/gitlab/markdown/` for specific processing information.
# 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:\" />
module Markdown module Markdown
# Provide autoload paths for filters to prevent a circular dependency error # 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 :CommitRangeReferenceFilter, 'gitlab/markdown/commit_range_reference_filter'
autoload :CommitReferenceFilter, 'gitlab/markdown/commit_reference_filter' autoload :CommitReferenceFilter, 'gitlab/markdown/commit_reference_filter'
autoload :EmojiFilter, 'gitlab/markdown/emoji_filter' autoload :EmojiFilter, 'gitlab/markdown/emoji_filter'
...@@ -37,7 +14,9 @@ module Gitlab ...@@ -37,7 +14,9 @@ module Gitlab
autoload :IssueReferenceFilter, 'gitlab/markdown/issue_reference_filter' autoload :IssueReferenceFilter, 'gitlab/markdown/issue_reference_filter'
autoload :LabelReferenceFilter, 'gitlab/markdown/label_reference_filter' autoload :LabelReferenceFilter, 'gitlab/markdown/label_reference_filter'
autoload :MergeRequestReferenceFilter, 'gitlab/markdown/merge_request_reference_filter' autoload :MergeRequestReferenceFilter, 'gitlab/markdown/merge_request_reference_filter'
autoload :SanitizationFilter, 'gitlab/markdown/sanitization_filter'
autoload :SnippetReferenceFilter, 'gitlab/markdown/snippet_reference_filter' autoload :SnippetReferenceFilter, 'gitlab/markdown/snippet_reference_filter'
autoload :TableOfContentsFilter, 'gitlab/markdown/table_of_contents_filter'
autoload :UserReferenceFilter, 'gitlab/markdown/user_reference_filter' autoload :UserReferenceFilter, 'gitlab/markdown/user_reference_filter'
# Public: Parse the provided text with GitLab-Flavored Markdown # Public: Parse the provided text with GitLab-Flavored Markdown
...@@ -74,13 +53,13 @@ module Gitlab ...@@ -74,13 +53,13 @@ module Gitlab
pipeline = HTML::Pipeline.new(filters) pipeline = HTML::Pipeline.new(filters)
context = { context = {
# SanitizationFilter
whitelist: sanitization_whitelist,
# EmojiFilter # EmojiFilter
asset_root: Gitlab.config.gitlab.url, asset_root: Gitlab.config.gitlab.url,
asset_host: Gitlab::Application.config.asset_host, asset_host: Gitlab::Application.config.asset_host,
# TableOfContentsFilter
no_header_anchors: options[:no_header_anchors],
# ReferenceFilter # ReferenceFilter
current_user: current_user, current_user: current_user,
only_path: options[:reference_only_path], only_path: options[:reference_only_path],
...@@ -111,12 +90,14 @@ module Gitlab ...@@ -111,12 +90,14 @@ module Gitlab
# SanitizationFilter should come first so that all generated reference HTML # SanitizationFilter should come first so that all generated reference HTML
# goes through untouched. # 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 def filters
[ [
HTML::Pipeline::SanitizationFilter, Gitlab::Markdown::SanitizationFilter,
Gitlab::Markdown::EmojiFilter, Gitlab::Markdown::EmojiFilter,
Gitlab::Markdown::TableOfContentsFilter,
Gitlab::Markdown::AutolinkFilter,
Gitlab::Markdown::UserReferenceFilter, Gitlab::Markdown::UserReferenceFilter,
Gitlab::Markdown::IssueReferenceFilter, Gitlab::Markdown::IssueReferenceFilter,
...@@ -125,36 +106,10 @@ module Gitlab ...@@ -125,36 +106,10 @@ module Gitlab
Gitlab::Markdown::SnippetReferenceFilter, Gitlab::Markdown::SnippetReferenceFilter,
Gitlab::Markdown::CommitRangeReferenceFilter, Gitlab::Markdown::CommitRangeReferenceFilter,
Gitlab::Markdown::CommitReferenceFilter, Gitlab::Markdown::CommitReferenceFilter,
Gitlab::Markdown::LabelReferenceFilter, Gitlab::Markdown::LabelReferenceFilter
] ]
end 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. # Turn list items that start with "[ ]" into HTML checkbox inputs.
def parse_tasks(text) def parse_tasks(text)
li_tag = '<li class="task-list-item">' li_tag = '<li class="task-list-item">'
......
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
...@@ -44,21 +44,20 @@ module Gitlab ...@@ -44,21 +44,20 @@ module Gitlab
# Returns a String with `#123` references replaced with links. All links # Returns a String with `#123` references replaced with links. All links
# have `gfm` and `gfm-issue` class names attached for styling. # have `gfm` and `gfm-issue` class names attached for styling.
def issue_link_filter(text) 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) project = self.project_from_ref(project_ref)
if project && project.issue_exists?(issue) if project && issue = project.get_issue(id)
# FIXME (rspeicher): Law of Demeter push_result(:issue, issue)
push_result(:issue, project.issues.where(iid: issue).first)
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) klass = reference_class(:issue)
%(<a href="#{url}" %(<a href="#{url}"
title="#{title}" title="#{title}"
class="#{klass}">#{project_ref}##{issue}</a>) class="#{klass}">#{project_ref}##{id}</a>)
else else
match match
end end
...@@ -68,10 +67,6 @@ module Gitlab ...@@ -68,10 +67,6 @@ module Gitlab
def url_for_issue(*args) def url_for_issue(*args)
IssuesHelper.url_for_issue(*args) IssuesHelper.url_for_issue(*args)
end end
def title_for_issue(*args)
IssuesHelper.title_for_issue(*args)
end
end end
end end
end end
...@@ -64,7 +64,6 @@ module Gitlab ...@@ -64,7 +64,6 @@ module Gitlab
end end
end end
# TODO (rspeicher): Cleanup
def url_for_merge_request(mr, project) def url_for_merge_request(mr, project)
h = Rails.application.routes.url_helpers h = Rails.application.routes.url_helpers
h.namespace_project_merge_request_url(project.namespace, project, mr, h.namespace_project_merge_request_url(project.namespace, project, mr,
......
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
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
...@@ -40,7 +40,7 @@ module Gitlab ...@@ -40,7 +40,7 @@ module Gitlab
end end
# Convenience method to get a space-separated String of all the theme # 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 # Returns a String
def self.body_classes def self.body_classes
......
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 attr_reader :template
alias_method :h, :template alias_method :h, :template
...@@ -8,24 +9,12 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML ...@@ -8,24 +9,12 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
@color_scheme = color_scheme @color_scheme = color_scheme
@project = @template.instance_variable_get("@project") @project = @template.instance_variable_get("@project")
@options = options.dup @options = options.dup
super options
end
def preprocess(full_document) super(options)
# Redcarpet doesn't allow SMB links when `safe_links_only` is enabled.
# FTP links are allowed, so we trick Redcarpet.
full_document.gsub("smb://", "ftp://smb:")
end 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) def normal_text(text)
return text unless text.present? ERB::Util.html_escape_once(text)
text.gsub("'", "&rsquo;")
end end
# Stolen from Rugments::Plugins::Redcarpet as this module is not required # Stolen from Rugments::Plugins::Redcarpet as this module is not required
...@@ -37,7 +26,7 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML ...@@ -37,7 +26,7 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
# so we assume you're not using leading spaces that aren't tabs, # so we assume you're not using leading spaces that aren't tabs,
# and just replace them here. # and just replace them here.
if lexer.tag == 'make' if lexer.tag == 'make'
code.gsub! /^ /, "\t" code.gsub!(/^ /, "\t")
end end
formatter = Rugments::Formatters::HTML.new( formatter = Rugments::Formatters::HTML.new(
...@@ -46,27 +35,11 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML ...@@ -46,27 +35,11 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
formatter.format(lexer.lex(code)) formatter.format(lexer.lex(code))
end 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) def postprocess(full_document)
full_document.gsub!("ftp://smb:", "smb://")
full_document.gsub!("&rsquo;", "'")
unless @template.instance_variable_get("@project_wiki") || @project.nil? unless @template.instance_variable_get("@project_wiki") || @project.nil?
full_document = h.create_relative_links(full_document) full_document = h.create_relative_links(full_document)
end end
h.gfm_with_options(full_document, @options) h.gfm_with_options(full_document, @options)
end end
end end
...@@ -27,21 +27,34 @@ describe Import::GoogleCodeController do ...@@ -27,21 +27,34 @@ describe Import::GoogleCodeController do
it "assigns variables" do it "assigns variables" do
@project = create(:project, import_type: 'google_code', creator_id: user.id) @project = create(:project, import_type: 'google_code', creator_id: user.id)
controller.stub_chain(:client, :repos).and_return([@repo]) controller.stub_chain(:client, :repos).and_return([@repo])
controller.stub_chain(:client, :incompatible_repos).and_return([])
get :status get :status
expect(assigns(:already_added_projects)).to eq([@project]) expect(assigns(:already_added_projects)).to eq([@project])
expect(assigns(:repos)).to eq([@repo]) expect(assigns(:repos)).to eq([@repo])
expect(assigns(:incompatible_repos)).to eq([])
end end
it "does not show already added project" do it "does not show already added project" do
@project = create(:project, import_type: 'google_code', creator_id: user.id, import_source: 'vim') @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, :repos).and_return([@repo])
controller.stub_chain(:client, :incompatible_repos).and_return([])
get :status get :status
expect(assigns(:already_added_projects)).to eq([@project]) expect(assigns(:already_added_projects)).to eq([@project])
expect(assigns(:repos)).to eq([]) expect(assigns(:repos)).to eq([])
end 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
end end
...@@ -94,10 +94,26 @@ FactoryGirl.define do ...@@ -94,10 +94,26 @@ FactoryGirl.define do
'new_issue_url' => 'http://redmine/projects/project_name_in_redmine/issues/new' 'new_issue_url' => 'http://redmine/projects/project_name_in_redmine/issues/new'
} }
) )
end
after :create do |project|
project.issues_tracker = 'redmine' project.issues_tracker = 'redmine'
project.issues_tracker_id = 'project_name_in_redmine' project.issues_tracker_id = 'project_name_in_redmine'
end end
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 end
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
# 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)
...@@ -107,8 +107,7 @@ describe GitlabMarkdownHelper do ...@@ -107,8 +107,7 @@ describe GitlabMarkdownHelper do
end end
it 'should not be confused by whitespace before bullets' do it 'should not be confused by whitespace before bullets' do
rendered_text_asterisk = markdown(@source_text_asterisk, rendered_text_asterisk = markdown(@source_text_asterisk, parse_tasks: true)
parse_tasks: true)
rendered_text_dash = markdown(@source_text_dash, parse_tasks: true) rendered_text_dash = markdown(@source_text_dash, parse_tasks: true)
expect(rendered_text_asterisk).to match( expect(rendered_text_asterisk).to match(
...@@ -207,78 +206,7 @@ describe GitlabMarkdownHelper do ...@@ -207,78 +206,7 @@ describe GitlabMarkdownHelper do
end end
describe "#markdown" do 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 # TODO (rspeicher): These belong in a relative link filter spec
context 'relative links' do context 'relative links' do
context 'with a valid repository' do context 'with a valid repository' do
before do before do
...@@ -333,11 +261,6 @@ describe GitlabMarkdownHelper do ...@@ -333,11 +261,6 @@ describe GitlabMarkdownHelper do
expected = "" expected = ""
expect(markdown(actual)).to match(expected) expect(markdown(actual)).to match(expected)
end 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 end
context 'with an empty repository' do context 'with an empty repository' do
...@@ -353,34 +276,6 @@ describe GitlabMarkdownHelper do ...@@ -353,34 +276,6 @@ describe GitlabMarkdownHelper do
end end
end 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 end
describe '#render_wiki_content' do describe '#render_wiki_content' do
......
...@@ -5,24 +5,6 @@ describe IssuesHelper do ...@@ -5,24 +5,6 @@ describe IssuesHelper do
let(:issue) { create :issue, project: project } let(:issue) { create :issue, project: project }
let(:ext_project) { create :redmine_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 describe "url_for_project_issues" do
let(:project_url) { ext_project.external_issue_tracker.project_url } let(:project_url) { ext_project.external_issue_tracker.project_url }
let(:ext_expected) do let(:ext_expected) do
......
...@@ -23,6 +23,7 @@ describe Gitlab::GoogleCodeImport::Client do ...@@ -23,6 +23,7 @@ describe Gitlab::GoogleCodeImport::Client do
describe "#repos" do describe "#repos" do
it "returns only Git repositories" do it "returns only Git repositories" do
expect(subject.repos.length).to eq(1) expect(subject.repos.length).to eq(1)
expect(subject.incompatible_repos.length).to eq(1)
end end
end end
......
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
...@@ -8,29 +8,12 @@ module Gitlab::Markdown ...@@ -8,29 +8,12 @@ module Gitlab::Markdown
IssuesHelper IssuesHelper
end end
let(:project) { create(:empty_project) } let(:project) { create(:jira_project) }
let(:issue) { double('issue', iid: 123) } let(:issue) { double('issue', iid: 123) }
context 'JIRA issue references' do context 'JIRA issue references' do
let(:reference) { "JIRA-#{issue.iid}" } 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 it 'requires project context' do
expect { described_class.call('Issue JIRA-123', {}) }. expect { described_class.call('Issue JIRA-123', {}) }.
to raise_error(ArgumentError, /:project/) to raise_error(ArgumentError, /:project/)
......
...@@ -27,7 +27,7 @@ module Gitlab::Markdown ...@@ -27,7 +27,7 @@ module Gitlab::Markdown
let(:reference) { "##{issue.iid}" } let(:reference) { "##{issue.iid}" }
it 'ignores valid references when using non-default tracker' do 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}" exp = act = "Issue ##{issue.iid}"
expect(filter(act).to_html).to eq exp expect(filter(act).to_html).to eq exp
...@@ -48,7 +48,7 @@ module Gitlab::Markdown ...@@ -48,7 +48,7 @@ module Gitlab::Markdown
it 'ignores invalid issue IDs' do it 'ignores invalid issue IDs' do
exp = act = "Fixed ##{issue.iid + 1}" 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 expect(filter(act).to_html).to eq exp
end end
...@@ -98,8 +98,8 @@ module Gitlab::Markdown ...@@ -98,8 +98,8 @@ module Gitlab::Markdown
before { allow_cross_reference! } before { allow_cross_reference! }
it 'ignores valid references when cross-reference project uses external tracker' do it 'ignores valid references when cross-reference project uses external tracker' do
expect_any_instance_of(Project).to receive(:issue_exists?). expect_any_instance_of(Project).to receive(:get_issue).
with(issue.iid).and_return(false) with(issue.iid).and_return(nil)
exp = act = "Issue ##{issue.iid}" exp = act = "Issue ##{issue.iid}"
expect(filter(act).to_html).to eq exp expect(filter(act).to_html).to eq exp
......
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
# 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
...@@ -11,9 +11,8 @@ describe Notify do ...@@ -11,9 +11,8 @@ describe Notify do
let(:recipient) { create(:user, email: 'recipient@example.com') } let(:recipient) { create(:user, email: 'recipient@example.com') }
let(:project) { create(:project) } let(:project) { create(:project) }
around(:each) { ActionMailer::Base.deliveries.clear }
before(:each) do before(:each) do
ActionMailer::Base.deliveries.clear
email = recipient.emails.create(email: "notifications@example.com") email = recipient.emails.create(email: "notifications@example.com")
recipient.update_attribute(:notification_email, email.email) recipient.update_attribute(:notification_email, email.email)
end end
......
...@@ -129,6 +129,48 @@ describe Project do ...@@ -129,6 +129,48 @@ describe Project do
end end
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 describe :update_merge_requests do
let(:project) { create(:project) } let(:project) { create(:project) }
let(:merge_request) { create(:merge_request, source_project: project, target_project: project) } let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
...@@ -180,25 +222,6 @@ describe Project do ...@@ -180,25 +222,6 @@ describe Project do
end end
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 describe :default_issues_tracker? do
let(:project) { create(:project) } let(:project) { create(:project) }
let(:ext_project) { create(:redmine_project) } let(:ext_project) { create(:redmine_project) }
......
...@@ -35,6 +35,7 @@ module TestEnv ...@@ -35,6 +35,7 @@ module TestEnv
# Create repository for FactoryGirl.create(:project) # Create repository for FactoryGirl.create(:project)
setup_factory_repo setup_factory_repo
# Create repository for FactoryGirl.create(:forked_project_with_submodules) # Create repository for FactoryGirl.create(:forked_project_with_submodules)
setup_forked_repo setup_forked_repo
end end
...@@ -54,7 +55,7 @@ module TestEnv ...@@ -54,7 +55,7 @@ module TestEnv
tmp_test_path = Rails.root.join('tmp', 'tests', '**') tmp_test_path = Rails.root.join('tmp', 'tests', '**')
Dir[tmp_test_path].each do |entry| 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) FileUtils.rm_rf(entry)
end end
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment