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/
public/uploads.*
public/uploads/
rails_best_practices_output.html
tags
/tags
tmp/
vendor/bundle/*
Please view this file on the master branch, on stable branches it's out of date.
v 7.11.0 (unreleased)
- Make Reply-To config apply to change e-mail confirmation and other Devise notifications (Stan Hu)
- Don't allow a merge request to be merged when its title starts with "WIP".
- Add a page title to every page.
- Get Gitorious importer to work again.
- Fix clone URL field and X11 Primary selection (Dmitry Medvinsky)
- Ignore invalid lines in .gitmodules
......@@ -11,6 +13,7 @@ v 7.11.0 (unreleased)
- Fix "Revspec not found" errors when viewing diffs in a forked project with submodules (Stan Hu)
-
- Fix broken file browsing with relative submodule in personal projects (Stan Hu)
- Fix DB error when trying to tag a repository (Stan Hu)
- Add "Reply quoting selected text" shortcut key (`r`)
- Fix bug causing `@whatever` inside an issue's first code block to be picked up as a user mention.
- Fix bug causing `@whatever` inside an inline code snippet (backtick-style) to be picked up as a user mention.
......@@ -21,7 +24,7 @@ v 7.11.0 (unreleased)
- Include commit comments in MR from a forked project.
- Fix adding new group members from admin area
- Add default project and snippet visibility settings to the admin web UI.
-
- Show incompatible projects in Google Code import status (Stan Hu)
- Fix bug where commit data would not appear in some subdirectories (Stan Hu)
- Fix bug where Slack service channel was not saved in admin template settings. (Stan Hu)
- Move snippets UI to fluid layout
......
......@@ -8,7 +8,6 @@ class Dispatcher
initPageScripts: ->
page = $('body').attr('data-page')
project_id = $('body').attr('data-project-id')
unless page
return false
......
......@@ -35,7 +35,12 @@ pre {
/* Link to current header. */
h1, h2, h3, h4, h5, h6 {
position: relative;
&:hover > :last-child {
a.anchor {
display: none;
}
&:hover > a.anchor {
$size: 16px;
position: absolute;
right: 100%;
......
......@@ -2,8 +2,8 @@
#
# Automatically sets the layout and ensures an administrator is logged in
class Admin::ApplicationController < ApplicationController
layout 'admin'
before_action :authenticate_admin!
layout 'admin'
def authenticate_admin!
return render_404 unless current_user.is_admin?
......
......@@ -3,6 +3,7 @@ require 'gon'
class ApplicationController < ActionController::Base
include Gitlab::CurrentSettings
include GitlabRoutingHelper
include PageLayoutHelper
PER_PAGE = 20
......
class Dashboard::ApplicationController < ApplicationController
layout 'dashboard'
end
class Dashboard::GroupsController < ApplicationController
class Dashboard::GroupsController < Dashboard::ApplicationController
def index
@group_members = current_user.group_members.page(params[:page]).per(PER_PAGE)
end
......
class Dashboard::MilestonesController < ApplicationController
class Dashboard::MilestonesController < Dashboard::ApplicationController
before_action :load_projects
def index
......
class Dashboard::ProjectsController < ApplicationController
class Dashboard::ProjectsController < Dashboard::ApplicationController
before_action :event_filter
def starred
......
class DashboardController < ApplicationController
respond_to :html
class DashboardController < Dashboard::ApplicationController
before_action :load_projects, except: [:projects]
before_action :event_filter, only: :show
respond_to :html
def show
@projects = @projects.includes(:namespace)
......
class Explore::ApplicationController < ApplicationController
layout 'explore'
end
class Explore::GroupsController < ApplicationController
class Explore::GroupsController < Explore::ApplicationController
skip_before_action :authenticate_user!,
:reject_blocked, :set_current_user_for_observers
layout "explore"
def index
@groups = GroupsFinder.new.execute(current_user)
@groups = @groups.search(params[:search]) if params[:search].present?
......
class Explore::ProjectsController < ApplicationController
class Explore::ProjectsController < Explore::ApplicationController
skip_before_action :authenticate_user!,
:reject_blocked
layout 'explore'
def index
@projects = ProjectsFinder.new.execute(current_user)
@tags = @projects.tags_on(:tags)
......
class Groups::ApplicationController < ApplicationController
layout 'group'
private
......@@ -17,12 +18,4 @@ class Groups::ApplicationController < ApplicationController
return render_404
end
end
def determine_layout
if current_user
'group'
else
'public_group'
end
end
end
class Groups::AvatarsController < ApplicationController
layout "profile"
def destroy
@group = Group.find_by(path: params[:group_id])
@group.remove_avatar!
......
......@@ -6,8 +6,6 @@ class Groups::GroupMembersController < Groups::ApplicationController
before_action :authorize_read_group!
before_action :authorize_admin_group!, except: [:index, :leave]
layout :determine_layout
def index
@project = @group.projects.find(params[:project_id]) if params[:project_id]
@members = @group.group_members
......
class Groups::MilestonesController < ApplicationController
layout 'group'
class Groups::MilestonesController < Groups::ApplicationController
before_action :authorize_group_milestone!, only: :update
def index
......
......@@ -11,7 +11,6 @@ class GroupsController < Groups::ApplicationController
# Load group projects
before_action :load_projects, except: [:new, :create, :projects, :edit, :update]
before_action :event_filter, only: :show
before_action :set_title, only: [:new, :create]
layout :determine_layout
......@@ -119,17 +118,11 @@ class GroupsController < Groups::ApplicationController
end
end
def set_title
@title = 'New Group'
end
def determine_layout
if [:new, :create].include?(action_name.to_sym)
'navless'
elsif current_user
'group'
'application'
else
'public_group'
'group'
end
end
......
class HelpController < ApplicationController
layout 'help'
def index
end
def show
category = clean_path_info(path_params[:category])
file = path_params[:file]
@category = clean_path_info(path_params[:category])
@file = path_params[:file]
respond_to do |format|
format.any(:markdown, :md, :html) do
path = Rails.root.join('doc', category, "#{file}.md")
path = Rails.root.join('doc', @category, "#{@file}.md")
if File.exist?(path)
@markdown = File.read(path)
......@@ -22,7 +24,7 @@ class HelpController < ApplicationController
# Allow access to images in the doc folder
format.any(:png, :gif, :jpeg) do
path = Rails.root.join('doc', category, "#{file}.#{params[:format]}")
path = Rails.root.join('doc', @category, "#{@file}.#{params[:format]}")
if File.exist?(path)
send_file(path, disposition: 'inline')
......
......@@ -72,6 +72,7 @@ class Import::GoogleCodeController < Import::BaseController
end
@repos = client.repos
@incompatible_repos = client.incompatible_repos
@already_added_projects = current_user.created_projects.where(import_type: "google_code")
already_added_projects_names = @already_added_projects.pluck(:import_source)
......
......@@ -4,8 +4,6 @@ class InvitesController < ApplicationController
respond_to :html
layout 'navless'
def show
end
......
class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
include PageLayoutHelper
before_action :authenticate_user!
layout "profile"
layout 'profile'
def index
head :forbidden and return
......
class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
before_action :authenticate_resource_owner!
layout "profile"
layout 'profile'
def new
if pre_auth.authorizable?
......
class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicationsController
layout "profile"
include PageLayoutHelper
layout 'profile'
def destroy
Doorkeeper::AccessToken.revoke_all_for(params[:id], current_resource_owner)
......
class Profiles::AccountsController < ApplicationController
layout "profile"
class Profiles::AccountsController < Profiles::ApplicationController
def show
@user = current_user
end
......
class Profiles::ApplicationController < ApplicationController
layout 'profile'
end
class Profiles::AvatarsController < ApplicationController
layout "profile"
class Profiles::AvatarsController < Profiles::ApplicationController
def destroy
@user = current_user
@user.remove_avatar!
......
class Profiles::EmailsController < ApplicationController
layout "profile"
class Profiles::EmailsController < Profiles::ApplicationController
def index
@primary = current_user.email
@public_email = current_user.public_email
......
class Profiles::KeysController < ApplicationController
layout "profile"
class Profiles::KeysController < Profiles::ApplicationController
skip_before_action :authenticate_user!, only: [:get_keys]
def index
......
class Profiles::NotificationsController < ApplicationController
layout 'profile'
class Profiles::NotificationsController < Profiles::ApplicationController
def show
@user = current_user
@notification = current_user.notification
......
class Profiles::PasswordsController < ApplicationController
layout :determine_layout
class Profiles::PasswordsController < Profiles::ApplicationController
skip_before_action :check_password_expiration, only: [:new, :create]
before_action :set_user
before_action :set_title
before_action :authorize_change_password!
layout :determine_layout
def new
end
......@@ -66,13 +65,9 @@ class Profiles::PasswordsController < ApplicationController
@user = current_user
end
def set_title
@title = "New password"
end
def determine_layout
if [:new, :create].include?(action_name.to_sym)
'navless'
'application'
else
'profile'
end
......
class ProfilesController < ApplicationController
class ProfilesController < Profiles::ApplicationController
include ActionView::Helpers::SanitizeHelper
before_action :user
before_action :authorize_change_username!, only: :update_username
skip_before_action :require_email, only: [:show, :update]
layout 'profile'
def show
end
......
class Projects::ApplicationController < ApplicationController
before_action :project
before_action :repository
layout :determine_layout
layout 'project'
def authenticate_user!
# Restrict access to Projects area only
......@@ -17,14 +17,6 @@ class Projects::ApplicationController < ApplicationController
super
end
def determine_layout
if current_user
'projects'
else
'public_projects'
end
end
def require_branch_head
unless @repository.branch_names.include?(@ref)
redirect_to(
......
class Projects::AvatarsController < Projects::ApplicationController
layout 'project'
before_action :project
def show
......
......@@ -18,7 +18,6 @@ class Projects::ForksController < Projects::ApplicationController
notice: 'Project was successfully forked.'
)
else
@title = 'Fork project'
render :error
end
end
......
class Projects::UploadsController < Projects::ApplicationController
layout 'project'
skip_before_action :authenticate_user!, :reject_blocked!, :project,
:repository, if: -> { action_name == 'show' && image? }
......
......@@ -6,17 +6,16 @@ class ProjectsController < ApplicationController
# Authorize
before_action :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer, :archive, :unarchive]
before_action :set_title, only: [:new, :create]
before_action :event_filter, only: :show
layout 'navless', only: [:new, :create, :fork]
layout :determine_layout
def new
@project = Project.new
end
def edit
render 'edit', layout: 'project_settings'
render 'edit'
end
def create
......@@ -46,7 +45,7 @@ class ProjectsController < ApplicationController
end
format.js
else
format.html { render 'edit', layout: 'project_settings' }
format.html { render 'edit' }
format.js
end
end
......@@ -72,13 +71,13 @@ class ProjectsController < ApplicationController
format.html do
if @project.repository_exists?
if @project.empty_repo?
render 'projects/empty', layout: user_layout
render 'projects/empty'
else
@last_push = current_user.recent_push(@project.id) if current_user
render :show, layout: user_layout
render :show
end
else
render 'projects/no_repo', layout: user_layout
render 'projects/no_repo'
end
end
......@@ -160,12 +159,14 @@ class ProjectsController < ApplicationController
private
def set_title
@title = 'New Project'
end
def user_layout
current_user ? 'projects' : 'public_projects'
def determine_layout
if [:new, :create].include?(action_name.to_sym)
'application'
elsif [:edit, :update].include?(action_name.to_sym)
'project_settings'
else
'project'
end
end
def load_events
......
class SearchController < ApplicationController
include SearchHelper
layout 'search'
def show
return if params[:search].nil? || params[:search].blank?
......
......@@ -7,14 +7,11 @@ class SnippetsController < ApplicationController
# Allow destroy snippet
before_action :authorize_admin_snippet!, only: [:destroy]
before_action :set_title
skip_before_action :authenticate_user!, only: [:index, :user_index, :show, :raw]
layout 'snippets'
respond_to :html
layout :determine_layout
def index
if params[:username].present?
@user = User.find_by(username: params[:username])
......@@ -98,16 +95,7 @@ class SnippetsController < ApplicationController
return render_404 unless can?(current_user, :admin_personal_snippet, @snippet)
end
def set_title
@title = 'Snippets'
@title_url = snippets_path
end
def snippet_params
params.require(:personal_snippet).permit(:title, :content, :file_name, :private, :visibility_level)
end
def determine_layout
current_user ? 'snippets' : 'public_users'
end
end
class UsersController < ApplicationController
skip_before_action :authenticate_user!
before_action :set_user
layout :determine_layout
def show
@contributed_projects = contributed_projects.joined(@user).
......@@ -13,9 +12,6 @@ class UsersController < ApplicationController
# Collect only groups common for both users
@groups = @user.groups & GroupsFinder.new.execute(current_user)
@title = @user.name
@title_url = user_path(@user)
respond_to do |format|
format.html
......@@ -51,14 +47,6 @@ class UsersController < ApplicationController
render 'calendar_activities', layout: false
end
def determine_layout
if current_user
'navless'
else
'public_users'
end
end
private
def set_user
......
......@@ -34,10 +34,8 @@ module GitlabMarkdownHelper
# see https://github.com/vmg/redcarpet#darling-i-packed-you-a-couple-renderers-for-lunch
rend = Redcarpet::Render::GitlabHTML.new(self, user_color_scheme_class, {
with_toc_data: true,
safe_links_only: true,
# Handled further down the line by HTML::Pipeline::SanitizationFilter
escape_html: false
# Handled further down the line by Gitlab::Markdown::SanitizationFilter
escape_html: false
}.merge(options))
# see https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use
......@@ -45,7 +43,6 @@ module GitlabMarkdownHelper
no_intra_emphasis: true,
tables: true,
fenced_code_blocks: true,
autolink: true,
strikethrough: true,
lax_spacing: true,
space_after_headers: true,
......
......@@ -19,24 +19,6 @@ module GroupsHelper
end
end
def group_head_title
title = @group.name
title = if current_action?(:issues)
"Issues - " + title
elsif current_action?(:merge_requests)
"Merge requests - " + title
elsif current_action?(:members)
"Members - " + title
elsif current_action?(:edit)
"Settings - " + title
else
title
end
title
end
def group_settings_page?
if current_controller?('groups')
current_action?('edit') || current_action?('projects')
......
......@@ -43,17 +43,6 @@ module IssuesHelper
end
end
def title_for_issue(issue_iid, project = @project)
return '' if project.nil?
if project.default_issues_tracker?
issue = project.issues.where(iid: issue_iid).first
return issue.title if issue
end
''
end
def issue_timestamp(issue)
# Shows the created at time and the updated at time if different
ts = "#{time_ago_with_tooltip(issue.created_at, 'bottom', 'note_created_ago')}"
......@@ -110,5 +99,5 @@ module IssuesHelper
end
# Required for Gitlab::Markdown::IssueReferenceFilter
module_function :url_for_issue, :title_for_issue
module_function :url_for_issue
end
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
'unknown'
end
def project_head_title
title = @project.name_with_namespace
title = if current_controller?(:tree)
"#{@project.path}\/#{@path} at #{@ref} - " + title
elsif current_controller?(:issues)
if current_action?(:show)
"Issue ##{@issue.iid} - #{@issue.title} - " + title
else
"Issues - " + title
end
elsif current_controller?(:blob)
if current_action?(:new) || current_action?(:create)
"New file at #{@ref}"
elsif current_action?(:show)
"#{@blob.path} at #{@ref}"
elsif @blob
"Edit file #{@blob.path} at #{@ref}"
end
elsif current_controller?(:commits)
"Commits at #{@ref} - " + title
elsif current_controller?(:merge_requests)
if current_action?(:show)
"Merge request ##{@merge_request.iid} - " + title
else
"Merge requests - " + title
end
elsif current_controller?(:wikis)
"Wiki - " + title
elsif current_controller?(:network)
"Network graph - " + title
elsif current_controller?(:graphs)
"Graphs - " + title
else
title
end
title
end
def default_url_to_repo(project = nil)
project = project || @project
current_user ? project.url_to_repo : project.http_url_to_repo
......
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
@issue_identifier.to_s
end
def title
"External Issue #{self}"
end
def ==(other)
other.is_a?(self.class) && (to_s == other.to_s)
end
......
......@@ -329,14 +329,18 @@ class Project < ActiveRecord::Base
self.id
end
def issue_exists?(issue_id)
def get_issue(issue_id)
if default_issues_tracker?
self.issues.where(iid: issue_id).first.present?
issues.find_by(iid: issue_id)
else
true
ExternalIssue.new(issue_id, self)
end
end
def issue_exists?(issue_id)
get_issue(issue_id)
end
def default_issue_tracker
gitlab_issue_tracker_service || create_gitlab_issue_tracker_service
end
......@@ -350,11 +354,7 @@ class Project < ActiveRecord::Base
end
def default_issues_tracker?
if external_issue_tracker
false
else
true
end
!external_issue_tracker
end
def external_issues_trackers
......
- page_title "Settings"
%h3.page-title Application settings
%hr
= render 'form'
- page_title "Edit", @application.name, "Applications"
%h3.page-title Edit application
- @url = admin_application_path(@application)
= render 'form', application: @application
\ No newline at end of file
= render 'form', application: @application
- page_title "Applications"
%h3.page-title
System OAuth applications
%p.light
......
- page_title "New Application"
%h3.page-title New application
- @url = admin_applications_path
= render 'form', application: @application
\ No newline at end of file
= render 'form', application: @application
- page_title @application.name, "Applications"
%h3.page-title
Application: #{@application.name}
......
- page_title "Background Jobs"
%h3.page-title Background Jobs
%p.light GitLab uses #{link_to "sidekiq", "http://sidekiq.org/"} library for async job processing
......
- page_title "Broadcast Messages"
%h3.page-title
Broadcast Messages
%p.light
......
- page_title "Deploy Keys"
.panel.panel-default
.panel-heading
Public deploy keys (#{@deploy_keys.count})
......
- page_title "New Deploy Key"
%h3.page-title New public deploy key
%hr
......
- page_title @deploy_key.title, "Deploy Keys"
.row
.col-md-4
.panel.panel-default
......
- page_title "Edit", @group.name, "Groups"
%h3.page-title Edit group: #{@group.name}
%hr
= render 'form'
- page_title "Groups"
%h3.page-title
Groups (#{@groups.total_count})
= link_to 'New Group', new_admin_group_path, class: "btn btn-new pull-right"
......
- page_title "New Group"
%h3.page-title New group
%hr
= render 'form'
- page_title @group.name, "Groups"
%h3.page-title
Group: #{@group.name}
......
- page_title "System Hooks"
%h3.page-title
System hooks
......
- page_title @key.title, "Keys"
= render "profiles/keys/key_details", admin: true
- page_title "Logs"
- loggers = [Gitlab::GitLogger, Gitlab::AppLogger,
Gitlab::ProductionLogger, Gitlab::SidekiqLogger]
%ul.nav.nav-tabs.log-tabs
......
- page_title "Projects"
= render 'shared/show_aside'
.row
......
- page_title @project.name_with_namespace, "Projects"
%h3.page-title
Project: #{@project.name_with_namespace}
= link_to edit_project_path(@project), class: "btn pull-right" do
......
- page_title @service.title, "Service Templates"
= render 'form'
- page_title "Service Templates"
%h3.page-title Service templates
%p.light Service template allows you to set default values for project services
......
- page_title "Edit", @user.name, "Users"
%h3.page-title
Edit user: #{@user.name}
.back-link
......
- page_title "Users"
= render 'shared/show_aside'
.row
......
- page_title "New User"
%h3.page-title
New user
%hr
......
- page_title @user.name, "Users"
%h3.page-title
User:
= @user.name
......
- page_title "Groups"
%h3.page-title
Group Membership
- if current_user.can_create_group?
......
- page_title "Issues"
= content_for :meta_tags do
- if current_user
= auto_discovery_link_tag(:atom, issues_dashboard_url(format: :atom, private_token: current_user.private_token), title: "#{current_user.name} issues")
......
- page_title "Merge Requests"
%h3.page-title
Merge Requests
......
- page_title "Milestones"
%h3.page-title
Milestones
%span.pull-right #{@dashboard_milestones.count} milestones
......
- page_title @dashboard_milestone.title, "Milestones"
%h4.page-title
.issue-box{ class: "issue-box-#{@dashboard_milestone.closed? ? 'closed' : 'open'}" }
- if @dashboard_milestone.closed?
......
- page_title "Starred Projects"
- if @projects.any?
= render 'shared/show_aside'
......
- page_title "Sign up"
= render 'devise/shared/signup_box'
= render 'devise/shared/sign_in_link'
\ No newline at end of file
= render 'devise/shared/sign_in_link'
- page_title "Sign in"
%div
- if signin_enabled? || ldap_enabled?
= render 'devise/shared/signin_box'
......
- page_title "Edit", @application.name, "Applications"
%h3.page-title Edit application
= render 'form', application: @application
\ No newline at end of file
= render 'form', application: @application
- page_title "Applications"
%h3.page-title Your applications
%p= link_to 'New Application', new_oauth_application_path, class: 'btn btn-success'
%table.table.table-striped
......@@ -13,4 +14,4 @@
%td= link_to application.name, oauth_application_path(application)
%td= application.redirect_uri
%td= link_to 'Edit', edit_oauth_application_path(application), class: 'btn btn-link'
%td= render 'delete_form', application: application
\ No newline at end of file
%td= render 'delete_form', application: application
- page_title @application.name, "Applications"
%h3.page-title
Application: #{@application.name}
......
- page_title "Access Denied"
%h1 403
%h3 Access Denied
%hr
......
- page_title "Encoding Error"
%h1 500
%h3 Encoding Error
%hr
......
- page_title "Git Resource Not Found"
%h1 404
%h3 Git Resource Not found
%hr
......
- page_title "Not Found"
%h1 404
%h3 The resource you were looking for doesn't exist.
%hr
......
- page_title "Auth Error"
%h1 422
%h3 Sign-in using #{@provider} auth failed
%hr
......
- page_title "Groups"
.clearfix
.pull-left
= form_tag explore_groups_path, method: :get, class: 'form-inline form-tiny' do |f|
......
- page_title "Projects"
.clearfix
= render 'filter'
......
- page_title "Starred Projects"
.explore-trending-block
%p.lead
%i.fa.fa-star
......
- page_title "Trending Projects"
.explore-title
%h3
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-heading
%strong= @group.name
......
- page_title "Members"
- show_roles = should_user_see_group_roles?(current_user, @group)
%h3.page-title
......
- page_title "Issues"
= content_for :meta_tags do
- if current_user
= auto_discovery_link_tag(:atom, issues_group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} issues")
......
- page_title "Merge Requests"
%h3.page-title
Merge Requests
......
- page_title "Milestones"
%h3.page-title
Milestones
%span.pull-right #{@group_milestones.count} milestones
......
- page_title @group_milestone.title, "Milestones"
%h4.page-title
.issue-box{ class: "issue-box-#{@group_milestone.closed? ? 'closed' : 'open'}" }
- if @group_milestone.closed?
......
- page_title 'New Group'
- header_title 'New Group'
= form_for @group, html: { class: 'group-form form-horizontal' } do |f|
- if @group.errors.any?
.alert.alert-danger
......
- page_title "Projects"
.panel.panel-default
.panel-heading
%strong= @group.name
......
- page_title @file.humanize, *@category.split("/").reverse.map(&:humanize)
.documentation.wiki
= 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."
.gitlab-ui-dev-kit
......
- page_title "Bitbucket import"
%h3.page-title
%i.fa.fa-bitbucket
Import projects from Bitbucket
......
- page_title "GitHub import"
%h3.page-title
%i.fa.fa-github
Import projects from GitHub
......
- page_title "GitLab.com import"
%h3.page-title
%i.fa.fa-heart
Import projects from GitLab.com
......
- page_title "Gitorious import"
%h3.page-title
%i.icon-gitorious.icon-gitorious-big
Import projects from Gitorious.org
......
- page_title "Google Code import"
%h3.page-title
%i.fa.fa-google
Import projects from Google Code
......
- page_title "User map", "Google Code import"
%h3.page-title
%i.fa.fa-google
Import projects from Google Code
......
- page_title "Google Code import"
%h3.page-title
%i.fa.fa-google
Import projects from Google Code
%p.light
Select projects you want to import.
%p.light
Optionally, you can
= link_to "customize", new_user_map_import_google_code_path
how Google Code email addresses and usernames are imported into GitLab.
%hr
%p
= button_tag 'Import all projects', class: "btn btn-success js-import-all"
- if @repos.any?
%p.light
Select projects you want to import.
%p.light
Optionally, you can
= link_to "customize", new_user_map_import_google_code_path
how Google Code email addresses and usernames are imported into GitLab.
%hr
%p
- if @incompatible_repos.any?
= button_tag 'Import all compatible projects', class: "btn btn-success js-import-all"
- else
= button_tag 'Import all projects', class: "btn btn-success js-import-all"
%table.table.import-jobs
%thead
......@@ -44,6 +49,22 @@
= "#{current_user.username}/#{repo.name}"
%td.import-actions.job-status
= button_tag "Import", class: "btn js-add-to-import"
- @incompatible_repos.each do |repo|
%tr{id: "repo_#{repo.id}"}
%td
= link_to repo.name, "https://code.google.com/p/#{repo.name}", target: "_blank"
%td.import-target
%td.import-actions-job-status
= label_tag "Incompatible Project", nil, class: "label label-danger"
- if @incompatible_repos.any?
%p
One or more of your Google Code projects cannot be imported into GitLab
directly because they use Subversion or Mercurial for version control,
rather than Git. Please convert them to Git on Google Code, and go
through the
= link_to "import flow", new_import_google_code_path
again.
:coffeescript
new ImporterStatus("#{jobs_import_google_code_path}", "#{import_google_code_path}")
- page_title "Invitation"
%h3.page-title Invitation
%p
......
- page_title "GitLab"
%head
%meta{charset: "utf-8"}
%meta{'http-equiv' => 'X-UA-Compatible', content: 'IE=edge'}
%meta{content: "GitLab Community Edition", name: "description"}
%title
= "#{title} | " if defined?(title)
GitLab
%title= page_title
= favicon_link_tag 'favicon.ico'
= stylesheet_link_tag "application", :media => "all"
......
.page-with-sidebar{ class: nav_sidebar_class }
= render "layouts/broadcast"
.sidebar-wrapper
- if defined?(sidebar)
= render(sidebar)
- if defined?(sidebar) && sidebar
= render "layouts/nav/#{sidebar}"
- elsif current_user
= render 'layouts/nav/dashboard'
.collapse-nav
......
!!! 5
%html{ lang: "en"}
= render "layouts/head", title: "Admin area"
%body{class: "#{app_theme} admin", :'data-page' => body_data_page}
= render "layouts/head_panel", title: link_to("Admin area", admin_root_path)
= render 'layouts/page', sidebar: 'layouts/nav/admin'
- page_title "Admin area"
- header_title "Admin area", admin_root_path
- sidebar "admin"
= render template: "layouts/application"
!!! 5
%html{ lang: "en"}
= render "layouts/head", title: "Dashboard"
%body{class: "#{app_theme} application", :'data-page' => body_data_page }
= render "layouts/head_panel", title: link_to("Dashboard", root_path)
= render 'layouts/page', sidebar: 'layouts/nav/dashboard'
= render "layouts/head"
%body{class: "#{app_theme}", :'data-page' => body_data_page}
- if current_user
= render "layouts/head_panel", title: header_title
- else
= render "layouts/public_head_panel", title: header_title
= render 'layouts/page', sidebar: sidebar
- page_title "Dashboard"
- header_title "Dashboard", root_path
- sidebar "dashboard"
= render template: "layouts/application"
!!! 5
%html{ lang: "en"}
= render "layouts/head", title: "Error"
= render "layouts/head"
%body{class: "#{app_theme} application"}
= render "layouts/head_panel", title: "" if current_user
.container.navless-container
......
- page_title = 'Explore GitLab'
!!! 5
%html{ lang: "en"}
= render "layouts/head", title: page_title
%body{class: "#{app_theme} application", :'data-page' => body_data_page}
= render "layouts/broadcast"
- if current_user
= render "layouts/head_panel", title: link_to(page_title, explore_root_path)
- else
= render "layouts/public_head_panel", title: link_to(page_title, explore_root_path)
- page_title "Explore"
- header_title "Explore GitLab", explore_root_path
- sidebar "explore"
= render 'layouts/page', sidebar: 'layouts/nav/explore'
= render template: "layouts/application"
!!! 5
%html{ lang: "en"}
= render "layouts/head", title: group_head_title
%body{class: "#{app_theme} application", :'data-page' => body_data_page}
= render "layouts/head_panel", title: link_to(@group.name, group_path(@group))
= render 'layouts/page', sidebar: 'layouts/nav/group'
- page_title @group.name
- header_title @group.name, group_path(@group)
- sidebar "group"
= render template: "layouts/application"
- page_title "Help"
- header_title "Help", help_path
= render template: "layouts/application"
......@@ -39,4 +39,15 @@
= icon ('angle-down fw')
- if group_settings_page?
= render 'groups/settings_nav'
%ul.sidebar-subnav
= nav_link(path: 'groups#edit') do
= link_to edit_group_path(@group), title: 'Group', data: {placement: 'right'} do
= icon('pencil-square-o')
%span
Group
= nav_link(path: 'groups#projects') do
= link_to projects_group_path(@group), title: 'Projects', data: {placement: 'right'} do
= icon('folder')
%span
Projects
%ul.project-navigation.nav.nav-sidebar
- if @project_settings_nav
= nav_link do
= link_to project_path(@project), title: 'Back to project', data: {placement: 'right'} do
= icon('caret-square-o-left fw')
= nav_link(path: 'projects#show', html_options: {class: 'home'}) do
= link_to project_path(@project), title: 'Project', class: 'shortcuts-project', data: {placement: 'right'} do
= icon('dashboard fw')
%span
Project
- if project_nav_tab? :files
= nav_link(controller: %w(tree blob blame edit_tree new_tree)) do
= link_to namespace_project_tree_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Files', class: 'shortcuts-tree', data: {placement: 'right'} do
= icon('files-o fw')
%span
Back to project
Files
%li.separate-item
= render 'projects/settings_nav'
- else
= nav_link(path: 'projects#show', html_options: {class: 'home'}) do
= link_to project_path(@project), title: 'Project', class: 'shortcuts-project', data: {placement: 'right'} do
= icon('dashboard fw')
- if project_nav_tab? :commits
= nav_link(controller: %w(commit commits compare repositories tags branches)) do
= link_to namespace_project_commits_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Commits', class: 'shortcuts-commits', data: {placement: 'right'} do
= icon('history fw')
%span
Project
- if project_nav_tab? :files
= nav_link(controller: %w(tree blob blame edit_tree new_tree)) do
= link_to namespace_project_tree_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Files', class: 'shortcuts-tree', data: {placement: 'right'} do
= icon('files-o fw')
%span
Files
- if project_nav_tab? :commits
= nav_link(controller: %w(commit commits compare repositories tags branches)) do
= link_to namespace_project_commits_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Commits', class: 'shortcuts-commits', data: {placement: 'right'} do
= icon('history fw')
%span
Commits
Commits
- if project_nav_tab? :network
= nav_link(controller: %w(network)) do
= link_to namespace_project_network_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Network', class: 'shortcuts-network', data: {placement: 'right'} do
= icon('code-fork fw')
%span
Network
- if project_nav_tab? :network
= nav_link(controller: %w(network)) do
= link_to namespace_project_network_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Network', class: 'shortcuts-network', data: {placement: 'right'} do
= icon('code-fork fw')
%span
Network
- if project_nav_tab? :graphs
= nav_link(controller: %w(graphs)) do
= link_to namespace_project_graph_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Graphs', class: 'shortcuts-graphs', data: {placement: 'right'} do
= icon('area-chart fw')
%span
Graphs
- if project_nav_tab? :graphs
= nav_link(controller: %w(graphs)) do
= link_to namespace_project_graph_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Graphs', class: 'shortcuts-graphs', data: {placement: 'right'} do
= icon('area-chart fw')
%span
Graphs
- if project_nav_tab? :milestones
= nav_link(controller: :milestones) do
= link_to namespace_project_milestones_path(@project.namespace, @project), title: 'Milestones', data: {placement: 'right'} do
= icon('clock-o fw')
%span
Milestones
- if project_nav_tab? :milestones
= nav_link(controller: :milestones) do
= link_to namespace_project_milestones_path(@project.namespace, @project), title: 'Milestones', data: {placement: 'right'} do
= icon('clock-o fw')
%span
Milestones
- if project_nav_tab? :issues
= nav_link(controller: :issues) do
= link_to url_for_project_issues(@project, only_path: true), title: 'Issues', class: 'shortcuts-issues', data: {placement: 'right'} do
= icon('exclamation-circle fw')
%span
Issues
- if @project.default_issues_tracker?
%span.count.issue_counter= @project.issues.opened.count
- if project_nav_tab? :issues
= nav_link(controller: :issues) do
= link_to url_for_project_issues(@project, only_path: true), title: 'Issues', class: 'shortcuts-issues', data: {placement: 'right'} do
= icon('exclamation-circle fw')
%span
Issues
- if @project.default_issues_tracker?
%span.count.issue_counter= @project.issues.opened.count
- if project_nav_tab? :merge_requests
= nav_link(controller: :merge_requests) do
= link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests', class: 'shortcuts-merge_requests', data: {placement: 'right'} do
= icon('tasks fw')
%span
Merge Requests
%span.count.merge_counter= @project.merge_requests.opened.count
- if project_nav_tab? :merge_requests
= nav_link(controller: :merge_requests) do
= link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests', class: 'shortcuts-merge_requests', data: {placement: 'right'} do
= icon('tasks fw')
%span
Merge Requests
%span.count.merge_counter= @project.merge_requests.opened.count
- if project_nav_tab? :labels
= nav_link(controller: :labels) do
= link_to namespace_project_labels_path(@project.namespace, @project), title: 'Labels', data: {placement: 'right'} do
= icon('tags fw')
%span
Labels
- if project_nav_tab? :labels
= nav_link(controller: :labels) do
= link_to namespace_project_labels_path(@project.namespace, @project), title: 'Labels', data: {placement: 'right'} do
= icon('tags fw')
%span
Labels
- if project_nav_tab? :wiki
= nav_link(controller: :wikis) do
= link_to get_project_wiki_path(@project), title: 'Wiki', class: 'shortcuts-wiki', data: {placement: 'right'} do
= icon('book fw')
%span
Wiki
- if project_nav_tab? :wiki
= nav_link(controller: :wikis) do
= link_to get_project_wiki_path(@project), title: 'Wiki', class: 'shortcuts-wiki', data: {placement: 'right'} do
= icon('book fw')
%span
Wiki
- if project_nav_tab? :snippets
= nav_link(controller: :snippets) do
= link_to namespace_project_snippets_path(@project.namespace, @project), title: 'Snippets', class: 'shortcuts-snippets', data: {placement: 'right'} do
= icon('file-text-o fw')
%span
Snippets
- if project_nav_tab? :snippets
= nav_link(controller: :snippets) do
= link_to namespace_project_snippets_path(@project.namespace, @project), title: 'Snippets', class: 'shortcuts-snippets', data: {placement: 'right'} do
= icon('file-text-o fw')
%span
Snippets
- if project_nav_tab? :settings
= nav_link(html_options: {class: "#{project_tab_class} separate-item"}) do
= link_to edit_project_path(@project), title: 'Settings', class: 'stat-tab tab no-highlight', data: {placement: 'right'} do
= icon('cogs fw')
%span
Settings
- if project_nav_tab? :settings
= nav_link(html_options: {class: "#{project_tab_class} separate-item"}) do
= link_to edit_project_path(@project), title: 'Settings', class: 'stat-tab tab no-highlight', data: {placement: 'right'} do
= icon('cogs fw')
%span
Settings
%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
= nav_link(path: user_snippets_path(current_user), html_options: {class: 'home'}) do
= link_to user_snippets_path(current_user), title: 'Your snippets', data: {placement: 'right'} do
= icon('dashboard fw')
%span
Your Snippets
- if current_user
= nav_link(path: user_snippets_path(current_user), html_options: {class: 'home'}) do
= link_to user_snippets_path(current_user), title: 'Your snippets', data: {placement: 'right'} do
= icon('dashboard fw')
%span
Your Snippets
= nav_link(path: snippets_path) do
= link_to snippets_path, title: 'Discover snippets', data: {placement: 'right'} do
= icon('globe fw')
......
!!! 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
%html{ lang: "en"}
= render "layouts/head", title: "Profile"
%body{class: "#{app_theme} profile", :'data-page' => body_data_page}
= render "layouts/head_panel", title: link_to("Profile", profile_path)
= render 'layouts/page', sidebar: 'layouts/nav/profile'
- page_title "Profile"
- header_title "Profile", profile_path
- sidebar "profile"
= render template: "layouts/application"
- 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
%html{ lang: "en"}
= render "layouts/head", title: @project.name_with_namespace
%body{class: "#{app_theme} project", :'data-page' => body_data_page, :'data-project-id' => @project.id }
= render "layouts/head_panel", title: project_title(@project)
= render "layouts/init_auto_complete"
- @project_settings_nav = true
= render 'layouts/page', sidebar: 'layouts/nav/project'
- page_title "Settings"
- sidebar "project_settings"
= render template: "layouts/project"
!!! 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
%html{ lang: "en"}
= render "layouts/head", title: "Search"
%body{class: "#{app_theme} application", :'data-page' => body_data_page}
= render "layouts/head_panel", title: link_to("Search", search_path)
= render 'layouts/page'
- page_title "Search"
- header_title "Search", search_path
= render template: "layouts/application"
!!! 5
%html{ lang: "en"}
= render "layouts/head", title: "Dashboard"
%body{class: "#{app_theme} application", :'data-page' => body_data_page }
= render "layouts/head_panel", title: link_to("Snippets", snippets_path)
= render 'layouts/page', sidebar: 'layouts/nav/snippets'
- page_title 'Snippets'
- header_title 'Snippets', snippets_path
- sidebar "snippets"
= render template: "layouts/application"
- page_title "Account"
- if current_user.ldap_user?
.alert.alert-info
Some options are unavailable for LDAP accounts
......
- page_title "Applications"
%h3.page-title
Application Settings
%p.light
......
- page_title "Design"
%h3.page-title
Design Settings
%p.light
......
- page_title "Emails"
%h3.page-title
Email Settings
%p.light
......
- page_title "History"
%h3.page-title
Your Account History
%p.light
......
- page_title "SSH Keys"
%h3.page-title
SSH Keys Settings
.pull-right
......
- page_title "Add SSH Keys"
%h3.page-title Add an SSH Key
%p.light
Paste your public key here. Read more about how to generate a key on #{link_to "the SSH help page", help_page_path("ssh", "README")}.
......
- page_title @key.title, "SSH Keys"
= render "key_details"
- page_title "Notifications"
%h3.page-title
Notifications Settings
%p.light
......
- page_title "Password"
%h3.page-title Password Settings
%p.light
- if @user.password_automatically_set?
......
- page_title "New Password"
- header_title "New Password"
%h3.page-title Setup new password
%hr
= form_for @user, url: profile_password_path, method: :post, html: { class: 'form-horizontal '} do |f|
......
- page_title "Settings"
%h3.page-title
Profile Settings
%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
#tree-holder.tree-holder
......
- page_title "Edit", @blob.path, @ref
.file-editor
%ul.nav.nav-tabs.js-edit-mode
%li.active
......
- page_title "New File", @ref
%h3.page-title New file
.file-editor
= form_tag(namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post, class: 'form-horizontal form-new-file') do
......
- page_title @blob.path, @ref
%div.tree-ref-holder
= render 'shared/ref_switcher', destination: 'blob', path: @path
......
- page_title "Branches"
= render "projects/commits/head"
%h3.page-title
Branches
......
- page_title "New Branch"
- if @error
.alert.alert-danger
%button{ type: "button", class: "close", "data-dismiss" => "alert"} &times;
......
- page_title "#{@commit.title} (#{@commit.short_id})", "Commits"
= render "commit_box"
= render "projects/diffs/diffs", diffs: @diffs, project: @project
= render "projects/notes/notes_with_form"
- page_title "Commits", @ref
= content_for :meta_tags do
- if current_user
= auto_discovery_link_tag(:atom, namespace_project_commits_url(@project.namespace, @project, @ref, format: :atom, private_token: current_user.private_token), title: "#{@project.name}:#{@ref} commits")
......
- page_title "Compare"
= render "projects/commits/head"
%h3.page-title
......
- page_title "#{params[:from]}...#{params[:to]}"
= render "projects/commits/head"
%h3.page-title
......
- page_title "Deploy Keys"
%h3.page-title
Deploy keys allow read-only access to the repository
......
- page_title "New Deploy Key"
%h3.page-title New Deploy key
%hr
......
- page_title @key.title, "Deploy Keys"
%h3.page-title
Deploy key:
= @key.title
......
- page_title "Fork project"
- if @forked_project && !@forked_project.saved?
.alert.alert-danger.alert-block
%h4
......
- page_title "Fork project"
%h3.page-title Fork project
%p.lead
Click to fork the project to a user or group
......
- page_title "Commit statistics"
= render 'head'
%p.lead
......
- page_title "Contributor statistics"
= render 'head'
.loading-graph
.center
......
- page_title "Web Hooks"
%h3.page-title
Web hooks
......
- page_title "Import repository"
%h3.page-title
- if @project.import_failed?
Import failed. Retry?
......
- page_title "Import in progress"
.save-project-loader
.center
%h2
......
- page_title "Edit", "#{@issue.title} (##{@issue.iid})", "Issues"
= render "form"
- page_title "Issues"
= content_for :meta_tags do
- if current_user
= auto_discovery_link_tag(:atom, namespace_project_issues_url(@project.namespace, @project, :atom, private_token: current_user.private_token), title: "#{@project.name} issues")
......
- page_title "#{@issue.title} (##{@issue.iid})", "Issues"
.issue
.issue-details
%h4.page-title
......
- page_title "Edit", @label.name, "Labels"
%h3
Edit label
%span.light #{@label.name}
......
- page_title "Labels"
- if can? current_user, :admin_label, @project
= link_to new_namespace_project_label_path(@project.namespace, @project), class: "pull-right btn btn-new" do
New label
......
- page_title "New Label"
%h3 New label
.back-link
= 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-details
= render "projects/merge_requests/show/mr_title"
......
- page_title "Edit", "#{@merge_request.title} (##{@merge_request.iid})", "Merge Requests"
%h3.page-title
= "Edit merge request ##{@merge_request.iid}"
%hr
......
- page_title "Merge Requests"
.append-bottom-10
.pull-right
= 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
= render "projects/merge_requests/show/mr_title"
= render "projects/merge_requests/show/mr_box"
......
- page_title "New Merge Request"
- if @merge_request.can_be_created
= render 'new_submit'
- else
......
- page_title "Edit", @milestone.title, "Milestones"
= render "form"
- page_title "Milestones"
.pull-right
- if can? current_user, :admin_milestone, @project
= link_to new_namespace_project_milestone_path(@project.namespace, @project), class: "pull-right btn btn-new", title: "New Milestone" do
......
- page_title "New Milestone"
= render "form"
- page_title @milestone.title, "Milestones"
%h4.page-title
.issue-box{ class: issue_box_class(@milestone) }
- if @milestone.closed?
......
- page_title "Network", @ref
= render "head"
.project-network
.controls
......
- page_title 'New Project'
- header_title 'New Project'
.project-edit-container
.project-edit-errors
= render 'projects/errors'
......
- page_title "Import members"
%h3.page-title
Import members from another project
%p.light
......
- page_title "Members"
%h3.page-title
Users with access to this project
......
- page_title "Protected branches"
%h3.page-title Protected branches
%p.light Keep stable branches secure and force developers to use Merge Requests
%hr
......
- page_title @service.title, "Services"
= render 'form'
- page_title "Services"
%h3.page-title Project services
%p.light Project services allow you to integrate GitLab with other applications
......
- page_title "Edit", @snippet.title, "Snippets"
%h3.page-title
Edit snippet
%hr
......
- page_title "Snippets"
%h3.page-title
Snippets
- if can? current_user, :write_project_snippet, @project
......
- page_title "New Snippets"
%h3.page-title
New snippet
%hr
......
- page_title @snippet.title, "Snippets"
%h3.page-title
= @snippet.title
......
- page_title "Tags"
= render "projects/commits/head"
%h3.page-title
......
- page_title "New Tag"
- if @error
.alert.alert-danger
%button{ type: "button", class: "close", "data-dismiss" => "alert"} &times;
......
- page_title @path.presence || "Files", @ref
= content_for :meta_tags do
- if current_user
= auto_discovery_link_tag(:atom, namespace_project_commits_url(@project.namespace, @project, @ref, format: :atom, private_token: current_user.private_token), title: "#{@project.name}:#{@ref} commits")
......
- page_title "Edit", @page.title, "Wiki"
= render 'nav'
.pull-right
= render 'main_links'
......
- page_title "Wiki"
%h3.page-title Empty page
%hr
.error_message
......
- page_title "Git Access", "Wiki"
= render 'nav'
.row
.col-sm-6
......
- page_title "History", @page.title, "Wiki"
= render 'nav'
%h3.page-title
%span.light History for
......
- page_title "All Pages", "Wiki"
= render 'nav'
%h3.page-title
All Pages
......
- page_title @page.title, "Wiki"
= render 'nav'
%h3.page-title
= @page.title
......
- page_title @search_term
= render 'search/form'
%hr
- if @search_term
......
- page_title "Your Snippets"
%h3.page-title
Your Snippets
.pull-right
......
- page_title "Edit", @snippet.title, "Snippets"
%h3.page-title
Edit snippet
%hr
......
- page_title "Public Snippets"
%h3.page-title
Public snippets
......
- page_title "New Snippet"
%h3.page-title
New snippet
%hr
......
- page_title @snippet.title, "Snippets"
%h3.page-title
= @snippet.title
......
- page_title "Snippets", @user.name
%h3.page-title
= image_tag avatar_icon(@user.email), class: "avatar s24"
= @user.name
......
- page_title @user.name
- header_title @user.name, user_path(@user)
= content_for :meta_tags do
= auto_discovery_link_tag(:atom, user_url(@user, format: :atom), title: "#{@user.name} activity")
......
......@@ -2,13 +2,8 @@
# four configuration values can also be set straight in your models.
Devise.setup do |config|
# ==> Mailer Configuration
# Configure the e-mail address which will be shown in Devise::Mailer,
# note that it will be overwritten if you use your own mailer class with default "from" parameter.
config.mailer_sender = "GitLab <#{Gitlab.config.gitlab.email_from}>"
# Configure the class responsible to send e-mails.
# config.mailer = "Devise::Mailer"
config.mailer = "DeviseMailer"
# ==> ORM configuration
# Load and configure the ORM. Supports :active_record (default) and
......
# 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
t.datetime "created_at"
end
add_index "taggings", ["tag_id"], name: "index_taggings_on_tag_id", using: :btree
add_index "taggings", ["tag_id", "taggable_id", "taggable_type", "context", "tagger_id", "tagger_type"], name: "taggings_idx", unique: true, using: :btree
add_index "taggings", ["taggable_id", "taggable_type", "context"], name: "index_taggings_on_taggable_id_and_taggable_type_and_context", using: :btree
create_table "tags", force: true do |t|
t.string "name"
t.string "name"
t.integer "taggings_count", default: 0
end
add_index "tags", ["name"], name: "index_tags_on_name", unique: true, using: :btree
create_table "users", force: true do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
......
......@@ -6,6 +6,7 @@
- [Session](session.md)
- [Projects](projects.md)
- [Project Snippets](project_snippets.md)
- [Services](services.md)
- [Repositories](repositories.md)
- [Repository Files](repository_files.md)
- [Commits](commits.md)
......
......@@ -27,10 +27,10 @@ If you have local changes to your GitLab repository the script will stash them a
Note: GitLab 7.9 adds `nodejs` as a dependency. GitLab 7.6 adds `libkrb5-dev` as a dependency (installed by default on Ubuntu and OSX). GitLab 7.2 adds `pkg-config` and `cmake` as dependency. Please check the dependencies in the [installation guide.](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md#1-packages-dependencies)
cd /home/git/gitlab
sudo -u git -H ruby -Ilib -e 'require "gitlab/upgrader"' -e 'class Gitlab::Upgrader' -e 'def latest_version_raw' -e '"v7.10.0"' -e 'end' -e 'end' -e 'Gitlab::Upgrader.new.execute'
sudo -u git -H ruby -Ilib -e 'require "gitlab/upgrader"' -e 'class Gitlab::Upgrader' -e 'def latest_version_raw' -e '"v7.10.1"' -e 'end' -e 'end' -e 'Gitlab::Upgrader.new.execute'
# to perform a non-interactive install (no user input required) you can add -y
# sudo -u git -H ruby -Ilib -e 'require "gitlab/upgrader"' -e 'class Gitlab::Upgrader' -e 'def latest_version_raw' -e '"v7.10.0"' -e 'end' -e 'end' -e 'Gitlab::Upgrader.new.execute' -- -y
# sudo -u git -H ruby -Ilib -e 'require "gitlab/upgrader"' -e 'class Gitlab::Upgrader' -e 'def latest_version_raw' -e '"v7.10.1"' -e 'end' -e 'end' -e 'Gitlab::Upgrader.new.execute' -- -y
## 3. Start application
......@@ -65,7 +65,7 @@ Here is a one line command with step 1 to 5 for the next time you upgrade:
cd /home/git/gitlab; \
sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production; \
sudo service gitlab stop; \
sudo -u git -H ruby -Ilib -e 'require "gitlab/upgrader"' -e 'class Gitlab::Upgrader' -e 'def latest_version_raw' -e '"v7.10.0"' -e 'end' -e 'end' -e 'Gitlab::Upgrader.new.execute' -- -y; \
sudo -u git -H ruby -Ilib -e 'require "gitlab/upgrader"' -e 'class Gitlab::Upgrader' -e 'def latest_version_raw' -e '"v7.10.1"' -e 'end' -e 'end' -e 'Gitlab::Upgrader.new.execute' -- -y; \
cd /home/git/gitlab-shell; \
sudo -u git -H git fetch; \
sudo -u git -H git checkout v`cat /home/git/gitlab/GITLAB_SHELL_VERSION`; \
......
......@@ -55,3 +55,10 @@ Feature: Project
Then I should see project "Forum" README
And I visit project "Shop" page
Then I should see project "Shop" README
Scenario: I tag a project
When I visit edit project "Shop" page
Then I should see project settings
And I add project tags
And I save project
Then I should see project tags
......@@ -94,4 +94,12 @@ class Spinach::Features::Project < Spinach::FeatureSteps
page.should have_link 'README.md'
page.should have_content 'testme'
end
step 'I add project tags' do
fill_in 'Tags', with: 'tag1, tag2'
end
step 'I should see project tags' do
expect(find_field('Tags').value).to eq 'tag1, tag2'
end
end
......@@ -2,8 +2,12 @@ module SharedMarkdown
include Spinach::DSL
def header_should_have_correct_id_and_link(level, text, id, parent = ".wiki")
find(:css, "#{parent} h#{level}##{id}").text.should == text
find(:css, "#{parent} h#{level}##{id} > :last-child")[:href].should =~ /##{id}$/
node = find("#{parent} h#{level} a##{id}")
node[:href].should == "##{id}"
# Work around a weird Capybara behavior where calling `parent` on a node
# returns the whole document, not the node's actual parent element
find(:xpath, "#{node.path}/..").text.should == text
end
def create_taskable(type, title)
......
......@@ -21,6 +21,10 @@ module Gitlab
@repos ||= raw_data["projects"].map { |raw_repo| GoogleCodeImport::Repository.new(raw_repo) }.select(&:git?)
end
def incompatible_repos
@incompatible_repos ||= raw_data["projects"].map { |raw_repo| GoogleCodeImport::Repository.new(raw_repo) }.reject(&:git?)
end
def repo(id)
repos.find { |repo| repo.id == id }
end
......
......@@ -3,33 +3,10 @@ require 'html/pipeline'
module Gitlab
# Custom parser for GitLab-flavored Markdown
#
# It replaces references in the text with links to the appropriate items in
# GitLab.
#
# Supported reference formats are:
# * @foo for team members
# * #123 for issues
# * JIRA-123 for Jira issues
# * !123 for merge requests
# * $123 for snippets
# * 1c002d for specific commit
# * 1c002d...35cfb2 for commit ranges (comparisons)
#
# It also parses Emoji codes to insert images. See
# http://www.emoji-cheat-sheet.com/ for a list of the supported icons.
#
# Examples
#
# >> gfm("Hey @david, can you fix this?")
# => "Hey <a href="/u/david">@david</a>, can you fix this?"
#
# >> gfm("Commit 35d5f7c closes #1234")
# => "Commit <a href="/gitlab/commits/35d5f7c">35d5f7c</a> closes <a href="/gitlab/issues/1234">#1234</a>"
#
# >> gfm(":trollface:")
# => "<img alt=\":trollface:\" class=\"emoji\" src=\"/images/trollface.png" title=\":trollface:\" />
# See the files in `lib/gitlab/markdown/` for specific processing information.
module Markdown
# Provide autoload paths for filters to prevent a circular dependency error
autoload :AutolinkFilter, 'gitlab/markdown/autolink_filter'
autoload :CommitRangeReferenceFilter, 'gitlab/markdown/commit_range_reference_filter'
autoload :CommitReferenceFilter, 'gitlab/markdown/commit_reference_filter'
autoload :EmojiFilter, 'gitlab/markdown/emoji_filter'
......@@ -37,7 +14,9 @@ module Gitlab
autoload :IssueReferenceFilter, 'gitlab/markdown/issue_reference_filter'
autoload :LabelReferenceFilter, 'gitlab/markdown/label_reference_filter'
autoload :MergeRequestReferenceFilter, 'gitlab/markdown/merge_request_reference_filter'
autoload :SanitizationFilter, 'gitlab/markdown/sanitization_filter'
autoload :SnippetReferenceFilter, 'gitlab/markdown/snippet_reference_filter'
autoload :TableOfContentsFilter, 'gitlab/markdown/table_of_contents_filter'
autoload :UserReferenceFilter, 'gitlab/markdown/user_reference_filter'
# Public: Parse the provided text with GitLab-Flavored Markdown
......@@ -74,13 +53,13 @@ module Gitlab
pipeline = HTML::Pipeline.new(filters)
context = {
# SanitizationFilter
whitelist: sanitization_whitelist,
# EmojiFilter
asset_root: Gitlab.config.gitlab.url,
asset_host: Gitlab::Application.config.asset_host,
# TableOfContentsFilter
no_header_anchors: options[:no_header_anchors],
# ReferenceFilter
current_user: current_user,
only_path: options[:reference_only_path],
......@@ -111,12 +90,14 @@ module Gitlab
# SanitizationFilter should come first so that all generated reference HTML
# goes through untouched.
#
# See https://gitlab.com/gitlab-org/html-pipeline-gitlab for more filters
# See https://github.com/jch/html-pipeline#filters for more filters.
def filters
[
HTML::Pipeline::SanitizationFilter,
Gitlab::Markdown::SanitizationFilter,
Gitlab::Markdown::EmojiFilter,
Gitlab::Markdown::TableOfContentsFilter,
Gitlab::Markdown::AutolinkFilter,
Gitlab::Markdown::UserReferenceFilter,
Gitlab::Markdown::IssueReferenceFilter,
......@@ -125,36 +106,10 @@ module Gitlab
Gitlab::Markdown::SnippetReferenceFilter,
Gitlab::Markdown::CommitRangeReferenceFilter,
Gitlab::Markdown::CommitReferenceFilter,
Gitlab::Markdown::LabelReferenceFilter,
Gitlab::Markdown::LabelReferenceFilter
]
end
# Customize the SanitizationFilter whitelist
#
# - Allow `class` and `id` attributes on all elements
# - Allow `span` elements
# - Remove `rel` attributes from `a` elements
# - Remove `a` nodes with `javascript:` in the `href` attribute
def sanitization_whitelist
whitelist = HTML::Pipeline::SanitizationFilter::WHITELIST
whitelist[:attributes][:all].push('class', 'id')
whitelist[:elements].push('span')
fix_anchors = lambda do |env|
name, node = env[:node_name], env[:node]
if name == 'a'
node.remove_attribute('rel')
if node['href'] && node['href'].match('javascript:')
node.remove_attribute('href')
end
end
end
whitelist[:transformers].push(fix_anchors)
whitelist
end
# Turn list items that start with "[ ]" into HTML checkbox inputs.
def parse_tasks(text)
li_tag = '<li class="task-list-item">'
......
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
# Returns a String with `#123` references replaced with links. All links
# have `gfm` and `gfm-issue` class names attached for styling.
def issue_link_filter(text)
self.class.references_in(text) do |match, issue, project_ref|
self.class.references_in(text) do |match, id, project_ref|
project = self.project_from_ref(project_ref)
if project && project.issue_exists?(issue)
# FIXME (rspeicher): Law of Demeter
push_result(:issue, project.issues.where(iid: issue).first)
if project && issue = project.get_issue(id)
push_result(:issue, issue)
url = url_for_issue(issue, project, only_path: context[:only_path])
url = url_for_issue(id, project, only_path: context[:only_path])
title = escape_once("Issue: #{title_for_issue(issue, project)}")
title = escape_once("Issue: #{issue.title}")
klass = reference_class(:issue)
%(<a href="#{url}"
title="#{title}"
class="#{klass}">#{project_ref}##{issue}</a>)
class="#{klass}">#{project_ref}##{id}</a>)
else
match
end
......@@ -68,10 +67,6 @@ module Gitlab
def url_for_issue(*args)
IssuesHelper.url_for_issue(*args)
end
def title_for_issue(*args)
IssuesHelper.title_for_issue(*args)
end
end
end
end
......@@ -64,7 +64,6 @@ module Gitlab
end
end
# TODO (rspeicher): Cleanup
def url_for_merge_request(mr, project)
h = Rails.application.routes.url_helpers
h.namespace_project_merge_request_url(project.namespace, project, mr,
......
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
end
# Convenience method to get a space-separated String of all the theme
# classes that mighty be applied to the `body` element
# classes that might be applied to the `body` element
#
# Returns a String
def self.body_classes
......
class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
require 'active_support/core_ext/string/output_safety'
class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
attr_reader :template
alias_method :h, :template
......@@ -8,24 +9,12 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
@color_scheme = color_scheme
@project = @template.instance_variable_get("@project")
@options = options.dup
super options
end
def preprocess(full_document)
# Redcarpet doesn't allow SMB links when `safe_links_only` is enabled.
# FTP links are allowed, so we trick Redcarpet.
full_document.gsub("smb://", "ftp://smb:")
super(options)
end
# If project has issue number 39, apostrophe will be linked in
# regular text to the issue as Redcarpet will convert apostrophe to
# #39;
# We replace apostrophe with right single quote before Redcarpet
# does the processing and put the apostrophe back in postprocessing.
# This only influences regular text, code blocks are untouched.
def normal_text(text)
return text unless text.present?
text.gsub("'", "&rsquo;")
ERB::Util.html_escape_once(text)
end
# Stolen from Rugments::Plugins::Redcarpet as this module is not required
......@@ -37,7 +26,7 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
# so we assume you're not using leading spaces that aren't tabs,
# and just replace them here.
if lexer.tag == 'make'
code.gsub! /^ /, "\t"
code.gsub!(/^ /, "\t")
end
formatter = Rugments::Formatters::HTML.new(
......@@ -46,27 +35,11 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
formatter.format(lexer.lex(code))
end
def link(link, title, content)
h.link_to_gfm(content, link, title: title)
end
def header(text, level)
if @options[:no_header_anchors]
"<h#{level}>#{text}</h#{level}>"
else
id = ActionController::Base.helpers.strip_tags(h.gfm(text)).downcase() \
.gsub(/[^a-z0-9_-]/, '-').gsub(/-+/, '-').gsub(/^-/, '').gsub(/-$/, '')
"<h#{level} id=\"#{id}\">#{text}<a href=\"\##{id}\"></a></h#{level}>"
end
end
def postprocess(full_document)
full_document.gsub!("ftp://smb:", "smb://")
full_document.gsub!("&rsquo;", "'")
unless @template.instance_variable_get("@project_wiki") || @project.nil?
full_document = h.create_relative_links(full_document)
end
h.gfm_with_options(full_document, @options)
end
end
......@@ -27,21 +27,34 @@ describe Import::GoogleCodeController do
it "assigns variables" do
@project = create(:project, import_type: 'google_code', creator_id: user.id)
controller.stub_chain(:client, :repos).and_return([@repo])
controller.stub_chain(:client, :incompatible_repos).and_return([])
get :status
expect(assigns(:already_added_projects)).to eq([@project])
expect(assigns(:repos)).to eq([@repo])
expect(assigns(:incompatible_repos)).to eq([])
end
it "does not show already added project" do
@project = create(:project, import_type: 'google_code', creator_id: user.id, import_source: 'vim')
controller.stub_chain(:client, :repos).and_return([@repo])
controller.stub_chain(:client, :incompatible_repos).and_return([])
get :status
expect(assigns(:already_added_projects)).to eq([@project])
expect(assigns(:repos)).to eq([])
end
it "does not show any invalid projects" do
controller.stub_chain(:client, :repos).and_return([])
controller.stub_chain(:client, :incompatible_repos).and_return([@repo])
get :status
expect(assigns(:repos)).to be_empty
expect(assigns(:incompatible_repos)).to eq([@repo])
end
end
end
......@@ -94,10 +94,26 @@ FactoryGirl.define do
'new_issue_url' => 'http://redmine/projects/project_name_in_redmine/issues/new'
}
)
end
after :create do |project|
project.issues_tracker = 'redmine'
project.issues_tracker_id = 'project_name_in_redmine'
end
end
factory :jira_project, parent: :project do
after :create do |project|
project.create_jira_service(
active: true,
properties: {
'title' => 'JIRA tracker',
'project_url' => 'http://jira.example/issues/?jql=project=A',
'issues_url' => 'http://jira.example/browse/:id',
'new_issue_url' => 'http://jira.example/secure/CreateIssue.jspa'
}
)
project.issues_tracker = 'jira'
project.issues_tracker_id = 'project_name_in_jira'
end
end
end
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
end
it 'should not be confused by whitespace before bullets' do
rendered_text_asterisk = markdown(@source_text_asterisk,
parse_tasks: true)
rendered_text_asterisk = markdown(@source_text_asterisk, parse_tasks: true)
rendered_text_dash = markdown(@source_text_dash, parse_tasks: true)
expect(rendered_text_asterisk).to match(
......@@ -207,78 +206,7 @@ describe GitlabMarkdownHelper do
end
describe "#markdown" do
# TODO (rspeicher) - This block tests multiple different contexts. Break this up!
it "should add ids and links to headers" do
# Test every rule except nested tags.
text = '..Ab_c-d. e..'
id = 'ab_c-d-e'
expect(markdown("# #{text}")).
to match(%r{<h1 id="#{id}">#{text}<a href="[^"]*##{id}"></a></h1>})
expect(markdown("# #{text}", {no_header_anchors:true})).
to eq("<h1>#{text}</h1>")
id = 'link-text'
expect(markdown("# [link text](url) ![img alt](url)")).to match(
%r{<h1 id="#{id}"><a href="[^"]*url">link text</a> <img[^>]*><a href="[^"]*##{id}"></a></h1>}
)
end
# REFERENCES (PART TWO: THE REVENGE) ---------------------------------------
it "should handle references in headers" do
actual = "\n# Working around ##{issue.iid}\n## Apply !#{merge_request.iid}"
expect(markdown(actual, no_header_anchors: true)).
to match(%r{<h1[^<]*>Working around <a.+>##{issue.iid}</a></h1>})
expect(markdown(actual, no_header_anchors: true)).
to match(%r{<h2[^<]*>Apply <a.+>!#{merge_request.iid}</a></h2>})
end
it "should handle references in <em>" do
actual = "Apply _!#{merge_request.iid}_ ASAP"
expect(markdown(actual)).
to match(%r{Apply <em><a.+>!#{merge_request.iid}</a></em>})
end
# CODE BLOCKS -------------------------------------------------------------
it "should leave code blocks untouched" do
allow(helper).to receive(:current_user).and_return(user)
allow(helper).to receive(:user_color_scheme_class).and_return(:white)
target_html = "<pre class=\"code highlight white plaintext\"><code>some code from $#{snippet.id}\nhere too\n</code></pre>\n"
expect(markdown("\n some code from $#{snippet.id}\n here too\n")).
to eq(target_html)
expect(markdown("\n```\nsome code from $#{snippet.id}\nhere too\n```\n")).
to eq(target_html)
end
it "should leave inline code untouched" do
expect(markdown("Don't use `$#{snippet.id}` here.")).
to eq "<p>Don't use <code>$#{snippet.id}</code> here.</p>\n"
end
# REF-LIKE AUTOLINKS? -----------------------------------------------------
# Basically: Don't parse references inside `<a>` tags.
it "should leave ref-like autolinks untouched" do
expect(markdown("look at http://example.tld/#!#{merge_request.iid}")).to eq("<p>look at <a href=\"http://example.tld/#!#{merge_request.iid}\">http://example.tld/#!#{merge_request.iid}</a></p>\n")
end
it "should leave ref-like href of 'manual' links untouched" do
expect(markdown("why not [inspect !#{merge_request.iid}](http://example.tld/#!#{merge_request.iid})")).to eq("<p>why not <a href=\"http://example.tld/#!#{merge_request.iid}\">inspect </a><a href=\"#{namespace_project_merge_request_path(project.namespace, project, merge_request)}\" title=\"Merge Request: #{merge_request.title}\" class=\"gfm gfm-merge_request\">!#{merge_request.iid}</a><a href=\"http://example.tld/#!#{merge_request.iid}\"></a></p>\n")
end
it "should leave ref-like src of images untouched" do
expect(markdown("screen shot: ![some image](http://example.tld/#!#{merge_request.iid})")).to eq("<p>screen shot: <img src=\"http://example.tld/#!#{merge_request.iid}\" alt=\"some image\"></p>\n")
end
# RELATIVE URLS -----------------------------------------------------------
# TODO (rspeicher): These belong in a relative link filter spec
context 'relative links' do
context 'with a valid repository' do
before do
......@@ -333,11 +261,6 @@ describe GitlabMarkdownHelper do
expected = ""
expect(markdown(actual)).to match(expected)
end
it 'should allow whitelisted HTML tags from the user' do
actual = '<dl><dt>Term</dt><dd>Definition</dd></dl>'
expect(markdown(actual)).to match(actual)
end
end
context 'with an empty repository' do
......@@ -353,34 +276,6 @@ describe GitlabMarkdownHelper do
end
end
end
# SANITIZATION ------------------------------------------------------------
# TODO (rspeicher): These are testing SanitizationFilter, not `markdown`
it 'should sanitize tags that are not whitelisted' do
actual = '<textarea>no inputs allowed</textarea> <blink>no blinks</blink>'
expected = 'no inputs allowed no blinks'
expect(markdown(actual)).to match(expected)
expect(markdown(actual)).not_to match('<.textarea>')
expect(markdown(actual)).not_to match('<.blink>')
end
it 'should allow whitelisted tag attributes from the user' do
actual = '<a class="custom">link text</a>'
expect(markdown(actual)).to match(actual)
end
it 'should sanitize tag attributes that are not whitelisted' do
actual = '<a href="http://example.com/bar.html" foo="bar">link text</a>'
expected = '<a href="http://example.com/bar.html">link text</a>'
expect(markdown(actual)).to match(expected)
end
it 'should sanitize javascript in attributes' do
actual = %q(<a href="javascript:alert('foo')">link text</a>)
expected = '<a>link text</a>'
expect(markdown(actual)).to match(expected)
end
end
describe '#render_wiki_content' do
......
......@@ -5,24 +5,6 @@ describe IssuesHelper do
let(:issue) { create :issue, project: project }
let(:ext_project) { create :redmine_project }
describe "title_for_issue" do
it "should return issue title if used internal tracker" do
@project = project
expect(title_for_issue(issue.iid)).to eq issue.title
end
it "should always return empty string if used external tracker" do
@project = ext_project
expect(title_for_issue(rand(100))).to eq ""
end
it "should always return empty string if project nil" do
@project = nil
expect(title_for_issue(rand(100))).to eq ""
end
end
describe "url_for_project_issues" do
let(:project_url) { ext_project.external_issue_tracker.project_url }
let(:ext_expected) do
......
......@@ -23,6 +23,7 @@ describe Gitlab::GoogleCodeImport::Client do
describe "#repos" do
it "returns only Git repositories" do
expect(subject.repos.length).to eq(1)
expect(subject.incompatible_repos.length).to eq(1)
end
end
......
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
IssuesHelper
end
let(:project) { create(:empty_project) }
let(:project) { create(:jira_project) }
let(:issue) { double('issue', iid: 123) }
context 'JIRA issue references' do
let(:reference) { "JIRA-#{issue.iid}" }
before do
jira = project.create_jira_service
props = {
'title' => 'JIRA tracker',
'project_url' => 'http://jira.example/issues/?jql=project=A',
'issues_url' => 'http://jira.example/browse/:id',
'new_issue_url' => 'http://jira.example/secure/CreateIssue.jspa'
}
jira.update_attributes(properties: props, active: true)
end
after do
project.jira_service.destroy
end
it 'requires project context' do
expect { described_class.call('Issue JIRA-123', {}) }.
to raise_error(ArgumentError, /:project/)
......
......@@ -27,7 +27,7 @@ module Gitlab::Markdown
let(:reference) { "##{issue.iid}" }
it 'ignores valid references when using non-default tracker' do
expect(project).to receive(:issue_exists?).with(issue.iid).and_return(false)
expect(project).to receive(:get_issue).with(issue.iid).and_return(nil)
exp = act = "Issue ##{issue.iid}"
expect(filter(act).to_html).to eq exp
......@@ -48,7 +48,7 @@ module Gitlab::Markdown
it 'ignores invalid issue IDs' do
exp = act = "Fixed ##{issue.iid + 1}"
expect(project).to receive(:issue_exists?).with(issue.iid + 1)
expect(project).to receive(:get_issue).with(issue.iid + 1).and_return(nil)
expect(filter(act).to_html).to eq exp
end
......@@ -98,8 +98,8 @@ module Gitlab::Markdown
before { allow_cross_reference! }
it 'ignores valid references when cross-reference project uses external tracker' do
expect_any_instance_of(Project).to receive(:issue_exists?).
with(issue.iid).and_return(false)
expect_any_instance_of(Project).to receive(:get_issue).
with(issue.iid).and_return(nil)
exp = act = "Issue ##{issue.iid}"
expect(filter(act).to_html).to eq exp
......
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
let(:recipient) { create(:user, email: 'recipient@example.com') }
let(:project) { create(:project) }
around(:each) { ActionMailer::Base.deliveries.clear }
before(:each) do
ActionMailer::Base.deliveries.clear
email = recipient.emails.create(email: "notifications@example.com")
recipient.update_attribute(:notification_email, email.email)
end
......
......@@ -129,6 +129,48 @@ describe Project do
end
end
describe '#get_issue' do
let(:project) { create(:empty_project) }
let(:issue) { create(:issue, project: project) }
context 'with default issues tracker' do
it 'returns an issue' do
expect(project.get_issue(issue.iid)).to eq issue
end
it 'returns nil when no issue found' do
expect(project.get_issue(999)).to be_nil
end
end
context 'with external issues tracker' do
before do
allow(project).to receive(:default_issues_tracker?).and_return(false)
end
it 'returns an ExternalIssue' do
issue = project.get_issue('FOO-1234')
expect(issue).to be_kind_of(ExternalIssue)
expect(issue.iid).to eq 'FOO-1234'
expect(issue.project).to eq project
end
end
end
describe '#issue_exists?' do
let(:project) { create(:empty_project) }
it 'is truthy when issue exists' do
expect(project).to receive(:get_issue).and_return(double)
expect(project.issue_exists?(1)).to be_truthy
end
it 'is falsey when issue does not exist' do
expect(project).to receive(:get_issue).and_return(nil)
expect(project.issue_exists?(1)).to be_falsey
end
end
describe :update_merge_requests do
let(:project) { create(:project) }
let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
......@@ -180,25 +222,6 @@ describe Project do
end
end
describe :issue_exists? do
let(:project) { create(:project) }
let(:existed_issue) { create(:issue, project: project) }
let(:not_existed_issue) { create(:issue) }
let(:ext_project) { create(:redmine_project) }
it 'should be true or if used internal tracker and issue exists' do
expect(project.issue_exists?(existed_issue.iid)).to be_truthy
end
it 'should be false or if used internal tracker and issue not exists' do
expect(project.issue_exists?(not_existed_issue.iid)).to be_falsey
end
it 'should always be true if used other tracker' do
expect(ext_project.issue_exists?(rand(100))).to be_truthy
end
end
describe :default_issues_tracker? do
let(:project) { create(:project) }
let(:ext_project) { create(:redmine_project) }
......
......@@ -35,6 +35,7 @@ module TestEnv
# Create repository for FactoryGirl.create(:project)
setup_factory_repo
# Create repository for FactoryGirl.create(:forked_project_with_submodules)
setup_forked_repo
end
......@@ -54,7 +55,7 @@ module TestEnv
tmp_test_path = Rails.root.join('tmp', 'tests', '**')
Dir[tmp_test_path].each do |entry|
unless File.basename(entry) =~ /\Agitlab-(shell|test)\z/
unless File.basename(entry) =~ /\Agitlab-(shell|test|test-fork)\z/
FileUtils.rm_rf(entry)
end
end
......
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