Merge branch 'master' into fixes/api

Conflicts:
	lib/api/projects.rb
This commit is contained in:
Sebastian Ziebell 2013-03-05 22:29:49 +01:00
commit 61cfa2a7a6
146 changed files with 1562 additions and 859 deletions

View file

@ -1,92 +0,0 @@
Copyright (c) 2010, Jan Gerner (post@yanone.de)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

View file

@ -0,0 +1,400 @@
!function(){
var BranchGraph = function(element, options){
this.element = element;
this.options = options;
this.preparedCommits = {};
this.mtime = 0;
this.mspace = 0;
this.parents = {};
this.colors = ["#000"];
this.load();
};
BranchGraph.prototype.load = function(){
$.ajax({
url: this.options.url,
method: 'get',
dataType: 'json',
success: $.proxy(function(data){
$('.loading', this.element).hide();
this.prepareData(data.days, data.commits);
this.buildGraph();
}, this)
});
};
BranchGraph.prototype.prepareData = function(days, commits){
this.days = days;
this.dayCount = days.length;
this.commits = commits;
this.commitCount = commits.length;
this.collectParents();
this.mtime += 4;
this.mspace += 10;
for (var i = 0; i < this.commitCount; i++) {
if (this.commits[i].id in this.parents) {
this.commits[i].isParent = true;
}
this.preparedCommits[this.commits[i].id] = this.commits[i];
}
this.collectColors();
};
BranchGraph.prototype.collectParents = function(){
for (var i = 0; i < this.commitCount; i++) {
for (var j = 0, jj = this.commits[i].parents.length; j < jj; j++) {
this.parents[this.commits[i].parents[j][0]] = true;
}
this.mtime = Math.max(this.mtime, this.commits[i].time);
this.mspace = Math.max(this.mspace, this.commits[i].space);
}
};
BranchGraph.prototype.collectColors = function(){
for (var k = 0; k < this.mspace; k++) {
this.colors.push(Raphael.getColor(.8));
// Skipping a few colors in the spectrum to get more contrast between colors
Raphael.getColor();Raphael.getColor();
}
};
BranchGraph.prototype.buildGraph = function(){
var graphWidth = $(this.element).width()
, ch = this.mspace * 20 + 100
, cw = Math.max(graphWidth, this.mtime * 20 + 260)
, r = Raphael(this.element.get(0), cw, ch)
, top = r.set()
, cuday = 0
, cumonth = ""
, offsetX = 20
, offsetY = 60
, barWidth = Math.max(graphWidth, this.dayCount * 20 + 320)
, scrollLeft = cw;
this.raphael = r;
r.rect(0, 0, barWidth, 20).attr({fill: "#222"});
r.rect(0, 20, barWidth, 20).attr({fill: "#444"});
for (mm = 0; mm < this.dayCount; mm++) {
if(this.days[mm] != null){
if(cuday != this.days[mm][0]){
// Dates
r.text(offsetX + mm * 20, 31, this.days[mm][0]).attr({
font: "12px Monaco, monospace",
fill: "#DDD"
});
cuday = this.days[mm][0];
}
if(cumonth != this.days[mm][1]){
// Months
r.text(offsetX + mm * 20, 11, this.days[mm][1]).attr({
font: "12px Monaco, monospace",
fill: "#EEE"
});
cumonth = this.days[mm][1];
}
}
}
for (i = 0; i < this.commitCount; i++) {
var x = offsetX + 20 * this.commits[i].time
, y = offsetY + 10 * this.commits[i].space
, c
, ps;
// Draw dot
r.circle(x, y, 3).attr({
fill: this.colors[this.commits[i].space],
stroke: "none"
});
// Draw lines
for (var j = 0, jj = this.commits[i].parents.length; j < jj; j++) {
c = this.preparedCommits[this.commits[i].parents[j][0]];
ps = this.commits[i].parent_spaces[j];
if (c) {
var cx = offsetX + 20 * c.time
, cy = offsetY + 10 * c.space
, psy = offsetY + 10 * ps;
if (c.space == this.commits[i].space && c.space == ps) {
r.path([
"M", x, y,
"L", cx, cy
]).attr({
stroke: this.colors[c.space],
"stroke-width": 2
});
} else if (c.space < this.commits[i].space) {
if (y == psy) {
r.path([
"M", x - 5, y,
"l-5,-2,0,4,5,-2",
"L", x - 10, y,
"L", x - 15, psy,
"L", cx + 5, psy,
"L", cx, cy])
.attr({
stroke: this.colors[this.commits[i].space],
"stroke-width": 2
});
} else {
r.path([
"M", x - 3, y - 6,
"l-4,-3,4,-2,0,5",
"L", x - 5, y - 10,
"L", x - 10, psy,
"L", cx + 5, psy,
"L", cx, cy])
.attr({
stroke: this.colors[this.commits[i].space],
"stroke-width": 2
});
}
} else {
r.path([
"M", x - 3, y + 6,
"l-4,3,4,2,0,-5",
"L", x - 5, y + 10,
"L", x - 10, psy,
"L", cx + 5, psy,
"L", cx, cy])
.attr({
stroke: this.colors[c.space],
"stroke-width": 2
});
}
}
}
if (this.commits[i].refs) {
this.appendLabel(x, y, this.commits[i].refs);
}
// mark commit and displayed in the center
if (this.commits[i].id == this.options.commit_id) {
r.path([
'M', x, y - 5,
'L', x + 4, y - 15,
'L', x - 4, y - 15,
'Z'
]).attr({
"fill": "#000",
"fill-opacity": .7,
"stroke": "none"
});
scrollLeft = x - graphWidth / 2;
}
this.appendAnchor(top, this.commits[i], x, y);
}
top.toFront();
this.element.scrollLeft(scrollLeft);
this.bindEvents();
};
BranchGraph.prototype.bindEvents = function(){
var drag = {}
, element = this.element;
var dragger = function(event){
element.scrollLeft(drag.sl - (event.clientX - drag.x));
element.scrollTop(drag.st - (event.clientY - drag.y));
};
element.on({
mousedown: function (event) {
drag = {
x: event.clientX,
y: event.clientY,
st: element.scrollTop(),
sl: element.scrollLeft()
};
$(window).on('mousemove', dragger);
}
});
$(window).on({
mouseup: function(){
//bars.animate({opacity: 0}, 300);
$(window).off('mousemove', dragger);
},
keydown: function(event){
if(event.keyCode == 37){
// left
element.scrollLeft( element.scrollLeft() - 50);
}
if(event.keyCode == 38){
// top
element.scrollTop( element.scrollTop() - 50);
}
if(event.keyCode == 39){
// right
element.scrollLeft( element.scrollLeft() + 50);
}
if(event.keyCode == 40){
// bottom
element.scrollTop( element.scrollTop() + 50);
}
}
});
};
BranchGraph.prototype.appendLabel = function(x, y, refs){
var r = this.raphael
, shortrefs = refs
, text, textbox, rect;
if (shortrefs.length > 17){
// Truncate if longer than 15 chars
shortrefs = shortrefs.substr(0,15) + "…";
}
text = r.text(x+5, y+8 + 10, shortrefs).attr({
font: "10px Monaco, monospace",
fill: "#FFF",
title: refs
});
textbox = text.getBBox();
text.transform([
't', textbox.height/-4, textbox.width/2 + 5,
'r90'
]);
// Create rectangle based on the size of the textbox
rect = r.rect(x, y, textbox.width + 15, textbox.height + 5, 4).attr({
"fill": "#000",
"fill-opacity": .7,
"stroke": "none"
});
triangle = r.path([
'M', x, y + 5,
'L', x + 4, y + 15,
'L', x - 4, y + 15,
'Z'
]).attr({
"fill": "#000",
"fill-opacity": .7,
"stroke": "none"
});
// Rotate and reposition rectangle over text
rect.transform([
'r', 90, x, y,
't', 15, -9
]);
// Set text to front
text.toFront();
};
BranchGraph.prototype.appendAnchor = function(top, commit, x, y) {
var r = this.raphael
, options = this.options
, anchor;
anchor = r.circle(x, y, 10).attr({
fill: "#000",
opacity: 0,
cursor: "pointer"
})
.click(function(){
window.open(options.commit_url.replace('%s', commit.id), '_blank');
})
.hover(function(){
this.tooltip = r.commitTooltip(x, y + 5, commit);
top.push(this.tooltip.insertBefore(this));
}, function(){
this.tooltip && this.tooltip.remove() && delete this.tooltip;
});
top.push(anchor);
};
this.BranchGraph = BranchGraph;
}(this);
Raphael.fn.commitTooltip = function(x, y, commit){
var icon, nameText, idText, messageText
, boxWidth = 300
, boxHeight = 200;
icon = this.image(commit.author.icon, x, y, 20, 20);
nameText = this.text(x + 25, y + 10, commit.author.name);
idText = this.text(x, y + 35, commit.id);
messageText = this.text(x, y + 50, commit.message);
textSet = this.set(icon, nameText, idText, messageText).attr({
"text-anchor": "start",
"font": "12px Monaco, monospace"
});
nameText.attr({
"font": "14px Arial",
"font-weight": "bold"
});
idText.attr({
"fill": "#AAA"
});
textWrap(messageText, boxWidth - 50);
var rect = this.rect(x - 10, y - 10, boxWidth, 100, 4).attr({
"fill": "#FFF",
"stroke": "#000",
"stroke-linecap": "round",
"stroke-width": 2
});
var tooltip = this.set(rect, textSet);
rect.attr({
"height" : tooltip.getBBox().height + 10,
"width" : tooltip.getBBox().width + 10
});
tooltip.transform([
't', 20, 20
]);
return tooltip;
};
function textWrap(t, width) {
var content = t.attr("text");
var abc = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
t.attr({
"text" : abc
});
var letterWidth = t.getBBox().width / abc.length;
t.attr({
"text" : content
});
var words = content.split(" ");
var x = 0, s = [];
for ( var i = 0; i < words.length; i++) {
var l = words[i].length;
if (x + (l * letterWidth) > width) {
s.push("\n");
x = 0;
}
x += l * letterWidth;
s.push(words[i] + " ");
}
t.attr({
"text" : s.join("")
});
var b = t.getBBox()
, h = Math.abs(b.y2) - Math.abs(b.y) + 1;
t.attr({
"y": b.y + h
});
}

View file

@ -54,10 +54,10 @@ $ ->
$(@).parents('form').submit()
# Flash
if (flash = $("#flash-container")).length > 0
flash.click -> $(@).slideUp("slow")
flash.slideDown "slow"
setTimeout (-> flash.slideUp("slow")), 3000
if (flash = $(".flash-container")).length > 0
flash.click -> $(@).fadeOut()
flash.show()
setTimeout (-> flash.fadeOut()), 3000
# Disable form buttons while a form is submitting
$('body').on 'ajax:complete, ajax:beforeSend, submit', 'form', (e) ->

View file

@ -18,3 +18,18 @@ $ ->
# Ref switcher
$('.project-refs-select').on 'change', ->
$(@).parents('form').submit()
$('#project_issues_enabled').change ->
if ($(this).is(':checked') == true)
$('#project_issues_tracker').removeAttr('disabled')
else
$('#project_issues_tracker').attr('disabled', 'disabled')
$('#project_issues_tracker').change()
$('#project_issues_tracker').change ->
if ($(this).val() == gon.default_issues_tracker || $(this).is(':disabled'))
$('#project_issues_tracker_id').attr('disabled', 'disabled')
else
$('#project_issues_tracker_id').removeAttr('disabled')

View file

@ -67,27 +67,17 @@ table a code {
}
/** FLASH message **/
#flash-container {
height: 50px;
position: fixed;
z-index: 10001;
top: 0px;
width: 100%;
margin-bottom: 15px;
overflow: hidden;
background: white;
cursor: pointer;
border-bottom: 1px solid #ccc;
text-align: center;
.flash-container {
display: none;
.alert {
cursor: pointer;
margin: 0;
text-align: center;
border-radius: 0;
h4 {
color: #666;
font-size: 18px;
line-height: 38px;
padding-top: 5px;
margin: 2px;
font-weight: normal;
span {
font-size: 14px;
}
}
}
@ -203,10 +193,6 @@ input[type=text] {
}
}
input.git_clone_url {
width: 325px;
}
.merge-request-form-holder {
select {
width: 300px;

View file

@ -30,6 +30,8 @@
border-color: #DDD;
}
.well { padding: 15px; }
/** HELPERS **/
.nothing_here_message {
text-align: center;

View file

@ -1,7 +1,2 @@
@font-face{
font-family: Yanone;
src: font-url('YanoneKaffeesatz-Light.ttf');
}
/** Typo **/
$monospace_font: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono', 'lucida console', monospace;

View file

@ -70,8 +70,19 @@
@mixin header-font {
color: $style_color;
text-shadow: 0 1px 1px #FFF;
font-family: 'Yanone', sans-serif;
font-size: 24px;
line-height: 36px;
font-size: 18px;
line-height: 42px;
font-weight: normal;
letter-spacing: -1px;
}
@mixin md-typography {
code { padding: 0 4px; }
p { font-size: 13px; }
h1 { font-size: 26px; line-height: 40px; margin: 10px 0;}
h2 { font-size: 22px; line-height: 40px; margin: 10px 0;}
h3 { font-size: 18px; line-height: 40px; margin: 10px 0;}
h4 { font-size: 16px; line-height: 20px; margin: 10px 0;}
h5 { font-size: 14px; line-height: 20px; margin: 10px 0;}
h6 { font-size: 12px; line-height: 20px; margin: 10px 0;}
}

View file

@ -87,16 +87,15 @@ a:focus {
*
*/
.wiki {
font-size: 13px;
@include md-typography;
font-size: 13px;
line-height: 20px;
code { padding: 0 4px; }
p { font-size: 13px; }
h1 { font-size: 32px; line-height: 40px; margin: 10px 0;}
h2 { font-size: 26px; line-height: 40px; margin: 10px 0;}
h3 { font-size: 22px; line-height: 40px; margin: 10px 0;}
h4 { font-size: 18px; line-height: 20px; margin: 10px 0;}
h5 { font-size: 14px; line-height: 20px; margin: 10px 0;}
h6 { font-size: 12px; line-height: 20px; margin: 10px 0;}
.white .highlight pre { background: #f5f5f5; }
ul { margin: 0 0 9px 25px !important; }
}
.md {
@include md-typography;
}

View file

@ -1,8 +1,7 @@
.black .highlight {
background-color: #333;
pre {
background-color: #333;
color: #eee;
background: inherit;
}
.hll { display: block; background-color: darken($hover, 65%) }

View file

@ -100,8 +100,9 @@
}
}
.line_content {
display: block;
white-space: pre;
height: 14px;
height: 18px;
margin: 0px;
padding: 0px;
border: none;

View file

@ -48,15 +48,13 @@
color: #666;
}
.event-note {
padding-top: 5px;
padding-left: 5px;
display: inline-block;
color: #555;
margin-top: 5px;
margin-left: 40px;
.note-file-attach {
margin-left: -25px;
float: left;
.note-image-attach {
margin-top: 4px;
margin-left: 0px;
max-width: 200px;
}
@ -66,8 +64,8 @@
color: #777;
float: left;
font-size: 16px;
line-height: 18px;
margin: 5px;
line-height: 16px;
margin-right: 5px;
}
}
.avatar {

View file

@ -67,7 +67,7 @@ header {
position: relative;
float: left;
margin: 0;
margin-left: 15px;
margin-left: 10px;
@include header-font;
}

View file

@ -1,7 +1,7 @@
/* Login Page */
body.login-page{
padding-top: 7%;
background: #666;
background: #EEE;
.container .content { padding-top: 5%; }
}
.login-box{

View file

@ -83,6 +83,7 @@ ul.notes {
margin-top: -20px;
}
.note-body {
@include md-typography;
margin-left: 45px;
}
.note-header {

View file

@ -80,6 +80,7 @@
border: 1px solid #BBB;
box-shadow: none;
margin-left: -1px;
background: #FFF;
}
}

View file

@ -7,12 +7,13 @@ class IssuesListContext < BaseContext
@issues = case params[:status]
when issues_filter[:all] then @project.issues
when issues_filter[:closed] then @project.issues.closed
when issues_filter[:to_me] then @project.issues.opened.assigned(current_user)
when issues_filter[:to_me] then @project.issues.assigned(current_user)
when issues_filter[:by_me] then @project.issues.authored(current_user)
else @project.issues.opened
end
@issues = @issues.tagged_with(params[:label_name]) if params[:label_name].present?
@issues = @issues.includes(:author, :project).order("updated_at")
@issues = @issues.includes(:author, :project)
# Filter by specific assignee_id (or lack thereof)?
if params[:assignee_id].present?

View file

@ -1,7 +1,7 @@
class Admin::Teams::MembersController < Admin::Teams::ApplicationController
def new
@users = User.potential_team_members(user_team)
@users = UserDecorator.decorate @users
@users = UserDecorator.decorate_collection @users
end
def create

View file

@ -45,7 +45,7 @@ class Admin::UsersController < Admin::ApplicationController
end
def unblock
if admin_user.update_attribute(:blocked, false)
if admin_user.activate
redirect_to :back, alert: "Successfully unblocked"
else
redirect_to :back, alert: "Error occured. User was not unblocked"

View file

@ -5,6 +5,7 @@ class ApplicationController < ActionController::Base
before_filter :add_abilities
before_filter :dev_tools if Rails.env == 'development'
before_filter :default_headers
before_filter :add_gon_variables
protect_from_forgery
@ -29,7 +30,7 @@ class ApplicationController < ActionController::Base
end
def reject_blocked!
if current_user && current_user.blocked
if current_user && current_user.blocked?
sign_out current_user
flash[:alert] = "Your account is blocked. Retry when an admin unblock it."
redirect_to new_user_session_path
@ -37,7 +38,7 @@ class ApplicationController < ActionController::Base
end
def after_sign_in_path_for resource
if resource.is_a?(User) && resource.respond_to?(:blocked) && resource.blocked
if resource.is_a?(User) && resource.respond_to?(:blocked?) && resource.blocked?
sign_out resource
flash[:alert] = "Your account is blocked. Retry when an admin unblock it."
new_user_session_path
@ -148,4 +149,8 @@ class ApplicationController < ActionController::Base
headers['X-Frame-Options'] = 'DENY'
headers['X-XSS-Protection'] = '1; mode=block'
end
def add_gon_variables
gon.default_issues_tracker = Project.issues_tracker.default_value
end
end

View file

@ -13,7 +13,7 @@ class CommitsController < ProjectResourceController
@limit, @offset = (params[:limit] || 40), (params[:offset] || 0)
@commits = @repo.commits(@ref, @path, @limit, @offset)
@commits = CommitDecorator.decorate(@commits)
@commits = CommitDecorator.decorate_collection(@commits)
respond_to do |format|
format.html # index.html.erb

View file

@ -16,7 +16,7 @@ class CompareController < ProjectResourceController
@refs_are_same = result[:same]
@line_notes = []
@commits = CommitDecorator.decorate(@commits)
@commits = CommitDecorator.decorate_collection(@commits)
end
def create

View file

@ -1,5 +1,6 @@
class GraphController < ProjectResourceController
include ExtractsPath
include ApplicationHelper
# Authorize
before_filter :authorize_read_project!
@ -20,7 +21,10 @@ class GraphController < ProjectResourceController
respond_to do |format|
format.html
format.json do
graph = Gitlab::Graph::JsonBuilder.new(project, @ref, @commit)
graph = Graph::JsonBuilder.new(project, @ref, @commit)
graph.commits.each do |c|
c.icon = gravatar_icon(c.author.email)
end
render :json => graph.to_json
end
end

View file

@ -81,7 +81,8 @@ class MergeRequestsController < ProjectResourceController
end
def automerge
return access_denied! unless can?(current_user, :accept_mr, @project)
return access_denied! unless allowed_to_merge?
if @merge_request.opened? && @merge_request.can_be_merged?
@merge_request.should_remove_source_branch = params[:should_remove_source_branch]
@merge_request.automerge!(current_user)
@ -142,6 +143,19 @@ class MergeRequestsController < ProjectResourceController
# Get commits from repository
# or from cache if already merged
@commits = @merge_request.commits
@commits = CommitDecorator.decorate(@commits)
@commits = CommitDecorator.decorate_collection(@commits)
@allowed_to_merge = allowed_to_merge?
@show_merge_controls = @merge_request.opened? && @commits.any? && @allowed_to_merge
end
def allowed_to_merge?
action = if project.protected_branch?(@merge_request.target_branch)
:push_code_to_protected_branches
else
:push_code
end
can?(current_user, action, @project)
end
end

View file

@ -32,7 +32,7 @@ class MilestonesController < ProjectResourceController
def show
@issues = @milestone.issues
@users = UserDecorator.decorate(@milestone.participants)
@users = UserDecorator.decorate_collection(@milestone.participants)
@merge_requests = @milestone.merge_requests
respond_to do |format|

View file

@ -1,5 +1,3 @@
require Rails.root.join('lib', 'gitlab', 'graph', 'json_builder')
class ProjectsController < ProjectResourceController
skip_before_filter :project, only: [:new, :create]
skip_before_filter :repository, only: [:new, :create]

View file

@ -8,7 +8,7 @@ class Teams::MembersController < Teams::ApplicationController
def new
@users = User.potential_team_members(user_team)
@users = UserDecorator.decorate @users
@users = UserDecorator.decorate_collection @users
end
def create

View file

@ -1,27 +1,28 @@
class ApplicationDecorator < Draper::Base
class ApplicationDecorator < Draper::Decorator
delegate_all
# Lazy Helpers
# PRO: Call Rails helpers without the h. proxy
# ex: number_to_currency(model.price)
# CON: Add a bazillion methods into your decorator's namespace
# and probably sacrifice performance/memory
#
#
# Enable them by uncommenting this line:
# lazy_helpers
# Shared Decorations
# Consider defining shared methods common to all your models.
#
#
# Example: standardize the formatting of timestamps
#
# def formatted_timestamp(time)
# h.content_tag :span, time.strftime("%a %m/%d/%y"),
# class: 'timestamp'
# h.content_tag :span, time.strftime("%a %m/%d/%y"),
# class: 'timestamp'
# end
#
#
# def created_at
# formatted_timestamp(model.created_at)
# end
#
#
# def updated_at
# formatted_timestamp(model.updated_at)
# end

View file

@ -164,7 +164,8 @@ module ApplicationHelper
end
def image_url(source)
root_url + path_to_image(source)
# prevent relative_root_path being added twice (it's part of root_url and path_to_image)
root_url.sub(/#{root_path}$/, path_to_image(source))
end
alias_method :url_to_image, :image_url

View file

@ -27,6 +27,7 @@ module IssuesHelper
all: "all",
closed: "closed",
to_me: "assigned-to-me",
by_me: "created-by-me",
open: "open"
}
end
@ -40,4 +41,39 @@ module IssuesHelper
def issues_active_milestones
@project.milestones.active.order("id desc").all
end
def url_for_project_issues
return "" if @project.nil?
if @project.used_default_issues_tracker?
project_issues_filter_path(@project)
else
url = Settings[:issues_tracker][@project.issues_tracker]["project_url"]
url.gsub(':project_id', @project.id.to_s)
.gsub(':issues_tracker_id', @project.issues_tracker_id.to_s)
end
end
def url_for_issue(issue_id)
return "" if @project.nil?
if @project.used_default_issues_tracker?
url = project_issue_url project_id: @project, id: issue_id
else
url = Settings[:issues_tracker][@project.issues_tracker]["issues_url"]
url.gsub(':id', issue_id.to_s)
.gsub(':project_id', @project.id.to_s)
.gsub(':issues_tracker_id', @project.issues_tracker_id.to_s)
end
end
def title_for_issue(issue_id)
return "" if @project.nil?
if @project.used_default_issues_tracker? && issue = @project.issues.where(id: issue_id).first
issue.title
else
""
end
end
end

View file

@ -13,13 +13,15 @@ module TreeHelper
tree += render partial: 'tree/tree_item', collection: folders, locals: {type: 'folder'} if folders.present?
files.each do |f|
if f.respond_to?(:url)
# Object is a Submodule
tree += render partial: 'tree/submodule_item', object: f
else
# Object is a Blob
tree += render partial: 'tree/tree_item', object: f, locals: {type: 'file'}
end
html = if f.respond_to?(:url)
# Object is a Submodule
render partial: 'tree/submodule_item', object: f
else
# Object is a Blob
render partial: 'tree/tree_item', object: f, locals: {type: 'file'}
end
tree += html if html.present?
end
tree.html_safe

View file

@ -91,7 +91,6 @@ class Ability
:admin_team_member,
:admin_merge_request,
:admin_note,
:accept_mr,
:admin_wiki,
:admin_project
]

View file

@ -0,0 +1,59 @@
require "grit"
module Graph
class Commit
include ActionView::Helpers::TagHelper
attr_accessor :time, :spaces, :refs, :parent_spaces, :icon
def initialize(commit)
@_commit = commit
@time = -1
@spaces = []
@parent_spaces = []
end
def method_missing(m, *args, &block)
@_commit.send(m, *args, &block)
end
def to_graph_hash
h = {}
h[:parents] = self.parents.collect do |p|
[p.id,0,0]
end
h[:author] = {
name: author.name,
email: author.email,
icon: icon
}
h[:time] = time
h[:space] = spaces.first
h[:parent_spaces] = parent_spaces
h[:refs] = refs.collect{|r|r.name}.join(" ") unless refs.nil?
h[:id] = sha
h[:date] = date
h[:message] = message
h
end
def add_refs(ref_cache, repo)
if ref_cache.empty?
repo.refs.each do |ref|
ref_cache[ref.commit.id] ||= []
ref_cache[ref.commit.id] << ref
end
end
@refs = ref_cache[@_commit.id] if ref_cache.include?(@_commit.id)
@refs ||= []
end
def space
if @spaces.size > 0
@spaces.first
else
0
end
end
end
end

View file

@ -0,0 +1,291 @@
require "grit"
module Graph
class JsonBuilder
attr_accessor :days, :commits, :ref_cache, :repo
def self.max_count
@max_count ||= 650
end
def initialize project, ref, commit
@project = project
@ref = ref
@commit = commit
@repo = project.repo
@ref_cache = {}
@commits = collect_commits
@days = index_commits
end
def to_json(*args)
{
days: @days.compact.map { |d| [d.day, d.strftime("%b")] },
commits: @commits.map(&:to_graph_hash)
}.to_json(*args)
end
protected
# Get commits from repository
#
def collect_commits
@commits = Grit::Commit.find_all(repo, nil, {date_order: true, max_count: self.class.max_count, skip: to_commit}).dup
# Decorate with app/models/commit.rb
@commits.map! { |commit| Commit.new(commit) }
# Decorate with lib/gitlab/graph/commit.rb
@commits.map! { |commit| Graph::Commit.new(commit) }
# add refs to each commit
@commits.each { |commit| commit.add_refs(ref_cache, repo) }
@commits
end
# Method is adding time and space on the
# list of commits. As well as returns date list
# corelated with time set on commits.
#
# @param [Array<Graph::Commit>] commits to index
#
# @return [Array<TimeDate>] list of commit dates corelated with time on commits
def index_commits
days, times = [], []
map = {}
commits.reverse.each_with_index do |c,i|
c.time = i
days[i] = c.committed_date
map[c.id] = c
times[i] = c
end
@_reserved = {}
days.each_index do |i|
@_reserved[i] = []
end
commits_sort_by_ref.each do |commit|
if map.include? commit.id then
place_chain(map[commit.id], map)
end
end
# find parent spaces for not overlap lines
times.each do |c|
c.parent_spaces.concat(find_free_parent_spaces(c, map, times))
end
days
end
# Skip count that the target commit is displayed in center.
def to_commit
commits = Grit::Commit.find_all(repo, nil, {date_order: true})
commit_index = commits.index do |c|
c.id == @commit.id
end
if commit_index && (self.class.max_count / 2 < commit_index) then
# get max index that commit is displayed in the center.
commit_index - self.class.max_count / 2
else
0
end
end
def commits_sort_by_ref
commits.sort do |a,b|
if include_ref?(a)
-1
elsif include_ref?(b)
1
else
b.committed_date <=> a.committed_date
end
end
end
def include_ref?(commit)
heads = commit.refs.select do |ref|
ref.is_a?(Grit::Head) or ref.is_a?(Grit::Remote) or ref.is_a?(Grit::Tag)
end
heads.map! do |head|
head.name
end
heads.include?(@ref)
end
def find_free_parent_spaces(commit, map, times)
spaces = []
commit.parents.each do |p|
if map.include?(p.id) then
parent = map[p.id]
range = if commit.time < parent.time then
commit.time..parent.time
else
parent.time..commit.time
end
space = if commit.space >= parent.space then
find_free_parent_space(range, parent.space, -1, commit.space, times)
else
find_free_parent_space(range, commit.space, -1, parent.space, times)
end
mark_reserved(range, space)
spaces << space
end
end
spaces
end
def find_free_parent_space(range, space_base, space_step, space_default, times)
if is_overlap?(range, times, space_default) then
find_free_space(range, space_step, space_base, space_default)
else
space_default
end
end
def is_overlap?(range, times, overlap_space)
range.each do |i|
if i != range.first &&
i != range.last &&
times[i].spaces.include?(overlap_space) then
return true;
end
end
false
end
# Add space mark on commit and its parents
#
# @param [Graph::Commit] the commit object.
# @param [Hash<String,Graph::Commit>] map of commits
def place_chain(commit, map, parent_time = nil)
leaves = take_left_leaves(commit, map)
if leaves.empty?
return
end
time_range = leaves.last.time..leaves.first.time
space_base = get_space_base(leaves, map)
space = find_free_space(time_range, 2, space_base)
leaves.each do |l|
l.spaces << space
# Also add space to parent
l.parents.each do |p|
if map.include?(p.id)
parent = map[p.id]
if parent.space > 0
parent.spaces << space
end
end
end
end
# and mark it as reserved
min_time = leaves.last.time
parents = leaves.last.parents.collect
parents.each do |p|
if map.include? p.id
parent = map[p.id]
if parent.time < min_time
min_time = parent.time
end
end
end
if parent_time.nil?
max_time = leaves.first.time
else
max_time = parent_time - 1
end
mark_reserved(min_time..max_time, space)
# Visit branching chains
leaves.each do |l|
parents = l.parents.collect.select{|p| map.include? p.id and map[p.id].space.zero?}
for p in parents
place_chain(map[p.id], map, l.time)
end
end
end
def get_space_base(leaves, map)
space_base = 1
if leaves.last.parents.size > 0
first_parent = leaves.last.parents.first
if map.include?(first_parent.id)
first_p = map[first_parent.id]
if first_p.space > 0
space_base = first_p.space
end
end
end
space_base
end
def mark_reserved(time_range, space)
for day in time_range
@_reserved[day].push(space)
end
end
def find_free_space(time_range, space_step, space_base = 1, space_default = nil)
space_default ||= space_base
reserved = []
for day in time_range
reserved += @_reserved[day]
end
reserved.uniq!
space = space_default
while reserved.include?(space) do
space += space_step
if space < space_base then
space_step *= -1
space = space_base + space_step
end
end
space
end
# Takes most left subtree branch of commits
# which don't have space mark yet.
#
# @param [Graph::Commit] the commit object.
# @param [Hash<String,Graph::Commit>] map of commits
#
# @return [Array<Graph::Commit>] list of branch commits
def take_left_leaves(commit, map)
leaves = []
leaves.push(commit) if commit.space.zero?
while true
return leaves if commit.parents.count.zero?
return leaves unless map.include? commit.parents.first.id
commit = map[commit.parents.first.id]
return leaves unless commit.space.zero?
leaves.push(commit)
end
end
end
end

View file

@ -2,13 +2,14 @@
#
# Table name: namespaces
#
# id :integer not null, primary key
# name :string(255) not null
# path :string(255) not null
# owner_id :integer not null
# created_at :datetime not null
# updated_at :datetime not null
# type :string(255)
# id :integer not null, primary key
# name :string(255) not null
# description :string(255) not null
# path :string(255) not null
# owner_id :integer not null
# created_at :datetime not null
# updated_at :datetime not null
# type :string(255)
#
class Group < Namespace

View file

@ -30,6 +30,10 @@ class Issue < ActiveRecord::Base
where('assignee_id = :user', user: user.id)
end
def authored(user)
where('author_id = :user', user: user.id)
end
def open_for(user)
opened.assigned(user)
end

View file

@ -2,17 +2,18 @@
#
# Table name: namespaces
#
# id :integer not null, primary key
# name :string(255) not null
# path :string(255) not null
# owner_id :integer not null
# created_at :datetime not null
# updated_at :datetime not null
# type :string(255)
# id :integer not null, primary key
# name :string(255) not null
# description :string(255) not null
# path :string(255) not null
# owner_id :integer not null
# created_at :datetime not null
# updated_at :datetime not null
# type :string(255)
#
class Namespace < ActiveRecord::Base
attr_accessible :name, :path
attr_accessible :name, :description, :path
has_many :projects, dependent: :destroy
belongs_to :owner, class_name: "User"
@ -22,7 +23,7 @@ class Namespace < ActiveRecord::Base
length: { within: 0..255 },
format: { with: Gitlab::Regex.name_regex,
message: "only letters, digits, spaces & '_' '-' '.' allowed." }
validates :description, length: { within: 0..255 }
validates :path, uniqueness: true, presence: true, length: { within: 1..255 },
format: { with: Gitlab::Regex.path_regex,
message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }

View file

@ -11,6 +11,7 @@
# creator_id :integer
# default_branch :string(255)
# issues_enabled :boolean default(TRUE), not null
# issues_tracker :string not null
# wall_enabled :boolean default(TRUE), not null
# merge_requests_enabled :boolean default(TRUE), not null
# wiki_enabled :boolean default(TRUE), not null
@ -22,11 +23,12 @@ require "grit"
class Project < ActiveRecord::Base
include Gitolited
extend Enumerize
class TransferError < StandardError; end
attr_accessible :name, :path, :description, :default_branch,
:issues_enabled, :wall_enabled, :merge_requests_enabled,
attr_accessible :name, :path, :description, :default_branch, :issues_tracker,
:issues_enabled, :wall_enabled, :merge_requests_enabled, :issues_tracker_id,
:wiki_enabled, :public, :import_url, as: [:default, :admin]
attr_accessible :namespace_id, :creator_id, as: :admin
@ -43,7 +45,7 @@ class Project < ActiveRecord::Base
has_many :events, dependent: :destroy
has_many :merge_requests, dependent: :destroy
has_many :issues, dependent: :destroy, order: "state, created_at DESC"
has_many :issues, dependent: :destroy, order: "state DESC, created_at DESC"
has_many :milestones, dependent: :destroy
has_many :users_projects, dependent: :destroy
has_many :notes, dependent: :destroy
@ -72,6 +74,7 @@ class Project < ActiveRecord::Base
message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
validates :issues_enabled, :wall_enabled, :merge_requests_enabled,
:wiki_enabled, inclusion: { in: [true, false] }
validates :issues_tracker_id, length: { within: 0..255 }
validates_uniqueness_of :name, scope: :namespace_id
validates_uniqueness_of :path, scope: :namespace_id
@ -93,6 +96,8 @@ class Project < ActiveRecord::Base
scope :joined, ->(user) { where("namespace_id != ?", user.namespace_id) }
scope :public_only, -> { where(public: true) }
enumerize :issues_tracker, :in => (Gitlab.config.issues_tracker.keys).append(:gitlab), :default => :gitlab
class << self
def abandoned
project_ids = Event.select('max(created_at) as latest_date, project_id').
@ -201,6 +206,22 @@ class Project < ActiveRecord::Base
issues.tag_counts_on(:labels)
end
def issue_exists?(issue_id)
if used_default_issues_tracker?
self.issues.where(id: issue_id).first.present?
else
true
end
end
def used_default_issues_tracker?
self.issues_tracker == Project.issues_tracker.default_value
end
def can_have_issues_tracker_id?
self.issues_enabled && !self.used_default_issues_tracker?
end
def services
[gitlab_ci_service].compact
end

View file

@ -137,7 +137,7 @@ class Repository
file_path = File.join(storage_path, self.path_with_namespace, file_name)
# Put files into a directory before archiving
prefix = self.path_with_namespace + "/"
prefix = File.basename(self.path_with_namespace) + "/"
# Create file if not exists
unless File.exists?(file_path)

View file

@ -25,7 +25,7 @@
# dark_scheme :boolean default(FALSE), not null
# theme_id :integer default(1), not null
# bio :string(255)
# blocked :boolean default(FALSE), not null
# state :string(255)
# failed_attempts :integer default(0)
# locked_at :datetime
# extern_uid :string(255)
@ -46,10 +46,35 @@ class User < ActiveRecord::Base
attr_accessor :force_random_password
# Namespace for personal projects
has_one :namespace, dependent: :destroy, foreign_key: :owner_id, class_name: "Namespace", conditions: 'type IS NULL'
#
# Relations
#
has_many :keys, dependent: :destroy
# Namespace for personal projects
has_one :namespace,
dependent: :destroy,
foreign_key: :owner_id,
class_name: "Namespace",
conditions: 'type IS NULL'
# Profile
has_many :keys, dependent: :destroy
# Groups
has_many :groups, class_name: "Group", foreign_key: :owner_id
# Teams
has_many :own_teams,
class_name: "UserTeam",
foreign_key: :owner_id,
dependent: :destroy
has_many :user_team_user_relationships, dependent: :destroy
has_many :user_teams, through: :user_team_user_relationships
has_many :user_team_project_relationships, through: :user_teams
has_many :team_projects, through: :user_team_project_relationships
# Projects
has_many :users_projects, dependent: :destroy
has_many :issues, dependent: :destroy, foreign_key: :author_id
has_many :notes, dependent: :destroy, foreign_key: :author_id
@ -57,18 +82,16 @@ class User < ActiveRecord::Base
has_many :events, dependent: :destroy, foreign_key: :author_id, class_name: "Event"
has_many :assigned_issues, dependent: :destroy, foreign_key: :assignee_id, class_name: "Issue"
has_many :assigned_merge_requests, dependent: :destroy, foreign_key: :assignee_id, class_name: "MergeRequest"
has_many :projects, through: :users_projects
has_many :groups, class_name: "Group", foreign_key: :owner_id
has_many :recent_events, class_name: "Event", foreign_key: :author_id, order: "id DESC"
has_many :projects, through: :users_projects
has_many :user_team_user_relationships, dependent: :destroy
has_many :user_teams, through: :user_team_user_relationships
has_many :user_team_project_relationships, through: :user_teams
has_many :team_projects, through: :user_team_project_relationships
has_many :recent_events,
class_name: "Event",
foreign_key: :author_id,
order: "id DESC"
#
# Validations
#
validates :name, presence: true
validates :email, presence: true, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/ }
validates :bio, length: { within: 0..255 }
@ -87,10 +110,27 @@ class User < ActiveRecord::Base
delegate :path, to: :namespace, allow_nil: true, prefix: true
state_machine :state, initial: :active do
after_transition any => :blocked do |user, transition|
# Remove user from all projects and
user.users_projects.find_each do |membership|
return false unless membership.destroy
end
end
event :block do
transition active: :blocked
end
event :activate do
transition blocked: :active
end
end
# Scopes
scope :admins, -> { where(admin: true) }
scope :blocked, -> { where(blocked: true) }
scope :active, -> { where(blocked: false) }
scope :blocked, -> { with_state(:blocked) }
scope :active, -> { with_state(:active) }
scope :alphabetically, -> { order('name ASC') }
scope :in_team, ->(team){ where(id: team.member_ids) }
scope :not_in_team, ->(team){ where('users.id NOT IN (:ids)', ids: team.member_ids) }
@ -260,17 +300,6 @@ class User < ActiveRecord::Base
MergeRequest.cared(self)
end
# Remove user from all projects and
# set blocked attribute to true
def block
users_projects.find_each do |membership|
return false unless membership.destroy
end
self.blocked = true
save
end
def projects_limit_percent
return 100 if projects_limit.zero?
(personal_projects.count.to_f / projects_limit) * 100

View file

@ -11,7 +11,7 @@
#
class UserTeam < ActiveRecord::Base
attr_accessible :name, :owner_id, :path
attr_accessible :name, :description, :owner_id, :path
belongs_to :owner, class_name: User
@ -26,6 +26,7 @@ class UserTeam < ActiveRecord::Base
length: { within: 0..255 },
format: { with: Gitlab::Regex.name_regex,
message: "only letters, digits, spaces & '_' '-' '.' allowed." }
validates :description, length: { within: 0..255 }
validates :path, uniqueness: true, presence: true, length: { within: 1..255 },
format: { with: Gitlab::Regex.path_regex,
message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }

View file

@ -27,7 +27,7 @@ class IssueObserver < ActiveRecord::Observer
def create_note(issue)
Note.create_status_change_note(issue, current_user, issue.state)
[issue.author, issue.assignee].compact.each do |recipient|
[issue.author, issue.assignee].compact.uniq.each do |recipient|
Notify.delay.issue_status_changed_email(recipient.id, issue.id, issue.state, current_user.id)
end
end

View file

@ -2,7 +2,8 @@ class UserObserver < ActiveRecord::Observer
def after_create(user)
log_info("User \"#{user.name}\" (#{user.email}) was created")
Notify.delay.new_user_email(user.id, user.password)
# Dont email omniauth created users
Notify.delay.new_user_email(user.id, user.password) unless user.extern_uid?
end
def after_destroy user

View file

@ -19,6 +19,8 @@ class GitPushService
# Collect data for this git push
@push_data = post_receive_data(oldrev, newrev, ref)
create_push_event
project.ensure_satellite_exists
project.discover_default_branch
@ -27,8 +29,6 @@ class GitPushService
project.execute_hooks(@push_data.dup)
project.execute_services(@push_data.dup)
end
create_push_event
end
# This method provide a sample data

View file

@ -25,7 +25,7 @@ class ProjectTransferService
Gitlab::ProjectMover.new(project, old_dir, new_dir).execute
save!
project.save!
end
rescue Gitlab::ProjectMover::ProjectMoveError => ex
raise Project::TransferError.new(ex.message)

View file

@ -1,4 +1,4 @@
%h3.page_title Rename Group
%h3.page_title Edit Group
%hr
= form_for [:admin, @group] do |f|
- if @group.errors.any?
@ -10,7 +10,10 @@
.input
= f.text_field :name, placeholder: "Example Group", class: "xxlarge"
.clearfix.group-description-holder
= f.label :description, "Details"
.input
= f.text_area :description, maxlength: 250, class: "xxlarge js-gfm-input", rows: 4
.clearfix.group_name_holder
= f.label :path do
@ -24,5 +27,5 @@
%li It will change the git path to repositories under this group.
.form-actions
= f.submit 'Rename group', class: "btn btn-remove"
= f.submit 'Edit group', class: "btn btn-remove"
= link_to 'Cancel', admin_groups_path, class: "btn btn-cancel"

View file

@ -17,6 +17,7 @@
Name
%i.icon-sort-down
%th Path
%th Description
%th Projects
%th Owner
%th.cred Danger Zone!
@ -25,11 +26,12 @@
%tr
%td
%strong= link_to group.name, [:admin, group]
%td= truncate group.description
%td= group.path
%td= group.projects.count
%td
= link_to group.owner_name, admin_user_path(group.owner)
%td.bgred
= link_to 'Rename', edit_admin_group_path(group), id: "edit_#{dom_id(group)}", class: "btn btn-small"
= link_to 'Edit', edit_admin_group_path(group), id: "edit_#{dom_id(group)}", class: "btn btn-small"
= link_to 'Destroy', [:admin, group], confirm: "REMOVE #{group.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove"
= paginate @groups, theme: "admin"

View file

@ -9,8 +9,14 @@
Group name is
.input
= f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left"
&nbsp;
= f.submit 'Create group', class: "btn btn-primary"
.clearfix.group-description-holder
= f.label :description, "Details"
.input
= f.text_area :description, maxlength: 250, class: "xxlarge js-gfm-input", rows: 4
.form-actions
= f.submit 'Create group', class: "btn btn-primary"
%hr
.padded
%ul

View file

@ -16,7 +16,13 @@
&nbsp;
= link_to edit_admin_group_path(@group), class: "btn btn-small pull-right" do
%i.icon-edit
Rename
Edit
%tr
%td
%b
Description:
%td
= @group.description
%tr
%td
%b

View file

@ -31,6 +31,15 @@
= f.label :issues_enabled, "Issues"
.input= f.check_box :issues_enabled
- if Project.issues_tracker.values.count > 1
.clearfix
= f.label :issues_tracker, "Issues tracker", class: 'control-label'
.input= f.select(:issues_tracker, Project.issues_tracker.values, {}, { disabled: !@project.issues_enabled })
.clearfix
= f.label :issues_tracker_id, "Project name or id in issues tracker", class: 'control-label'
.input= f.text_field :issues_tracker_id, class: "xxlarge", disabled: !@project.can_have_issues_tracker_id?
.clearfix
= f.label :merge_requests_enabled, "Merge Requests"
.input= f.check_box :merge_requests_enabled

View file

@ -1,4 +1,4 @@
%h3.page_title Rename Team
%h3.page_title Edit Team
%hr
= form_for @team, url: admin_team_path(@team), method: :put do |f|
- if @team.errors.any?
@ -10,6 +10,11 @@
.input
= f.text_field :name, placeholder: "Example Team", class: "xxlarge"
.clearfix.team-description-holder
= f.label :description, "Details"
.input
= f.text_area :description, maxlength: 250, class: "xxlarge js-gfm-input", rows: 4
.clearfix.team_name_holder
= f.label :path do
%span.cred Team path is
@ -19,5 +24,5 @@
%li It will change web url for access team and team projects.
.form-actions
= f.submit 'Rename team', class: "btn btn-remove"
= f.submit 'Edit team', class: "btn btn-remove"
= link_to 'Cancel', admin_teams_path, class: "btn btn-cancel"

View file

@ -16,6 +16,7 @@
%th
Name
%i.icon-sort-down
%th Description
%th Path
%th Projects
%th Members
@ -26,13 +27,17 @@
%tr
%td
%strong= link_to team.name, admin_team_path(team)
%td= truncate team.description
%td= team.path
%td= team.projects.count
%td= team.members.count
%td
= link_to team.owner.name, admin_user_path(team.owner)
- if team.owner
= link_to team.owner.name, admin_user_path(team.owner)
- else
(deleted)
%td.bgred
= link_to 'Rename', edit_admin_team_path(team), id: "edit_#{dom_id(team)}", class: "btn btn-small"
= link_to 'Edit', edit_admin_team_path(team), id: "edit_#{dom_id(team)}", class: "btn btn-small"
= link_to 'Destroy', admin_team_path(team), confirm: "REMOVE #{team.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove"
= paginate @teams, theme: "admin"

View file

@ -9,8 +9,15 @@
Team name is
.input
= f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left"
&nbsp;
= f.submit 'Create team', class: "btn btn-primary"
.clearfix.team-description-holder
= f.label :description, "Details"
.input
= f.text_area :description, maxlength: 250, class: "xxlarge js-gfm-input", rows: 4
.form-actions
= f.submit 'Create team', class: "btn btn-primary"
%hr
.padded
%ul

View file

@ -16,7 +16,13 @@
&nbsp;
= link_to edit_admin_team_path(@team), class: "btn btn-small pull-right" do
%i.icon-edit
Rename
Edit
%tr
%td
%b
Description:
%td
= @team.description
%tr
%td
%b

View file

@ -61,7 +61,7 @@
.span4
- unless @admin_user.new_record?
.alert.alert-error
- if @admin_user.blocked
- if @admin_user.blocked?
%p This user is blocked and is not able to login to GitLab
= link_to 'Unblock User', unblock_admin_user_path(@admin_user), method: :put, class: "btn btn-small"
- else

View file

@ -53,7 +53,7 @@
&nbsp;
= link_to 'Edit', edit_admin_user_path(user), id: "edit_#{dom_id(user)}", class: "btn btn-small"
- unless user == current_user
- if user.blocked
- if user.blocked?
= link_to 'Unblock', unblock_admin_user_path(user), method: :put, class: "btn btn-small success"
- else
= link_to 'Block', block_admin_user_path(user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn btn-small btn-remove"

View file

@ -3,7 +3,7 @@
%h3.page_title
= image_tag gravatar_icon(@admin_user.email, 90), class: "avatar s90"
= @admin_user.name
- if @admin_user.blocked
- if @admin_user.blocked?
%span.cred (Blocked)
- if @admin_user.admin
%span.cred (Admin)

View file

@ -1,4 +1,4 @@
- @commits.group_by { |c| c.committed_date.to_date }.each do |day, commits|
- @commits.group_by { |c| c.committed_date.to_date }.sort.reverse.each do |day, commits|
%div.ui-box
%h5.title
%i.icon-calendar

View file

@ -21,9 +21,10 @@
= event.project_name
.event-body
%i.icon-comment-alt.event-note-icon
%span.event-note
= markdown truncate(event.target.note, length: 70)
.event-note
.md
%i.icon-comment-alt.event-note-icon
= sanitize(markdown(truncate(event.target.note, length: 150)), tags: %w(a img b pre p))
- note = event.target
- if note.attachment.url
= link_to note.attachment.secure_url, target: "_blank", class: 'note-file-attach' do

View file

@ -9,8 +9,15 @@
Group name is
.input
= f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left"
&nbsp;
= f.submit 'Save group', class: "btn btn-save"
.clearfix.group-description-holder
= f.label :description, "Details"
.input
= f.text_area :description, maxlength: 250, class: "xxlarge js-gfm-input", rows: 4
.form-actions
= f.submit 'Save group', class: "btn btn-save"
%hr

View file

@ -9,9 +9,16 @@
Group name is
.input
= f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left"
&nbsp;
= f.submit 'Create group', class: "btn btn-create"
%hr
.clearfix.group-description-holder
= f.label :description, "Details"
.input
= f.text_area :description, maxlength: 250, class: "xxlarge js-gfm-input", rows: 4
.form-actions
= f.submit 'Create group', class: "btn btn-create"
.padded
%ul
%li Group is kind of directory for several projects

View file

@ -12,6 +12,9 @@
%p.nothing_here_message Project activity will be displayed here
.loading.hide
.side.span4
- if @group.description.present?
.description.well.light
= @group.description
= render "projects", projects: @projects
%div
%span.rss-icon

View file

@ -6,7 +6,10 @@
Open
%li{class: ("active" if params[:status] == 'assigned-to-me')}
= link_to project_issues_path(@project, status: 'assigned-to-me') do
Assigned To Me
Assigned to me
%li{class: ("active" if params[:status] == 'created-by-me')}
= link_to project_issues_path(@project, status: 'created-by-me') do
Created by me
%li{class: ("active" if params[:status] == 'closed')}
= link_to project_issues_path(@project, status: 'closed') do
Closed

View file

@ -1,3 +1,8 @@
- if text = alert || notice
#flash-container
%h4= text
.flash-container
- if alert
.alert
%span= alert
- elsif notice
.alert.alert-info
%span= notice

View file

@ -7,6 +7,7 @@
= stylesheet_link_tag "application"
= javascript_include_tag "application"
= csrf_meta_tags
= include_gon
-# Atom feed
- if current_user

View file

@ -8,6 +8,9 @@
%span.separator
%h1.project_name= title
%ul.nav
%li
= link_to public_root_path, title: "Public area", class: 'has_bottom_tooltip', 'data-original-title' => 'Public area' do
%i.icon-globe
- if current_user.is_admin?
%li
= link_to admin_root_path, title: "Admin area", class: 'has_bottom_tooltip', 'data-original-title' => 'Admin area' do

View file

@ -2,8 +2,8 @@
%html{ lang: "en"}
= render "layouts/head", title: "Admin area"
%body{class: "#{app_theme} admin"}
= render "layouts/flash"
= render "layouts/head_panel", title: "Admin area"
= render "layouts/flash"
.container
%ul.main_menu
= nav_link(controller: :dashboard, html_options: {class: 'home'}) do

View file

@ -2,8 +2,8 @@
%html{ lang: "en"}
= render "layouts/head", title: "Dashboard"
%body{class: "#{app_theme} application"}
= render "layouts/flash"
= render "layouts/head_panel", title: "Dashboard"
= render "layouts/flash"
.container
%ul.main_menu
= nav_link(path: 'dashboard#show', html_options: {class: 'home'}) do

View file

@ -3,4 +3,6 @@
= render "layouts/head"
%body.ui_basic.login-page
= render "layouts/flash"
.container= yield
.container
.content
= yield

View file

@ -2,8 +2,8 @@
%html{ lang: "en"}
= render "layouts/head", title: "Error"
%body{class: "#{app_theme} application"}
= render "layouts/flash"
= render "layouts/head_panel", title: ""
= render "layouts/flash"
.container
.content
%center.padded.prepend-top-20

View file

@ -2,8 +2,8 @@
%html{ lang: "en"}
= render "layouts/head", title: "#{@group.name}"
%body{class: "#{app_theme} application"}
= render "layouts/flash"
= render "layouts/head_panel", title: "group: #{@group.name}"
= render "layouts/flash"
.container
%ul.main_menu
= nav_link(path: 'groups#show', html_options: {class: 'home'}) do

View file

@ -2,8 +2,8 @@
%html{ lang: "en"}
= render "layouts/head", title: "Profile"
%body{class: "#{app_theme} profile"}
= render "layouts/flash"
= render "layouts/head_panel", title: "Profile"
= render "layouts/flash"
.container
%ul.main_menu
= nav_link(path: 'profiles#show', html_options: {class: 'home'}) do

View file

@ -2,8 +2,8 @@
%html{ lang: "en"}
= render "layouts/head", title: @project.name_with_namespace
%body{class: "#{app_theme} project"}
= render "layouts/flash"
= render "layouts/head_panel", title: project_title(@project)
= render "layouts/flash"
- if can?(current_user, :download_code, @project)
= render 'shared/no_ssh'
@ -22,11 +22,12 @@
= nav_link(controller: %w(graph)) do
= link_to "Network", project_graph_path(@project, @ref || @repository.root_ref)
- if @project.issues_enabled
- if @project.issues_enabled
= nav_link(controller: %w(issues milestones labels)) do
= link_to project_issues_filter_path(@project) do
= link_to url_for_project_issues do
Issues
%span.count.issue_counter= @project.issues.opened.count
- if @project.used_default_issues_tracker?
%span.count.issue_counter= @project.issues.opened.count
- if @project.repo_exists? && @project.merge_requests_enabled
= nav_link(controller: :merge_requests) do

View file

@ -2,8 +2,8 @@
%html{ lang: "en"}
= render "layouts/head", title: "#{@team.name}"
%body{class: "#{app_theme} application"}
= render "layouts/flash"
= render "layouts/head_panel", title: "team: #{@team.name}"
= render "layouts/flash"
.container
%ul.main_menu
= nav_link(path: 'teams#show', html_options: {class: 'home'}) do

View file

@ -1,9 +1,9 @@
- unless can?(current_user, :accept_mr, @project)
- unless @allowed_to_merge
.alert
%strong Only masters can accept MR
%strong You don't have enough permissions to merge this MR
- if @merge_request.opened? && @commits.any? && can?(current_user, :accept_mr, @project)
- if @show_merge_controls
.automerge_widget.can_be_merged{style: "display:none"}
.alert.alert-success
%span

View file

@ -0,0 +1,4 @@
Issue was <%= @issue_status %> by <%= @updated_by.name %>
Issue <%= @issue.id %>: <%= url_for(project_issue_url(@issue.project, @issue)) %>

View file

@ -0,0 +1,4 @@
New Issue was created and assigned to you.
Issue <%= @issue.id %>: <%= url_for(project_issue_url(@issue.project, @issue)) %>

View file

@ -0,0 +1,9 @@
New Merge Request <%= @merge_request.id %>
<%= url_for(project_merge_request_url(@merge_request.project, @merge_request)) %>
Branches: <%= @merge_request.source_branch %> to <%= @merge_request.target_branch %>
Author: <%= @merge_request.author_name %>
Asignee: <%= @merge_request.assignee_name %>

View file

@ -0,0 +1,10 @@
Hi <%= @user.name %>!
Administrator created account for you. Now you are a member of company GitLab application.
login.................. <%= @user.email %>
<% unless Gitlab.config.gitlab.signup_enabled %>
password............... <%= @password %>
<% end %>
Click here to login: <%= url_for(root_url) %>

View file

@ -0,0 +1,9 @@
New comment for Commit <%= @commit.short_id %>
<%= url_for(project_commit_url(@note.project, id: @commit.id, anchor: "note_#{@note.id}")) %>
Author: <%= @note.author_name %>
<%= @note.note %>

View file

@ -0,0 +1,9 @@
New comment for Issue <%= @issue.id %>
<%= url_for(project_issue_url(@issue.project, @issue, anchor: "note_#{@note.id}")) %>
Author: <%= @note.author_name %>
<%= @note.note %>

View file

@ -0,0 +1,9 @@
New comment for Merge Request <%= @merge_request.id %>
<%= url_for(project_merge_request_url(@merge_request.project, @merge_request, anchor: "note_#{@note.id}")) %>
<%= @note.author_name %>
<%= @note.note %>

View file

@ -0,0 +1,9 @@
New message on the project wall <%= @note.project %>
<%= url_for(wall_project_url(@note.project, anchor: "note_#{@note.id}")) %>
<%= @note.author_name %>
<%= @note.note %>

View file

@ -0,0 +1,4 @@
You have been granted <%= @users_project.project_access_human %> access to project <%= @project.name_with_namespace %>
<%= url_for(project_url(@project)) %>

View file

@ -0,0 +1,8 @@
Project was moved to another location
The project is now located under
<%= url_for(link_to project_url(@project)) %>
To update the remote url in your local repository run:
git remote set-url origin <%= @project.ssh_url_to_repo %>

View file

@ -0,0 +1,7 @@
Reassigned Issue <%= @issue.id %>
<%= url_for(project_issue_url(@issue.project, @issue)) %>
Assignee changed from <%= @previous_assignee.name %> to <%= @issue.assignee_name %>

View file

@ -0,0 +1,7 @@
Reassigned Merge Request <%= @merge_request.id %>
<%= url_for(project_merge_request_url(@merge_request.project, @merge_request)) %>
Assignee changed from <%= @previous_assignee.name %> to <%= @merge_request.assignee_name %>

View file

@ -24,6 +24,15 @@
= f.check_box :issues_enabled
%span.descr Lightweight issue tracking system for this project
- if Project.issues_tracker.values.count > 1
.control-group
= f.label :issues_tracker, "Issues tracker", class: 'control-label'
.input= f.select(:issues_tracker, Project.issues_tracker.values, {}, { disabled: !@project.issues_enabled })
.clearfix
= f.label :issues_tracker_id, "Project name or id in issues tracker", class: 'control-label'
.input= f.text_field :issues_tracker_id, class: "xxlarge", disabled: !@project.can_have_issues_tracker_id?
.control-group
= f.label :merge_requests_enabled, "Merge Requests", class: 'control-label'
.controls

View file

@ -5,14 +5,14 @@
%fieldset
%legend Git global setup:
%pre.dark
= preserve do
:preserve
git config --global user.name "#{current_user.name}"
git config --global user.email "#{current_user.email}"
%fieldset
%legend Create Repository
%pre.dark
= preserve do
:preserve
mkdir #{@project.path}
cd #{@project.path}
git init
@ -25,7 +25,7 @@
%fieldset
%legend Existing Git Repo?
%pre.dark
= preserve do
:preserve
cd existing_git_repo
git remote add origin #{@project.url_to_repo}
git push -u origin master

View file

@ -12,5 +12,7 @@
.pull-right
%pre.dark.tiny git clone #{project.http_url_to_repo}
- unless @projects.present?
%h3.nothing_here_message No public projects
= paginate @projects, theme: "admin"

View file

@ -1,5 +1,4 @@
.input-prepend.project_clone_holder
%button{class: "btn active", :"data-clone" => @project.ssh_url_to_repo} SSH
%button{class: "btn", :"data-clone" => @project.http_url_to_repo}= Gitlab.config.gitlab.protocol.upcase
= text_field_tag :project_clone, @project.url_to_repo, class: "one_click_select input-xxlarge"
= text_field_tag :project_clone, @project.url_to_repo, class: "one_click_select input-xxlarge", readonly: true

View file

@ -20,7 +20,7 @@
%span.label This is you!
- if @project.namespace_owner == user
%span.label Owner
- elsif user.blocked
- elsif user.blocked?
%span.label Blocked
- elsif allow_admin
= link_to project_team_member_path(@project, user), confirm: remove_from_project_team_message(@project, user), method: :delete, class: "btn-tiny btn btn-remove" do

View file

@ -12,13 +12,19 @@
.input
= f.text_field :name, placeholder: "Ex. OpenSource", class: "xlarge left"
.clearfix.team-description-holder
= f.label :description, "Details"
.input
= f.text_area :description, maxlength: 250, class: "xlarge js-gfm-input", rows: 4
.clearfix
= f.label :path do
Team path is
.input
= f.text_field :path, placeholder: "opensource", class: "xlarge left"
.form-actions
= f.submit 'Save team changes', class: "btn btn-save"
= f.submit 'Save team changes', class: "btn btn-primary"
.span5
.ui-box
%h5.title Remove team
@ -26,4 +32,3 @@
%p
Removed team can not be restored!
= link_to 'Remove team', team_path(@team), method: :delete, confirm: "You are sure?", class: "btn btn-remove btn-small"

View file

@ -23,7 +23,7 @@
%span.btn.disabled This is you!
- if @team.owner == user
%span.btn.disabled Owner
- elsif user.blocked
- elsif user.blocked?
%span.btn.disabled.blocked Blocked
- elsif allow_admin
= link_to team_member_path(@team, user), confirm: remove_from_user_team_message(@team, user), method: :delete, class: "btn-tiny btn btn-remove", title: "Remove from team" do

View file

@ -9,9 +9,15 @@
Team name is
.input
= f.text_field :name, placeholder: "Ex. Ruby Developers", class: "xxlarge left"
&nbsp;
= f.submit 'Create team', class: "btn btn-create"
%hr
.clearfix.team-description-holder
= f.label :description, "Details"
.input
= f.text_area :description, maxlength: 250, class: "xxlarge js-gfm-input", rows: 4
.form-actions
= f.submit 'Create team', class: "btn btn-create"
.padded
%ul
%li All created teams are public (users can view who enter into team and which project are assigned for this team)

View file

@ -11,6 +11,9 @@
%p.nothing_here_message Projects activity will be displayed here
.loading.hide
.side.span4
- if @team.description.present?
.description.well.light
= @team.description
= render "projects", projects: @projects
%div
%span.rss-icon