Commit 278935a3 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge branch 'release/6-3' of /home/git/repositories/gitlab/gitlab-ee

parents 6d688217 4e77b0b5
language: ruby language: ruby
env: env:
- DB=mysql TRAVIS=true global:
- DB=mysql
- TRAVIS=true
matrix:
- TASK=spinach
- TASK=spec
- TASK=jasmine:ci
before_install: before_install:
- sudo apt-get install libicu-dev -y - sudo apt-get install libicu-dev -y
- gem install charlock_holmes -v="0.6.9"
branches: branches:
only: only:
- 'master' - 'master'
...@@ -11,8 +16,9 @@ rvm: ...@@ -11,8 +16,9 @@ rvm:
- 2.0.0 - 2.0.0
services: services:
- mysql - mysql
- postgresql
before_script: before_script:
- "cp config/database.yml.$DB config/database.yml" - "cp config/database.yml.$DB config/database.yml"
- "cp config/gitlab.yml.example config/gitlab.yml" - "cp config/gitlab.yml.example config/gitlab.yml"
script: "bundle exec rake gitlab:test --trace" - "bundle exec rake db:setup"
- "bundle exec rake db:seed_fu"
script: "bundle exec rake $TASK --trace"
v 6.3.0
- API for adding gitlab-ci service
- Init script now waits for pids to appear after (re)starting before reporting status (Rovanion Luckey)
- Restyle project home page
- Grammar fixes
- Show branches list (which branches contains commit) on commit page (Andrew Kumanyaev)
- Security improvements
- Added support for GitLab CI 4.0
- Fixed issue with 500 error when group did not exist
- Ability to leave project
- You can create file in repo using UI
- You can remove file from repo using UI
- API: dropped default_branch attribute from project during creation
- Project default_branch is not stored in db any more. It takes from repo now.
- Admin broadcast messages
- UI improvements
- Dont show last push widget if user removed this branch
- Fix 500 error for repos with newline in file name
- Extended html titles
- API: create/update/delete repo files
- Admin can transfer project to any namespace
- API: projects/all for admin users
- Fix recent branches order
v 6.2.4
- Security: Cast API private_token to string (CVE-2013-4580)
- Security: Require gitlab-shell 1.7.8 (CVE-2013-4581, CVE-2013-4582, CVE-2013-4583)
- Fix for Git SSH access for LDAP users
v 6.2.3
- Security: More protection against CVE-2013-4489
- Security: Require gitlab-shell 1.7.4 (CVE-2013-4490, CVE-2013-4546)
- Fix sidekiq rake tasks
v 6.2.2
- Security: Update gitlab_git (CVE-2013-4489)
v 6.2.1
- Security: Fix issue with generated passwords for new users
v 6.2.0 v 6.2.0
- Public project pages are now visible to everyone (files, issues, wik, etc.) - 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 THIS MEANS YOUR ISSUES AND WIKI FOR PUBLIC PROJECTS ARE PUBLICLY VISIBLE AFTER THE UPGRADE
...@@ -15,7 +55,7 @@ v 6.2.0 ...@@ -15,7 +55,7 @@ v 6.2.0
- Extended User API to expose admin and can_create_group for user creation/updating (Boyan Tabakov) - Extended User API to expose admin and can_create_group for user creation/updating (Boyan Tabakov)
- API: Remove group - API: Remove group
- API: Remove project - API: Remove project
- Avatar upload on profile page with a maximum of 200KB (Steven Thonus) - Avatar upload on profile page with a maximum of 100KB (Steven Thonus)
- Store the sessions in Redis instead of the cookie store - Store the sessions in Redis instead of the cookie store
- Fixed relative links in markdown - Fixed relative links in markdown
- User must confirm his email if signup enabled - User must confirm his email if signup enabled
...@@ -83,6 +123,14 @@ v 6.0.0 ...@@ -83,6 +123,14 @@ v 6.0.0
- Improved MR comments logic - Improved MR comments logic
- Render readme file for projects in public area - Render readme file for projects in public area
v 5.4.2
- Security: Cast API private_token to string (CVE-2013-4580)
- Security: Require gitlab-shell 1.7.8 (CVE-2013-4581, CVE-2013-4582, CVE-2013-4583)
v 5.4.1
- Security: Fixes for CVE-2013-4489
- Security: Require gitlab-shell 1.7.4 (CVE-2013-4490, CVE-2013-4546)
v 5.4.0 v 5.4.0
- Ability to edit own comments - Ability to edit own comments
- Documentation improvements - Documentation improvements
......
...@@ -5,9 +5,18 @@ This guide details how to use issues and pull requests to improve GitLab. ...@@ -5,9 +5,18 @@ This guide details how to use issues and pull requests to improve GitLab.
- [Closing policy for issues and pull requests](#closing-policy-for-issues-and-pull-requests) - [Closing policy for issues and pull requests](#closing-policy-for-issues-and-pull-requests)
- [Issue tracker](#issue-tracker) - [Issue tracker](#issue-tracker)
- [Pull requests](#pull-requests) - [Pull requests](#pull-requests)
- [Security vulnerabilities](#security-vulnerabilities)
If you want to know how the GitLab team handles contributions have a look at [the GitLab contributing process](PROCESS.md). If you want to know how the GitLab team handles contributions have a look at [the GitLab contributing process](PROCESS.md).
## Contributor license agreement
By submitting code as an individual you agree to the [individual contributor license agreement](doc/legal/individual_contributor_license_agreement.md). By submitting code as an entity you agree to the [corporate contributor license agreement](doc/legal/corporate_contributor_license_agreement.md).
## Security vulnerability disclosure
Please report suspected security vulnerabilities in private to support@gitlab.com, also see the [disclosure section on the GitLab.com website](http://www.gitlab.com/disclosure/). Please do NOT create publicly viewable issues for suspected security vulnerabilities.
## Closing policy for issues and pull requests ## Closing policy for issues and pull requests
GitLab is a popular open source project and the capacity to deal with issues and pull requests is limited. Out of respect for our volunteers, issues and pull requests not in line with the guidelines listed in this document may be closed without notice. GitLab is a popular open source project and the capacity to deal with issues and pull requests is limited. Out of respect for our volunteers, issues and pull requests not in line with the guidelines listed in this document may be closed without notice.
......
...@@ -8,7 +8,7 @@ def linux_only(require_as) ...@@ -8,7 +8,7 @@ def linux_only(require_as)
RUBY_PLATFORM.include?('linux') && require_as RUBY_PLATFORM.include?('linux') && require_as
end end
gem "rails", "3.2.13" gem "rails", "3.2.15"
# Supported DBs # Supported DBs
gem "mysql2", group: :mysql gem "mysql2", group: :mysql
......
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
actionmailer (3.2.13) actionmailer (3.2.15)
actionpack (= 3.2.13) actionpack (= 3.2.15)
mail (~> 2.5.3) mail (~> 2.5.4)
actionpack (3.2.13) actionpack (3.2.15)
activemodel (= 3.2.13) activemodel (= 3.2.15)
activesupport (= 3.2.13) activesupport (= 3.2.15)
builder (~> 3.0.0) builder (~> 3.0.0)
erubis (~> 2.7.0) erubis (~> 2.7.0)
journey (~> 1.0.4) journey (~> 1.0.4)
...@@ -14,19 +14,19 @@ GEM ...@@ -14,19 +14,19 @@ GEM
rack-cache (~> 1.2) rack-cache (~> 1.2)
rack-test (~> 0.6.1) rack-test (~> 0.6.1)
sprockets (~> 2.2.1) sprockets (~> 2.2.1)
activemodel (3.2.13) activemodel (3.2.15)
activesupport (= 3.2.13) activesupport (= 3.2.15)
builder (~> 3.0.0) builder (~> 3.0.0)
activerecord (3.2.13) activerecord (3.2.15)
activemodel (= 3.2.13) activemodel (= 3.2.15)
activesupport (= 3.2.13) activesupport (= 3.2.15)
arel (~> 3.0.2) arel (~> 3.0.2)
tzinfo (~> 0.3.29) tzinfo (~> 0.3.29)
activeresource (3.2.13) activeresource (3.2.15)
activemodel (= 3.2.13) activemodel (= 3.2.15)
activesupport (= 3.2.13) activesupport (= 3.2.15)
activesupport (3.2.13) activesupport (3.2.15)
i18n (= 0.6.1) i18n (~> 0.6, >= 0.6.4)
multi_json (~> 1.0) multi_json (~> 1.0)
acts-as-taggable-on (2.4.1) acts-as-taggable-on (2.4.1)
rails (>= 3, < 5) rails (>= 3, < 5)
...@@ -34,7 +34,7 @@ GEM ...@@ -34,7 +34,7 @@ GEM
annotate (2.6.0.beta2) annotate (2.6.0.beta2)
activerecord (>= 2.3.0) activerecord (>= 2.3.0)
rake (>= 0.8.7) rake (>= 0.8.7)
arel (3.0.2) arel (3.0.3)
asciidoctor (0.1.3) asciidoctor (0.1.3)
awesome_print (1.2.0) awesome_print (1.2.0)
backports (3.3.2) backports (3.3.2)
...@@ -108,7 +108,7 @@ GEM ...@@ -108,7 +108,7 @@ GEM
warden (~> 1.2.1) warden (~> 1.2.1)
devise-async (0.8.0) devise-async (0.8.0)
devise (>= 2.2, < 3.2) devise (>= 2.2, < 3.2)
diff-lcs (1.2.4) diff-lcs (1.2.5)
dotenv (0.8.0) dotenv (0.8.0)
email_spec (1.4.0) email_spec (1.4.0)
launchy (~> 2.1) launchy (~> 2.1)
...@@ -171,7 +171,7 @@ GEM ...@@ -171,7 +171,7 @@ GEM
stringex (~> 1.5.1) stringex (~> 1.5.1)
gitlab-grack (1.0.1) gitlab-grack (1.0.1)
rack (~> 1.4.1) rack (~> 1.4.1)
gitlab-grit (2.6.1) gitlab-grit (2.6.3)
charlock_holmes (~> 0.6.9) charlock_holmes (~> 0.6.9)
diff-lcs (~> 1.1) diff-lcs (~> 1.1)
mime-types (~> 1.15) mime-types (~> 1.15)
...@@ -235,7 +235,7 @@ GEM ...@@ -235,7 +235,7 @@ GEM
multi_json (~> 1.0) multi_json (~> 1.0)
multi_xml (>= 0.5.2) multi_xml (>= 0.5.2)
httpauth (0.2.0) httpauth (0.2.0)
i18n (0.6.1) i18n (0.6.5)
jasmine (1.3.2) jasmine (1.3.2)
jasmine-core (~> 1.3.1) jasmine-core (~> 1.3.1)
rack (~> 1.0) rack (~> 1.0)
...@@ -274,11 +274,11 @@ GEM ...@@ -274,11 +274,11 @@ GEM
mime-types (~> 1.16) mime-types (~> 1.16)
treetop (~> 1.4.8) treetop (~> 1.4.8)
method_source (0.8.1) method_source (0.8.1)
mime-types (1.25) mime-types (1.25.1)
minitest (4.7.4) minitest (4.7.4)
modernizr (2.6.2) modernizr (2.6.2)
sprockets (~> 2.0) sprockets (~> 2.0)
multi_json (1.8.0) multi_json (1.8.2)
multi_xml (0.5.4) multi_xml (0.5.4)
multipart-post (1.2.0) multipart-post (1.2.0)
mysql2 (0.3.11) mysql2 (0.3.11)
...@@ -348,14 +348,14 @@ GEM ...@@ -348,14 +348,14 @@ GEM
rack rack
rack-test (0.6.2) rack-test (0.6.2)
rack (>= 1.0) rack (>= 1.0)
rails (3.2.13) rails (3.2.15)
actionmailer (= 3.2.13) actionmailer (= 3.2.15)
actionpack (= 3.2.13) actionpack (= 3.2.15)
activerecord (= 3.2.13) activerecord (= 3.2.15)
activeresource (= 3.2.13) activeresource (= 3.2.15)
activesupport (= 3.2.13) activesupport (= 3.2.15)
bundler (~> 1.0) bundler (~> 1.0)
railties (= 3.2.13) railties (= 3.2.15)
rails-dev-tweaks (0.6.1) rails-dev-tweaks (0.6.1)
actionpack (~> 3.1) actionpack (~> 3.1)
railties (~> 3.1) railties (~> 3.1)
...@@ -368,9 +368,9 @@ GEM ...@@ -368,9 +368,9 @@ GEM
i18n i18n
require_all require_all
ruby-progressbar ruby-progressbar
railties (3.2.13) railties (3.2.15)
actionpack (= 3.2.13) actionpack (= 3.2.15)
activesupport (= 3.2.13) activesupport (= 3.2.15)
rack-ssl (~> 1.3.2) rack-ssl (~> 1.3.2)
rake (>= 0.8.7) rake (>= 0.8.7)
rdoc (~> 3.4) rdoc (~> 3.4)
...@@ -514,7 +514,7 @@ GEM ...@@ -514,7 +514,7 @@ GEM
multi_json (~> 1.5) multi_json (~> 1.5)
twitter-stream (~> 0.1) twitter-stream (~> 0.1)
tins (0.11.0) tins (0.11.0)
treetop (1.4.14) treetop (1.4.15)
polyglot polyglot
polyglot (>= 0.3.1) polyglot (>= 0.3.1)
turbolinks (1.2.0) turbolinks (1.2.0)
...@@ -523,7 +523,7 @@ GEM ...@@ -523,7 +523,7 @@ GEM
eventmachine (>= 0.12.8) eventmachine (>= 0.12.8)
http_parser.rb (~> 0.5.1) http_parser.rb (~> 0.5.1)
simple_oauth (~> 0.1.4) simple_oauth (~> 0.1.4)
tzinfo (0.3.37) tzinfo (0.3.38)
uglifier (2.1.1) uglifier (2.1.1)
execjs (>= 0.3.0) execjs (>= 0.3.0)
multi_json (~> 1.0, >= 1.0.2) multi_json (~> 1.0, >= 1.0.2)
...@@ -615,7 +615,7 @@ DEPENDENCIES ...@@ -615,7 +615,7 @@ DEPENDENCIES
quiet_assets (~> 1.0.1) quiet_assets (~> 1.0.1)
rack-attack rack-attack
rack-mini-profiler rack-mini-profiler
rails (= 3.2.13) rails (= 3.2.15)
rails-dev-tweaks rails-dev-tweaks
rails_best_practices rails_best_practices
raphael-rails (~> 2.1.2) raphael-rails (~> 2.1.2)
......
...@@ -64,9 +64,10 @@ If you want to contribute, please first read our [Contributing Guidelines](https ...@@ -64,9 +64,10 @@ If you want to contribute, please first read our [Contributing Guidelines](https
* [Installation guides](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Unofficial-Installation-Guides) public wiki with unofficial guides to install GitLab on different operating systems. * [Installation guides](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Unofficial-Installation-Guides) public wiki with unofficial guides to install GitLab on different operating systems.
* [BitNami one-click installers](http://bitnami.com/stack/gitlab)
* [TurnKey Linux virtual appliance](http://www.turnkeylinux.org/gitlab) * [Digital Ocean 1-Click Application Install](https://www.digitalocean.com/) Have a new server up in 55 seconds. Digital Ocean uses SSD disks which is great for an IO intensive app as GitLab. Look for GitLab under 'Select Image' => 'Applications' when creating a droplet.
* [BitNami one-click installers](http://bitnami.com/stack/gitlab) Get an image with GitLab and GitLab CI preinstalled for Amazon Web Services, Azure, VMware or your local server.
### New versions and upgrading ### New versions and upgrading
...@@ -99,7 +100,7 @@ Start it with [Foreman](https://github.com/ddollar/foreman) ...@@ -99,7 +100,7 @@ Start it with [Foreman](https://github.com/ddollar/foreman)
or start each component separately or start each component separately
bundle exec rails s bundle exec rails s
bundle exec rake sidekiq:start script/background_jobs start
### Run the tests ### Run the tests
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
user_path: "/api/:version/users/:id.json" user_path: "/api/:version/users/:id.json"
notes_path: "/api/:version/projects/:id/notes.json" notes_path: "/api/:version/projects/:id/notes.json"
ldap_groups_path: "/api/:version/ldap/groups.json" ldap_groups_path: "/api/:version/ldap/groups.json"
namespaces_path: "/api/:version/namespaces.json"
# Get 20 (depends on api) recent notes # Get 20 (depends on api) recent notes
# and sort the ascending from oldest to newest # and sort the ascending from oldest to newest
...@@ -50,6 +51,20 @@ ...@@ -50,6 +51,20 @@
).done (users) -> ).done (users) ->
callback(users) callback(users)
# Return namespaces list. Filtered by query
namespaces: (query, callback) ->
url = Api.buildUrl(Api.namespaces_path)
$.ajax(
url: url
data:
private_token: gon.api_token
search: query
per_page: 20
dataType: "json"
).done (namespaces) ->
callback(namespaces)
buildUrl: (url) -> buildUrl: (url) ->
url = gon.relative_url_root + url if gon.relative_url_root? url = gon.relative_url_root + url if gon.relative_url_root?
return url.replace(':version', gon.api_version) return url.replace(':version', gon.api_version)
......
...@@ -123,6 +123,10 @@ $ -> ...@@ -123,6 +123,10 @@ $ ->
$(@).next('table').show() $(@).next('table').show()
$(@).remove() $(@).remove()
$(".content").on "click", ".js-details-expand", ->
$(@).next('.js-details-contain').removeClass("hide")
$(@).remove()
(($) -> (($) ->
_chosen = $.fn.chosen _chosen = $.fn.chosen
$.fn.extend chosen: (options) -> $.fn.extend chosen: (options) ->
......
$ ->
namespaceFormatResult = (namespace) ->
markup = "<div class='namespace-result'>"
markup += "<span class='namespace-kind'>" + namespace.kind + "</span>"
markup += "<span class='namespace-path'>" + namespace.path + "</span>"
markup += "</div>"
markup
formatSelection = (namespace) ->
namespace.kind + ": " + namespace.path
$('.ajax-namespace-select').each (i, select) ->
$(select).select2
placeholder: "Search for namespace"
multiple: $(select).hasClass('multiselect')
minimumInputLength: 0
query: (query) ->
Api.namespaces query.term, (namespaces) ->
data = { results: namespaces }
query.callback(data)
dropdownCssClass: "ajax-namespace-dropdown"
formatResult: namespaceFormatResult
formatSelection: formatSelection
...@@ -233,10 +233,12 @@ var NoteList = { ...@@ -233,10 +233,12 @@ var NoteList = {
form.show(); form.show();
var textarea = form.find("textarea"); var textarea = form.find("textarea");
var p = $("<p></p>").text(textarea.val()); if (form.find(".note-original-content").length === 0) {
var hidden_div = $('<div class="note-original-content"></div>').append(p); var p = $("<p></p>").text(textarea.val());
form.append(hidden_div); var hidden_div = $('<div class="note-original-content"></div>').append(p);
hidden_div.hide(); form.append(hidden_div);
hidden_div.hide();
}
textarea.focus(); textarea.focus();
}, },
...@@ -532,6 +534,8 @@ var NoteList = { ...@@ -532,6 +534,8 @@ var NoteList = {
note_text.html(response.note).show(); note_text.html(response.note).show();
var note_form = note_li.find(".note-edit-form"); var note_form = note_li.find(".note-edit-form");
var original_content = note_form.find(".note-original-content");
original_content.remove();
note_form.hide(); note_form.hide();
note_form.find(".btn-save").enableButton(); note_form.find(".btn-save").enableButton();
......
...@@ -6,10 +6,10 @@ class Project ...@@ -6,10 +6,10 @@ class Project
@initEvents() @initEvents()
initEvents: -> initEvents: ->
disableButtonIfEmptyField '#project_name', '.project-submit' disableButtonIfEmptyField '#project_name', '.project-submit'
$('#project_issues_enabled').change -> $('#project_issues_enabled').change ->
if ($(this).is(':checked') == true) if ($(this).is(':checked') == true)
$('#project_issues_tracker').removeAttr('disabled') $('#project_issues_tracker').removeAttr('disabled')
...@@ -29,7 +29,7 @@ class Project ...@@ -29,7 +29,7 @@ class Project
$ -> $ ->
# Git clone panel switcher # Git clone panel switcher
scope = $ '.project_clone_holder' scope = $ '.git-clone-holder'
if scope.length > 0 if scope.length > 0
$('a, button', scope).click -> $('a, button', scope).click ->
$('a, button', scope).removeClass 'active' $('a, button', scope).removeClass 'active'
...@@ -40,3 +40,9 @@ $ -> ...@@ -40,3 +40,9 @@ $ ->
# Ref switcher # Ref switcher
$('.project-refs-select').on 'change', -> $('.project-refs-select').on 'change', ->
$(@).parents('form').submit() $(@).parents('form').submit()
$('.hide-no-ssh-message').on 'click', (e) ->
path = '/'
$.cookie('hide_no_ssh_message', 'false', { path: path })
$(@).parents('.no-ssh-key-message').hide()
e.preventDefault()
...@@ -86,13 +86,6 @@ span.update-author { ...@@ -86,13 +86,6 @@ span.update-author {
font-weight: bold; font-weight: bold;
} }
.label {
padding: 1px 4px;
font-size: 12px;
font-style: normal;
font-weight: normal;
}
.field_with_errors { .field_with_errors {
display: inline; display: inline;
} }
...@@ -136,6 +129,10 @@ p.time { ...@@ -136,6 +129,10 @@ p.time {
} }
} }
.highlight {
text-shadow: none;
}
.highlight_word { .highlight_word {
border-bottom: 2px solid #F90; border-bottom: 2px solid #F90;
} }
...@@ -223,7 +220,6 @@ li.note { ...@@ -223,7 +220,6 @@ li.note {
.error-message { .error-message {
padding: 10px; padding: 10px;
background: #C67; background: #C67;
padding-left: 20px;
margin: 0; margin: 0;
color: #FFF; color: #FFF;
...@@ -231,8 +227,18 @@ li.note { ...@@ -231,8 +227,18 @@ li.note {
color: #fff; color: #fff;
text-decoration: underline; text-decoration: underline;
} }
&.centered { }
text-align: center;
.no-ssh-key-message {
padding: 10px 0;
background: #C67;
margin: 0;
color: #FFF;
text-align: center;
a {
color: #fff;
text-decoration: underline;
} }
} }
...@@ -349,3 +355,40 @@ table { ...@@ -349,3 +355,40 @@ table {
@extend .btn-new; @extend .btn-new;
padding: 5px 15px; padding: 5px 15px;
} }
.broadcast-message {
padding: 10px;
text-align: center;
background: #555;
color: #BBB;
}
.ajax-users-select {
width: 400px;
&.input-large {
width: 210px;
}
}
.user-result {
.user-image {
float: left;
}
.user-name {
}
.user-username {
color: #999;
}
}
.namespace-result {
.namespace-kind {
color: #AAA;
font-weight: normal;
}
.namespace-path {
margin-left: 10px;
font-weight: bolder;
}
}
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
float: left; float: left;
margin-right: 12px; margin-right: 12px;
width: 40px; width: 40px;
border: 1px solid #ddd;
padding: 1px; padding: 1px;
@include border-radius(4px);
&.avatar-inline { &.avatar-inline {
float: none; float: none;
......
...@@ -32,12 +32,9 @@ ...@@ -32,12 +32,9 @@
} }
&.ui-box-show { &.ui-box-show {
text-shadow: 0 1px 1px #fff;
color: #666; color: #666;
margin:20px 0; margin:20px 0;
background: #FFF; background: #FAFAFA;
box-shadow: inset 0 1px 0 #fff, 0 1px 5px #f1f1f1;
@include linear-gradient(#fafafa, #f1f1f1);
.control-group { .control-group {
margin-bottom: 0; margin-bottom: 0;
...@@ -45,11 +42,13 @@ ...@@ -45,11 +42,13 @@
} }
&.ui-box-danger { &.ui-box-danger {
background: #f7f7f7;
border: none;
.title { .title {
@include linear-gradient(#F26E5E, #bd362f); background: #D65;
color: #fff; color: #fff;
text-shadow: 0 1px 1px #900; text-shadow: 0 1px 1px #900;
font-weight: bold;
} }
} }
...@@ -99,9 +98,9 @@ ...@@ -99,9 +98,9 @@
} }
.title { .title {
@include bg-gray-gradient; background-color: #EEE;
border-bottom: 1px solid #CCC; border-bottom: 1px solid #DDD;
color: #456; color: #666;
font-size: 16px; font-size: 16px;
text-shadow: 0 1px 1px #fff; text-shadow: 0 1px 1px #fff;
padding: 0 10px; padding: 0 10px;
......
.btn { .btn {
display: inline-block; display: inline-block;
padding: 6px 12px;
margin-bottom: 0; margin-bottom: 0;
font-size: 13px; font-weight: normal;
line-height: $baseLineHeight;
text-align: center; text-align: center;
vertical-align: middle; vertical-align: middle;
cursor: pointer; cursor: pointer;
border: 1px solid #BBB; background-image: none;
color: $style_color; border: 1px solid transparent;
@include border-radius($baseBorderRadius); white-space: nowrap;
@include box-shadow(inset 0 1px 0 rgba(255,255,255,.2)); padding: 6px 12px;
@include linear-gradient(#f1f1f1, #e1e1e1); font-size: 13px;
text-shadow: 0 1px 1px #FFF; line-height: 18px;
text-decoration: none; border-radius: 4px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-o-user-select: none;
user-select: none;
color: #444444;
background-color: #fff;
border-color: #ccc;
text-shadow: none;
&.hover, &.hover,
&:hover { &:hover {
color: $style_color; color: #444444;
background: #f1f1f1;
border-color: #AAA;
text-decoration: none; text-decoration: none;
@include linear-gradient(#fAfAfA, #f1f1f1); background-color: #ebebeb;
border-color: #adadad;
} }
&.focus, &.focus,
&:focus { &:focus {
color: #444444;
text-decoration: none; text-decoration: none;
@include box-shadow(inset 0 2px 4px rgba(0,0,0,.15)); outline: thin dotted #333;
outline: 5px auto -webkit-focus-ring-color;
outline-offset: -2px;
} }
&.active, &.active,
&:active { &:active {
background-image: none;
outline: 0; outline: 0;
text-decoration: none; background-image: none;
@include box-shadow(inset 0 2px 4px rgba(0,0,0,.15)); -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
} }
&.disabled, &.disabled,
&[disabled] { &[disabled] {
cursor: default; cursor: not-allowed;
background-image: none; pointer-events: none;
@include opacity(65); opacity: 0.65;
@include box-shadow(none); filter: alpha(opacity=65);
-webkit-box-shadow: none;
box-shadow: none;
} }
&.btn-primary { &.btn-primary {
color: #FFF; color: #ffffff;
border-color: #189; background-color: #429bca;
text-shadow: 0 1px 1px #189; border-color: #358ebd;
@include linear-gradient(#4AC, #289);
&.hover, &.hover,
&:hover, &:hover,
&.disabled, &.disabled,
&[disabled] { &[disabled] {
color: #FFF; color: #ffffff;
background: #389; background-color: #3286b1;
border-color: #286e8e;
} }
} }
&.btn-success { &.btn-success {
color: #FFF; color: #ffffff;
border-color: #1A1; background-color: #5cb85c;
text-shadow: 0 1px 1px #FFF; border-color: #4cae4c;
text-shadow: 0 1px 1px #181;
@include linear-gradient(#62C452, #51a351);
&.hover, &.hover,
&:hover, &:hover,
&.disabled, &.disabled,
&[disabled] { &[disabled] {
color: #FFF; color: #ffffff;
background: #2A2; background-color: #47a447;
border-color: #398439;
} }
} }
&.btn-danger { &.btn-danger {
color: #FFF; color: #ffffff;
text-shadow: 0 1px 1px #811; background-color: #d9534f;
border-color: #BD362F; border-color: #d43f3a;
@include linear-gradient(#EE5F5B, #BD362F);
&.hover, &.hover,
&:hover, &:hover,
&.disabled, &.disabled,
&[disabled] { &[disabled] {
color: #FFF; color: #ffffff;
background: #A22; background-color: #d2322d;
border-color: #ac2925;
} }
} }
...@@ -138,4 +148,11 @@ ...@@ -138,4 +148,11 @@
margin-right: 7px; margin-right: 7px;
float: left; float: left;
} }
&.btn-block {
width: 100%;
margin: 0;
padding: 6px 0;
margin-bottom: 15px;
}
} }
/** COLORS **/ /** COLORS **/
.cgray { color: gray } .cgray { color: gray }
.clgray { color: #BBB }
.cred { color: #D12F19 } .cred { color: #D12F19 }
.cgreen { color: #4a2 } .cgreen { color: #4a2 }
.cblue { color: #29A } .cblue { color: #29A }
...@@ -88,6 +89,19 @@ pre.well-pre { ...@@ -88,6 +89,19 @@ pre.well-pre {
@include box-shadow(inset 0 2px 4px rgba(0,0,0,.15)); @include box-shadow(inset 0 2px 4px rgba(0,0,0,.15));
} }
.label {
padding: 2px 4px;
font-size: 12px;
font-style: normal;
font-weight: normal;
&.label-gray {
background-color: #eee;
color: #999;
text-shadow: none;
}
}
/** Big Labels **/ /** Big Labels **/
.state-label { .state-label {
font-size: 14px; font-size: 14px;
...@@ -109,3 +123,7 @@ pre.well-pre { ...@@ -109,3 +123,7 @@ pre.well-pre {
color: #FFF; color: #FFF;
} }
} }
.dropdown-menu > li > a {
text-shadow: none;
}
...@@ -11,8 +11,8 @@ ...@@ -11,8 +11,8 @@
} }
.file-title { .file-title {
border-bottom: 1px solid #bbb; background: #DDD;
@include bg-dark-gray-gradient; border-bottom: 1px solid #CCC;
text-shadow: 0 1px 1px #fff; text-shadow: 0 1px 1px #fff;
margin: 0; margin: 0;
font-weight: normal; font-weight: normal;
......
...@@ -3,6 +3,16 @@ form { ...@@ -3,6 +3,16 @@ form {
label { label {
@extend .control-label; @extend .control-label;
&.radio-label {
text-align: left;
width: 100%;
margin-left: 0;
input[type="radio"] {
margin-top: 1px !important;
}
}
} }
} }
...@@ -49,3 +59,9 @@ fieldset legend { ...@@ -49,3 +59,9 @@ fieldset legend {
font-size: 16px; font-size: 16px;
margin-bottom: 10px; margin-bottom: 10px;
} }
.datetime-controls {
select {
width: 100px;
}
}
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
background-image: -webkit-gradient(linear, 0 0, 0 100%, from($from), to($to)); background-image: -webkit-gradient(linear, 0 0, 0 100%, from($from), to($to));
background-image: -webkit-linear-gradient($from, $to); background-image: -webkit-linear-gradient($from, $to);
background-image: -moz-linear-gradient($from, $to); background-image: -moz-linear-gradient($from, $to);
background-image: -ms-linear-gradient($from, $to);
background-image: -o-linear-gradient($from, $to); background-image: -o-linear-gradient($from, $to);
} }
...@@ -45,6 +46,7 @@ ...@@ -45,6 +46,7 @@
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #f5f5f5), to(#e1e1e1)); background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #f5f5f5), to(#e1e1e1));
background-image: -webkit-linear-gradient(#f5f5f5 6.6%, #e1e1e1); background-image: -webkit-linear-gradient(#f5f5f5 6.6%, #e1e1e1);
background-image: -moz-linear-gradient(#f5f5f5 6.6%, #e1e1e1); background-image: -moz-linear-gradient(#f5f5f5 6.6%, #e1e1e1);
background-image: -ms-linear-gradient(#f5f5f5 6.6%, #e1e1e1);
background-image: -o-linear-gradient(#f5f5f5 6.6%, #e1e1e1); background-image: -o-linear-gradient(#f5f5f5 6.6%, #e1e1e1);
} }
...@@ -53,6 +55,7 @@ ...@@ -53,6 +55,7 @@
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf)); background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));
background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf); background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf); background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -ms-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf); background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
} }
...@@ -60,6 +63,7 @@ ...@@ -60,6 +63,7 @@
background: #eee; background: #eee;
background-image: -webkit-linear-gradient(#e9e9e9, #d7d7d7); background-image: -webkit-linear-gradient(#e9e9e9, #d7d7d7);
background-image: -moz-linear-gradient(#e9e9e9, #d7d7d7); background-image: -moz-linear-gradient(#e9e9e9, #d7d7d7);
background-image: -ms-linear-gradient(#e9e9e9, #d7d7d7);
background-image: -o-linear-gradient(#e9e9e9, #d7d7d7); background-image: -o-linear-gradient(#e9e9e9, #d7d7d7);
} }
...@@ -85,10 +89,26 @@ ...@@ -85,10 +89,26 @@
} }
code { padding: 0 4px; } code { padding: 0 4px; }
h1 { margin-top: 30px;}
h2 { margin-top: 25px;} h1 {
h3 { margin-top: 20px;} margin-top: 45px;
h4 { margin-top: 15px;} font-size: 2.5em;
}
h2 {
margin-top: 40px;
font-size: 2em;
}
h3 {
margin-top: 35px;
font-size: 2em;
}
h4 {
margin-top: 30px;
font-size: 1.5em;
}
blockquote p { blockquote p {
color: #888; color: #888;
...@@ -103,6 +123,16 @@ ...@@ -103,6 +123,16 @@
background: #EEE; background: #EEE;
} }
} }
code {
font-size: inherit;
font-weight: inherit;
color: #555;
}
li {
line-height: 1.5;
}
} }
@mixin page-title { @mixin page-title {
......
...@@ -21,3 +21,9 @@ ...@@ -21,3 +21,9 @@
.controls { margin-left: 130px; } .controls { margin-left: 130px; }
.form-actions { padding-left: 130px; background: #fff } .form-actions { padding-left: 130px; background: #fff }
} }
.broadcast-messages {
.message {
line-height: 2;
}
}
...@@ -16,36 +16,29 @@ ...@@ -16,36 +16,29 @@
.header { .header {
@extend .clearfix; @extend .clearfix;
background: #DDD;
border-bottom: 1px solid #CCC;
padding: 5px 5px 5px 10px; padding: 5px 5px 5px 10px;
color: #555; color: #555;
border-bottom: 1px solid #CCC;
background: #eee;
// TODO Replace with linear-gradient mixin
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));
background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
a{
color: $style_color;
}
> span { > span {
font-family: $monospace_font; font-family: $monospace_font;
font-size: 14px; font-size: 14px;
line-height: 30px; line-height: 2;
} }
a.view-file{ .view-file {
font-weight: bold; font-weight: bold;
float: right;
background-color: #EEE;
} }
.commit-short-id{ .commit-short-id {
font-family: $monospace_font; font-family: $monospace_font;
font-size: smaller; font-size: smaller;
} }
.file-mode{ .file-mode {
font-family: $monospace_font; font-family: $monospace_font;
} }
} }
...@@ -55,13 +48,13 @@ ...@@ -55,13 +48,13 @@
background: #FFF; background: #FFF;
color: #333; color: #333;
font-size: 12px; font-size: 12px;
.old{ .old {
span.idiff{ span.idiff {
background-color: #FAA; background-color: #FAA;
} }
} }
.new{ .new {
span.idiff{ span.idiff {
background-color: #AFA; background-color: #AFA;
} }
} }
...@@ -293,6 +286,7 @@ ...@@ -293,6 +286,7 @@
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf)); background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));
background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf); background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf); background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -ms-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf); background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
ul, li{ ul, li{
......
...@@ -51,7 +51,7 @@ ...@@ -51,7 +51,7 @@
li { li {
&.active { &.active {
a { a {
@include linear-gradient(#f5f5f5, #eee); background-color: #EEE;
border-bottom: 1px solid #EEE !important; border-bottom: 1px solid #EEE !important;
&:hover { &:hover {
background: #eee; background: #eee;
...@@ -74,7 +74,7 @@ ...@@ -74,7 +74,7 @@
} }
.project-name, .group-name { .project-name, .group-name {
font-size: 16px; font-size: 15px;
} }
.arrow { .arrow {
......
...@@ -47,7 +47,7 @@ ...@@ -47,7 +47,7 @@
.event-title { .event-title {
color: #333; color: #333;
font-weight: normal; font-weight: normal;
font-size: 15px; font-size: 14px;
.author_name { .author_name {
color: #333; color: #333;
} }
...@@ -60,14 +60,14 @@ ...@@ -60,14 +60,14 @@
color: #666; color: #666;
} }
.event-note { .event-note {
color: #555; color: #666;
margin-top: 5px; margin-top: 5px;
pre { pre {
border: none; border: none;
background: #f9f9f9; background: #f9f9f9;
border-radius: 0; border-radius: 0;
color: #555; color: #666;
margin: 0 20px; margin: 0 20px;
} }
...@@ -142,19 +142,19 @@ ...@@ -142,19 +142,19 @@
.filter_icon { .filter_icon {
a { a {
text-align:center; text-align:center;
border-left: 3px solid $primary_color; background: #EEE;
background: #f9f9f9;
margin-bottom: 10px; margin-bottom: 10px;
float: left; float: left;
padding: 9px 7px; padding: 9px 6px;
font-size: 18px; font-size: 18px;
width: 26px; width: 26px;
@include border-radius(3px);
} }
&.inactive { &.inactive {
a { a {
color: #DDD; color: #DDD;
border-left: 3px solid #EEE; background: #f9f9f9;
} }
} }
} }
......
...@@ -36,8 +36,8 @@ header { ...@@ -36,8 +36,8 @@ header {
float: left; float: left;
margin-right: 9px; margin-right: 9px;
position: relative; position: relative;
top: -5px; top: -3px;
padding-top: 5px; padding-top: 3px;
a { a {
float: left; float: left;
......
...@@ -110,9 +110,29 @@ ...@@ -110,9 +110,29 @@
.merge-request-angle { .merge-request-angle {
text-align: center; text-align: center;
margin: 0; margin: 0 auto;
background: #eee;
border-radius: 100px;
width: 60px;
line-height: 60px;
color: #777;
text-shadow: 0 1px 2px #FFF;
} }
.merge-request-form-info { .merge-request-form-info {
padding: 15px 0; padding-top: 15px;
}
.merge-request-branches {
.commit-row-message {
font-weight: normal !important;
}
.chosen-container .chosen-single {
padding: 2px 0 2px 10px;
span {
font-weight: bold;
color: #555;
}
}
} }
...@@ -14,25 +14,76 @@ ...@@ -14,25 +14,76 @@
} }
} }
.project_clone_panel { .project-home-panel {
border-bottom: 1px solid #DDD;
padding-bottom: 25px;
margin-bottom: 30px;
.project-home-title {
font-size: 18px;
color: #777;
margin: 0;
line-height: 32px;
}
.project-home-dropdown {
margin-left: 10px;
float: right;
}
.project-home-extra {
margin-top: 15px;
.project-home-desc {
float: left;
color: #999;
}
.project-home-links {
float: right;
a {
margin-left: 10px;
}
}
}
.public-label {
font-size: 14px;
background: #f1f1f1;
padding: 8px 10px;
border-radius: 4px;
margin-left: 10px;
color: #888;
text-shadow: 0 1px 1px #FFF;
}
}
.git-clone-holder {
float: right;
border: 1px solid #E1E1E1;
@include border-radius(4px); @include border-radius(4px);
@include bg-gray-gradient;
padding: 4px 7px;
border: 1px solid #CCC;
margin-bottom: 20px;
.btn { .btn {
padding: 4px 12px; margin-left: 3px;
border: none;
background: none;
box-shadow: none;
color: #29b;
padding: 6px;
&.active {
color: #333;
font-weight: bold;
}
} }
}
.project_clone_holder {
input[type="text"] { input[type="text"] {
margin-left: 2px;
border: none;
border-radius: 0;
border-left: 1px solid #E1E1E1;
@extend .monospace; @extend .monospace;
border: 1px solid #BBB;
box-shadow: none; box-shadow: none;
margin-left: -1px; background: #FAFAFA;
background: #FFF; padding: 6px 10px;
} }
} }
...@@ -81,16 +132,16 @@ ul.nav.nav-projects-tabs { ...@@ -81,16 +132,16 @@ ul.nav.nav-projects-tabs {
.my-projects { .my-projects {
li { li {
.project-title {
font-size: 14px;
}
.project-info { .project-info {
margin-bottom: 10px; margin-bottom: 10px;
} }
.access-icon i { .access-icon {
color: #AAA; color: #AAA;
margin-left: 10px;
i {
color: #AAA;
}
} }
} }
} }
...@@ -115,3 +166,61 @@ ul.nav.nav-projects-tabs { ...@@ -115,3 +166,61 @@ ul.nav.nav-projects-tabs {
color: #777; color: #777;
} }
} }
.project-side {
.btn-block {
background-image: none;
background-color: #F1f1f1;
border-color: #EEE;
&:hover {
background-color: #eee;
border-color: #DDD;
}
}
.project-fork-icon {
float: left;
font-size: 26px;
margin-right: 10px;
line-height: 1.5;
}
}
.transfer-project .chosen-container {
min-width: 200px;
}
/** Branch/tag selector **/
.project-refs-form {
margin: 0;
span {
background:none !important;
position:static !important;
width:auto !important;
height:auto !important;
}
}
.project-refs-select {
width: 120px;
}
.project-refs-form .chosen-container {
position: relative;
top: 0;
left: 0;
margin-right: 10px;
.chosen-single span {
font-weight: bold;
color: #555;
}
&.chosen-container-active {
.chosen-drop {
min-width: 400px;
}
.chosen-results {
max-height: 400px;
}
}
}
/* CHZN reset few styles */ /** Chosen.js selectbox style override **/
.chosen-container-single .chosen-single {
background: #FFF;
border: 1px solid #bbb;
box-shadow: none;
}
.chosen-container-active .chosen-single {
background: #fff;
}
.ajax-users-select {
width: 400px;
&.input-large {
width: 210px;
}
}
.user-result {
.user-image {
float: left;
}
.user-name {
}
.user-username {
color: #999;
}
}
/** Branch/tag selector **/
.project-refs-form {
margin: 0;
span {
background:none !important;
position:static !important;
width:auto !important;
height:auto !important;
}
}
.project-refs-select {
width: 120px;
}
.project-refs-form .chosen-container {
position: relative;
top: 0;
left: 0;
margin-right: 10px;
.chosen-drop {
min-width: 400px;
.chosen-results {
max-height: 300px;
}
.chosen-search input {
min-width: 365px;
}
}
}
/** Fix for Search Dropdown Border **/
.chosen-container { .chosen-container {
min-width: 100px; min-width: 100px;
.chosen-search { .chosen-single {
input:focus { background: #EEE !important;
@include box-shadow(none); border: 1px solid #DDD !important;
} @include box-shadow(none !important);
@include border-radius(4px !important);
} }
.chosen-drop { .chosen-results li.highlighted {
margin: 7px 0; background: #29b;
min-width: 200px;
border: 1px solid #bbb;
@include border-radius(0);
.chosen-results {
margin-top: 5px;
max-height: 300px;
.group-result {
color: $style_color;
border-bottom: 1px solid #EEE;
padding: 8px;
}
.active-result {
@include border-radius(0);
&.highlighted {
background: $hover;
color: $style_color;
}
&.result-selected {
background: #EEE;
border-left: 4px solid #CCC;
}
}
}
.chosen-search {
@include bg-gray-gradient;
input {
min-width: 165px;
border-color: #CCC;
}
}
} }
}
.chosen-container .chosen-single, .chosen-drop {
.chosen-container.chosen-with-drop .chosen-single { margin-top: 10px;
@include bg-light-gray-gradient; border: 1px solid #DDD !important;
@include border-radius(4px !important);
div {
background: transparent;
border-left: none;
} }
span { .chosen-search input {
font-weight: normal; border: 1px solid #CCC !important;
@include box-shadow(none !important);
} }
} }
/** Select2 styling **/ /** Select2 styling **/
.select2-container .select2-choice { .select2-container .select2-choice {
background: #f1f1f1; @include bg-light-gray-gradient;
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, whitesmoke), to(#e1e1e1));
background-image: -webkit-linear-gradient(whitesmoke 6.6%, #e1e1e1);
background-image: -moz-linear-gradient(whitesmoke 6.6%, #e1e1e1);
background-image: -o-linear-gradient(whitesmoke 6.6%, #e1e1e1);
} }
.select2-container .select2-choice div { .select2-container .select2-choice div {
......
...@@ -27,6 +27,12 @@ ...@@ -27,6 +27,12 @@
background: #435; background: #435;
border-left: 1px solid #658; border-left: 1px solid #658;
} }
.nav > li > a {
color: #98B;
}
.search-input {
border-color: #98B;
}
} }
} }
} }
......
...@@ -23,12 +23,17 @@ ...@@ -23,12 +23,17 @@
background-color: #373D47; background-color: #373D47;
} }
} }
.separator {
background: #373D47;
border-left: 1px solid #575D67;
}
.nav > li > a {
color: #979DA7;
}
.search-input {
border-color: #979DA7;
}
} }
} }
.separator {
background: #31363E;
border-left: 1px solid #666;
}
} }
} }
...@@ -27,6 +27,12 @@ ...@@ -27,6 +27,12 @@
background: #234; background: #234;
border-left: 1px solid #456; border-left: 1px solid #456;
} }
.nav > li > a {
color: #89A;
}
.search-input {
border-color: #89A;
}
} }
} }
} }
......
...@@ -17,4 +17,3 @@ class BaseContext ...@@ -17,4 +17,3 @@ class BaseContext
abilities.allowed?(object, action, subject) abilities.allowed?(object, action, subject)
end end
end end
...@@ -18,6 +18,7 @@ class CommitLoadContext < BaseContext ...@@ -18,6 +18,7 @@ class CommitLoadContext < BaseContext
result[:note] = project.build_commit_note(commit) result[:note] = project.build_commit_note(commit)
result[:line_notes] = line_notes result[:line_notes] = line_notes
result[:notes_count] = project.notes.for_commit_id(commit.id).count result[:notes_count] = project.notes.for_commit_id(commit.id).count
result[:branches] = project.repository.branch_names_contains(commit.id)
begin begin
result[:suppress_diff] = true if commit.diff_suppress? && !params[:force_show_diff] result[:suppress_diff] = true if commit.diff_suppress? && !params[:force_show_diff]
......
module Files
class BaseContext < ::BaseContext
attr_reader :ref, :path
def initialize(project, user, params, ref, path = nil)
@project, @current_user, @params = project, user, params.dup
@ref = ref
@path = path
end
private
def error(message)
{
error: message,
status: :error
}
end
def success
{
error: '',
status: :success
}
end
def repository
project.repository
end
end
end
module Files
class CreateContext < BaseContext
def execute
allowed = if project.protected_branch?(ref)
can?(current_user, :push_code_to_protected_branches, project)
else
can?(current_user, :push_code, project)
end
unless allowed
return error("You are not allowed to create file in this branch")
end
unless repository.branch_names.include?(ref)
return error("You can only create files if you are on top of a branch")
end
file_name = File.basename(path)
file_path = path
unless file_name =~ Gitlab::Regex.path_regex
return error("Your changes could not be commited, because file name contains not allowed characters")
end
blob = repository.blob_at(ref, file_path)
if blob
return error("Your changes could not be commited, because file with such name exists")
end
new_file_action = Gitlab::Satellite::NewFileAction.new(current_user, project, ref, file_path)
created_successfully = new_file_action.commit!(
params[:content],
params[:commit_message]
)
if created_successfully
success
else
error("Your changes could not be commited, because the file has been changed")
end
end
end
end
module Files
class DeleteContext < BaseContext
def execute
allowed = if project.protected_branch?(ref)
can?(current_user, :push_code_to_protected_branches, project)
else
can?(current_user, :push_code, project)
end
unless allowed
return error("You are not allowed to push into this branch")
end
unless repository.branch_names.include?(ref)
return error("You can only create files if you are on top of a branch")
end
blob = repository.blob_at(ref, path)
unless blob
return error("You can only edit text files")
end
delete_file_action = Gitlab::Satellite::DeleteFileAction.new(current_user, project, ref, path)
deleted_successfully = delete_file_action.commit!(
nil,
params[:commit_message]
)
if deleted_successfully
success
else
error("Your changes could not be commited, because the file has been changed")
end
end
end
end
module Files
class UpdateContext < BaseContext
def execute
allowed = if project.protected_branch?(ref)
can?(current_user, :push_code_to_protected_branches, project)
else
can?(current_user, :push_code, project)
end
unless allowed
return error("You are not allowed to push into this branch")
end
unless repository.branch_names.include?(ref)
return error("You can only create files if you are on top of a branch")
end
blob = repository.blob_at(ref, path)
unless blob
return error("You can only edit text files")
end
new_file_action = Gitlab::Satellite::EditFileAction.new(current_user, project, ref, path)
created_successfully = new_file_action.commit!(
params[:content],
params[:commit_message]
)
if created_successfully
success
else
error("Your changes could not be commited, because the file has been changed")
end
end
end
end
...@@ -47,8 +47,6 @@ module Projects ...@@ -47,8 +47,6 @@ module Projects
@project.creator = current_user @project.creator = current_user
if @project.save if @project.save
@project.discover_default_branch
unless @project.group unless @project.group
@project.users_projects.create( @project.users_projects.create(
project_access: UsersProject::MASTER, project_access: UsersProject::MASTER,
......
...@@ -3,6 +3,16 @@ module Projects ...@@ -3,6 +3,16 @@ module Projects
def execute(role = :default) def execute(role = :default)
params[:project].delete(:namespace_id) params[:project].delete(:namespace_id)
params[:project].delete(:public) unless can?(current_user, :change_public_mode, project) params[:project].delete(:public) unless can?(current_user, :change_public_mode, project)
new_branch = params[:project].delete(:default_branch)
if project.repository.exists? && new_branch != project.repository.root_ref
GitlabShellWorker.perform_async(
:update_repository_head,
project.path_with_namespace,
new_branch
)
end
project.update_attributes(params[:project], as: role) project.update_attributes(params[:project], as: role)
end end
end end
......
...@@ -10,11 +10,14 @@ class SearchContext ...@@ -10,11 +10,14 @@ class SearchContext
query = Shellwords.shellescape(query) if query.present? query = Shellwords.shellescape(query) if query.present?
return result unless query.present? return result unless query.present?
result[:projects] = Project.where("projects.id in (?) OR projects.public = true", project_ids).search(query).limit(20)
projects = Project.where(id: project_ids)
result[:projects] = projects.search(query).limit(20)
# Search inside single project # Search inside single project
single_project_search(Project.where(id: project_ids), query)
result
end
def single_project_search(projects, query)
project = projects.first if projects.length == 1 project = projects.first if projects.length == 1
if params[:search_code].present? if params[:search_code].present?
...@@ -24,7 +27,6 @@ class SearchContext ...@@ -24,7 +27,6 @@ class SearchContext
result[:issues] = Issue.where(project_id: project_ids).search(query).order('updated_at DESC').limit(20) result[:issues] = Issue.where(project_id: project_ids).search(query).order('updated_at DESC').limit(20)
result[:wiki_pages] = [] result[:wiki_pages] = []
end end
result
end end
def result def result
......
class Admin::BackgroundJobsController < Admin::ApplicationController class Admin::BackgroundJobsController < Admin::ApplicationController
def show def show
@sidekiq_processes = `ps -U #{Settings.gitlab.user} -o euser,pid,pcpu,pmem,stat,start,command | grep sidekiq | grep -v grep`
end end
end end
class Admin::BroadcastMessagesController < Admin::ApplicationController
before_filter :broadcast_messages
def index
@broadcast_message = BroadcastMessage.new
end
def create
@broadcast_message = BroadcastMessage.new(params[:broadcast_message])
if @broadcast_message.save
redirect_to admin_broadcast_messages_path, notice: 'Broadcast Message was successfully created.'
else
render :index
end
end
def destroy
BroadcastMessage.find(params[:id]).destroy
respond_to do |format|
format.html { redirect_to :back }
format.js { render nothing: true }
end
end
protected
def broadcast_messages
@broadcast_messages ||= BroadcastMessage.order("starts_at DESC").page(params[:page])
end
end
...@@ -2,5 +2,6 @@ class Admin::DashboardController < Admin::ApplicationController ...@@ -2,5 +2,6 @@ class Admin::DashboardController < Admin::ApplicationController
def index def index
@projects = Project.order("created_at DESC").limit(10) @projects = Project.order("created_at DESC").limit(10)
@users = User.order("created_at DESC").limit(10) @users = User.order("created_at DESC").limit(10)
@groups = Group.order("created_at DESC").limit(10)
end end
end end
class Admin::ProjectsController < Admin::ApplicationController class Admin::ProjectsController < Admin::ApplicationController
before_filter :project, only: [:edit, :show, :update, :destroy, :team_update] before_filter :project, only: [:show, :transfer]
before_filter :group, only: [:show, :transfer]
before_filter :repository, only: [:show, :transfer]
def index def index
owner_id = params[:owner_id] owner_id = params[:owner_id]
...@@ -14,8 +16,16 @@ class Admin::ProjectsController < Admin::ApplicationController ...@@ -14,8 +16,16 @@ class Admin::ProjectsController < Admin::ApplicationController
end end
def show def show
@repository = @project.repository end
@group = @project.group
def transfer
result = ::Projects::TransferContext.new(@project, current_user, project: params).execute(:admin)
if result
redirect_to [:admin, @project]
else
render :show
end
end end
protected protected
...@@ -26,4 +36,12 @@ class Admin::ProjectsController < Admin::ApplicationController ...@@ -26,4 +36,12 @@ class Admin::ProjectsController < Admin::ApplicationController
@project = Project.find_with_namespace(id) @project = Project.find_with_namespace(id)
@project || render_404 @project || render_404
end end
def group
@group ||= project.group
end
def repository
@repository ||= project.repository
end
end end
...@@ -104,7 +104,7 @@ class GroupsController < ApplicationController ...@@ -104,7 +104,7 @@ class GroupsController < ApplicationController
# Dont allow unauthorized access to group # Dont allow unauthorized access to group
def authorize_read_group! def authorize_read_group!
unless projects.present? or can?(current_user, :read_group, @group) unless @group and (projects.present? or can?(current_user, :read_group, @group))
return render_404 return render_404
end end
end end
......
...@@ -23,4 +23,10 @@ class Projects::ApplicationController < ApplicationController ...@@ -23,4 +23,10 @@ class Projects::ApplicationController < ApplicationController
'public_projects' 'public_projects'
end end
end end
def require_branch_head
unless @repository.branch_names.include?(@ref)
redirect_to project_tree_path(@project, @ref), notice: "This action is not allowed unless you are on top of a branch"
end
end
end end
class Projects::BaseTreeController < Projects::ApplicationController
include ExtractsPath
before_filter :authorize_read_project!
before_filter :authorize_code_access!
before_filter :require_non_empty_project
end
...@@ -7,9 +7,30 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -7,9 +7,30 @@ class Projects::BlobController < Projects::ApplicationController
before_filter :authorize_code_access! before_filter :authorize_code_access!
before_filter :require_non_empty_project before_filter :require_non_empty_project
before_filter :blob
def show def show
@blob = @repository.blob_at(@commit.id, @path) end
def destroy
result = Files::DeleteContext.new(@project, current_user, params, @ref, @path).execute
if result[:status] == :success
flash[:notice] = "Your changes have been successfully commited"
redirect_to project_tree_path(@project, @ref)
else
flash[:alert] = result[:error]
render :show
end
end
private
def blob
@blob ||= @repository.blob_at(@commit.id, @path)
return not_found! unless @blob
not_found! unless @blob @blob
end end
end end
...@@ -22,6 +22,7 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -22,6 +22,7 @@ class Projects::CommitController < Projects::ApplicationController
@note = result[:note] @note = result[:note]
@line_notes = result[:line_notes] @line_notes = result[:line_notes]
@branches = result[:branches]
@notes_count = result[:notes_count] @notes_count = result[:notes_count]
@target_type = :commit @target_type = :commit
@target_id = @commit.id @target_id = @commit.id
......
# Controller for edit a repository's file class Projects::EditTreeController < Projects::BaseTreeController
class Projects::EditTreeController < Projects::ApplicationController before_filter :require_branch_head
include ExtractsPath before_filter :blob
# Authorize
before_filter :authorize_read_project!
before_filter :authorize_code_access!
before_filter :require_non_empty_project
before_filter :edit_requirements, only: [:show, :update]
def show def show
@last_commit = Gitlab::Git::Commit.last_for_path(@repository, @ref, @path).sha @last_commit = Gitlab::Git::Commit.last_for_path(@repository, @ref, @path).sha
end end
def update def update
edit_file_action = Gitlab::Satellite::EditFileAction.new(current_user, @project, @ref, @path) result = Files::UpdateContext.new(@project, current_user, params, @ref, @path).execute
updated_successfully = edit_file_action.commit!(
params[:content],
params[:commit_message],
params[:last_commit]
)
if updated_successfully if result[:status] == :success
redirect_to project_blob_path(@project, @id), notice: "Your changes have been successfully commited" flash[:notice] = "Your changes have been successfully commited"
redirect_to project_blob_path(@project, @id)
else else
flash[:notice] = "Your changes could not be commited, because the file has been changed" flash[:alert] = result[:error]
render :show render :show
end end
end end
private private
def edit_requirements def blob
@blob = @repository.blob_at(@commit.id, @path) @blob ||= @repository.blob_at(@commit.id, @path)
unless @blob
redirect_to project_blob_path(@project, @id), notice: "You can only edit text files"
end
allowed = if project.protected_branch? @ref
can?(current_user, :push_code_to_protected_branches, project)
else
can?(current_user, :push_code, project)
end
return access_denied! unless allowed
end end
end end
class Projects::NewTreeController < Projects::BaseTreeController
before_filter :require_branch_head
def show
end
def update
file_path = File.join(@path, File.basename(params[:file_name]))
result = Files::CreateContext.new(@project, current_user, params, @ref, file_path).execute
if result[:status] == :success
flash[:notice] = "Your changes have been successfully commited"
redirect_to project_blob_path(@project, File.join(@ref, file_path))
else
flash[:alert] = result[:error]
render :show
end
end
end
class Projects::TeamMembersController < Projects::ApplicationController class Projects::TeamMembersController < Projects::ApplicationController
# Authorize # Authorize
before_filter :authorize_admin_project! before_filter :authorize_admin_project!, except: :leave
layout "project_settings" layout "project_settings"
...@@ -45,6 +45,15 @@ class Projects::TeamMembersController < Projects::ApplicationController ...@@ -45,6 +45,15 @@ class Projects::TeamMembersController < Projects::ApplicationController
end end
end end
def leave
project.users_projects.find_by_user_id(current_user).destroy
respond_to do |format|
format.html { redirect_to :back }
format.js { render nothing: true }
end
end
def apply_import def apply_import
giver = Project.find(params[:source_project_id]) giver = Project.find(params[:source_project_id])
status = @project.team.import(giver) status = @project.team.import(giver)
......
# Controller for viewing a repository's file structure # Controller for viewing a repository's file structure
class Projects::TreeController < Projects::ApplicationController class Projects::TreeController < Projects::BaseTreeController
include ExtractsPath
# Authorize
before_filter :authorize_read_project!
before_filter :authorize_code_access!
before_filter :require_non_empty_project
def show def show
return not_found! if tree.entries.empty? return not_found! if tree.entries.empty?
......
...@@ -62,10 +62,6 @@ class ProjectsController < ApplicationController ...@@ -62,10 +62,6 @@ class ProjectsController < ApplicationController
@events = event_filter.apply_filter(@events) @events = event_filter.apply_filter(@events)
@events = @events.limit(limit).offset(params[:offset] || 0) @events = @events.limit(limit).offset(params[:offset] || 0)
# Ensure project default branch is set if it possible
# Normally it defined on push or during creation
@project.discover_default_branch
respond_to do |format| respond_to do |format|
format.html do format.html do
if @project.empty_repo? if @project.empty_repo?
......
...@@ -84,8 +84,8 @@ module ApplicationHelper ...@@ -84,8 +84,8 @@ module ApplicationHelper
repository = @project.repository repository = @project.repository
options = [ options = [
["Branch", repository.branch_names ], ["Branches", repository.branch_names],
[ "Tag", repository.tag_names ] ["Tags", repository.tag_names]
] ]
# If reference is commit id - # If reference is commit id -
...@@ -126,6 +126,9 @@ module ApplicationHelper ...@@ -126,6 +126,9 @@ module ApplicationHelper
# Skip if user already created appropriate MR # Skip if user already created appropriate MR
return false if project.merge_requests.where(source_branch: event.branch_name).opened.any? return false if project.merge_requests.where(source_branch: event.branch_name).opened.any?
# Skip if user removed branch right after that
return false unless project.repository.branch_names.include?(event.branch_name)
true true
end end
...@@ -184,14 +187,6 @@ module ApplicationHelper ...@@ -184,14 +187,6 @@ module ApplicationHelper
Gitlab.config.extra Gitlab.config.extra
end end
def public_icon
content_tag :i, nil, class: 'icon-globe cblue'
end
def private_icon
content_tag :i, nil, class: 'icon-lock cgreen'
end
def search_placeholder def search_placeholder
if @project && @project.persisted? if @project && @project.persisted?
"Search in this project" "Search in this project"
...@@ -208,4 +203,8 @@ module ApplicationHelper ...@@ -208,4 +203,8 @@ module ApplicationHelper
line += "..." if lines.size > 1 line += "..." if lines.size > 1
line line
end end
def broadcast_message
BroadcastMessage.current
end
end end
...@@ -94,6 +94,17 @@ module CommitsHelper ...@@ -94,6 +94,17 @@ module CommitsHelper
crumbs.html_safe crumbs.html_safe
end end
# Return Project default branch, if it present in array
# Else - first branch in array (mb last actual branch)
def commit_default_branch(project, branches)
branches.include?(project.default_branch) ? branches.delete(project.default_branch) : branches.pop
end
# Returns the sorted alphabetically links to branches, separated by a comma
def commit_branches_links(project, branches)
branches.sort.map { |branch| link_to(branch, project_tree_path(project, branch)) }.join(", ").html_safe
end
protected protected
# Private: Returns a link to a person. If the person has a matching user and # Private: Returns a link to a person. If the person has a matching user and
...@@ -114,7 +125,9 @@ module CommitsHelper ...@@ -114,7 +125,9 @@ module CommitsHelper
source_name source_name
end end
user = User.where('name like ? or email like ?', source_name, source_email).first # Prefer email match over name match
user = User.where(email: source_email).first
user ||= User.where(name: source_name).first
options = { options = {
class: "commit-#{options[:source]}-link has_tooltip", class: "commit-#{options[:source]}-link has_tooltip",
......
...@@ -127,4 +127,10 @@ module EventsHelper ...@@ -127,4 +127,10 @@ module EventsHelper
text = truncate(text, length: 150) text = truncate(text, length: 150)
sanitize(markdown(text), tags: %w(a img b pre p)) sanitize(markdown(text), tags: %w(a img b pre p))
end end
def event_commit_title(message)
escape_once(truncate(message.split("\n").first, length: 70))
rescue
"--broken encoding"
end
end end
...@@ -64,7 +64,9 @@ module GitlabMarkdownHelper ...@@ -64,7 +64,9 @@ module GitlabMarkdownHelper
# ref - name of the branch or reference, eg. stable # 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 # 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 # wiki - whether the markdown is from wiki or not
def create_relative_links(text, project_path_with_namespace, ref, requested_path, wiki = false) def create_relative_links(text, project, ref, requested_path, wiki = false)
@path_to_satellite = project.satellite.path
project_path_with_namespace = project.path_with_namespace
paths = extract_paths(text) paths = extract_paths(text)
paths.each do |file_path| paths.each do |file_path|
new_path = rebuild_path(project_path_with_namespace, file_path, requested_path, ref) new_path = rebuild_path(project_path_with_namespace, file_path, requested_path, ref)
...@@ -145,13 +147,18 @@ module GitlabMarkdownHelper ...@@ -145,13 +147,18 @@ module GitlabMarkdownHelper
def file_exists?(path) def file_exists?(path)
return false if path.nil? || path.empty? return false if path.nil? || path.empty?
File.exists?(Rails.root.join(path)) File.exists?(path_on_fs(path))
end end
# Check if the path is pointing to a directory(tree) or a file(blob) # 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 # eg. doc/api is directory and doc/README.md is file
def local_path(path) def local_path(path)
File.directory?(Rails.root.join(path)) ? "tree" : "blob" File.directory?(path_on_fs(path)) ? "tree" : "blob"
end
# Path to the file in the satellites repository on the filesystem
def path_on_fs(path)
[@path_to_satellite, path].join("/")
end end
# We will assume that if no ref exists we can point to master # We will assume that if no ref exists we can point to master
......
...@@ -2,4 +2,23 @@ module GroupsHelper ...@@ -2,4 +2,23 @@ module GroupsHelper
def remove_user_from_group_message(group, user) def remove_user_from_group_message(group, user)
"You are going to remove #{user.name} from #{group.name} Group. Are you sure?" "You are going to remove #{user.name} from #{group.name} Group. Are you sure?"
end end
def group_head_title
title = @group.name
title = if current_action?(:issues)
"Issues - " + title
elsif current_action?(:merge_requests)
"Merge requests - " + title
elsif current_action?(:members)
"Members - " + title
elsif current_action?(:edit)
"Settings - " + title
else
title
end
title
end
end end
module IconsHelper
def boolean_to_icon(value)
if value.to_s == "true"
content_tag :i, nil, class: 'icon-ok cgreen'
else
content_tag :i, nil, class: 'icon-off clgray'
end
end
def public_icon
content_tag :i, nil, class: 'icon-globe cblue'
end
def private_icon
content_tag :i, nil, class: 'icon-lock cgreen'
end
end
module NamespacesHelper module NamespacesHelper
def namespaces_options(selected = :current_user, scope = :default) def namespaces_options(selected = :current_user, scope = :default)
groups = current_user.owned_groups.select {|n| n.type == 'Group'} groups = current_user.owned_groups
users = current_user.namespaces.reject {|n| n.type == 'Group'} users = [current_user.namespace]
group_opts = ["Groups", groups.sort_by(&:human_name).map {|g| [g.human_name, g.id]} ] group_opts = ["Groups", groups.sort_by(&:human_name).map {|g| [g.human_name, g.id]} ]
users_opts = [ "Users", users.sort_by(&:human_name).map {|u| [u.human_name, u.id]} ] users_opts = [ "Users", users.sort_by(&:human_name).map {|u| [u.human_name, u.id]} ]
...@@ -16,4 +16,13 @@ module NamespacesHelper ...@@ -16,4 +16,13 @@ module NamespacesHelper
grouped_options_for_select(options, selected) grouped_options_for_select(options, selected)
end end
def namespace_select_tag(id, opts = {})
css_class = "ajax-namespace-select "
css_class << "multiselect " if opts[:multiple]
css_class << (opts[:class] || '')
value = opts[:selected] || ''
hidden_field_tag(id, value, class: css_class)
end
end end
...@@ -45,7 +45,10 @@ module ProjectsHelper ...@@ -45,7 +45,10 @@ module ProjectsHelper
link_to(simple_sanitize(project.group.name), group_path(project.group)) + " / " + project.name link_to(simple_sanitize(project.group.name), group_path(project.group)) + " / " + project.name
end end
else else
project.name owner = project.namespace.owner
content_tag :span do
link_to(simple_sanitize(owner.name), user_path(owner)) + " / " + project.name
end
end end
end end
...@@ -80,7 +83,7 @@ module ProjectsHelper ...@@ -80,7 +83,7 @@ module ProjectsHelper
@project.milestones.active.order("due_date, title ASC").all @project.milestones.active.order("due_date, title ASC").all
end end
def project_issues_trackers def project_issues_trackers(current_tracker = nil)
values = Project.issues_tracker.values.map do |tracker_key| values = Project.issues_tracker.values.map do |tracker_key|
if tracker_key.to_sym == :gitlab if tracker_key.to_sym == :gitlab
['GitLab', tracker_key] ['GitLab', tracker_key]
...@@ -89,7 +92,7 @@ module ProjectsHelper ...@@ -89,7 +92,7 @@ module ProjectsHelper
end end
end end
options_for_select(values) options_for_select(values, current_tracker)
end end
private private
...@@ -140,4 +143,38 @@ module ProjectsHelper ...@@ -140,4 +143,38 @@ module ProjectsHelper
# to calculate repo size - just show 'Unknown' # to calculate repo size - just show 'Unknown'
'unknown' 'unknown'
end end
def project_head_title
title = @project.name_with_namespace
title = if current_controller?(:tree)
"#{@project.path}\/#{@path} at #{@ref} - " + title
elsif current_controller?(:issues)
if current_action?(:show)
"Issue ##{@issue.iid} - " + title
else
"Issues - " + title
end
elsif current_controller?(:blob)
"#{@project.path}\/#{@blob.path} at #{@ref} - " + title
elsif current_controller?(:commits)
"Commits at #{@ref} - " + title
elsif current_controller?(:merge_requests)
if current_action?(:show)
"Merge request ##{@merge_request.iid} - " + title
else
"Merge requests - " + title
end
elsif current_controller?(:wikis)
"Wiki - " + title
elsif current_controller?(:network)
"Network graph - " + title
elsif current_controller?(:graphs)
"Graphs - " + title
else
title
end
title
end
end end
...@@ -5,7 +5,7 @@ module Emails ...@@ -5,7 +5,7 @@ module Emails
@group = @membership.group @group = @membership.group
mail(to: @membership.user.email, mail(to: @membership.user.email,
subject: subject("access to group was granted")) subject: subject("Access to group was granted"))
end end
end end
end end
...@@ -3,14 +3,14 @@ module Emails ...@@ -3,14 +3,14 @@ module Emails
def new_issue_email(recipient_id, issue_id) def new_issue_email(recipient_id, issue_id)
@issue = Issue.find(issue_id) @issue = Issue.find(issue_id)
@project = @issue.project @project = @issue.project
mail(to: recipient(recipient_id), subject: subject("new issue ##{@issue.iid}", @issue.title)) mail(to: recipient(recipient_id), subject: subject("New issue ##{@issue.iid}", @issue.title))
end end
def reassigned_issue_email(recipient_id, issue_id, previous_assignee_id) def reassigned_issue_email(recipient_id, issue_id, previous_assignee_id)
@issue = Issue.find(issue_id) @issue = Issue.find(issue_id)
@previous_assignee = User.find_by_id(previous_assignee_id) if previous_assignee_id @previous_assignee = User.find_by_id(previous_assignee_id) if previous_assignee_id
@project = @issue.project @project = @issue.project
mail(to: recipient(recipient_id), subject: subject("changed issue ##{@issue.iid}", @issue.title)) mail(to: recipient(recipient_id), subject: subject("Changed issue ##{@issue.iid}", @issue.title))
end end
def closed_issue_email(recipient_id, issue_id, updated_by_user_id) def closed_issue_email(recipient_id, issue_id, updated_by_user_id)
...@@ -27,7 +27,7 @@ module Emails ...@@ -27,7 +27,7 @@ module Emails
@project = @issue.project @project = @issue.project
@updated_by = User.find updated_by_user_id @updated_by = User.find updated_by_user_id
mail(to: recipient(recipient_id), mail(to: recipient(recipient_id),
subject: subject("changed issue ##{@issue.iid}", @issue.title)) subject: subject("Changed issue ##{@issue.iid}", @issue.title))
end end
end end
end end
...@@ -2,24 +2,24 @@ module Emails ...@@ -2,24 +2,24 @@ module Emails
module MergeRequests module MergeRequests
def new_merge_request_email(recipient_id, merge_request_id) def new_merge_request_email(recipient_id, merge_request_id)
@merge_request = MergeRequest.find(merge_request_id) @merge_request = MergeRequest.find(merge_request_id)
mail(to: recipient(recipient_id), subject: subject("new merge request !#{@merge_request.iid}", @merge_request.title)) mail(to: recipient(recipient_id), subject: subject("New merge request ##{@merge_request.iid}", @merge_request.title))
end end
def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id) def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id)
@merge_request = MergeRequest.find(merge_request_id) @merge_request = MergeRequest.find(merge_request_id)
@previous_assignee = User.find_by_id(previous_assignee_id) if previous_assignee_id @previous_assignee = User.find_by_id(previous_assignee_id) if previous_assignee_id
mail(to: recipient(recipient_id), subject: subject("changed merge request !#{@merge_request.iid}", @merge_request.title)) mail(to: recipient(recipient_id), subject: subject("Changed merge request ##{@merge_request.iid}", @merge_request.title))
end end
def closed_merge_request_email(recipient_id, merge_request_id, updated_by_user_id) def closed_merge_request_email(recipient_id, merge_request_id, updated_by_user_id)
@merge_request = MergeRequest.find(merge_request_id) @merge_request = MergeRequest.find(merge_request_id)
@updated_by = User.find updated_by_user_id @updated_by = User.find updated_by_user_id
mail(to: recipient(recipient_id), subject: subject("Closed merge request !#{@merge_request.iid}", @merge_request.title)) mail(to: recipient(recipient_id), subject: subject("Closed merge request ##{@merge_request.iid}", @merge_request.title))
end end
def merged_merge_request_email(recipient_id, merge_request_id) def merged_merge_request_email(recipient_id, merge_request_id)
@merge_request = MergeRequest.find(merge_request_id) @merge_request = MergeRequest.find(merge_request_id)
mail(to: recipient(recipient_id), subject: subject("Accepted merge request !#{@merge_request.iid}", @merge_request.title)) mail(to: recipient(recipient_id), subject: subject("Accepted merge request ##{@merge_request.iid}", @merge_request.title))
end end
end end
......
...@@ -4,27 +4,27 @@ module Emails ...@@ -4,27 +4,27 @@ module Emails
@note = Note.find(note_id) @note = Note.find(note_id)
@commit = @note.noteable @commit = @note.noteable
@project = @note.project @project = @note.project
mail(to: recipient(recipient_id), subject: subject("note for commit #{@commit.short_id}", @commit.title)) mail(to: recipient(recipient_id), subject: subject("Note for commit #{@commit.short_id}", @commit.title))
end end
def note_issue_email(recipient_id, note_id) def note_issue_email(recipient_id, note_id)
@note = Note.find(note_id) @note = Note.find(note_id)
@issue = @note.noteable @issue = @note.noteable
@project = @note.project @project = @note.project
mail(to: recipient(recipient_id), subject: subject("note for issue ##{@issue.iid}")) mail(to: recipient(recipient_id), subject: subject("Note for issue ##{@issue.iid}"))
end end
def note_merge_request_email(recipient_id, note_id) def note_merge_request_email(recipient_id, note_id)
@note = Note.find(note_id) @note = Note.find(note_id)
@merge_request = @note.noteable @merge_request = @note.noteable
@project = @note.project @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 end
def note_wall_email(recipient_id, note_id) def note_wall_email(recipient_id, note_id)
@note = Note.find(note_id) @note = Note.find(note_id)
@project = @note.project @project = @note.project
mail(to: recipient(recipient_id), subject: subject("note on wall")) mail(to: recipient(recipient_id), subject: subject("Note on wall"))
end end
end end
end end
...@@ -4,14 +4,14 @@ module Emails ...@@ -4,14 +4,14 @@ module Emails
@users_project = UsersProject.find user_project_id @users_project = UsersProject.find user_project_id
@project = @users_project.project @project = @users_project.project
mail(to: @users_project.user.email, mail(to: @users_project.user.email,
subject: subject("access to project was granted")) subject: subject("Access to project was granted"))
end end
def project_was_moved_email(project_id, user_id) def project_was_moved_email(project_id, user_id)
@user = User.find user_id @user = User.find user_id
@project = Project.find project_id @project = Project.find project_id
mail(to: @user.email, mail(to: @user.email,
subject: subject("project was moved")) subject: subject("Project was moved"))
end end
end end
end end
# == Schema Information
#
# Table name: broadcast_messages
#
# id :integer not null, primary key
# message :text default(""), not null
# starts_at :datetime
# ends_at :datetime
# alert_type :integer
# created_at :datetime not null
# updated_at :datetime not null
#
class BroadcastMessage < ActiveRecord::Base
attr_accessible :alert_type, :ends_at, :message, :starts_at
validates :message, presence: true
validates :starts_at, presence: true
validates :ends_at, presence: true
def self.current
where("ends_at > :now AND starts_at < :now", now: Time.zone.now).last
end
end
...@@ -168,7 +168,7 @@ class Event < ActiveRecord::Base ...@@ -168,7 +168,7 @@ class Event < ActiveRecord::Base
end end
def valid_push? def valid_push?
data[:ref] data[:ref] && ref_name.present?
rescue => ex rescue => ex
false false
end end
......
...@@ -11,6 +11,8 @@ ...@@ -11,6 +11,8 @@
# updated_at :datetime not null # updated_at :datetime not null
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# project_url :string(255) # project_url :string(255)
# subdomain :string(255)
# room :string(255)
# #
require "flowdock-git-hook" require "flowdock-git-hook"
......
...@@ -25,7 +25,7 @@ class HipchatService < Service ...@@ -25,7 +25,7 @@ class HipchatService < Service
end end
def description def description
'Simple web-based real-time group chat' 'Private group chat and IM'
end end
def to_param def to_param
...@@ -71,5 +71,4 @@ class HipchatService < Service ...@@ -71,5 +71,4 @@ class HipchatService < Service
message message
end end
end end
...@@ -21,6 +21,8 @@ class Issue < ActiveRecord::Base ...@@ -21,6 +21,8 @@ class Issue < ActiveRecord::Base
include Issuable include Issuable
include InternalId include InternalId
ActsAsTaggableOn.strict_case_match = true
belongs_to :project belongs_to :project
validates :project, presence: true validates :project, presence: true
......
...@@ -87,4 +87,8 @@ class Namespace < ActiveRecord::Base ...@@ -87,4 +87,8 @@ class Namespace < ActiveRecord::Base
def send_update_instructions def send_update_instructions
projects.each(&:send_move_instructions) projects.each(&:send_move_instructions)
end end
def kind
type == 'Group' ? 'group' : 'user'
end
end end
...@@ -9,7 +9,6 @@ ...@@ -9,7 +9,6 @@
# created_at :datetime not null # created_at :datetime not null
# updated_at :datetime not null # updated_at :datetime not null
# creator_id :integer # creator_id :integer
# default_branch :string(255)
# issues_enabled :boolean default(TRUE), not null # issues_enabled :boolean default(TRUE), not null
# wall_enabled :boolean default(TRUE), not null # wall_enabled :boolean default(TRUE), not null
# merge_requests_enabled :boolean default(TRUE), not null # merge_requests_enabled :boolean default(TRUE), not null
...@@ -27,8 +26,10 @@ ...@@ -27,8 +26,10 @@
class Project < ActiveRecord::Base class Project < ActiveRecord::Base
include Gitlab::ShellAdapter include Gitlab::ShellAdapter
extend Enumerize extend Enumerize
ActsAsTaggableOn.strict_case_match = true
attr_accessible :name, :path, :description, :default_branch, :issues_tracker, :label_list, attr_accessible :name, :path, :description, :issues_tracker, :label_list,
:issues_enabled, :wall_enabled, :merge_requests_enabled, :snippets_enabled, :issues_tracker_id, :issues_enabled, :wall_enabled, :merge_requests_enabled, :snippets_enabled, :issues_tracker_id,
:wiki_enabled, :public, :import_url, :last_activity_at, as: [:default, :admin] :wiki_enabled, :public, :import_url, :last_activity_at, as: [:default, :admin]
...@@ -36,6 +37,8 @@ class Project < ActiveRecord::Base ...@@ -36,6 +37,8 @@ class Project < ActiveRecord::Base
acts_as_taggable_on :labels, :issues_default_labels acts_as_taggable_on :labels, :issues_default_labels
attr_accessor :new_default_branch
# Relations # Relations
belongs_to :creator, foreign_key: "creator_id", class_name: "User" belongs_to :creator, foreign_key: "creator_id", class_name: "User"
belongs_to :group, foreign_key: "namespace_id", conditions: "type = 'Group'" belongs_to :group, foreign_key: "namespace_id", conditions: "type = 'Group'"
...@@ -125,7 +128,7 @@ class Project < ActiveRecord::Base ...@@ -125,7 +128,7 @@ class Project < ActiveRecord::Base
end end
def search query def search query
where("projects.name LIKE :query OR projects.path LIKE :query", query: "%#{query}%") joins(:namespace).where("projects.name LIKE :query OR projects.path LIKE :query OR namespaces.name LIKE :query OR projects.description LIKE :query", query: "%#{query}%")
end end
def find_with_namespace(id) def find_with_namespace(id)
...@@ -146,7 +149,7 @@ class Project < ActiveRecord::Base ...@@ -146,7 +149,7 @@ class Project < ActiveRecord::Base
end end
def repository def repository
@repository ||= Repository.new(path_with_namespace, default_branch) @repository ||= Repository.new(path_with_namespace)
end end
def saved? def saved?
...@@ -303,14 +306,6 @@ class Project < ActiveRecord::Base ...@@ -303,14 +306,6 @@ class Project < ActiveRecord::Base
end end
end end
def discover_default_branch
# Discover the default branch, but only if it hasn't already been set to
# something else
if repository.exists? && default_branch.nil?
update_attributes(default_branch: self.repository.discover_default_branch)
end
end
def update_merge_requests(oldrev, newrev, ref, user) def update_merge_requests(oldrev, newrev, ref, user)
return true unless ref =~ /heads/ return true unless ref =~ /heads/
branch_name = ref.gsub("refs/heads/", "") branch_name = ref.gsub("refs/heads/", "")
...@@ -320,7 +315,7 @@ class Project < ActiveRecord::Base ...@@ -320,7 +315,7 @@ class Project < ActiveRecord::Base
mrs = self.merge_requests.opened.by_branch(branch_name).all mrs = self.merge_requests.opened.by_branch(branch_name).all
# Update code for merge requests between project and project fork # Update code for merge requests between project and project fork
mrs += self.fork_merge_requests.opened.by_branch(branch_name).all mrs += self.fork_merge_requests.opened.by_branch(branch_name).all
mrs.each { |merge_request| merge_request.reload_code; merge_request.mark_as_unchecked } mrs.each { |merge_request| merge_request.reload_code; merge_request.mark_as_unchecked }
# Close merge requests # Close merge requests
...@@ -450,4 +445,12 @@ class Project < ActiveRecord::Base ...@@ -450,4 +445,12 @@ class Project < ActiveRecord::Base
order('id DESC').limit(100). order('id DESC').limit(100).
update_all(updated_at: Time.now) update_all(updated_at: Time.now)
end end
def project_member(user)
users_projects.where(user_id: user).first
end
def default_branch
@default_branch ||= repository.root_ref if repository.exists?
end
end end
...@@ -3,7 +3,7 @@ class Repository ...@@ -3,7 +3,7 @@ class Repository
attr_accessor :raw_repository, :path_with_namespace attr_accessor :raw_repository, :path_with_namespace
def initialize(path_with_namespace, default_branch) def initialize(path_with_namespace, default_branch = nil)
@path_with_namespace = path_with_namespace @path_with_namespace = path_with_namespace
@raw_repository = Gitlab::Git::Repository.new(path_to_repo) if path_with_namespace @raw_repository = Gitlab::Git::Repository.new(path_to_repo) if path_with_namespace
rescue Gitlab::Git::Repository::NoRepository rescue Gitlab::Git::Repository::NoRepository
...@@ -57,7 +57,7 @@ class Repository ...@@ -57,7 +57,7 @@ class Repository
def recent_branches(limit = 20) def recent_branches(limit = 20)
branches.sort do |a, b| branches.sort do |a, b|
a.commit.committed_date <=> b.commit.committed_date b.commit.committed_date <=> a.commit.committed_date
end[0..limit] end[0..limit]
end end
......
...@@ -36,6 +36,11 @@ ...@@ -36,6 +36,11 @@
# notification_level :integer default(1), not null # notification_level :integer default(1), not null
# password_expires_at :datetime # password_expires_at :datetime
# created_by_id :integer # created_by_id :integer
# avatar :string(255)
# confirmation_token :string(255)
# confirmed_at :datetime
# confirmation_sent_at :datetime
# unconfirmed_email :string(255)
# #
require 'carrierwave/orm/activerecord' require 'carrierwave/orm/activerecord'
...@@ -69,20 +74,20 @@ class User < ActiveRecord::Base ...@@ -69,20 +74,20 @@ class User < ActiveRecord::Base
# Namespace for personal projects # Namespace for personal projects
has_one :namespace, dependent: :destroy, foreign_key: :owner_id, class_name: "Namespace", conditions: 'type IS NULL' has_one :namespace, dependent: :destroy, foreign_key: :owner_id, class_name: "Namespace", conditions: 'type IS NULL'
# Namespaces (owned groups and own namespace)
has_many :namespaces, foreign_key: :owner_id
# Profile # Profile
has_many :keys, dependent: :destroy has_many :keys, dependent: :destroy
# Groups # Groups
has_many :own_groups, class_name: "Group", foreign_key: :owner_id
has_many :owned_groups, through: :users_groups, source: :group, conditions: { users_groups: { group_access: UsersGroup::OWNER } }
has_many :users_groups, dependent: :destroy has_many :users_groups, dependent: :destroy
has_many :groups, through: :users_groups has_many :groups, through: :users_groups
has_many :owned_groups, through: :users_groups, source: :group, conditions: { users_groups: { group_access: UsersGroup::OWNER } }
# Projects # Projects
has_many :groups_projects, through: :groups, source: :projects
has_many :personal_projects, through: :namespace, source: :projects
has_many :projects, through: :users_projects
has_many :created_projects, foreign_key: :creator_id, class_name: 'Project'
has_many :snippets, dependent: :destroy, foreign_key: :author_id, class_name: "Snippet" has_many :snippets, dependent: :destroy, foreign_key: :author_id, class_name: "Snippet"
has_many :users_projects, dependent: :destroy has_many :users_projects, dependent: :destroy
has_many :issues, dependent: :destroy, foreign_key: :author_id has_many :issues, dependent: :destroy, foreign_key: :author_id
...@@ -93,11 +98,6 @@ class User < ActiveRecord::Base ...@@ -93,11 +98,6 @@ class User < ActiveRecord::Base
has_many :assigned_issues, dependent: :destroy, foreign_key: :assignee_id, class_name: "Issue" 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 :assigned_merge_requests, dependent: :destroy, foreign_key: :assignee_id, class_name: "MergeRequest"
has_many :groups_projects, through: :groups, source: :projects
has_many :personal_projects, through: :namespace, source: :projects
has_many :projects, through: :users_projects
has_many :own_projects, foreign_key: :creator_id, class_name: 'Project'
has_many :owned_projects, through: :namespaces, source: :projects
# #
# Validations # Validations
...@@ -247,7 +247,7 @@ class User < ActiveRecord::Base ...@@ -247,7 +247,7 @@ class User < ActiveRecord::Base
# Groups user has access to # Groups user has access to
def authorized_groups def authorized_groups
@authorized_groups ||= begin @authorized_groups ||= begin
group_ids = (groups.pluck(:id) + own_groups.pluck(:id) + authorized_projects.pluck(:namespace_id)) group_ids = (groups.pluck(:id) + authorized_projects.pluck(:namespace_id))
Group.where(id: group_ids).order('namespaces.name ASC') Group.where(id: group_ids).order('namespaces.name ASC')
end end
end end
...@@ -256,7 +256,7 @@ class User < ActiveRecord::Base ...@@ -256,7 +256,7 @@ class User < ActiveRecord::Base
# Projects user has access to # Projects user has access to
def authorized_projects def authorized_projects
@authorized_projects ||= begin @authorized_projects ||= begin
project_ids = owned_projects.pluck(:id) project_ids = personal_projects.pluck(:id)
project_ids += groups_projects.pluck(:id) project_ids += groups_projects.pluck(:id)
project_ids += projects.pluck(:id) project_ids += projects.pluck(:id)
project_ids += groups.joins(:shared_projects).pluck(:project_id) project_ids += groups.joins(:shared_projects).pluck(:project_id)
...@@ -265,6 +265,12 @@ class User < ActiveRecord::Base ...@@ -265,6 +265,12 @@ class User < ActiveRecord::Base
end end
end end
def owned_projects
@owned_projects ||= begin
Project.where(namespace_id: owned_groups.pluck(:id).push(namespace.id)).joins(:namespace)
end
end
# Team membership in authorized projects # Team membership in authorized projects
def tm_in_authorized_projects def tm_in_authorized_projects
UsersProject.where(project_id: authorized_projects.map(&:id), user_id: self.id) UsersProject.where(project_id: authorized_projects.map(&:id), user_id: self.id)
...@@ -337,7 +343,7 @@ class User < ActiveRecord::Base ...@@ -337,7 +343,7 @@ class User < ActiveRecord::Base
end end
def several_namespaces? def several_namespaces?
namespaces.many? || owned_groups.any? owned_groups.any?
end end
def namespace_id def namespace_id
...@@ -402,4 +408,9 @@ class User < ActiveRecord::Base ...@@ -402,4 +408,9 @@ class User < ActiveRecord::Base
self self
end end
def can_leave_project?(project)
project.namespace != namespace &&
project.project_member(self)
end
end end
...@@ -30,12 +30,6 @@ class ProjectObserver < BaseObserver ...@@ -30,12 +30,6 @@ class ProjectObserver < BaseObserver
def after_update(project) def after_update(project)
project.send_move_instructions if project.namespace_id_changed? project.send_move_instructions if project.namespace_id_changed?
project.rename_repo if project.path_changed? project.rename_repo if project.path_changed?
GitlabShellWorker.perform_async(
:update_repository_head,
project.path_with_namespace,
project.default_branch
) if project.default_branch_changed?
end end
def before_destroy(project) def before_destroy(project)
......
...@@ -24,7 +24,6 @@ class GitPushService ...@@ -24,7 +24,6 @@ class GitPushService
create_push_event create_push_event
project.ensure_satellite_exists project.ensure_satellite_exists
project.discover_default_branch
project.repository.expire_cache project.repository.expire_cache
if push_to_existing_branch?(ref, oldrev) if push_to_existing_branch?(ref, oldrev)
......
...@@ -18,6 +18,10 @@ class ProjectTransferService ...@@ -18,6 +18,10 @@ class ProjectTransferService
raise TransferError.new("Project with same path in target namespace already exists") raise TransferError.new("Project with same path in target namespace already exists")
end end
# Remove old satellite
project.satellite.destroy
# Apply new namespace id
project.namespace = new_namespace project.namespace = new_namespace
project.save! project.save!
...@@ -29,8 +33,8 @@ class ProjectTransferService ...@@ -29,8 +33,8 @@ class ProjectTransferService
# Move wiki repo also if present # Move wiki repo also if present
gitlab_shell.mv_repository("#{old_path}.wiki", "#{new_path}.wiki") gitlab_shell.mv_repository("#{old_path}.wiki", "#{new_path}.wiki")
# create satellite repo # Create a new satellite (reload project from DB)
project.ensure_satellite_exists Project.find(project.id).ensure_satellite_exists
# clear project cached events # clear project cached events
project.reset_events_cache project.reset_events_cache
......
%h3.page-title Background Jobs %h3.page-title Background Jobs
%br %p.light GitLab use #{link_to "sidekiq", "http://sidekiq.org/"} library for async job processing
%hr
.ui-box
.title Sidekiq running processes
.ui-box-body
- if @sidekiq_processes.empty?
%h4.cred
%i.icon-warning-sign
There are no running sidekiq processes. Please restart GitLab
- else
%table.table
%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(" ")
.clearfix
%p
%i.icon-exclamation-sign
If '[25 of 25 busy]' is shown, restart GitLab with 'sudo service gitlab reload'.
%p
%i.icon-exclamation-sign
If more than one sidekiq process is listed, stop GitLab, kill the remaining sidekiq processes (sudo pkill -u #{Settings.gitlab.user} -f sidekiq) and restart GitLab.
.ui-box .ui-box
%iframe{src: sidekiq_path, width: '100%', height: 900, style: "border: none"} %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
Broadcast Messages
%p.light
Broadcast messages are displayed for every user and can be used to notify users about scheduled maintenance, recent upgrades and more.
%hr
= form_for [:admin, @broadcast_message] do |f|
-if @broadcast_message.errors.any?
.alert.alert-error
- @broadcast_message.errors.full_messages.each do |msg|
%p= msg
.control-group
= f.label :message
.controls
= f.text_area :message, class: "input-xxlarge", rows: 2, required: true
.control-group
= f.label :starts_at
.controls.datetime-controls
= f.datetime_select :starts_at
.control-group
= f.label :ends_at
.controls.datetime-controls
= f.datetime_select :ends_at
.form-actions
= f.submit "Add broadcast message", class: "btn btn-create"
-if @broadcast_messages.any?
%ul.bordered-list.broadcast-messages
- @broadcast_messages.each do |broadcast_message|
%li
.pull-right
- if broadcast_message.starts_at
%strong
#{broadcast_message.starts_at.to_s(:short)}
\...
- if broadcast_message.ends_at
%strong
#{broadcast_message.ends_at.to_s(:short)}
&nbsp;
= link_to [:admin, broadcast_message], method: :delete, remote: true, class: 'remove-row btn btn-tiny' do
%i.icon-remove.cred
.message= broadcast_message.message
= paginate @broadcast_messages
...@@ -51,6 +51,19 @@ ...@@ -51,6 +51,19 @@
= time_ago_in_words user.created_at = time_ago_in_words user.created_at
ago ago
.span4
%h4 Latest groups
%hr
- @groups.each do |group|
%p
= link_to [:admin, group] do
= group.name
%span.light.pull-right
= time_ago_in_words group.created_at
ago
%br
.row
.span4 .span4
%h4 Stats %h4 Stats
%hr %hr
...@@ -82,3 +95,34 @@ ...@@ -82,3 +95,34 @@
Milestones Milestones
%span.light.pull-right %span.light.pull-right
= Milestone.count = Milestone.count
.span4
%h4
Features
%hr
%p
Sign up
%span.light.pull-right
= boolean_to_icon gitlab_config.signup_enabled
%p
LDAP
%span.light.pull-right
= boolean_to_icon Gitlab.config.ldap.enabled
%p
Gravatar
%span.light.pull-right
= boolean_to_icon Gitlab.config.gravatar.enabled
%p
OmniAuth
%span.light.pull-right
= boolean_to_icon Gitlab.config.omniauth.enabled
.span4
%h4 Components
%hr
%p
GitLab
%span.pull-right
= Gitlab::VERSION
%p
GitLab Shell
%span.pull-right
= Gitlab::Shell.new.version
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
%span= @group.errors.full_messages.first %span= @group.errors.full_messages.first
.control-group.group_name_holder .control-group.group_name_holder
= f.label :name do = f.label :name do
Group name is Group name
.controls .controls
= f.text_field :name, placeholder: "Example Group", class: "input-xxlarge" = f.text_field :name, placeholder: "Example Group", class: "input-xxlarge"
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
.control-group.group_name_holder .control-group.group_name_holder
= f.label :path do = f.label :path do
%span.cred Group path is %span.cred Group path
.controls .controls
= f.text_field :path, placeholder: "example-group", class: "input-xxlarge danger" = f.text_field :path, placeholder: "example-group", class: "input-xxlarge danger"
%ul.cred %ul.cred
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
%span= @group.errors.full_messages.first %span= @group.errors.full_messages.first
.control-group .control-group
= f.label :name do = f.label :name do
Group name is Group name
.controls .controls
= f.text_field :name, placeholder: "Ex. OpenSource", class: "input-xxlarge left" = f.text_field :name, placeholder: "Ex. OpenSource", class: "input-xxlarge left"
.control-group.group-description-holder .control-group.group-description-holder
......
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
= @group.description = @group.description
%li %li
%span.light Created at: %span.light Created on:
%strong %strong
= @group.created_at.stamp("March 1, 1999") = @group.created_at.stamp("March 1, 1999")
......
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
= @project.creator.try(:name) || '(deleted)' = @project.creator.try(:name) || '(deleted)'
%li %li
%span.light Created at: %span.light Created on:
%strong %strong
= @project.created_at.stamp("March 1, 1999") = @project.created_at.stamp("March 1, 1999")
...@@ -74,6 +74,23 @@ ...@@ -74,6 +74,23 @@
%span.cgreen %span.cgreen
%i.icon-lock %i.icon-lock
Private Private
.ui-box
.title
Transfer project
.ui-box-body
= form_for @project, url: transfer_admin_project_path(@project), method: :put do |f|
.control-group
= f.label :namespace_id, "Namespace"
.controls
= namespace_select_tag :namespace_id, selected: params[:namespace_id], class: 'input-large'
.control-group
.controls
= f.submit 'Transfer', class: 'btn btn-primary'
.span6 .span6
- if @group - if @group
.ui-box .ui-box
......
...@@ -54,15 +54,20 @@ ...@@ -54,15 +54,20 @@
.span9 .span9
%ul.bordered-list.my-projects.top-list %ul.bordered-list.my-projects.top-list
- @projects.each do |project| - @projects.each do |project|
%li %li.my-project-row
%h4.project-title %h4.project-title
%span.access-icon
- if project.public
= public_icon
- else
= private_icon
= link_to project_path(project), class: dom_class(project) do = link_to project_path(project), class: dom_class(project) do
%strong= project.name_with_namespace = project.name_with_namespace
- if project.public
%small.access-icon
= public_icon
Public
- if current_user.can_leave_project?(project)
.pull-right
= link_to leave_project_team_members_path(project), confirm: "Leave project?", method: :delete, remote: true, class: "btn-tiny btn remove-row", title: 'Leave project' do
%i.icon-signout
Leave
- if project.forked_from_project - if project.forked_from_project
%small.pull-right %small.pull-right
...@@ -81,6 +86,7 @@ ...@@ -81,6 +86,7 @@
%span.light Last activity: %span.light Last activity:
%span.date= project_last_activity(project) %span.date= project_last_activity(project)
- if @projects.blank? - if @projects.blank?
%li %li
%h3.nothing_here_message There are no projects here. %h3.nothing_here_message There are no projects here.
......
<h2>Resend confirmation instructions</h2>
<%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %>
<%= devise_error_messages! %>
<div><%= f.label :email %><br />
<%= f.email_field :email %></div>
<div><%= f.submit "Resend confirmation instructions" %></div>
<% end %>
<%= render partial: "devise/shared/links" %>
.login-box
%h3.page-title Resend confirmation instructions
= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f|
= devise_error_messages!
= f.email_field :email, placeholder: 'Email'
%div= f.submit "Resend confirmation instructions", class: 'btn btn-success'
%hr
= link_to "Sign in", new_session_path(resource_name)
...@@ -7,5 +7,8 @@ ...@@ -7,5 +7,8 @@
%div %div
= f.password_field :password_confirmation, class: "text bottom", placeholder: "Confirm new password" = f.password_field :password_confirmation, class: "text bottom", placeholder: "Confirm new password"
%div %div
.clearfix.append-bottom-10
= f.submit "Change my password", class: "btn btn-primary" = f.submit "Change my password", class: "btn btn-primary"
.pull-right= render partial: "devise/shared/links" = link_to "Sign in", new_session_path(resource_name), class: "btn pull-right"
%div
= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name)
...@@ -2,4 +2,4 @@ ...@@ -2,4 +2,4 @@
.commit-row-title .commit-row-title
= link_to commit[:id][0..8], project_commit_path(project, commit[:id]), class: "commit_short_id", alt: '' = link_to commit[:id][0..8], project_commit_path(project, commit[:id]), class: "commit_short_id", alt: ''
&nbsp; &nbsp;
= gfm escape_once(truncate(commit[:message], length: 70)) rescue "--broken encoding" = gfm event_commit_title(commit[:message])
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
%span= @group.errors.full_messages.first %span= @group.errors.full_messages.first
.control-group .control-group
= f.label :name do = f.label :name do
Group name is Group name
.controls .controls
= f.text_field :name, placeholder: "Ex. OpenSource", class: "input-xxlarge left" = f.text_field :name, placeholder: "Ex. OpenSource", class: "input-xxlarge left"
...@@ -90,7 +90,7 @@ ...@@ -90,7 +90,7 @@
.title Remove group .title Remove group
.ui-box-body .ui-box-body
%p %p
Remove of group will cause removing all child projects and resources. Removing group will cause all child projects and resources to be removed.
%p %p
%strong Removed group can not be restored! %strong Removed group can not be restored!
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
%span= @group.errors.full_messages.first %span= @group.errors.full_messages.first
.control-group .control-group
= f.label :name do = f.label :name do
Group name is Group name
.controls .controls
= f.text_field :name, placeholder: "Ex. OpenSource", class: "input-xxlarge left" = f.text_field :name, placeholder: "Ex. OpenSource", class: "input-xxlarge left"
...@@ -16,11 +16,11 @@ ...@@ -16,11 +16,11 @@
.control-group .control-group
.controls .controls
%ul %ul
%li Group is kind of directory for several projects %li A group is a collection of several projects
%li All created groups are private %li Groups are private by default
%li People within a group see only projects they have access to %li Members of a group may only view projects they have permission to access
%li All projects of group will be stored in a group directory %li Group project URLs are prefixed with the group namespace
%li You will be able to move existing projects into group %li Existing projects may be moved into a group
.form-actions .form-actions
= f.submit 'Create group', class: "btn btn-create" = f.submit 'Create group', class: "btn btn-create"
......
%h2.page-title .hero-unit
GitLab %h2
%span.light Enterprise Edition GitLab
.pull-right %span.light Enterprise Edition
%span= Gitlab::VERSION %span= Gitlab::VERSION
%small= Gitlab::REVISION %small= Gitlab::REVISION
%p.slead %p.slead
Self Hosted Git Management GitLab is open source software to collaborate on code.
%br %br
Fast, secure and stable solution based on Ruby on Rails. Create projects and repositories, manage access and do code reviews.
%br
Read more about GitLab at #{link_to "gitlab.org", "http://gitlab.org/", target: "_blank"}.
.row .row
.span4 .span4
......
- if broadcast_message.present?
.broadcast-message
%i.icon-bullhorn
= broadcast_message.message
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
%html{ lang: "en"} %html{ lang: "en"}
= render "layouts/head", title: "Dashboard" = render "layouts/head", title: "Dashboard"
%body{class: "#{app_theme} application", :'data-page' => body_data_page } %body{class: "#{app_theme} application", :'data-page' => body_data_page }
= render "layouts/broadcast"
= render "layouts/head_panel", title: "Dashboard" = render "layouts/head_panel", title: "Dashboard"
= render "layouts/flash" = render "layouts/flash"
%nav.main-nav %nav.main-nav
......
!!! 5 !!! 5
%html{ lang: "en"} %html{ lang: "en"}
= render "layouts/head", title: "#{@group.name}" = render "layouts/head", title: group_head_title
%body{class: "#{app_theme} application", :'data-page' => body_data_page} %body{class: "#{app_theme} application", :'data-page' => body_data_page}
= render "layouts/head_panel", title: "group: #{@group.name}" = render "layouts/head_panel", title: "group: #{@group.name}"
= render "layouts/flash" = render "layouts/flash"
......
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
= link_to "Users", admin_users_path = link_to "Users", admin_users_path
= nav_link(controller: :logs) do = nav_link(controller: :logs) do
= link_to "Logs", admin_logs_path = link_to "Logs", admin_logs_path
= nav_link(controller: :broadcast_messages) do
= link_to "Messages", admin_broadcast_messages_path
= nav_link(controller: :hooks) do = nav_link(controller: :hooks) do
= link_to "Hooks", admin_hooks_path = link_to "Hooks", admin_hooks_path
= nav_link(controller: :background_jobs) do = nav_link(controller: :background_jobs) do
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
%i.icon-home %i.icon-home
- if project_nav_tab? :files - if project_nav_tab? :files
= nav_link(controller: %w(tree blob blame edit_tree)) do = nav_link(controller: %w(tree blob blame edit_tree new_tree)) do
= link_to 'Files', project_tree_path(@project, @ref || @repository.root_ref) = link_to 'Files', project_tree_path(@project, @ref || @repository.root_ref)
- if project_nav_tab? :commits - if project_nav_tab? :commits
......
!!! 5 !!! 5
%html{ lang: "en"} %html{ lang: "en"}
= render "layouts/head", title: @project.name_with_namespace = render "layouts/head", title: project_head_title
%body{class: "#{app_theme} project", :'data-page' => body_data_page, :'data-project-id' => @project.id } %body{class: "#{app_theme} project", :'data-page' => body_data_page, :'data-project-id' => @project.id }
= render "layouts/broadcast"
= render "layouts/head_panel", title: project_title(@project) = render "layouts/head_panel", title: project_title(@project)
= render "layouts/init_auto_complete" = render "layouts/init_auto_complete"
= render "layouts/flash" = render "layouts/flash"
......
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