Commit c4854635 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge branch 'upstream/6-2-stable' of /home/git/repositories/gitlab/gitlab-ee

parents a430680a 81ae87f6
......@@ -20,6 +20,7 @@ Vagrantfile
config/gitlab.yml
config/database.yml
config/initializers/omniauth.rb
config/initializers/rack_attack.rb
config/unicorn.rb
config/resque.yml
config/aws.yml
......
......@@ -8,7 +8,6 @@ branches:
only:
- 'master'
rvm:
- 1.9.3-p392
- 2.0.0
services:
- mysql
......@@ -16,7 +15,4 @@ services:
before_script:
- "cp config/database.yml.$DB config/database.yml"
- "cp config/gitlab.yml.example config/gitlab.yml"
- "bundle exec rake db:setup RAILS_ENV=test"
- "bundle exec rake db:seed_fu RAILS_ENV=test"
- "sh -e /etc/init.d/xvfb start"
script: "bundle exec rake travis --trace"
script: "bundle exec rake gitlab:test --trace"
v 6.2.0
- Public project pages are now visible to everyone (files, issues, wik, etc.)
THIS MEANS YOUR ISSUES AND WIKI FOR PUBLIC PROJECTS ARE PUBLICLY VISIBLE AFTER THE UPGRADE
- Add group access to permissions page
- Require current password to change one
- Group owner or admin can remove other group owners
- Remove group transfer since we have multiple owners
- Respect authorization in Repository API
- Improve UI for Project#files page
- Add more security specs
- Added search for projects by name to api (Izaak Alpert)
- Make default user theme configurable (Izaak Alpert)
- Update logic for validates_merge_request for tree of MR (Andrew Kumanyaev)
- Rake tasks for web hooks management (Jonhnny Weslley)
- Extended User API to expose admin and can_create_group for user creation/updating (Boyan Tabakov)
- API: Remove group
- API: Remove project
- Avatar upload on profile page with a maximum of 200KB (Steven Thonus)
- Store the sessions in Redis instead of the cookie store
- Fixed relative links in markdown
- User must confirm his email if signup enabled
- User must confirm changed email
v 6.1.0
- Project specific IDs for issues, mr, milestones
Above items will get a new id and for example all bookmarked issue urls will change.
......
......@@ -16,6 +16,7 @@ gem "pg", group: :postgres
# Auth
gem "devise", '~> 2.2'
gem "devise-async"
gem 'omniauth', "~> 1.1.3"
gem 'omniauth-google-oauth2'
gem 'omniauth-twitter'
......@@ -23,7 +24,7 @@ gem 'omniauth-github'
# Extracting information from a git repository
# Provide access to Gitlab::Git library
gem "gitlab_git", '2.3.1'
gem "gitlab_git", "~> 3.0.0.rc2"
# Ruby/Rack Git Smart-HTTP Server Handler
gem 'gitlab-grack', '~> 1.0.1', require: 'grack'
......@@ -112,6 +113,9 @@ gem 'tinder', '~> 1.9.2'
# HipChat integration
gem "hipchat", "~> 0.9.0"
# Flowdock integration
gem "gitlab-flowdock-git-hook", "~> 0.4.2"
# d3
gem "d3_rails", "~> 3.1.4"
......@@ -121,6 +125,9 @@ gem "underscore-rails", "~> 1.4.4"
# Sanitize user input
gem "sanitize"
# Protect against bruteforcing
gem "rack-attack"
group :assets do
gem "sass-rails"
gem "coffee-rails"
......@@ -143,10 +150,11 @@ group :assets do
end
group :development do
gem "annotate", git: "https://github.com/ctran/annotate_models.git"
gem "annotate", "~> 2.6.0.beta2"
gem "letter_opener"
gem 'quiet_assets', '~> 1.0.1'
gem 'rack-mini-profiler'
# Better errors handler
gem 'better_errors'
gem 'binding_of_caller'
......
GIT
remote: https://github.com/ctran/annotate_models.git
revision: 18a4e2eb77c8f3ef695b563e4a7ca45dfede819b
specs:
annotate (2.6.0.beta2)
activerecord (>= 2.3.0)
rake (>= 0.8.7)
GEM
remote: https://rubygems.org/
specs:
......@@ -39,17 +31,20 @@ GEM
acts-as-taggable-on (2.4.1)
rails (>= 3, < 5)
addressable (2.3.4)
annotate (2.6.0.beta2)
activerecord (>= 2.3.0)
rake (>= 0.8.7)
arel (3.0.2)
asciidoctor (0.1.3)
awesome_print (1.1.0)
awesome_print (1.2.0)
backports (3.3.2)
bcrypt-ruby (3.1.1)
better_errors (0.9.0)
better_errors (1.0.1)
coderay (>= 1.0.0)
erubis (>= 2.6.6)
binding_of_caller (0.7.2)
debug_inspector (>= 0.0.1)
bootstrap-sass (2.3.2.0)
bootstrap-sass (2.3.2.2)
sass (~> 3.2)
builder (3.0.4)
capybara (2.1.0)
......@@ -73,7 +68,7 @@ GEM
sass-rails (>= 3.2)
chunky_png (1.2.8)
cliver (0.2.1)
code_analyzer (0.3.2)
code_analyzer (0.4.3)
sexp_processor
coderay (1.0.9)
coffee-rails (3.2.2)
......@@ -92,11 +87,11 @@ GEM
compass-rails (1.0.3)
compass (>= 0.12.2, < 0.14)
connection_pool (1.1.0)
coveralls (0.6.7)
colorize
coveralls (0.7.0)
multi_json (~> 1.3)
rest-client
simplecov (>= 0.7)
term-ansicolor
thor
crack (0.4.0)
safe_yaml (~> 0.9.0)
......@@ -111,6 +106,8 @@ GEM
orm_adapter (~> 0.1)
railties (~> 3.1)
warden (~> 1.2.1)
devise-async (0.8.0)
devise (>= 2.2, < 3.2)
diff-lcs (1.2.4)
dotenv (0.8.0)
email_spec (1.4.0)
......@@ -133,7 +130,7 @@ GEM
multipart-post (~> 1.1)
faraday_middleware (0.9.0)
faraday (>= 0.7.4, < 0.9)
ffaker (1.16.1)
ffaker (1.18.0)
ffi (1.9.0)
fog (1.3.1)
builder
......@@ -145,7 +142,7 @@ GEM
net-ssh (>= 2.1.3)
nokogiri (~> 1.5.0)
ruby-hmac
font-awesome-rails (3.2.1.2)
font-awesome-rails (3.2.1.3)
railties (>= 3.2, < 5.0)
foreman (0.63.0)
dotenv (>= 0.7)
......@@ -161,6 +158,9 @@ GEM
pygments.rb (>= 0.2.13)
github-markdown (0.5.3)
github-markup (0.7.5)
gitlab-flowdock-git-hook (0.4.2.2)
gitlab-grit (>= 2.4.1)
multi_json
gitlab-gollum-lib (1.0.1)
github-markdown (~> 0.5.3)
github-markup (>= 0.7.5, < 1.0.0)
......@@ -171,7 +171,7 @@ GEM
stringex (~> 1.5.1)
gitlab-grack (1.0.1)
rack (~> 1.4.1)
gitlab-grit (2.6.0)
gitlab-grit (2.6.1)
charlock_holmes (~> 0.6.9)
diff-lcs (~> 1.1)
mime-types (~> 1.15)
......@@ -179,10 +179,10 @@ GEM
gitlab-pygments.rb (0.3.2)
posix-spawn (~> 0.3.6)
yajl-ruby (~> 1.1.0)
gitlab_git (2.3.1)
gitlab_git (3.0.0.rc2)
activesupport (~> 3.2.13)
github-linguist (~> 2.3.4)
gitlab-grit (~> 2.6.0)
gitlab-grit (~> 2.6.1)
gitlab_meta (6.0)
gitlab_omniauth-ldap (1.0.3)
net-ldap (~> 0.3.1)
......@@ -278,7 +278,7 @@ GEM
minitest (4.7.4)
modernizr (2.6.2)
sprockets (~> 2.0)
multi_json (1.7.9)
multi_json (1.8.0)
multi_xml (0.5.4)
multipart-post (1.2.0)
mysql2 (0.3.11)
......@@ -334,9 +334,11 @@ GEM
rack (1.4.5)
rack-accept (0.4.5)
rack (>= 0.4)
rack-attack (2.2.1)
rack
rack-cache (1.2)
rack (>= 0.4)
rack-mini-profiler (0.1.26)
rack-mini-profiler (0.1.31)
rack (>= 1.1.3)
rack-mount (0.8.3)
rack (>= 1.0.0)
......@@ -357,13 +359,14 @@ GEM
rails-dev-tweaks (0.6.1)
actionpack (~> 3.1)
railties (~> 3.1)
rails_best_practices (1.13.8)
rails_best_practices (1.14.4)
activesupport
awesome_print
code_analyzer
code_analyzer (>= 0.4.3)
colored
erubis
i18n
require_all
ruby-progressbar
railties (3.2.13)
actionpack (= 3.2.13)
......@@ -403,6 +406,7 @@ GEM
redis-store (1.1.4)
redis (>= 2.2)
ref (1.0.5)
require_all (1.3.1)
rest-client (1.6.7)
mime-types (>= 1.16)
rspec (2.13.0)
......@@ -427,7 +431,7 @@ GEM
safe_yaml (0.9.3)
sanitize (2.0.3)
nokogiri (>= 1.4.4, < 1.6)
sass (3.2.9)
sass (3.2.11)
sass-rails (3.2.6)
railties (~> 3.2.0)
sass (>= 3.1.10)
......@@ -447,7 +451,7 @@ GEM
rubyzip
websocket (~> 1.0.4)
settingslogic (2.0.9)
sexp_processor (4.2.1)
sexp_processor (4.3.0)
shoulda-matchers (2.1.0)
activesupport (>= 3.0.0)
sidekiq (2.14.0)
......@@ -487,7 +491,9 @@ GEM
state_machine (1.2.0)
stringex (1.5.1)
temple (0.6.5)
test_after_commit (0.2.0)
term-ansicolor (1.2.2)
tins (~> 0.8)
test_after_commit (0.2.1)
therubyracer (0.11.4)
libv8 (~> 3.11.8.12)
ref
......@@ -507,6 +513,7 @@ GEM
mime-types (~> 1.19)
multi_json (~> 1.5)
twitter-stream (~> 0.1)
tins (0.11.0)
treetop (1.4.14)
polyglot
polyglot (>= 0.3.1)
......@@ -544,7 +551,7 @@ PLATFORMS
DEPENDENCIES
acts-as-taggable-on
annotate!
annotate (~> 2.6.0.beta2)
asciidoctor
awesome_print
better_errors
......@@ -559,6 +566,7 @@ DEPENDENCIES
d3_rails (~> 3.1.4)
database_cleaner
devise (~> 2.2)
devise-async
email_spec
enumerize
factory_girl_rails
......@@ -569,10 +577,11 @@ DEPENDENCIES
gemoji (~> 1.2.1)
github-linguist
github-markup (~> 0.7.4)
gitlab-flowdock-git-hook (~> 0.4.2)
gitlab-gollum-lib (~> 1.0.1)
gitlab-grack (~> 1.0.1)
gitlab-pygments.rb (~> 0.3.2)
gitlab_git (= 2.3.1)
gitlab_git (~> 3.0.0.rc2)
gitlab_meta (= 6.0)
gitlab_omniauth-ldap (= 1.0.3)
gon
......@@ -604,6 +613,7 @@ DEPENDENCIES
poltergeist (~> 1.4.1)
pry
quiet_assets (~> 1.0.1)
rack-attack
rack-mini-profiler
rails (= 3.2.13)
rails-dev-tweaks
......
......@@ -127,14 +127,17 @@ or start each component separately
### GitLab interfaces
* [GitLab API](doc/api/README.md)
* [GitLab API doc](doc/api/README.md) or see the [GitLab API website](http://api.gitlab.org/)
* [Rake tasks](doc/raketasks)
* [Rake tasks](doc/raketasks) including a [backup and restore procedure](doc/raketasks/backup_restore.md)
* [Directory structure](doc/install/structure.md)
* [Databases](doc/install/databases.md)
* [Database installation](doc/install/databases.md)
* [Markdown specification](doc/markdown/markdown.md)
* [Security guide](doc/security/rack_attack.md) to throttle abusive requests
### Getting help
......@@ -157,10 +160,8 @@ or start each component separately
### Getting in touch
* [Core team](https://github.com/gitlabhq?tab=members)
* [Contributors](https://github.com/gitlabhq/gitlabhq/graphs/contributors)
* [Core team](http://gitlab.org/team/)
* [Leader](https://github.com/randx)
* [Contributors](http://contributors.gitlab.org/)
* [Contact page](http://gitlab.org/contact/)
* [Community](http://gitlab.org/community/)
......@@ -16,3 +16,13 @@ $ ->
$('.update-notifications').on 'ajax:complete', ->
$(this).find('.btn-save').enableButton()
$('.js-choose-user-avatar-button').bind "click", ->
form = $(this).closest("form")
form.find(".js-user-avatar-input").click()
$('.js-user-avatar-input').bind "change", ->
form = $(this).closest("form")
filename = $(this).val().replace(/^.*[\\\/]/, '')
form.find(".js-avatar-filename").text(filename)
$ ->
userFormatResult = (user) ->
avatar = gon.gravatar_url
avatar = avatar.replace('%{hash}', md5(user.email))
avatar = avatar.replace('%{size}', '24')
if user.avatar
avatar = user.avatar.url
else
avatar = gon.gravatar_url
avatar = avatar.replace('%{hash}', md5(user.email))
avatar = avatar.replace('%{size}', '24')
markup = "<div class='user-result'>"
markup += "<div class='user-image'><img class='avatar s24' src='" + avatar + "'></div>"
markup += "<div class='user-name'>" + user.name + "</div>"
......
......@@ -140,27 +140,6 @@ p.time {
border-bottom: 2px solid #F90;
}
.status_info {
font-size: 14px;
padding: 5px 15px;
line-height: 26px;
text-align: center;
float: right;
position: relative;
top: -5px;
@include border-radius(4px);
&.success {
background: #4A4;
color: #FFF;
}
&.error {
background: #DA4E49;
color: #FFF;
}
}
.thin_area{
height: 150px;
}
......@@ -270,27 +249,6 @@ li.note {
}
}
.oauth_select_holder {
padding: 20px;
img {
padding: 5px;
margin-right: 10px;
}
.active {
img {
border: 1px solid #ccc;
background: $hover;
@include border-radius(5px);
}
}
}
.btn-build-token {
float: left;
padding: 6px 20px;
margin-right: 12px;
}
.gitlab-promo {
a {
color: #aaa;
......@@ -386,3 +344,8 @@ table {
width: 50px;
min-height: 100px;
}
.navbar-gitlab .navbar-inner .nav > li .btn-sign-in {
@extend .btn-new;
padding: 5px 15px;
}
......@@ -62,6 +62,5 @@ $baseLineHeight: 18px !default;
@import "gitlab_bootstrap/buttons.scss";
@import "gitlab_bootstrap/blocks.scss";
@import "gitlab_bootstrap/files.scss";
@import "gitlab_bootstrap/tables.scss";
@import "gitlab_bootstrap/lists.scss";
@import "gitlab_bootstrap/forms.scss";
......@@ -19,4 +19,5 @@
&.s32 { width: 32px; height: 32px; margin-right: 10px; }
&.s60 { width: 60px; height: 60px; margin-right: 12px; }
&.s90 { width: 90px; height: 90px; margin-right: 15px; }
&.s160 { width: 160px; height: 160px; margin-right: 20px; }
}
......@@ -12,7 +12,7 @@
.ui-box {
background: #FFF;
margin-bottom: 20px;
border: 1px solid #CCC;
border: 1px solid #DDD;
word-wrap: break-word;
&.small-box {
......@@ -32,8 +32,12 @@
}
&.ui-box-show {
text-shadow: 0 1px 1px #fff;
color: #666;
margin:20px 0;
background: #FFF;
box-shadow: inset 0 1px 0 #fff, 0 1px 5px #f1f1f1;
@include linear-gradient(#fafafa, #f1f1f1);
.control-group {
margin-bottom: 0;
......@@ -69,7 +73,6 @@
.ui-box-head {
.box-title {
color: $style_color;
font-size: 18px;
font-weight: normal;
line-height: 28px;
......
......@@ -87,3 +87,25 @@ pre.well-pre {
font-weight: bold;
@include box-shadow(inset 0 2px 4px rgba(0,0,0,.15));
}
/** Big Labels **/
.state-label {
font-size: 14px;
padding: 5px 15px;
text-align: center;
float: right;
position: relative;
top: -5px;
@include border-radius(4px);
text-shadow: none;
&.state-label-green {
background: #4A4;
color: #FFF;
}
&.state-label-red {
background: #DA4E49;
color: #FFF;
}
}
......@@ -6,6 +6,10 @@
border: 1px solid #CCC;
margin-bottom: 1em;
table {
@extend .table;
}
.file-title {
border-bottom: 1px solid #bbb;
@include bg-dark-gray-gradient;
......@@ -65,6 +69,12 @@
}
&.blob-no-preview {
background: #eee;
text-shadow: 0 1px 2px #FFF;
padding: 100px 0;
}
/**
* Blame file
*/
......
......@@ -95,6 +95,14 @@
font-size: 14px;
line-height: 1.5;
}
table {
@extend .table;
@extend .table-bordered;
th {
background: #EEE;
}
}
}
@mixin page-title {
......
table {
@extend .table;
@extend .table-striped;
border: 1px solid #CCC;
width: 100%;
&.low {
td {
line-height: 18px;
}
}
&.headless {
tr:first-child td{
border-top: 1px solid #CCC;
}
}
th {
font-weight: bold;
vertical-align: middle;
border-bottom: 1px solid #CCC;
text-shadow: 0 1px 1px #fff;
@include bg-dark-gray-gradient;
ul.nav {
text-shadow: none;
margin: 0;
}
}
th, td {
padding: 10px;
line-height: 18px;
text-align: left;
}
td {
border-color: #f1f1f1;
line-height: 28px;
.s16 {
margin-top: 5px;
margin-right: 5px;
}
&:first-child {
border-left: 1px solid #CCC;
}
&:last-child {
border-right: 1px solid #CCC;
}
}
&.bordered {
@extend .table-bordered;
}
&.lite {
border: none;
box-shadow: none;
tr, td {
border: none;
background:none !important;
}
}
}
......@@ -67,6 +67,7 @@
}
table {
width: 100%;
font-family: $monospace_font;
border: none;
margin: 0px;
......@@ -418,7 +419,6 @@
.commit-title {
margin: 0;
font-size: 20px;
font-weight: bold;
}
.commit-description {
......
......@@ -60,7 +60,7 @@
}
a {
border-color: #CCC !important;
border-color: #DDD !important;
}
}
}
......@@ -85,12 +85,18 @@
color: #666;
}
.last-activity, .owner-info {
.last-activity {
color: #AAA;
display: block;
margin-top: 5px;
.date, .owner {
.date {
color: #777;
}
}
}
.group-row {
.arrow {
padding: 2px 5px;
}
}
......@@ -46,8 +46,8 @@
border-bottom: 1px solid #eee;
.event-title {
color: #333;
font-weight: bold;
font-size: 14px;
font-weight: normal;
font-size: 15px;
.author_name {
color: #333;
}
......
/* Login Page */
body.login-page{
background: #474D57;
.container .content { padding-top: 4%; }
.container > .content {
padding-top: 20px;
}
}
.login-box{
......
......@@ -135,11 +135,15 @@ ul.notes {
background-color: $white;
border-width: 1px 0;
padding-top: 0;
li {
padding: 5px;
}
}
}
.reply-btn {
margin-top: 8px;
margin: 5px;
}
}
......
......@@ -4,3 +4,41 @@
margin-bottom: 0;
}
}
.account-page {
fieldset {
margin-bottom: 15px;
border-bottom: 1px dashed #ddd;
padding-bottom: 15px;
&:last-child {
border: none;
}
legend {
border: none;
margin: 0;
}
}
}
.oauth_select_holder {
img {
padding: 2px;
margin-right: 10px;
}
.active {
img {
border: 1px solid #4BD;
background: $hover;
@include border-radius(5px);
}
}
}
.btn-build-token {
float: left;
padding: 6px 20px;
margin-right: 12px;
}
......@@ -79,21 +79,6 @@ ul.nav.nav-projects-tabs {
margin: 0px;
}
.public-projects {
li {
.project-title {
font-size: 14px;
line-height: 2;
font-weight: normal;
}
.description {
margin-left: 15px;
color: #aaa;
}
}
}
.my-projects {
li {
.project-title {
......@@ -110,7 +95,6 @@ ul.nav.nav-projects-tabs {
}
}
.public-clone {
background: #333;
color: #f5f5f5;
......@@ -123,3 +107,11 @@ ul.nav.nav-projects-tabs {
position: relative;
top: -5px;
}
.public-projects .repo-info {
color: #777;
a {
color: #777;
}
}
......@@ -13,13 +13,21 @@
}
.tree-table {
@extend .table;
@include border-radius(0);
.tree-item {
td {
tr {
td, th {
padding: 8px 10px;
strong {
font-weight: normal;
}
line-height: 20px;
}
th {
font-weight: normal;
font-size: 15px;
border-bottom: 1px solid #CCC;
}
td {
border-color: #F1F1F1;
}
&:hover {
td {
......@@ -29,12 +37,11 @@
}
cursor: pointer;
}
&.selected {
td {
background: $hover;
border-top: 1px solid #ADF;
border-bottom: 1px solid #ADF;
background: #f5f5f5;
border-top: 1px solid #EEE;
border-bottom: 1px solid #EEE;
}
}
}
......@@ -56,24 +63,8 @@
}
}
.tree-table {
th .btn {
margin: -2px -1px;
padding: 2px 10px;
}
td {
line-height: 20px;
background: #fafafa;
}
}
.tree_author {
padding-right: 8px;
img.avatar {
margin-top: 0;
width: 16px;
}
}
.tree_commit {
......
......@@ -13,7 +13,7 @@ class SearchContext
projects = Project.where(id: project_ids)
result[:projects] = projects.search(query).limit(20)
# Search inside singe project
# Search inside single project
project = projects.first if projects.length == 1
if params[:search_code].present?
......
......@@ -20,9 +20,9 @@ class Admin::GroupsController < Admin::ApplicationController
def create
@group = Group.new(params[:group])
@group.path = @group.name.dup.parameterize if @group.name
@group.owner = current_user
if @group.save
@group.add_owner(current_user)
redirect_to [:admin, @group], notice: 'Group was successfully created.'
else
render "new"
......@@ -30,14 +30,7 @@ class Admin::GroupsController < Admin::ApplicationController
end
def update
group_params = params[:group].dup
owner_id =group_params.delete(:owner_id)
if owner_id
@group.change_owner(User.find(owner_id))
end
if @group.update_attributes(group_params)
if @group.update_attributes(params[:group])
redirect_to [:admin, @group], notice: 'Group was successfully updated.'
else
render "edit"
......
......@@ -47,6 +47,8 @@ class Admin::UsersController < Admin::ApplicationController
@user = User.build_user(params[:user].merge(opts), as: :admin)
@user.admin = (admin && admin.to_i > 0)
@user.created_by_id = current_user.id
@user.generate_password
@user.confirm!
respond_to do |format|
if @user.save
......@@ -71,6 +73,7 @@ class Admin::UsersController < Admin::ApplicationController
respond_to do |format|
if user.update_attributes(params[:user], as: :admin)
user.confirm!
format.html { redirect_to [:admin, user], notice: 'User was successfully updated.' }
format.json { head :ok }
else
......
......@@ -2,7 +2,7 @@ class ApplicationController < ActionController::Base
before_filter :authenticate_user!
before_filter :reject_blocked!
before_filter :check_password_expiration
before_filter :set_current_user_for_thread
around_filter :set_current_user_for_thread
before_filter :add_abilities
before_filter :ldap_security_check
before_filter :dev_tools if Rails.env == 'development'
......@@ -51,6 +51,11 @@ class ApplicationController < ActionController::Base
def set_current_user_for_thread
Thread.current[:current_user] = current_user
begin
yield
ensure
Thread.current[:current_user] = nil
end
end
def abilities
......@@ -64,6 +69,15 @@ class ApplicationController < ActionController::Base
def project
id = params[:project_id] || params[:id]
# Redirect from
# localhost/group/project.git
# to
# localhost/group/project
#
if id =~ /\.git\Z/
redirect_to request.original_url.gsub(/\.git\Z/, '') and return
end
@project = Project.find_with_namespace(id)
if @project and can?(current_user, :read_project, @project)
......
......@@ -5,10 +5,14 @@ class DashboardController < ApplicationController
before_filter :event_filter, only: :show
def show
# Fetch only 30 projects.
# If user needs more - point to Dashboard#projects page
@projects_limit = 30
@groups = current_user.authorized_groups.sort_by(&:human_name)
@has_authorized_projects = @projects.count > 0
@projects_count = @projects.count
@projects = @projects.limit(20)
@projects = @projects.limit(@projects_limit)
@events = Event.in_projects(current_user.authorized_projects.pluck(:id))
@events = @event_filter.apply_filter(@events)
......
......@@ -21,9 +21,9 @@ class GroupsController < ApplicationController
def create
@group = Group.new(params[:group])
@group.path = @group.name.dup.parameterize if @group.name
@group.owner = current_user
if @group.save
@group.add_owner(current_user)
redirect_to @group, notice: 'Group was successfully created.'
else
render action: "new"
......@@ -75,15 +75,7 @@ class GroupsController < ApplicationController
end
def update
group_params = params[:group].dup
owner_id = group_params.delete(:owner_id)
if owner_id
@group.owner = User.find(owner_id)
@group.save
end
if @group.update_attributes(group_params)
if @group.update_attributes(params[:group])
redirect_to @group, notice: 'Group was successfully updated.'
else
render action: "edit"
......
class Profiles::AccountsController < ApplicationController
layout "profile"
def show
@user = current_user
end
end
......@@ -8,8 +8,8 @@ class Profiles::GroupsController < ApplicationController
def leave
@users_group = group.users_groups.where(user_id: current_user.id).first
if group.owner == current_user
redirect_to(profile_groups_path, alert: "You can't leave group. You must transfer it to another owner before leaving.")
if group.last_owner?(current_user)
redirect_to(profile_groups_path, alert: "You can't leave group. You must add at least one more owner to it.")
else
@users_group.destroy
redirect_to(profile_groups_path, info: "You left #{group.name} group.")
......
class Profiles::PasswordsController < ApplicationController
layout 'navless'
layout :determine_layout
skip_before_filter :check_password_expiration
skip_before_filter :check_password_expiration, only: [:new, :create]
before_filter :set_user
before_filter :set_title
before_filter :authorize_change_password!
def new
end
......@@ -26,6 +27,32 @@ class Profiles::PasswordsController < ApplicationController
end
end
def edit
end
def update
password_attributes = params[:user].select do |key, value|
%w(password password_confirmation).include?(key.to_s)
end
unless @user.valid_password?(params[:user][:current_password])
redirect_to edit_profile_password_path, alert: 'You must provide a valid current password'
return
end
if @user.update_attributes(password_attributes)
flash[:notice] = "Password was successfully updated. Please login with it"
redirect_to new_user_session_path
else
render 'edit'
end
end
def reset
current_user.send_reset_password_instructions
redirect_to edit_profile_password_path, notice: 'We sent you an email with reset password instructions'
end
private
def set_user
......@@ -35,4 +62,16 @@ class Profiles::PasswordsController < ApplicationController
def set_title
@title = "New password"
end
def determine_layout
if [:new, :create].include?(action_name.to_sym)
'navless'
else
'profile'
end
end
def authorize_change_password!
return render_404 if @user.ldap_user?
end
end
......@@ -2,7 +2,6 @@ class ProfilesController < ApplicationController
include ActionView::Helpers::SanitizeHelper
before_filter :user
before_filter :authorize_change_password!, only: :update_password
before_filter :authorize_change_username!, only: :update_username
layout 'profile'
......@@ -13,9 +12,6 @@ class ProfilesController < ApplicationController
def design
end
def account
end
def update
if @user.update_attributes(params[:user])
flash[:notice] = "Profile was successfully updated"
......@@ -29,26 +25,12 @@ class ProfilesController < ApplicationController
end
end
def token
end
def update_password
params[:user].reject!{ |k, v| k != "password" && k != "password_confirmation"}
if @user.update_attributes(params[:user])
flash[:notice] = "Password was successfully updated. Please login with it"
redirect_to new_user_session_path
else
render 'account'
end
end
def reset_private_token
if current_user.reset_authentication_token!
flash[:notice] = "Token was successfully updated"
end
redirect_to account_profile_path
redirect_to profile_account_path
end
def history
......@@ -69,10 +51,6 @@ class ProfilesController < ApplicationController
@user = current_user
end
def authorize_change_password!
return render_404 if @user.ldap_user?
end
def authorize_change_username!
return render_404 unless @user.can_change_username?
end
......
class Projects::ApplicationController < ApplicationController
before_filter :project
before_filter :repository
layout 'projects'
layout :determine_layout
def authenticate_user!
# Restrict access to Projects area only
# for non-signed users
if !current_user
id = params[:project_id] || params[:id]
@project = Project.find_with_namespace(id)
return if @project && @project.public
end
super
end
def determine_layout
if current_user
'projects'
else
'public_projects'
end
end
end
......@@ -8,7 +8,7 @@ class Projects::BlameController < Projects::ApplicationController
before_filter :require_non_empty_project
def show
@blob = Gitlab::Git::Blob.new(@repository, @commit.id, @ref, @path)
@blob = @repository.blob_at(@commit.id, @path)
@blame = Gitlab::Git::Blame.new(project.repository, @commit.id, @path)
end
end
......@@ -8,6 +8,8 @@ class Projects::BlobController < Projects::ApplicationController
before_filter :require_non_empty_project
def show
@blob = Gitlab::Git::Blob.new(@repository, @commit.id, @ref, @path)
@blob = @repository.blob_at(@commit.id, @path)
not_found! unless @blob
end
end
......@@ -10,7 +10,7 @@ class Projects::EditTreeController < Projects::ApplicationController
before_filter :edit_requirements, only: [:show, :update]
def show
@last_commit = Gitlab::Git::Commit.last_for_path(@project.repository, @ref, @path).sha
@last_commit = Gitlab::Git::Commit.last_for_path(@repository, @ref, @path).sha
end
def update
......@@ -32,9 +32,9 @@ class Projects::EditTreeController < Projects::ApplicationController
private
def edit_requirements
@blob = Gitlab::Git::Blob.new(@repository, @commit.id, @ref, @path)
@blob = @repository.blob_at(@commit.id, @path)
unless @blob.exists? && @blob.text?
unless @blob
redirect_to project_blob_path(@project, @id), notice: "You can only edit text files"
end
......
class Projects::HooksController < Projects::ApplicationController
# Authorize
before_filter :authorize_read_project!
before_filter :authorize_admin_project!, only: [:new, :create, :destroy]
before_filter :authorize_admin_project!
respond_to :html
......
......@@ -157,12 +157,15 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
def validates_merge_request
# Show git not found page if target branch doesn't exist
return invalid_mr unless @merge_request.target_project.repository.branch_names.include?(@merge_request.target_branch)
# Show git not found page if source branch doesn't exist
# and there is no saved commits between source & target branch
return invalid_mr if !@merge_request.source_project.repository.branch_names.include?(@merge_request.source_branch) && @merge_request.commits.blank?
# Show git not found page
# if there is no saved commits between source & target branch
if @merge_request.commits.blank?
# and if source target doesn't exist
return invalid_mr unless @merge_request.target_project.repository.branch_names.include?(@merge_request.target_branch)
# or if source branch doesn't exist
return invalid_mr unless @merge_request.source_project.repository.branch_names.include?(@merge_request.source_branch)
end
end
def define_show_vars
......
......@@ -14,7 +14,7 @@ class Projects::MilestonesController < Projects::ApplicationController
@milestones = case params[:f]
when 'all'; @project.milestones.order("state, due_date DESC")
when 'closed'; @project.milestones.closed.order("due_date DESC")
else @project.milestones.active.order("due_date DESC")
else @project.milestones.active.order("due_date ASC")
end
@milestones = @milestones.includes(:project)
......
......@@ -8,9 +8,9 @@ class Projects::RawController < Projects::ApplicationController
before_filter :require_non_empty_project
def show
@blob = Gitlab::Git::Blob.new(@repository, @commit.id, @ref, @path)
@blob = @repository.blob_at(@commit.id, @path)
if @blob.exists?
if @blob
type = if @blob.mime_type =~ /html|javascript/
'text/plain; charset=utf-8'
else
......
......@@ -24,13 +24,14 @@ class Projects::RefsController < Projects::ApplicationController
format.js do
@ref = params[:ref]
define_tree_vars
tree
render "tree"
end
end
end
def logs_tree
contents = @tree.entries
contents = tree.entries
@logs = contents.map do |content|
file = params[:path] ? File.join(params[:path], content.name) : content.name
last_commit = @repo.commits(@commit.id, file, 1).last
......
......@@ -14,8 +14,6 @@ class Projects::SnippetsController < Projects::ApplicationController
# Allow destroy snippet
before_filter :authorize_admin_project_snippet!, only: [:destroy]
layout 'projects'
respond_to :html
def index
......
class Projects::TeamMembersController < Projects::ApplicationController
# Authorize
before_filter :authorize_read_project!
before_filter :authorize_admin_project!, except: [:index, :show]
before_filter :authorize_admin_project!
layout "project_settings"
......
......@@ -8,6 +8,8 @@ class Projects::TreeController < Projects::ApplicationController
before_filter :require_non_empty_project
def show
return not_found! if tree.entries.empty?
respond_to do |format|
format.html
# Disable cache so browser history works
......
class ProjectsController < Projects::ApplicationController
skip_before_filter :project, only: [:new, :create]
skip_before_filter :repository, only: [:new, :create]
class ProjectsController < ApplicationController
skip_before_filter :authenticate_user!, only: [:show]
before_filter :project, except: [:new, :create]
before_filter :repository, except: [:new, :create]
# Authorize
before_filter :authorize_read_project!, except: [:index, :new, :create]
......@@ -54,8 +55,9 @@ class ProjectsController < Projects::ApplicationController
end
def show
limit = (params[:limit] || 20).to_i
return authenticate_user! unless @project.public || current_user
limit = (params[:limit] || 20).to_i
@events = @project.events.recent
@events = event_filter.apply_filter(@events)
@events = @events.limit(limit).offset(params[:offset] || 0)
......@@ -67,10 +69,12 @@ class ProjectsController < Projects::ApplicationController
respond_to do |format|
format.html do
if @project.empty_repo?
render "projects/empty"
render "projects/empty", layout: user_layout
else
@last_push = current_user.recent_push(@project.id)
render :show
if current_user
@last_push = current_user.recent_push(@project.id)
end
render :show, layout: user_layout
end
end
format.js
......@@ -121,4 +125,8 @@ class ProjectsController < Projects::ApplicationController
def set_title
@title = 'New Project'
end
def user_layout
current_user ? "projects" : "public_projects"
end
end
class Public::ProjectsController < ApplicationController
skip_before_filter :authenticate_user!,
:reject_blocked, :set_current_user_for_observers,
:add_abilities
:reject_blocked, :set_current_user_for_observers,
:add_abilities
layout 'public'
......@@ -10,15 +10,4 @@ class Public::ProjectsController < ApplicationController
@projects = @projects.search(params[:search]) if params[:search].present?
@projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page]).per(20)
end
def show
@project = Project.public_only.find_with_namespace(params[:id])
render_404 and return unless @project
@repository = @project.repository
@recent_tags = @repository.tags.first(10)
@commit = @repository.commit(params[:ref])
@tree = Tree.new(@repository, @commit.id)
end
end
......@@ -19,7 +19,7 @@ class UsersGroupsController < ApplicationController
def destroy
@users_group = @group.users_groups.find(params[:id])
@users_group.destroy unless @users_group.user == @group.owner
@users_group.destroy
respond_to do |format|
format.html { redirect_to members_group_path(@group), notice: 'User was successfully removed from group.' }
......
......@@ -49,6 +49,15 @@ module ApplicationHelper
args.any? { |v| v.to_s.downcase == action_name }
end
def avatar_icon(user_email = '', size = nil)
user = User.find_by_email(user_email)
if user && user.avatar.present?
user.avatar.url
else
gravatar_icon(user_email, size)
end
end
def gravatar_icon(user_email = '', size = nil)
size = 40 if size.nil? || size <= 0
......@@ -89,49 +98,6 @@ module ApplicationHelper
grouped_options_for_select(options, @ref || @project.default_branch)
end
def search_autocomplete_source
projects = current_user.authorized_projects.map { |p| { label: "project: #{simple_sanitize(p.name_with_namespace)}", url: project_path(p) } }
groups = current_user.authorized_groups.map { |group| { label: "group: #{simple_sanitize(group.name)}", url: group_path(group) } }
default_nav = [
{ label: "My Profile", url: profile_path },
{ label: "My SSH Keys", url: profile_keys_path },
{ label: "My Dashboard", url: root_path },
{ label: "Admin Section", url: admin_root_path },
]
help_nav = [
{ label: "help: API Help", url: help_api_path },
{ label: "help: Markdown Help", url: help_markdown_path },
{ label: "help: Permissions Help", url: help_permissions_path },
{ label: "help: Public Access Help", url: help_public_access_path },
{ label: "help: Rake Tasks Help", url: help_raketasks_path },
{ label: "help: SSH Keys Help", url: help_ssh_path },
{ label: "help: System Hooks Help", url: help_system_hooks_path },
{ label: "help: Web Hooks Help", url: help_web_hooks_path },
{ label: "help: Workflow Help", url: help_workflow_path },
]
project_nav = []
if @project && @project.repository.exists? && @project.repository.root_ref
project_nav = [
{ label: "#{simple_sanitize(@project.name_with_namespace)} - Files", url: project_tree_path(@project, @ref || @project.repository.root_ref) },
{ label: "#{simple_sanitize(@project.name_with_namespace)} - Commits", url: project_commits_path(@project, @ref || @project.repository.root_ref) },
{ label: "#{simple_sanitize(@project.name_with_namespace)} - Network", url: project_network_path(@project, @ref || @project.repository.root_ref) },
{ label: "#{simple_sanitize(@project.name_with_namespace)} - Graph", url: project_graph_path(@project, @ref || @project.repository.root_ref) },
{ label: "#{simple_sanitize(@project.name_with_namespace)} - Issues", url: project_issues_path(@project) },
{ label: "#{simple_sanitize(@project.name_with_namespace)} - Merge Requests", url: project_merge_requests_path(@project) },
{ label: "#{simple_sanitize(@project.name_with_namespace)} - Milestones", url: project_milestones_path(@project) },
{ label: "#{simple_sanitize(@project.name_with_namespace)} - Snippets", url: project_snippets_path(@project) },
{ label: "#{simple_sanitize(@project.name_with_namespace)} - Team", url: project_team_index_path(@project) },
{ label: "#{simple_sanitize(@project.name_with_namespace)} - Wall", url: project_wall_path(@project) },
{ label: "#{simple_sanitize(@project.name_with_namespace)} - Wiki", url: project_wikis_path(@project) },
]
end
[groups, projects, default_nav, project_nav, help_nav].flatten.to_json
end
def emoji_autocomplete_source
# should be an array of strings
# so to_s can be called, because it is sufficient and to_json is too slow
......@@ -181,7 +147,7 @@ module ApplicationHelper
alt: "Sign in with #{provider.to_s.titleize}")
end
def simple_sanitize str
def simple_sanitize(str)
sanitize(str, tags: %w(a span))
end
......
......@@ -108,7 +108,7 @@ module CommitsHelper
source_name = commit.send "#{options[:source]}_name".to_sym
source_email = commit.send "#{options[:source]}_email".to_sym
text = if options[:avatar]
avatar = image_tag(gravatar_icon(source_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size], alt: "")
avatar = image_tag(avatar_icon(source_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size], alt: "")
%Q{#{avatar} <span class="commit-#{options[:source]}-name">#{source_name}</span>}
else
source_name
......
......@@ -34,7 +34,8 @@ module GitlabMarkdownHelper
# see https://github.com/vmg/redcarpet#darling-i-packed-you-a-couple-renderers-for-lunch-
filter_html: true,
with_toc_data: true,
hard_wrap: true)
hard_wrap: true,
safe_links_only: true)
@markdown = Redcarpet::Markdown.new(gitlab_renderer,
# see https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use
no_intra_emphasis: true,
......@@ -57,4 +58,104 @@ module GitlabMarkdownHelper
wiki_page.formatted_content.html_safe
end
end
# text - whole text from a markdown file
# project_path_with_namespace - namespace/projectname, eg. gitlabhq/gitlabhq
# ref - name of the branch or reference, eg. stable
# requested_path - path of request, eg. doc/api/README.md, used in special case when path is pointing to the .md file were the original request is coming from
# wiki - whether the markdown is from wiki or not
def create_relative_links(text, project_path_with_namespace, ref, requested_path, wiki = false)
paths = extract_paths(text)
paths.each do |file_path|
new_path = rebuild_path(project_path_with_namespace, file_path, requested_path, ref)
# Replacing old string with a new one with brackets ]() to prevent replacing occurence of a word
# e.g. If we have a markdown like [test](test) this will replace ](test) and not the word test
text.gsub!("](#{file_path})", "](/#{new_path})")
end
text
end
def extract_paths(markdown_text)
all_markdown_paths = pick_out_paths(markdown_text)
paths = remove_empty(all_markdown_paths)
select_relative(paths)
end
# Split the markdown text to each line and find all paths, this will match anything with - ]("some_text")
def pick_out_paths(markdown_text)
markdown_text.split("\n").map { |text| text.scan(/\]\(([^(]+)\)/) }
end
# Removes any empty result produced by not matching the regexp
def remove_empty(paths)
paths.reject{|l| l.empty? }.flatten
end
# Reject any path that contains ignored protocol
# eg. reject "https://gitlab.org} but accept "doc/api/README.md"
def select_relative(paths)
paths.reject{|path| ignored_protocols.map{|protocol| path.include?(protocol)}.any?}
end
def ignored_protocols
["http://","https://", "ftp://", "mailto:"]
end
def rebuild_path(path_with_namespace, path, requested_path, ref)
file_path = relative_file_path(path, requested_path)
[
path_with_namespace,
path_with_ref(file_path, ref),
file_path
].compact.join("/")
end
# Checks if the path exists in the repo
# eg. checks if doc/README.md exists, if it doesn't then it is a wiki link
def path_with_ref(path, ref)
if file_exists?(path)
"#{local_path(path)}/#{correct_ref(ref)}"
else
"wikis"
end
end
def relative_file_path(path, requested_path)
nested_path = build_nested_path(path, requested_path)
return nested_path if file_exists?(nested_path)
path
end
# Covering a special case, when the link is referencing file in the same directory eg:
# If we are at doc/api/README.md and the README.md contains relative links like [Users](users.md)
# this takes the request path(doc/api/README.md), and replaces the README.md with users.md so the path looks like doc/api/users.md
# If we are at doc/api and the README.md shown in below the tree view
# this takes the rquest path(doc/api) and adds users.md so the path looks like doc/api/users.md
def build_nested_path(path, request_path)
return path unless request_path
if local_path(request_path) == "tree"
base = request_path.split("/").push(path)
base.join("/")
else
base = request_path.split("/")
base.pop
base.push(path).join("/")
end
end
def file_exists?(path)
return false if path.nil? || path.empty?
File.exists?(Rails.root.join(path))
end
# Check if the path is pointing to a directory(tree) or a file(blob)
# eg. doc/api is directory and doc/README.md is file
def local_path(path)
File.directory?(Rails.root.join(path)) ? "tree" : "blob"
end
# We will assume that if no ref exists we can point to master
def correct_ref(ref)
ref ? ref : "master"
end
end
......@@ -16,7 +16,7 @@ module IssuesHelper
def url_for_project_issues
return "" if @project.nil?
if @project.used_default_issues_tracker?
if @project.used_default_issues_tracker? || !external_issues_tracker_enabled?
project_issues_path(@project)
else
url = Gitlab.config.issues_tracker[@project.issues_tracker]["project_url"]
......@@ -28,7 +28,7 @@ module IssuesHelper
def url_for_new_issue
return "" if @project.nil?
if @project.used_default_issues_tracker?
if @project.used_default_issues_tracker? || !external_issues_tracker_enabled?
url = new_project_issue_path project_id: @project
else
url = Gitlab.config.issues_tracker[@project.issues_tracker]["new_issue_url"]
......@@ -40,7 +40,7 @@ module IssuesHelper
def url_for_issue(issue_iid)
return "" if @project.nil?
if @project.used_default_issues_tracker?
if @project.used_default_issues_tracker? || !external_issues_tracker_enabled?
url = project_issue_url project_id: @project, id: issue_iid
else
url = Gitlab.config.issues_tracker[@project.issues_tracker]["issues_url"]
......@@ -59,4 +59,13 @@ module IssuesHelper
""
end
end
# Checks if issues_tracker setting exists in gitlab.yml
def external_issues_tracker_enabled?
if Gitlab.config.issues_tracker && Gitlab.config.issues_tracker.values.any?
true
else
false
end
end
end
......@@ -14,6 +14,6 @@ module ProfileHelper
end
def show_profile_remove_tab?
Gitlab.config.gitlab.signup_enabled && !current_user.ldap_user?
gitlab_config.signup_enabled && !current_user.ldap_user?
end
end
......@@ -5,10 +5,10 @@ module ProjectsHelper
def link_to_project project
link_to project do
title = content_tag(:strong, project.name)
title = content_tag(:span, project.name, class: 'projet-name')
if project.namespace
namespace = content_tag(:span, "#{project.namespace.human_name} / ", class: 'tiny')
namespace = content_tag(:span, "#{project.namespace.human_name} / ", class: 'namespace-name')
title = namespace + title
end
......@@ -25,7 +25,7 @@ module ProjectsHelper
author_html = ""
# Build avatar image tag
author_html << image_tag(gravatar_icon(author.try(:email), opts[:size]), width: opts[:size], class: "avatar avatar-inline #{"s#{opts[:size]}" if opts[:size]}", alt:'') if opts[:avatar]
author_html << image_tag(avatar_icon(author.try(:email), opts[:size]), width: opts[:size], class: "avatar avatar-inline #{"s#{opts[:size]}" if opts[:size]}", alt:'') if opts[:avatar]
# Build name span tag
author_html << content_tag(:span, sanitize(author.name), class: 'author') if opts[:name]
......@@ -77,7 +77,19 @@ module ProjectsHelper
end
def project_active_milestones
@project.milestones.active.order("id desc").all
@project.milestones.active.order("due_date, title ASC").all
end
def project_issues_trackers
values = Project.issues_tracker.values.map do |tracker_key|
if tracker_key.to_sym == :gitlab
['GitLab', tracker_key]
else
[Gitlab.config.issues_tracker[tracker_key]['title'] || tracker_key, tracker_key]
end
end
options_for_select(values)
end
private
......@@ -103,4 +115,29 @@ module ProjectsHelper
nav_tabs.flatten
end
def git_user_name
if current_user
current_user.name
else
"Your name"
end
end
def git_user_email
if current_user
current_user.email
else
"your@email.com"
end
end
def repository_size
"#{@project.repository.size} MB"
rescue
# In order to prevent 500 error
# when application cannot allocate memory
# to calculate repo size - just show 'Unknown'
'unknown'
end
end
module SearchHelper
def search_autocomplete_source
return unless current_user
[
groups_autocomplete,
projects_autocomplete,
default_autocomplete,
project_autocomplete,
help_autocomplete
].flatten.to_json
end
private
# Autocomplete results for various settings pages
def default_autocomplete
[
{ label: "My Profile settings", url: profile_path },
{ label: "My SSH Keys", url: profile_keys_path },
{ label: "My Dashboard", url: root_path },
{ label: "Admin Section", url: admin_root_path },
]
end
# Autocomplete results for internal help pages
def help_autocomplete
[
{ label: "help: API Help", url: help_api_path },
{ label: "help: Markdown Help", url: help_markdown_path },
{ label: "help: Permissions Help", url: help_permissions_path },
{ label: "help: Public Access Help", url: help_public_access_path },
{ label: "help: Rake Tasks Help", url: help_raketasks_path },
{ label: "help: SSH Keys Help", url: help_ssh_path },
{ label: "help: System Hooks Help", url: help_system_hooks_path },
{ label: "help: Web Hooks Help", url: help_web_hooks_path },
{ label: "help: Workflow Help", url: help_workflow_path },
]
end
# Autocomplete results for the current project, if it's defined
def project_autocomplete
if @project && @project.repository.exists? && @project.repository.root_ref
prefix = simple_sanitize(@project.name_with_namespace)
ref = @ref || @project.repository.root_ref
[
{ label: "#{prefix} - Files", url: project_tree_path(@project, ref) },
{ label: "#{prefix} - Commits", url: project_commits_path(@project, ref) },
{ label: "#{prefix} - Network", url: project_network_path(@project, ref) },
{ label: "#{prefix} - Graph", url: project_graph_path(@project, ref) },
{ label: "#{prefix} - Issues", url: project_issues_path(@project) },
{ label: "#{prefix} - Merge Requests", url: project_merge_requests_path(@project) },
{ label: "#{prefix} - Milestones", url: project_milestones_path(@project) },
{ label: "#{prefix} - Snippets", url: project_snippets_path(@project) },
{ label: "#{prefix} - Team", url: project_team_index_path(@project) },
{ label: "#{prefix} - Wall", url: project_wall_path(@project) },
{ label: "#{prefix} - Wiki", url: project_wikis_path(@project) },
]
else
[]
end
end
# Autocomplete results for the current user's groups
def groups_autocomplete
current_user.authorized_groups.map do |group|
{ label: "group: #{simple_sanitize(group.name)}", url: group_path(group) }
end
end
# Autocomplete results for the current user's projects
def projects_autocomplete
current_user.authorized_projects.map do |p|
{ label: "project: #{simple_sanitize(p.name_with_namespace)}", url: project_path(p) }
end
end
end
......@@ -67,9 +67,9 @@ module TreeHelper
end
def tree_breadcrumbs(tree, max_links = 2)
if tree.path
if @path.present?
part_path = ""
parts = tree.path.split("\/")
parts = @path.split("\/")
yield('..', nil) if parts.count > max_links
......@@ -78,14 +78,14 @@ module TreeHelper
part_path = part if part_path.empty?
next unless parts.last(2).include?(part) if parts.count > max_links
yield(part, tree_join(tree.ref, part_path))
yield(part, tree_join(@ref, part_path))
end
end
end
def up_dir_path tree
file = File.join(tree.path, "..")
tree_join(tree.ref, file)
file = File.join(@path, "..")
tree_join(@ref, file)
end
def leave_edit_message
......
......@@ -18,7 +18,7 @@ module Emails
@note = Note.find(note_id)
@merge_request = @note.noteable
@project = @note.project
mail(to: recipient(recipient_id), subject: subject("note for merge request !#{@merge_request.iid}"))
mail(to: recipient(recipient_id), subject: subject("note for merge request ##{@merge_request.iid}"))
end
def note_wall_email(recipient_id, note_id)
......
class Ability
class << self
def allowed(user, subject)
return not_auth_abilities(user, subject) if user.nil?
return [] unless user.kind_of?(User)
return [] if user.blocked?
......@@ -17,6 +18,34 @@ class Ability
end.concat(global_abilities(user))
end
# List of possible abilities
# for non-authenticated user
def not_auth_abilities(user, subject)
project = if subject.kind_of?(Project)
subject
elsif subject.respond_to?(:project)
subject.project
else
nil
end
if project && project.public
[
:read_project,
:read_wiki,
:read_issue,
:read_milestone,
:read_project_snippet,
:read_team_member,
:read_merge_request,
:read_note,
:download_code
]
else
[]
end
end
def global_abilities(user)
rules = []
rules << :create_group if user.can_create_group
......@@ -50,7 +79,7 @@ class Ability
rules << project_admin_rules
end
if project.group && project.group.owners.include?(user)
if project.group && project.group.has_owner?(user)
rules << project_admin_rules
end
......@@ -58,19 +87,9 @@ class Ability
end
def public_project_rules
[
project_guest_rules + [
:download_code,
:fork_project,
:read_project,
:read_wiki,
:read_issue,
:read_milestone,
:read_project_snippet,
:read_team_member,
:read_merge_request,
:read_note,
:write_issue,
:write_note
]
end
......@@ -135,12 +154,12 @@ class Ability
def group_abilities user, group
rules = []
if group.users.include?(user)
if group.users.include?(user) || user.admin?
rules << :read_group
end
# Only group owner and administrators can manage group
if group.owners.include?(user) || user.admin?
if group.has_owner?(user) || user.admin?
rules << [
:manage_group,
:manage_namespace
......
# == Schema Information
#
# Table name: services
#
# id :integer not null, primary key
# type :string(255)
# title :string(255)
# token :string(255)
# project_id :integer not null
# created_at :datetime not null
# updated_at :datetime not null
# active :boolean default(FALSE), not null
# project_url :string(255)
#
require "flowdock-git-hook"
class FlowdockService < Service
validates :token, presence: true, if: :activated?
def title
'Flowdock'
end
def description
'Flowdock is a collaboration web app for technical teams.'
end
def to_param
'flowdock'
end
def fields
[
{ type: 'text', name: 'token', placeholder: '' }
]
end
def execute(push_data)
repo_path = File.join(Gitlab.config.gitlab_shell.repos_path, "#{project.path_with_namespace}.git")
Flowdock::Git.post(
push_data[:ref],
push_data[:before],
push_data[:after],
token: token,
repo: repo_path,
repo_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}",
commit_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/commit/%s",
diff_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/compare/%s...%s",
)
end
end
......@@ -45,6 +45,10 @@ class GollumWiki
end
end
def empty?
pages.empty?
end
# Returns an Array of Gitlab WikiPage instances or an
# empty Array if this Wiki has no pages.
def pages
......
......@@ -5,7 +5,7 @@
# id :integer not null, primary key
# name :string(255) not null
# path :string(255) not null
# owner_id :integer not null
# owner_id :integer
# created_at :datetime not null
# updated_at :datetime not null
# type :string(255)
......@@ -26,36 +26,38 @@ class Group < Namespace
presence: true,
if: ->(group) { group.ldap_cn.present? }
after_create :add_owner
def human_name
name
end
def owners
@owners ||= (users_groups.owners.map(&:user) << owner).uniq
@owners ||= users_groups.owners.map(&:user)
end
def add_users(user_ids, group_access)
user_ids.compact.each do |user_id|
self.users_groups.create(user_id: user_id, group_access: group_access)
user = self.users_groups.find_or_initialize_by_user_id(user_id)
user.update_attributes(group_access: group_access)
end
end
def change_owner(user)
self.owner = user
membership = users_groups.where(user_id: user.id).first
def add_user(user, group_access)
self.users_groups.create(user_id: user.id, group_access: group_access)
end
if membership
membership.update_attributes(group_access: UsersGroup::OWNER)
else
add_owner
end
def add_owner(user)
self.add_user(user, UsersGroup::OWNER)
end
private
def has_owner?(user)
owners.include?(user)
end
def last_owner?(user)
has_owner?(user) && owners.size == 1
end
def add_owner
self.add_users([owner.id], UsersGroup::OWNER)
def members
users_groups
end
end
......@@ -18,6 +18,7 @@
# merge_status :string(255)
# target_project_id :integer not null
# iid :integer
# description :text
#
require Rails.root.join("app/models/commit")
......@@ -221,7 +222,11 @@ class MergeRequest < ActiveRecord::Base
def mr_and_commit_notes
commit_ids = commits.map(&:id)
Note.where("(noteable_type = 'MergeRequest' AND noteable_id = :mr_id) OR (noteable_type = 'Commit' AND commit_id IN (:commit_ids))", mr_id: id, commit_ids: commit_ids)
project.notes.where(
"(noteable_type = 'MergeRequest' AND noteable_id = :mr_id) OR (noteable_type = 'Commit' AND commit_id IN (:commit_ids))",
mr_id: id,
commit_ids: commit_ids
)
end
# Returns the raw diff for this merge request
......
......@@ -5,7 +5,7 @@
# id :integer not null, primary key
# name :string(255) not null
# path :string(255) not null
# owner_id :integer not null
# owner_id :integer
# created_at :datetime not null
# updated_at :datetime not null
# type :string(255)
......@@ -20,7 +20,7 @@ class Namespace < ActiveRecord::Base
has_many :projects, dependent: :destroy
belongs_to :owner, class_name: "User"
validates :owner, presence: true
validates :owner, presence: true, unless: ->(n) { n.type == "Group" }
validates :name, presence: true, uniqueness: true,
length: { within: 0..255 },
format: { with: Gitlab::Regex.name_regex,
......
require "grit"
module Network
class Commit
include ActionView::Helpers::TagHelper
......
......@@ -14,6 +14,7 @@
# commit_id :string(255)
# noteable_id :integer
# st_diff :text
# system :boolean default(FALSE), not null
#
require 'carrierwave/orm/activerecord'
......
......@@ -10,6 +10,9 @@
# created_at :datetime not null
# updated_at :datetime not null
# active :boolean default(FALSE), not null
# project_url :string(255)
# subdomain :string(255)
# room :string(255)
#
class PivotaltrackerService < Service
......
......@@ -24,8 +24,6 @@
# import_url :string(255)
#
require "grit"
class Project < ActiveRecord::Base
include Gitlab::ShellAdapter
extend Enumerize
......@@ -48,12 +46,14 @@ class Project < ActiveRecord::Base
has_one :campfire_service, dependent: :destroy
has_one :pivotaltracker_service, dependent: :destroy
has_one :hipchat_service, dependent: :destroy
has_one :flowdock_service, dependent: :destroy
has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id"
has_one :forked_from_project, through: :forked_project_link
has_many :services, dependent: :destroy
has_many :events, dependent: :destroy
has_many :merge_requests, dependent: :destroy, foreign_key: "target_project_id"
has_many :fork_merge_requests,dependent: :destroy, foreign_key: "source_project_id", class_name: MergeRequest
has_many :issues, dependent: :destroy, order: "state DESC, created_at DESC"
has_many :milestones, dependent: :destroy
has_many :notes, dependent: :destroy
......@@ -224,7 +224,7 @@ class Project < ActiveRecord::Base
end
def available_services_names
%w(gitlab_ci campfire hipchat pivotaltracker)
%w(gitlab_ci campfire hipchat pivotaltracker flowdock)
end
def gitlab_ci?
......@@ -252,10 +252,10 @@ class Project < ActiveRecord::Base
end
def owner
if namespace
namespace_owner
if group
group
else
creator
namespace.try(:owner)
end
end
......@@ -279,10 +279,6 @@ class Project < ActiveRecord::Base
end
end
def namespace_owner
namespace.try(:owner)
end
def path_with_namespace
if namespace
namespace.path + '/' + path
......@@ -320,8 +316,11 @@ class Project < ActiveRecord::Base
branch_name = ref.gsub("refs/heads/", "")
c_ids = self.repository.commits_between(oldrev, newrev).map(&:id)
# Update code for merge requests
# Update code for merge requests into project between project branches
mrs = self.merge_requests.opened.by_branch(branch_name).all
# Update code for merge requests between project and project fork
mrs += self.fork_merge_requests.opened.by_branch(branch_name).all
mrs.each { |merge_request| merge_request.reload_code; merge_request.mark_as_unchecked }
# Close merge requests
......
class Repository
include Gitlab::ShellAdapter
attr_accessor :raw_repository
attr_accessor :raw_repository, :path_with_namespace
def initialize(path_with_namespace, default_branch)
@raw_repository = Gitlab::Git::Repository.new(path_with_namespace, default_branch)
@path_with_namespace = path_with_namespace
@raw_repository = Gitlab::Git::Repository.new(path_to_repo) if path_with_namespace
rescue Gitlab::Git::Repository::NoRepository
nil
end
def path_to_repo
@path_to_repo ||= File.join(Gitlab.config.gitlab_shell.repos_path, path_with_namespace + ".git")
end
def exists?
raw_repository
end
......@@ -150,4 +155,8 @@ class Repository
super
end
def blob_at(sha, path)
Gitlab::Git::Blob.find(self, sha, path)
end
end
class Tree
attr_accessor :raw
attr_accessor :entries, :readme
def initialize(repository, sha, ref = nil, path = nil)
@raw = Gitlab::Git::Tree.new(repository, sha, ref, path)
def initialize(repository, sha, path = '/')
path = '/' if path.blank?
git_repo = repository.raw_repository
@entries = Gitlab::Git::Tree.where(git_repo, sha, path)
if readme_tree = @entries.find(&:readme?)
readme_path = path == '/' ? readme_tree.name : File.join(path, readme_tree.name)
@readme = Gitlab::Git::Blob.find(git_repo, sha, readme_path)
end
end
def method_missing(m, *args, &block)
@raw.send(m, *args, &block)
def trees
@entries.select(&:dir?)
end
def respond_to?(method)
return true if @raw.respond_to?(method)
def blobs
@entries.select(&:file?)
end
super
def submodules
@entries.select(&:submodule?)
end
end
......@@ -38,13 +38,16 @@
# created_by_id :integer
#
require 'carrierwave/orm/activerecord'
require 'file_size_validator'
class User < ActiveRecord::Base
devise :database_authenticatable, :token_authenticatable, :lockable,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable, :registerable
devise :database_authenticatable, :token_authenticatable, :lockable, :async,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable, :confirmable, :registerable
attr_accessible :email, :password, :password_confirmation, :remember_me, :bio, :name, :username,
:skype, :linkedin, :twitter, :color_scheme_id, :theme_id, :force_random_password,
:extern_uid, :provider, :password_expires_at,
:extern_uid, :provider, :password_expires_at, :avatar,
as: [:default, :admin]
attr_accessible :projects_limit, :can_create_group,
......@@ -113,6 +116,8 @@ class User < ActiveRecord::Base
validate :namespace_uniq, if: ->(user) { user.username_changed? }
validates :avatar, file_size: { maximum: 100.kilobytes.to_i }
before_validation :generate_password, on: :create
before_validation :sanitize_attrs
......@@ -135,7 +140,7 @@ class User < ActiveRecord::Base
# Remove user from all groups
user.users_groups.find_each do |membership|
# skip owned resources
next if membership.group.owners.include?(user)
next if membership.group.last_owner?(user)
return false unless membership.destroy
end
......@@ -150,6 +155,8 @@ class User < ActiveRecord::Base
end
end
mount_uploader :avatar, AttachmentUploader
# Scopes
scope :admins, -> { where(admin: true) }
scope :blocked, -> { with_state(:blocked) }
......@@ -167,7 +174,7 @@ class User < ActiveRecord::Base
# Class methods
#
class << self
# Devise method overridden to allow sing in with email or username
# Devise method overridden to allow sign in with email or username
def find_for_database_authentication(warden_conditions)
conditions = warden_conditions.dup
if login = conditions.delete(:login)
......@@ -192,11 +199,7 @@ class User < ActiveRecord::Base
end
def by_username_or_id(name_or_id)
if (name_or_id.is_a?(Integer))
User.find_by_id(name_or_id)
else
User.find_by_username(name_or_id)
end
where('username = ? OR id = ?', name_or_id, name_or_id).first
end
def build_user(attrs = {}, options= {})
......@@ -211,7 +214,7 @@ class User < ActiveRecord::Base
{
projects_limit: Gitlab.config.gitlab.default_projects_limit,
can_create_group: Gitlab.config.gitlab.default_can_create_group,
theme_id: Gitlab::Theme::MARS
theme_id: Gitlab.config.gitlab.default_theme
}
end
end
......
......@@ -14,6 +14,17 @@ class ProjectObserver < BaseObserver
log_info("#{project.owner.name} created a new project \"#{project.name_with_namespace}\"")
end
if project.wiki_enabled?
begin
# force the creation of a wiki,
GollumWiki.new(project, project.owner).wiki
rescue GollumWiki::CouldNotCreateWikiError => ex
# Prevent project observer crash
# if failed to create wiki
nil
end
end
end
def after_update(project)
......
......@@ -112,6 +112,7 @@ class GitPushService
# ref: String,
# user_id: String,
# user_name: String,
# project_id: String,
# repository: {
# name: String,
# url: String,
......@@ -136,6 +137,7 @@ class GitPushService
ref: ref,
user_id: user.id,
user_name: user.name,
project_id: project.id,
repository: {
name: project.name,
url: project.url_to_repo,
......
......@@ -23,18 +23,21 @@ class SystemHooksService
case model
when Project
owner = model.owner
data.merge!({
name: model.name,
path: model.path,
path_with_namespace: model.path_with_namespace,
project_id: model.id,
owner_name: model.owner.name,
owner_email: model.owner.email
owner_name: owner.name,
owner_email: owner.respond_to?(:email) ? owner.email : nil
})
when User
data.merge!({
name: model.name,
email: model.email
email: model.email,
user_id: model.id
})
when UsersProject
data.merge!({
......
......@@ -2,3 +2,37 @@
%br
.ui-box
%iframe{src: sidekiq_path, width: '100%', height: 900, style: "border: none"}
%h4 Sidekiq running processes
- sidekiq_processes = `ps -eo euser,pid,pcpu,pmem,stat,start,command | grep sidekiq | grep -v grep`
- if sidekiq_processes.empty?
%b There are no running sidekiq processes
%b Please restart GitLab
- else
.ui-box
%table.zebra-striped
%thead
%th USER
%th
%th PID
%th
%th CPU
%th
%th MEM
%th
%th STATE
%th
%th START
%th
%th COMMAND
%th
- sidekiq_processes.split("\n").each do |process|
- next unless process.match(/(sidekiq \d+\.\d+\.\d+.+$)/)
- data = process.gsub!(/\s+/m, '|').strip.split('|')
%tr
- 6.times do
%td= data.shift
%td
%td= data.join(" ")
%b If '[25 of 25 busy]' is shown, restart GitLab with 'sudo service gitlab reload'.
%br
%b If more than one sidekiq process is listed, stop GitLab, kill the remaining sidekiq processes (sudo pkill -u git -f sidekiq) and restart GitLab.
%h3.page-title
Admin area
%p.light
You can manage projects, users and other GitLab data from here.
%hr
.admin_dash.row
.span4
.ui-box
.title Projects
.data.padded
.light-well
%h4 Projects
.data
= link_to admin_projects_path do
%h1= Project.count
%hr
= link_to 'New Project', new_project_path, class: "btn btn-small"
= link_to 'New Project', new_project_path, class: "btn btn-new"
.span4
.ui-box
.title Users
.data.padded
.light-well
%h4 Users
.data
= link_to admin_users_path do
%h1= User.count
%hr
= link_to 'New User', new_admin_user_path, class: "btn btn-small"
= link_to 'New User', new_admin_user_path, class: "btn btn-new"
.span4
.ui-box
.title Groups
.data.padded
.light-well
%h4 Groups
.data
= link_to admin_groups_path do
%h1= Group.count
%hr
= link_to 'New Group', new_admin_group_path, class: "btn btn-small"
= link_to 'New Group', new_admin_group_path, class: "btn btn-new"
.row
.row.prepend-top-10
.span4
%h4 Latest projects
%hr
......
......@@ -31,11 +31,8 @@
.clearfix.light.append-bottom-10
%span
%b Owner:
- if group.owner
= link_to group.owner_name, admin_user_path(group.owner)
- else
(deleted)
%b Members:
%span.badge= group.members.size
\|
%span
%b Projects:
......
......@@ -24,26 +24,6 @@
%strong
= @group.description
%li
%span.light Owned by:
%strong
- if @group.owner
= link_to @group.owner_name, admin_user_path(@group.owner)
- else
(deleted)
.pull-right
= link_to "#", class: "btn btn-small change-owner-link" do
%i.icon-edit
Change owner
%li.change-owner-holder.hide.bgred
.form-holder
%strong.cred New Owner:
= form_for [:admin, @group] do |f|
= users_select_tag(:"group[owner_id]")
.prepend-top-10
= f.submit 'Change Owner', class: "btn btn-remove"
= link_to "Cancel", "#", class: "btn change-owner-cancel-link"
%li
%span.light Created at:
%strong
......@@ -92,4 +72,5 @@
= link_to user.name, admin_user_path(user)
%span.pull-right.light
= member.human_access
= link_to group_users_group_path(@group, member), confirm: remove_user_from_group_message(@group, user), method: :delete, remote: true, class: "btn-tiny btn btn-remove", title: 'Remove user from group' do
%i.icon-minus.icon-white
......@@ -52,7 +52,8 @@
"created_at": "2012-07-21T07:44:07Z",
"email": "js@gitlabhq.com",
"event_name": "user_create",
"name": "John Smith"
"name": "John Smith",
"user_id": 41
}
6. User removed:
......@@ -60,7 +61,8 @@
"created_at": "2012-07-21T07:44:07Z",
"email": "js@gitlabhq.com",
"event_name": "user_destroy",
"name": "John Smith"
"name": "John Smith",
"user_id": 41
}
eos
......
.alert.alert-info
%span
Post-receive hooks for binding events.
%br
Read more about system hooks
%strong #{link_to "here", help_system_hooks_path, class: "vlink"}
%h3.page-title
System Hooks
%p.light
#{link_to "System hooks ", help_system_hooks_path, class: "vlink"} can be
used for binding events when GitLab creates a User or Project.
%hr
= form_for @hook, as: :hook, url: admin_hooks_path, html: { class: 'form-inline' } do |f|
-if @hook.errors.any?
......
......@@ -25,7 +25,7 @@
%span.light Owned by:
%strong
- if @project.owner
= link_to @project.owner_name, admin_user_path(@project.owner)
= link_to @project.owner_name, [:admin, @project.owner]
- else
(deleted)
......
%h3.page-title
User:
%span.cgray User:
= @user.name
- if @user.blocked?
%span.cred (Blocked)
......@@ -10,8 +10,6 @@
= link_to edit_admin_user_path(@user), class: "btn grouped" do
%i.icon-edit
Edit
- if @user.blocked?
= link_to 'Unblock', unblock_admin_user_path(@user), method: :put, class: "btn grouped success"
%hr
.row
......@@ -20,7 +18,7 @@
.title
Account:
.pull-right
= image_tag gravatar_icon(@user.email, 32), class: "avatar s32"
= image_tag avatar_icon(@user.email, 32), class: "avatar s32"
%ul.well-list
%li
%span.light Name:
......@@ -67,17 +65,30 @@
= link_to @user.created_by.name, [:admin, @user.created_by]
- unless @user == current_user
.alert
%h4 Block user
%br
%p Blocking user has the following effects:
%ul
%li User will not be able to login
%li User will not be able to access git repositories
%li User will be removed from joined projects and groups
%li Personal projects will be left
%li Owned groups will be left
= link_to 'Block user', block_admin_user_path(@user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn btn-remove"
- if @user.blocked?
.alert.alert-info
%h4 This user is blocked
%br
%p Blocking user has the following effects:
%ul
%li User will not be able to login
%li User will not be able to access git repositories
%li User will be removed from joined projects and groups
%li Personal projects will be left
%li Owned groups will be left
= link_to 'Unblock user', unblock_admin_user_path(@user), method: :put, class: "btn btn-new", confirm: 'Are you sure?'
- else
.alert
%h4 Block this user
%br
%p Blocking user has the following effects:
%ul
%li User will not be able to login
%li User will not be able to access git repositories
%li User will be removed from joined projects and groups
%li Personal projects will be left
%li Owned groups will be left
= link_to 'Block user', block_admin_user_path(@user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn btn-remove"
.alert.alert-error
%h4
......@@ -117,11 +128,7 @@
- tm = project.team.find_tm(@user.id)
%li.users_project
= link_to admin_project_path(project), class: dom_class(project) do
- if project.namespace
= project.namespace.human_name
\/
%strong.well-title
= truncate(project.name, length: 45)
= project.name_with_namespace
- if tm
.pull-right
......
......@@ -14,9 +14,6 @@
= truncate(group.name, length: 35)
%span.arrow
%i.icon-angle-right
%span.owner-info
%span Owner:
%span.owner= group.owner_name
- if groups.blank?
%li
%h3.nothing_here_message You have no groups yet.
= link_to project_path(project), class: dom_class(project) do
%span.namespace-name
- if project.namespace
= project.namespace.human_name
\/
%span.project-name.filter-title
= truncate(project.name, length: 25)
%span.arrow
%i.icon-angle-right
%span.last-activity
%span Last activity:
%span.date= project_last_activity(project)
......@@ -10,21 +10,16 @@
%ul.well-list.dash-list
- projects.each do |project|
%li.project-row
= link_to project_path(project), class: dom_class(project) do
%span.namespace-name
- if project.namespace
= project.namespace.human_name
\/
%span.project-name.filter-title
= truncate(project.name, length: 25)
%span.arrow
%i.icon-angle-right
%span.last-activity
%span Last activity:
%span.date= project_last_activity(project)
= render "project", project: project
- if projects.blank?
%li
%h3.nothing_here_message There are no projects here.
- if @projects_count > 20
- if @projects_count > @projects_limit
%li.bottom
%strong= link_to "show all projects", projects_dashboard_path
%span.light
#{@projects_limit} of #{pluralize(@projects_count, 'project')} displayed.
.pull-right.append-right-10
= link_to projects_dashboard_path do
Show all
%i.icon-angle-right
......@@ -12,7 +12,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
xml.link :href => project_issue_url(issue.project, issue)
xml.title truncate(issue.title, :length => 80)
xml.updated issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ")
xml.media :thumbnail, :width => "40", :height => "40", :url => gravatar_icon(issue.author_email)
xml.media :thumbnail, :width => "40", :height => "40", :url => avatar_icon(issue.author_email)
xml.author do |author|
xml.name issue.author_name
xml.email issue.author_email
......
......@@ -17,7 +17,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
xml.link :href => event_link
xml.title truncate(event_title, :length => 80)
xml.updated event.created_at.strftime("%Y-%m-%dT%H:%M:%SZ")
xml.media :thumbnail, :width => "40", :height => "40", :url => gravatar_icon(event.author_email)
xml.media :thumbnail, :width => "40", :height => "40", :url => avatar_icon(event.author_email)
xml.author do |author|
xml.name event.author_name
xml.email event.author_email
......
......@@ -18,7 +18,7 @@
= render 'devise/sessions/oauth_providers' if devise_mapping.omniauthable?
- if Gitlab.config.gitlab.signup_enabled
- if gitlab_config.signup_enabled
%hr
%div
Don't have an account?
......
......@@ -2,7 +2,7 @@
<%= link_to "Sign in", new_session_path(resource_name), class: "btn" %><br />
<% end -%>
<%- if devise_mapping.registerable? && controller_name != 'registrations' %>
<%- if devise_mapping.registerable? && controller_name != 'registrations' && gitlab_config.signup_enabled %>
<%= link_to "Sign up", new_registration_path(resource_name) %><br />
<% end -%>
......
......@@ -4,7 +4,7 @@
#{time_ago_in_words(event.created_at)} ago.
= cache event do
= image_tag gravatar_icon(event.author_email), class: "avatar s24", alt:''
= image_tag avatar_icon(event.author_email), class: "avatar s24", alt:''
- if event.push?
= render "events/event/push", event: event
......
......@@ -7,7 +7,7 @@
= link_to project_commits_path(event.project, event.ref_name) do
%strong= truncate(event.ref_name, length: 30)
at
%strong= link_to_project event.project
= link_to_project event.project
- if event.push_with_commits?
- project = event.project
......
......@@ -11,8 +11,6 @@
Projects
%li
= link_to 'LDAP', '#tab-ldap', 'data-toggle' => 'tab'
%li
= link_to 'Transfer', '#tab-transfer', 'data-toggle' => 'tab'
%li
= link_to 'Remove', '#tab-remove', 'data-toggle' => 'tab'
......@@ -87,17 +85,6 @@
.form-actions
= f.submit 'Save group', class: "btn btn-save"
.tab-pane#tab-transfer
.ui-box.ui-box-danger
.title Transfer group
.ui-box-body
%p
Transferring group will cause loss of admin control over group and all child projects
= form_for @group do |f|
= users_select_tag(:'group[owner_id]')
%hr
= f.submit 'Transfer group', class: "btn btn-small btn-remove"
.tab-pane#tab-remove
.ui-box.ui-box-danger
.title Remove group
......
......@@ -12,7 +12,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
xml.link :href => project_issue_url(issue.project, issue)
xml.title truncate(issue.title, :length => 80)
xml.updated issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ")
xml.media :thumbnail, :width => "40", :height => "40", :url => gravatar_icon(issue.author_email)
xml.media :thumbnail, :width => "40", :height => "40", :url => avatar_icon(issue.author_email)
xml.author do |author|
xml.name issue.author_name
xml.email issue.author_email
......
......@@ -2,6 +2,9 @@
Group members
%p.light
Members of group have access to all group projects.
Read more about permissions
%strong= link_to "here", help_permissions_path, class: "vlink"
%hr
- can_manage_group = current_user.can? :manage_group, @group
.ui-box
......
......@@ -16,7 +16,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
xml.link :href => event_link
xml.title truncate(event_title, :length => 80)
xml.updated event.created_at.strftime("%Y-%m-%dT%H:%M:%SZ")
xml.media :thumbnail, :width => "40", :height => "40", :url => gravatar_icon(event.author_email)
xml.media :thumbnail, :width => "40", :height => "40", :url => avatar_icon(event.author_email)
xml.author do |author|
xml.name event.author_name
xml.email event.author_email
......
......@@ -30,5 +30,8 @@
%li
%strong= link_to "Public Access", help_public_access_path
%li
%strong= link_to "Security", help_security_path
.span9.pull-right
= yield
......@@ -56,7 +56,7 @@
%p Get familiar with GitLab's permission levels.
%li
%strong= link_to "API", help_api_path
%strong= link_to "API", help_api_file_path(category: 'README')
%p Explore how you can access GitLab via a simple and powerful API.
%li
......@@ -80,3 +80,7 @@
%li
%strong= link_to "Public Access", help_public_access_path
%p Learn how you can allow public access to a project.
%li
%strong= link_to "Security", help_security_path
%p Learn what you can do to secure your GitLab instance.
= render layout: 'help/layout' do
%h3.page-title Permissions
%p.light Users have different abilities depending on the access level they have in particular group or project.
%p.light If a user is both in a project group and in the project itself the highest permission level is used.
%p.light If a user is a GitLab administrator they receive all permissions.
%hr
%h4 Project:
%table.table
%thead
%tr
......@@ -158,3 +163,50 @@
%td
%td
%td.permission-x &#10003;
%h4 Group
%table.table
%thead
%tr
%th Action
%th Guest
%th Reporter
%th Developer
%th Master
%th Owner
%tbody
%tr
%td Browse group
%td.permission-x &#10003;
%td.permission-x &#10003;
%td.permission-x &#10003;
%td.permission-x &#10003;
%td.permission-x &#10003;
%tr
%td Edit group
%td
%td
%td
%td
%td.permission-x &#10003;
%tr
%td Create project in group
%td
%td
%td
%td
%td.permission-x &#10003;
%tr
%td Manage group members
%td
%td
%td
%td
%td.permission-x &#10003;
%tr
%td Remove group
%td
%td
%td
%td
%td.permission-x &#10003;
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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