Commit 1b54b212 authored by Sacred Seven's avatar Sacred Seven

Merged v7.7.1

parents 56565a0b 41ab9e1f
*.log
*.swp
.DS_Store
.bundle
.chef
.directory
.envrc
.gitlab_shell_secret
.idea
.rbenv-version
.rbx/
db/*.sqlite3
db/*.sqlite3-journal
log/*.log*
tmp/
.sass-cache/
coverage/*
backups/*
*.swp
public/uploads/
.ruby-version
.ruby-gemset
.ruby-version
.rvmrc
.rbenv-version
.directory
nohup.out
Vagrantfile
.sass-cache/
.secret
.vagrant
config/gitlab.yml
Vagrantfile
backups/*
config/aws.yml
config/database.yml
config/gitlab.yml
config/initializers/omniauth.rb
config/initializers/rack_attack.rb
config/initializers/smtp_settings.rb
config/unicorn.rb
config/resque.yml
config/aws.yml
config/unicorn.rb
coverage/*
db/*.sqlite3
db/*.sqlite3-journal
db/data.yml
.idea
.DS_Store
.chef
vendor/bundle/*
rails_best_practices_output.html
doc/code/*
.secret
*.log
public/uploads.*
public/assets/
.envrc
dump.rdb
log/*.log*
nohup.out
public/assets/
public/uploads.*
public/uploads/
rails_best_practices_output.html
tags
.AppleDouble
.gitlab_shell_secret
tmp/
vendor/bundle/*
Note: The upcoming release contains empty lines to reduce the number of merge conflicts, scroll down to see past releases.
v 7.7.0
- Import from GitHub.com feature
- Add Jetbrains Teamcity CI service (Jason Lippert)
- Mention notification level
- Markdown preview in wiki (Yuriy Glukhov)
- Raise group avatar filesize limit to 200kb
- OAuth applications feature
- Show user SSH keys in admin area
- Developer can push to protected branches option
- Set project path instead of project name in create form
- Block Git HTTP access after 10 failed authentication attempts
- Updates to the messages returned by API (sponsored by O'Reilly Media)
- New UI layout with side navigation
- Add alert message in case of outdated browser (IE < 10)
- Added API support for sorting projects
- Update gitlab_git to version 7.0.0.rc14
- Add API project search filter option for authorized projects
- Fix File blame not respecting branch selection
- Change some of application settings on fly in admin area UI
- Redesign signin/signup pages
- Close standard input in Gitlab::Popen.popen
- Trigger GitLab CI when push tags
- When accept merge request - do merge using sidaekiq job
- Enable web signups by default
- Fixes for diff comments: drag-n-drop images, selecting images
- Fixes for edit comments: drag-n-drop images, preview mode, selecting images, save & update
- Remove password strength indicator
- Fix commit pagination
v 7.6.0
- Fork repository to groups
- New rugged version
......@@ -22,8 +55,15 @@ v 7.6.0
- Possibility to create Milestones or Labels when Issues are disabled
- Fix bug with showing gpg signature in tag
v 7.5.3
- Bump gitlab_git to 7.0.0.rc12 (includes Rugged 0.21.2)
v 7.5.2
- Don't log Sidekiq arguments by default
- Fix restore of wiki repositories from backups
v 7.5.1
- Add missing timestamps to 'members' table
v 7.5.0
- API: Add support for Hipchat (Kevin Houdebert)
......@@ -43,7 +83,7 @@ v 7.5.0
- Performance improvements
- Fix post-receive issue for projects with deleted forks
- New gitlab-shell version with custom hooks support
- Improve code
- Improve code
- GitLab CI 5.2+ support (does not support older versions)
- Fixed bug when you can not push commits starting with 000000 to protected branches
- Added a password strength indicator
......
......@@ -101,6 +101,16 @@ Please ensure you support the feature you contribute through all of these steps.
1. Community questions answered
1. Answers to questions radiated (in docs/wiki/etc.)
If you add a dependency in GitLab (such as an operating system package) please consider updating the following and note the applicability of each in your merge request:
1. Note the addition in the release blog post (create one if it doesn't exist yet) https://gitlab.com/gitlab-com/www-gitlab-com/merge_requests/
1. Upgrade guide, for example https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/update/7.5-to-7.6.md
1. Upgrader https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/update/upgrader.md#2-run-gitlab-upgrade-tool
1. Installation guide https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md#1-packages-dependencies
1. GitLab Development Kit https://gitlab.com/gitlab-org/gitlab-development-kit
1. Test suite https://gitlab.com/gitlab-org/gitlab-ci/blob/master/doc/examples/configure_a_runner_to_run_the_gitlab_ce_test_suite.md
1. Omnibus package creator https://gitlab.com/gitlab-org/omnibus-gitlab
## Merge request description format
1. What does this MR do?
......@@ -118,6 +128,7 @@ Please ensure you support the feature you contribute through all of these steps.
1. Can merge without problems (if not please merge `master`, never rebase commits pushed to the remote server)
1. Does not break any existing functionality
1. Fixes one specific issue or implements one specific feature (do not combine things, send separate merge requests if needed)
1. Migrations should do only one thing (eg: either create a table, move data to a new table or remove an old table) to aid retrying on failure
1. Keeps the GitLab code base clean and well structured
1. Contains functionality we think other users will benefit from too
1. Doesn't add configuration options since they complicate future changes
......@@ -140,6 +151,7 @@ Please ensure you support the feature you contribute through all of these steps.
1. [CoffeeScript](https://github.com/thoughtbot/guides/tree/master/style#coffeescript)
1. [Shell commands](doc/development/shell_commands.md) created by GitLab contributors to enhance security
1. [Markdown](http://www.cirosantilli.com/markdown-styleguide)
1. Interface text should be written subjectively instead of objectively. It should be the gitlab core team addressing a person. It should be written in present time and never use past tense (has been/was). For example instead of "prohibited this user from being saved due to the following errors:" the text should be "sorry, we could not create your account because:". Also these [excellent writing guidelines](https://github.com/NARKOZ/guides#writing).
This is also the style used by linting tools such as [RuboCop](https://github.com/bbatsov/rubocop), [PullReview](https://www.pullreview.com/) and [Hound CI](https://houndci.com).
......
......@@ -32,10 +32,15 @@ gem 'omniauth-twitter'
gem 'omniauth-github'
gem 'omniauth-shibboleth'
gem 'omniauth-kerberos'
gem 'doorkeeper', '2.1.0'
gem "rack-oauth2", "~> 1.0.5"
# Browser detection
gem "browser"
# Extracting information from a git repository
# Provide access to Gitlab::Git library
gem "gitlab_git", '7.0.0.rc12'
gem "gitlab_git", '7.0.0.rc14'
# Ruby/Rack Git Smart-HTTP Server Handler
gem 'gitlab-grack', '~> 2.0.0.pre', require: 'grack'
......@@ -93,7 +98,7 @@ gem "github-markup"
gem 'redcarpet', '~> 3.1.2'
gem 'RedCloth'
gem 'rdoc', '~>3.6'
gem 'org-ruby', '= 0.9.9'
gem 'org-ruby', '= 0.9.12'
gem 'creole', '~>0.3.6'
gem 'wikicloth', '=0.8.1'
gem 'asciidoctor', '= 0.1.4'
......@@ -173,7 +178,6 @@ gem 'semantic-ui-sass', '~> 0.16.1.0'
gem "sass-rails", '~> 4.0.2'
gem "coffee-rails"
gem "uglifier"
gem "therubyracer"
gem 'turbolinks'
gem 'jquery-turbolinks'
......@@ -258,6 +262,9 @@ end
group :production do
gem "gitlab_meta", '7.0'
gem "therubyracer"
end
gem "newrelic_rpm"
gem 'octokit', '3.7.0'
......@@ -37,6 +37,7 @@ GEM
rake (>= 0.8.7)
arel (5.0.1.20140414130214)
asciidoctor (0.1.4)
attr_required (1.0.0)
awesome_print (1.2.0)
axiom-types (0.0.5)
descendants_tracker (~> 0.0.1)
......@@ -49,6 +50,7 @@ GEM
debug_inspector (>= 0.0.1)
bootstrap-sass (3.0.3.0)
sass (~> 3.2)
browser (0.7.2)
builder (3.2.2)
capybara (2.2.1)
mime-types (>= 1.16)
......@@ -107,6 +109,8 @@ GEM
diff-lcs (1.2.5)
diffy (3.0.3)
docile (1.1.5)
doorkeeper (2.1.0)
railties (>= 3.1)
dotenv (0.9.0)
dropzonejs-rails (0.4.14)
rails (> 3.1)
......@@ -120,7 +124,7 @@ GEM
equalizer (0.0.8)
erubis (2.7.0)
escape_utils (0.2.4)
eventmachine (1.0.3)
eventmachine (1.0.4)
excon (0.32.1)
execjs (2.0.2)
expression_parser (0.9.0)
......@@ -158,7 +162,7 @@ GEM
dotenv (>= 0.7)
thor (>= 0.13.6)
formatador (0.2.4)
gemnasium-gitlab-service (0.2.2)
gemnasium-gitlab-service (0.2.3)
rugged (~> 0.19)
gherkin-ruby (0.3.1)
racc
......@@ -179,7 +183,7 @@ GEM
mime-types (~> 1.19)
gitlab_emoji (0.0.1.1)
emoji (~> 1.0.1)
gitlab_git (7.0.0.rc12)
gitlab_git (7.0.0.rc14)
activesupport (~> 4.0)
charlock_holmes (~> 0.6)
gitlab-linguist (~> 3.0)
......@@ -250,6 +254,7 @@ GEM
json (~> 1.8)
multi_xml (>= 0.5.2)
httpauth (0.2.1)
httpclient (2.5.3.3)
i18n (0.6.11)
ice_nine (0.10.0)
jalalidate (0.3.3)
......@@ -276,7 +281,7 @@ GEM
kaminari (0.15.1)
actionpack (>= 3.0.0)
activesupport (>= 3.0.0)
kgio (2.8.1)
kgio (2.9.2)
launchy (2.4.2)
addressable (~> 2.3)
letter_opener (1.1.2)
......@@ -314,6 +319,8 @@ GEM
jwt (~> 0.1.4)
multi_json (~> 1.0)
rack (~> 1.2)
octokit (3.7.0)
sawyer (~> 0.6.0, >= 0.5.3)
omniauth (1.1.4)
hashie (>= 1.2, < 3)
rack
......@@ -339,7 +346,7 @@ GEM
omniauth-twitter (1.0.1)
multi_json (~> 1.3)
omniauth-oauth (~> 1.0)
org-ruby (0.9.9)
org-ruby (0.9.12)
rubypants (~> 0.2)
orm_adapter (0.5.0)
pg (0.15.1)
......@@ -362,13 +369,19 @@ GEM
rack (1.5.2)
rack-accept (0.4.5)
rack (>= 0.4)
rack-attack (2.3.0)
rack-attack (4.2.0)
rack
rack-cors (0.2.9)
rack-mini-profiler (0.9.0)
rack (>= 1.1.3)
rack-mount (0.8.3)
rack (>= 1.0.0)
rack-oauth2 (1.0.8)
activesupport (>= 2.3)
attr_required (>= 0.0.5)
httpclient (>= 2.2.0.2)
multi_json (>= 1.3.6)
rack (>= 1.1)
rack-protection (1.5.1)
rack
rack-test (0.6.2)
......@@ -399,7 +412,7 @@ GEM
activesupport (= 4.1.1)
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
raindrops (0.12.0)
raindrops (0.13.0)
rake (10.3.2)
raphael-rails (2.1.2)
rb-fsevent (0.9.3)
......@@ -462,6 +475,9 @@ GEM
sass (~> 3.2.0)
sprockets (~> 2.8, <= 2.11.0)
sprockets-rails (~> 2.0)
sawyer (0.6.0)
addressable (~> 2.3.5)
faraday (~> 0.8, < 0.10)
sdoc (0.3.20)
json (>= 1.1.3)
rdoc (~> 3.10)
......@@ -605,6 +621,7 @@ DEPENDENCIES
better_errors
binding_of_caller
bootstrap-sass (~> 3.0)
browser
capybara (~> 2.2.1)
carrierwave
coffee-rails
......@@ -617,6 +634,7 @@ DEPENDENCIES
devise (= 3.2.4)
devise-async (= 0.9.0)
diffy (~> 3.0.3)
doorkeeper (= 2.1.0)
dropzonejs-rails
email_spec
enumerize
......@@ -631,7 +649,7 @@ DEPENDENCIES
gitlab-grack (~> 2.0.0.pre)
gitlab-linguist (~> 3.0.0)
gitlab_emoji (~> 0.0.1.1)
gitlab_git (= 7.0.0.rc12)
gitlab_git (= 7.0.0.rc14)
gitlab_meta (= 7.0)
gitlab_omniauth-ldap (= 1.2.0)
gollum-lib (~> 3.0.0)
......@@ -660,13 +678,14 @@ DEPENDENCIES
mysql2
newrelic_rpm
nprogress-rails
octokit (= 3.7.0)
omniauth (~> 1.1.3)
omniauth-github
omniauth-google-oauth2
omniauth-kerberos
omniauth-shibboleth
omniauth-twitter
org-ruby (= 0.9.9)
org-ruby (= 0.9.12)
pg
poltergeist (~> 1.5.1)
pry
......@@ -674,6 +693,7 @@ DEPENDENCIES
rack-attack
rack-cors
rack-mini-profiler
rack-oauth2 (~> 1.0.5)
rails (~> 4.1.0)
rails_autolink (~> 1.1)
rails_best_practices
......
web: bundle exec unicorn_rails -p ${PORT:="3000"} -E ${RAILS_ENV:="development"} -c ${UNICORN_CONFIG:="config/unicorn.rb"}
worker: bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,common,default,gitlab_shell
worker: bundle exec sidekiq -q post_receive -q mailer -q system_hook -q project_web_hook -q gitlab_shell -q common -q default
......@@ -11,6 +11,14 @@
- Completely free and open source (MIT Expat license)
- Powered by Ruby on Rails
## Editions
There are two editions of GitLab.
GitLab [Community Edition](https://about.gitlab.com/features/) (CE) is available without any costs under an MIT license.
GitLab Enterprise Edition (EE) includes [extra features](https://about.gitlab.com/features/#compare) that are most useful for organizations with more than 100 users.
To get access to the EE and support please [become a subscriber](https://about.gitlab.com/pricing/).
## Canonical source
- The source of GitLab Community Edition is [hosted on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/) and there are mirrors to make [contributing](CONTRIBUTING.md) as easy as possible.
......
7.6.1
\ No newline at end of file
7.7.1
\ No newline at end of file
......@@ -12,7 +12,7 @@ class @Activities
toggleFilter: (sender) ->
sender.parent().toggleClass "inactive"
sender.parent().toggleClass "active"
event_filters = $.cookie("event_filter")
filter = sender.attr("id").split("_")[0]
if event_filters
......
@Api =
groups_path: "/api/:version/groups.json"
group_path: "/api/:version/groups/:id.json"
users_path: "/api/:version/users.json"
user_path: "/api/:version/users/:id.json"
notes_path: "/api/:version/projects/:id/notes.json"
......@@ -51,6 +53,33 @@
).done (users) ->
callback(users)
group: (group_id, callback) ->
url = Api.buildUrl(Api.group_path)
url = url.replace(':id', group_id)
$.ajax(
url: url
data:
private_token: gon.api_token
dataType: "json"
).done (group) ->
callback(group)
# Return groups list. Filtered by query
# Only active groups retrieved
groups: (query, skip_ldap, callback) ->
url = Api.buildUrl(Api.groups_path)
$.ajax(
url: url
data:
private_token: gon.api_token
search: query
per_page: 20
dataType: "json"
).done (groups) ->
callback(groups)
# Return project users list. Filtered by query
# Only active users retrieved
projectUsers: (project_id, query, callback) ->
......
......@@ -18,7 +18,6 @@
#= require jquery.turbolinks
#= require turbolinks
#= require bootstrap
#= require password_strength
#= require select2
#= require raphael
#= require g.raphael-min
......@@ -56,12 +55,6 @@ window.ajaxGet = (url) ->
window.showAndHide = (selector) ->
window.errorMessage = (message) ->
ehtml = $("<p>")
ehtml.addClass("error_message")
ehtml.html(message)
ehtml
window.split = (val) ->
return val.split( /,\s*/ )
......@@ -120,9 +113,19 @@ window.unbindEvents = ->
$(document).unbind('scroll')
$(document).off('scroll')
window.shiftWindow = ->
scrollBy 0, -50
document.addEventListener("page:fetch", unbindEvents)
# Scroll the window to avoid the topnav bar
# https://github.com/twitter/bootstrap/issues/1768
if location.hash
setTimeout shiftWindow, 1
window.addEventListener "hashchange", shiftWindow
$ ->
# Click a .one_click_select field, select the contents
$(".one_click_select").on 'click', -> $(@).select()
......
......@@ -33,17 +33,20 @@ class Dispatcher
GitLab.GfmAutoComplete.setup()
shortcut_handler = new ShortcutsNavigation()
new ZenMode()
new DropzoneInput($('.issue-form'))
when 'projects:merge_requests:new', 'projects:merge_requests:edit'
GitLab.GfmAutoComplete.setup()
new Diff()
shortcut_handler = new ShortcutsNavigation()
new ZenMode()
new DropzoneInput($('.merge-request-form'))
when 'projects:merge_requests:show'
new Diff()
shortcut_handler = new ShortcutsIssueable()
new ZenMode()
when "projects:merge_requests:diffs"
new Diff()
new ZenMode()
when 'projects:merge_requests:index'
shortcut_handler = new ShortcutsNavigation()
when 'dashboard:show'
......@@ -108,6 +111,7 @@ class Dispatcher
new Wikis()
shortcut_handler = new ShortcutsNavigation()
new ZenMode()
new DropzoneInput($('.wiki-form'))
when 'snippets', 'labels', 'graphs'
shortcut_handler = new ShortcutsNavigation()
when 'team_members', 'deploy_keys', 'hooks', 'services', 'protected_branches'
......
class @DropzoneInput
constructor: (form) ->
Dropzone.autoDiscover = false
alertClass = "alert alert-danger alert-dismissable div-dropzone-alert"
alertAttr = "class=\"close\" data-dismiss=\"alert\"" + "aria-hidden=\"true\""
divHover = "<div class=\"div-dropzone-hover\"></div>"
divSpinner = "<div class=\"div-dropzone-spinner\"></div>"
divAlert = "<div class=\"" + alertClass + "\"></div>"
iconPicture = "<i class=\"fa fa-picture-o div-dropzone-icon\"></i>"
iconSpinner = "<i class=\"fa fa-spinner fa-spin div-dropzone-icon\"></i>"
btnAlert = "<button type=\"button\"" + alertAttr + ">&times;</button>"
project_image_path_upload = window.project_image_path_upload or null
form_textarea = $(form).find("textarea.markdown-area")
form_textarea.wrap "<div class=\"div-dropzone\"></div>"
form_dropzone = $(form).find('.div-dropzone')
form_dropzone.parent().addClass "div-dropzone-wrapper"
form_dropzone.append divHover
$(".div-dropzone-hover").append iconPicture
form_dropzone.append divSpinner
$(".div-dropzone-spinner").append iconSpinner
$(".div-dropzone-spinner").css
"opacity": 0
"display": "none"
# Preview button
$(document).off "click", ".js-md-preview-button"
$(document).on "click", ".js-md-preview-button", (e) ->
###
Shows the Markdown preview.
Lets the server render GFM into Html and displays it.
###
e.preventDefault()
form = $(this).closest("form")
# toggle tabs
form.find(".js-md-write-button").parent().removeClass "active"
form.find(".js-md-preview-button").parent().addClass "active"
# toggle content
form.find(".md-write-holder").hide()
form.find(".md-preview-holder").show()
preview = form.find(".js-md-preview")
mdText = form.find(".markdown-area").val()
if mdText.trim().length is 0
preview.text "Nothing to preview."
else
preview.text "Loading..."
$.get($(this).data("url"),
md_text: mdText
).success (previewData) ->
preview.html previewData
# Write button
$(document).off "click", ".js-md-write-button"
$(document).on "click", ".js-md-write-button", (e) ->
###
Shows the Markdown textarea.
###
e.preventDefault()
form = $(this).closest("form")
# toggle tabs
form.find(".js-md-write-button").parent().addClass "active"
form.find(".js-md-preview-button").parent().removeClass "active"
# toggle content
form.find(".md-write-holder").show()
form.find(".md-preview-holder").hide()
dropzone = form_dropzone.dropzone(
url: project_image_path_upload
dictDefaultMessage: ""
clickable: true
paramName: "markdown_img"
maxFilesize: 10
uploadMultiple: false
acceptedFiles: "image/jpg,image/jpeg,image/gif,image/png"
headers:
"X-CSRF-Token": $("meta[name=\"csrf-token\"]").attr("content")
previewContainer: false
processing: ->
$(".div-dropzone-alert").alert "close"
dragover: ->
form_textarea.addClass "div-dropzone-focus"
form.find(".div-dropzone-hover").css "opacity", 0.7
return
dragleave: ->
form_textarea.removeClass "div-dropzone-focus"
form.find(".div-dropzone-hover").css "opacity", 0
return
drop: ->
form_textarea.removeClass "div-dropzone-focus"
form.find(".div-dropzone-hover").css "opacity", 0
form_textarea.focus()
return
success: (header, response) ->
child = $(dropzone[0]).children("textarea")
$(child).val $(child).val() + formatLink(response.link) + "\n"
return
error: (temp, errorMessage) ->
checkIfMsgExists = $(".error-alert").children().length
if checkIfMsgExists is 0
$(".error-alert").append divAlert
$(".div-dropzone-alert").append btnAlert + errorMessage
return
sending: ->
form_dropzone.find(".div-dropzone-spinner").css
"opacity": 0.7
"display": "inherit"
return
complete: ->
$(".dz-preview").remove()
$(".markdown-area").trigger "input"
$(".div-dropzone-spinner").css
"opacity": 0
"display": "none"
return
)
child = $(dropzone[0]).children("textarea")
formatLink = (str) ->
"![" + str.alt + "](" + str.url + ")"
handlePaste = (e) ->
e.preventDefault()
my_event = e.originalEvent
if my_event.clipboardData and my_event.clipboardData.items
processItem(my_event)
processItem = (e) ->
image = isImage(e)
if image
filename = getFilename(e) or "image.png"
text = "{{" + filename + "}}"
pasteText(text)
uploadFile image.getAsFile(), filename
else
text = e.clipboardData.getData("text/plain")
pasteText(text)
isImage = (data) ->
i = 0
while i < data.clipboardData.items.length
item = data.clipboardData.items[i]
if item.type.indexOf("image") isnt -1
return item
i++
return false
pasteText = (text) ->
caretStart = $(child)[0].selectionStart
caretEnd = $(child)[0].selectionEnd
textEnd = $(child).val().length
beforeSelection = $(child).val().substring 0, caretStart
afterSelection = $(child).val().substring caretEnd, textEnd
$(child).val beforeSelection + text + afterSelection
form_textarea.trigger "input"
getFilename = (e) ->
if window.clipboardData and window.clipboardData.getData
value = window.clipboardData.getData("Text")
else if e.clipboardData and e.clipboardData.getData
value = e.clipboardData.getData("text/plain")
value = value.split("\r")
value.first()
uploadFile = (item, filename) ->
formData = new FormData()
formData.append "markdown_img", item, filename
$.ajax
url: project_image_path_upload
type: "POST"
data: formData
dataType: "json"
processData: false
contentType: false
headers:
"X-CSRF-Token": $("meta[name=\"csrf-token\"]").attr("content")
beforeSend: ->
showSpinner()
closeAlertMessage()
success: (e, textStatus, response) ->
insertToTextArea(filename, formatLink(response.responseJSON.link))
error: (response) ->
showError(response.responseJSON.message)
complete: ->
closeSpinner()
insertToTextArea = (filename, url) ->
$(child).val (index, val) ->
val.replace("{{" + filename + "}}", url + "\n")
appendToTextArea = (url) ->
$(child).val (index, val) ->
val + url + "\n"
showSpinner = (e) ->
form.find(".div-dropzone-spinner").css
"opacity": 0.7
"display": "inherit"
closeSpinner = ->
form.find(".div-dropzone-spinner").css
"opacity": 0
"display": "none"
showError = (message) ->
checkIfMsgExists = $(".error-alert").children().length
if checkIfMsgExists is 0
$(".error-alert").append divAlert
$(".div-dropzone-alert").append btnAlert + message
closeAlertMessage = ->
form.find(".div-dropzone-alert").alert "close"
form.find(".markdown-selector").click (e) ->
e.preventDefault()
$(@).closest('.gfm-form').find('.div-dropzone').click()
return
formatLink: (str) ->
"![" + str.alt + "](" + str.url + ")"
class @GroupsSelect
constructor: ->
$('.ajax-groups-select').each (i, select) =>
skip_ldap = $(select).hasClass('skip_ldap')
$(select).select2
placeholder: "Search for a group"
multiple: $(select).hasClass('multiselect')
minimumInputLength: 0
query: (query) ->
Api.groups query.term, skip_ldap, (groups) ->
data = { results: groups }
query.callback(data)
initSelection: (element, callback) ->
id = $(element).val()
if id isnt ""
Api.group(id, callback)
formatResult: (args...) =>
@formatResult(args...)
formatSelection: (args...) =>
@formatSelection(args...)
dropdownCssClass: "ajax-groups-dropdown"
escapeMarkup: (m) -> # we do not want to escape markup since we are displaying html in results
m
formatResult: (group) ->
if group.avatar_url
avatar = group.avatar_url
else
avatar = gon.default_avatar_url
"<div class='group-result'>
<div class='group-name'>#{group.name}</div>
<div class='group-path'>#{group.path}</div>
</div>"
formatSelection: (group) ->
group.name
class @Issue
constructor: ->
$('.edit-issue.inline-update input[type="submit"]').hide()
$(".issue-box .inline-update").on "change", "select", ->
$(".context .inline-update").on "change", "select", ->
$(this).submit()
$(".issue-box .inline-update").on "change", "#issue_assignee_id", ->
$(".context .inline-update").on "change", "#issue_assignee_id", ->
$(this).submit()
if $("a.btn-close").length
......
formatLink = (str) ->
"![" + str.alt + "](" + str.url + ")"
$(document).ready ->
alertClass = "alert alert-danger alert-dismissable div-dropzone-alert"
alertAttr = "class=\"close\" data-dismiss=\"alert\"" + "aria-hidden=\"true\""
divHover = "<div class=\"div-dropzone-hover\"></div>"
divSpinner = "<div class=\"div-dropzone-spinner\"></div>"
divAlert = "<div class=\"" + alertClass + "\"></div>"
iconPicture = "<i class=\"fa fa-picture-o div-dropzone-icon\"></i>"
iconSpinner = "<i class=\"fa fa-spinner fa-spin div-dropzone-icon\"></i>"
btnAlert = "<button type=\"button\"" + alertAttr + ">&times;</button>"
project_image_path_upload = window.project_image_path_upload or null
$("textarea.markdown-area").wrap "<div class=\"div-dropzone\"></div>"
$(".div-dropzone").parent().addClass "div-dropzone-wrapper"
$(".div-dropzone").append divHover
$(".div-dropzone-hover").append iconPicture
$(".div-dropzone").append divSpinner
$(".div-dropzone-spinner").append iconSpinner
$(".div-dropzone-spinner").css
"opacity": 0
"display": "none"
# Preview button
$(document).off "click", ".js-md-preview-button"
$(document).on "click", ".js-md-preview-button", (e) ->
###
Shows the Markdown preview.
Lets the server render GFM into Html and displays it.
###
e.preventDefault()
form = $(this).closest("form")
# toggle tabs
form.find(".js-md-write-button").parent().removeClass "active"
form.find(".js-md-preview-button").parent().addClass "active"
# toggle content
form.find(".md-write-holder").hide()
form.find(".md-preview-holder").show()
preview = form.find(".js-md-preview")
mdText = form.find(".markdown-area").val()
if mdText.trim().length is 0
preview.text "Nothing to preview."
else
preview.text "Loading..."
$.get($(this).data("url"),
md_text: mdText
).success (previewData) ->
preview.html previewData
# Write button
$(document).off "click", ".js-md-write-button"
$(document).on "click", ".js-md-write-button", (e) ->
###
Shows the Markdown textarea.
###
e.preventDefault()
form = $(this).closest("form")
# toggle tabs
form.find(".js-md-write-button").parent().addClass "active"
form.find(".js-md-preview-button").parent().removeClass "active"
# toggle content
form.find(".md-write-holder").show()
form.find(".md-preview-holder").hide()
dropzone = $(".div-dropzone").dropzone(
url: project_image_path_upload
dictDefaultMessage: ""
clickable: true
paramName: "markdown_img"
maxFilesize: 10
uploadMultiple: false
acceptedFiles: "image/jpg,image/jpeg,image/gif,image/png"
headers:
"X-CSRF-Token": $("meta[name=\"csrf-token\"]").attr("content")
previewContainer: false
processing: ->
$(".div-dropzone-alert").alert "close"
dragover: ->
$(".div-dropzone > textarea").addClass "div-dropzone-focus"
$(".div-dropzone-hover").css "opacity", 0.7
return
dragleave: ->
$(".div-dropzone > textarea").removeClass "div-dropzone-focus"
$(".div-dropzone-hover").css "opacity", 0
return
drop: ->
$(".div-dropzone > textarea").removeClass "div-dropzone-focus"
$(".div-dropzone-hover").css "opacity", 0
$(".div-dropzone > textarea").focus()
return
success: (header, response) ->
child = $(dropzone[0]).children("textarea")
$(child).val $(child).val() + formatLink(response.link) + "\n"
return
error: (temp, errorMessage) ->
checkIfMsgExists = $(".error-alert").children().length
if checkIfMsgExists is 0
$(".error-alert").append divAlert
$(".div-dropzone-alert").append btnAlert + errorMessage
return
sending: ->
$(".div-dropzone-spinner").css
"opacity": 0.7
"display": "inherit"
return
complete: ->
$(".dz-preview").remove()
$(".markdown-area").trigger "input"
$(".div-dropzone-spinner").css
"opacity": 0
"display": "none"
return
)
child = $(dropzone[0]).children("textarea")
formatLink = (str) ->
"![" + str.alt + "](" + str.url + ")"
handlePaste = (e) ->
e.preventDefault()
my_event = e.originalEvent
if my_event.clipboardData and my_event.clipboardData.items
processItem(my_event)
processItem = (e) ->
image = isImage(e)
if image
filename = getFilename(e) or "image.png"
text = "{{" + filename + "}}"
pasteText(text)
uploadFile image.getAsFile(), filename
else
text = e.clipboardData.getData("text/plain")
pasteText(text)
isImage = (data) ->
i = 0
while i < data.clipboardData.items.length
item = data.clipboardData.items[i]
if item.type.indexOf("image") isnt -1
return item
i++
return false
pasteText = (text) ->
caretStart = $(child)[0].selectionStart
caretEnd = $(child)[0].selectionEnd
textEnd = $(child).val().length
beforeSelection = $(child).val().substring 0, caretStart
afterSelection = $(child).val().substring caretEnd, textEnd
$(child).val beforeSelection + text + afterSelection
$(".markdown-area").trigger "input"
getFilename = (e) ->
if window.clipboardData and window.clipboardData.getData
value = window.clipboardData.getData("Text")
else if e.clipboardData and e.clipboardData.getData
value = e.clipboardData.getData("text/plain")
value = value.split("\r")
value.first()
uploadFile = (item, filename) ->
formData = new FormData()
formData.append "markdown_img", item, filename
$.ajax
url: project_image_path_upload
type: "POST"
data: formData
dataType: "json"
processData: false
contentType: false
headers:
"X-CSRF-Token": $("meta[name=\"csrf-token\"]").attr("content")
beforeSend: ->
showSpinner()
closeAlertMessage()
success: (e, textStatus, response) ->
insertToTextArea(filename, formatLink(response.responseJSON.link))
error: (response) ->
showError(response.responseJSON.message)
complete: ->
closeSpinner()
insertToTextArea = (filename, url) ->
$(child).val (index, val) ->
val.replace("{{" + filename + "}}", url + "\n")
appendToTextArea = (url) ->
$(child).val (index, val) ->
val + url + "\n"
showSpinner = (e) ->
$(".div-dropzone-spinner").css
"opacity": 0.7
"display": "inherit"
closeSpinner = ->
$(".div-dropzone-spinner").css
"opacity": 0
"display": "none"
showError = (message) ->
checkIfMsgExists = $(".error-alert").children().length
if checkIfMsgExists is 0
$(".error-alert").append divAlert
$(".div-dropzone-alert").append btnAlert + message
closeAlertMessage = ->
$(".div-dropzone-alert").alert "close"
$(".markdown-selector").click (e) ->
e.preventDefault()
$(@).closest('.gfm-form').find('.div-dropzone').click()
return
return
......@@ -26,9 +26,9 @@ class @MergeRequest
initContextWidget: ->
$('.edit-merge_request.inline-update input[type="submit"]').hide()
$(".issue-box .inline-update").on "change", "select", ->
$(".context .inline-update").on "change", "select", ->
$(this).submit()
$(".issue-box .inline-update").on "change", "#merge_request_assignee_id", ->
$(".context .inline-update").on "change", "#merge_request_assignee_id", ->
$(this).submit()
initMergeWidget: ->
......@@ -89,6 +89,9 @@ class @MergeRequest
this.$('.merge-request-tabs .diffs-tab').addClass 'active'
this.loadDiff() unless @diffs_loaded
this.$('.diffs').show()
when 'commits'
this.$('.merge-request-tabs .commits-tab').addClass 'active'
this.$('.commits').show()
else
this.$('.merge-request-tabs .notes-tab').addClass 'active'
this.$('.notes').show()
......@@ -132,3 +135,16 @@ class @MergeRequest
this.$('.automerge_widget').hide()
this.$('.merge-in-progress').hide()
this.$('.automerge_widget.already_cannot_be_merged').show()
mergeInProgress: ->
$.ajax
type: 'GET'
url: $('.merge-request').data('url')
success: (data) =>
switch data.state
when 'merged'
location.reload()
else
setTimeout(merge_request.mergeInProgress, 3000)
dataType: 'json'
......@@ -219,6 +219,7 @@ class @Notes
setupNoteForm: (form) ->
disableButtonIfEmptyField form.find(".js-note-text"), form.find(".js-comment-button")
form.removeClass "js-new-note-form"
form.find('.div-dropzone').remove()
# setup preview buttons
form.find(".js-md-write-button, .js-md-preview-button").tooltip placement: "left"
......@@ -233,6 +234,7 @@ class @Notes
# remove notify commit author checkbox for non-commit notes
form.find(".js-notify-commit-author").remove() if form.find("#note_noteable_type").val() isnt "Commit"
GitLab.GfmAutoComplete.setup()
new DropzoneInput(form)
form.show()
......@@ -259,8 +261,10 @@ class @Notes
Updates the current note field.
###
updateNote: (xhr, note, status) =>
note_li = $("#note_" + note.id)
note_li = $(".note-row-" + note.id)
note_li.replaceWith(note.html)
note_li.find('.note-edit-form').hide()
note_li.find('.note-text').show()
code = "#note_" + note.id + " .highlight pre code"
$(code).each (i, e) ->
hljs.highlightBlock(e)
......@@ -276,11 +280,19 @@ class @Notes
e.preventDefault()
note = $(this).closest(".note")
note.find(".note-text").hide()
note.find(".note-header").hide()
base_form = note.find(".note-edit-form")
form = base_form.clone().insertAfter(base_form)
form.addClass('current-note-edit-form')
form.find('.div-dropzone').remove()
# Show the attachment delete link
note.find(".js-note-attachment-delete").show()
# Setup markdown form
GitLab.GfmAutoComplete.setup()
form = note.find(".note-edit-form")
new DropzoneInput(form)
form.show()
textarea = form.find("textarea")
textarea.focus()
......@@ -295,8 +307,8 @@ class @Notes
e.preventDefault()
note = $(this).closest(".note")
note.find(".note-text").show()
note.find(".js-note-attachment-delete").hide()
note.find(".note-edit-form").hide()
note.find(".note-header").show()
note.find(".current-note-edit-form").remove()
###
Called in response to deleting a note of any kind.
......@@ -375,7 +387,7 @@ class @Notes
###
addDiffNote: (e) =>
e.preventDefault()
link = e.target
link = e.currentTarget
form = $(".js-new-note-form")
row = $(link).closest("tr")
nextRow = row.next()
......
#= require pwstrength-bootstrap-1.2.2
overwritten_messages =
wordSimilarToUsername: "Your password should not contain your username"
overwritten_rules =
wordSequences: false
options =
showProgressBar: false
showVerdicts: false
showPopover: true
showErrors: true
showStatus: true
errorMessages: overwritten_messages
$(document).ready ->
profileOptions = {}
profileOptions.ui = options
profileOptions.rules =
activated: overwritten_rules
deviseOptions = {}
deviseOptions.common =
usernameField: "#user_username"
deviseOptions.ui = options
deviseOptions.rules =
activated: overwritten_rules
$("#user_password_profile").pwstrength profileOptions
$("#user_password_sign_up").pwstrength deviseOptions
$("#user_password_recover").pwstrength deviseOptions
$ ->
$(":checkbox").change ->
name = $(this).attr("name")
if name == "developers_can_push"
id = $(this).val()
checked = $(this).is(":checked")
url = $(this).data("url")
$.ajax
type: "PUT"
url: url
dataType: "json"
data:
id: id
developers_can_push: checked
success: ->
new Flash("Branch updated.", "notice")
location.reload true
error: ->
new Flash("Failed to update branch!", "alert")
......@@ -46,7 +46,7 @@ class @ContributorsGraph
class @ContributorsMasterGraph extends ContributorsGraph
constructor: (@data) ->
@width = $('.container').width() - 70
@width = $('.container').width() - 345
@height = 200
@x = null
@y = null
......@@ -119,7 +119,7 @@ class @ContributorsMasterGraph extends ContributorsGraph
class @ContributorsAuthorGraph extends ContributorsGraph
constructor: (@data) ->
@width = $('.container').width()/2 - 100
@width = $('.container').width()/2 - 225
@height = 200
@x = null
@y = null
......
......@@ -10,7 +10,15 @@ class @ZenMode
if not @active_checkbox
@scroll_position = window.pageYOffset
$('body').on 'change', '.zennable input[type=checkbox]', (e) =>
$('body').on 'click', '.zen-enter-link', (e) =>
e.preventDefault()
$(e.currentTarget).closest('.zennable').find('.zen-toggle-comment').prop('checked', true)
$('body').on 'click', '.zen-leave-link', (e) =>
e.preventDefault()
$(e.currentTarget).closest('.zennable').find('.zen-toggle-comment').prop('checked', false)
$('body').on 'change', '.zen-toggle-comment', (e) =>
checkbox = e.currentTarget
if checkbox.checked
# Disable other keyboard shortcuts in ZEN mode
......@@ -32,8 +40,6 @@ class @ZenMode
@active_zen_area = @active_checkbox.parent().find('textarea')
@active_zen_area.focus()
window.location.hash = ZenMode.fullscreen_prefix + @active_checkbox.prop('id')
# Disable dropzone in ZEN mode
Dropzone.forElement('.div-dropzone').disable()
exitZenMode: =>
if @active_zen_area isnt null
......
......@@ -207,24 +207,16 @@ li.note {
}
}
.no-ssh-key-message {
padding: 10px 0;
background: #C67;
margin: 0;
color: #FFF;
margin-top: -1px;
.browser-alert {
padding: 10px;
text-align: center;
background: #C67;
color: #fff;
font-weight: bold;
a {
color: #fff;
text-decoration: underline;
}
.links-xs {
text-align: center;
font-size: 16px;
padding: 5px;
}
}
.warning_message {
......@@ -281,10 +273,6 @@ img.emoji {
height: 220px;
}
.navless-container {
margin-top: 20px;
}
.description-block {
@extend .light-well;
@extend .light;
......@@ -300,11 +288,17 @@ table {
.dashboard-intro-icon {
float: left;
text-align: center;
font-size: 32px;
color: #AAA;
padding: 5px 0;
width: 50px;
min-height: 100px;
width: 60px;
}
.dashboard-intro-text {
display: inline-block;
margin-left: -60px;
padding-left: 60px;
width: 100%;
}
.broadcast-message {
......@@ -355,3 +349,9 @@ table {
.task-status {
margin-left: 10px;
}
#nprogress .spinner {
top: auto !important;
bottom: 20px !important;
left: 20px !important;
}
......@@ -31,7 +31,12 @@ fieldset legend {
margin-bottom: 18px;
background-color: whitesmoke;
border-top: 1px solid #e5e5e5;
padding-left: 17%;
}
@media (min-width: $screen-sm-min) {
.form-actions {
padding-left: 17%;
}
}
label {
......@@ -88,139 +93,7 @@ label {
@include box-shadow(none);
}
.issuable-description {
.issuable-description,
.wiki-content {
margin-top: 35px;
}
.zennable {
position: relative;
input {
display: none;
}
.collapse {
display: none;
opacity: 0.5;
&:before {
content: '\f066';
font-family: FontAwesome;
color: #000;
font-size: 28px;
position: relative;
padding: 30px 40px 0 0;
}
&:hover {
opacity: 0.8;
}
}
.expand {
opacity: 0.5;
&:before {
content: '\f065';
font-family: FontAwesome;
color: #000;
font-size: 14px;
line-height: 14px;
padding-right: 20px;
position: relative;
vertical-align: middle;
}
&:hover {
opacity: 0.8;
}
}
input:checked ~ .zen-backdrop .expand {
display: none;
}
input:checked ~ .zen-backdrop .collapse {
display: block;
position: absolute;
top: 0;
}
label {
position: absolute;
top: -26px;
right: 0;
font-variant: small-caps;
text-transform: uppercase;
font-size: 10px;
padding: 4px;
font-weight: 500;
letter-spacing: 1px;
&:before {
display: inline-block;
width: 10px;
height: 14px;
}
}
input:checked ~ .zen-backdrop {
background-color: white;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 1031;
textarea {
border: none;
box-shadow: none;
border-radius: 0;
color: #000;
font-size: 20px;
line-height: 26px;
padding: 30px;
display: block;
outline: none;
resize: none;
height: 100vh;
max-width: 900px;
margin: 0 auto;
}
}
.zen-backdrop textarea::-webkit-input-placeholder {
color: white;
}
.zen-backdrop textarea:-moz-placeholder {
color: white;
}
.zen-backdrop textarea::-moz-placeholder {
color: white;
}
.zen-backdrop textarea:-ms-input-placeholder {
color: white;
}
input:checked ~ .zen-backdrop textarea::-webkit-input-placeholder {
color: #999;
}
input:checked ~ .zen-backdrop textarea:-moz-placeholder {
color: #999;
opacity: 1;
}
input:checked ~ .zen-backdrop textarea::-moz-placeholder {
color: #999;
opacity: 1;
}
input:checked ~ .zen-backdrop textarea:-ms-input-placeholder {
color: #999;
}
}
/**
* Issue box:
* Huge block (one per page) for storing title, descripion and other information.
* Issue box for showing Open/Closed state:
* Used for Issue#show page, MergeRequest#show page etc
*
* CLasses:
* .issue-box - Regular box
*/
.issue-box {
color: #555;
margin:20px 0;
background: $box_bg;
@include box-shadow(0 1px 1px rgba(0, 0, 0, 0.09));
display: inline-block;
padding: 7px 13px;
font-weight: normal;
margin-right: 5px;
&.issue-box-closed {
.state {
background-color: #F3CECE;
border-color: $border_danger;
}
.state-label {
background-color: $bg_danger;
color: #FFF;
}
background-color: $bg_danger;
color: #FFF;
}
&.issue-box-merged {
.state {
background-color: #B7CEE7;
border-color: $border_primary;
}
.state-label {
background-color: $bg_primary;
color: #FFF;
}
background-color: $bg_primary;
color: #FFF;
}
&.issue-box-open {
.state {
background-color: #D6F1D7;
border-color: $bg_success;
}
.state-label {
background-color: $bg_success;
color: #FFF;
}
background-color: $bg_success;
color: #FFF;
}
&.issue-box-expired {
.state {
background-color: #EEE9B3;
border-color: #faebcc;
}
.state-label {
background: #cea61b;
color: #FFF;
}
}
.control-group {
margin-bottom: 0;
}
.state {
background-color: #f9f9f9;
}
.title {
font-size: 28px;
font-weight: normal;
line-height: 1.5;
margin: 0;
color: #333;
padding: 10px 15px;
}
.context {
border: none;
border-top: 1px solid #eee;
padding: 10px 15px;
// Reset text align for children
.text-right > * { text-align: left; }
@media (max-width: $screen-xs-max) {
// Don't right align on mobile
.text-right { text-align: left; }
.row .col-md-6 {
padding-top: 5px;
}
}
}
.description {
padding: 0 15px 10px 15px;
code {
white-space: pre-wrap;
}
}
.title, .context, .description {
.clearfix {
margin: 0;
}
}
.state-label {
font-size: 14px;
float: left;
font-weight: bold;
padding: 10px 15px;
}
.cross-project-ref {
float: left;
padding: 10px 15px;
}
.creator {
float: right;
padding: 10px 15px;
a {
text-decoration: underline;
}
background: #cea61b;
color: #FFF;
}
}
......@@ -65,6 +65,7 @@
.edit_note,
.issuable-description,
.milestone-description,
.wiki-content,
.merge-request-form {
.nav-tabs {
margin-bottom: 0;
......
......@@ -116,6 +116,18 @@ select {
}
}
.group-result {
.group-image {
float: left;
}
.group-name {
font-weight: bold;
}
.group-path {
color: #999;
}
}
.user-result {
.user-image {
float: left;
......
table {
&.table {
tr {
td, th {
padding: 8px 10px;
line-height: 20px;
vertical-align: middle;
}
th {
font-weight: normal;
font-size: 15px;
border-bottom: 1px solid #CCC !important;
}
td {
border-color: #F1F1F1 !important;
border-bottom: 1px solid;
}
}
}
}
......@@ -74,6 +74,42 @@
}
}
}
.system-note .timeline-entry-inner {
.timeline-icon {
background: none;
margin-left: 12px;
margin-top: 0;
@include box-shadow(none);
span {
margin: 0 2px;
font-size: 16px;
color: #eeeeee;
}
}
.timeline-content {
background: none;
margin-left: 45px;
padding: 0px 15px;
&:after { border: 0; }
.note-header {
span { font-size: 12px; }
.avatar {
margin-right: 5px;
}
}
.note-text {
font-size: 12px;
margin-left: 20px;
}
}
}
}
@media (max-width: $screen-xs-max) {
......
.zennable {
position: relative;
input {
display: none;
}
.zen-enter-link {
color: #888;
position: absolute;
top: -26px;
right: 4px;
}
.zen-leave-link {
display: none;
color: #888;
position: absolute;
top: 10px;
right: 10px;
padding: 5px;
font-size: 36px;
&:hover {
color: #111;
}
}
input:checked ~ .zen-backdrop .zen-enter-link {
display: none;
}
input:checked ~ .zen-backdrop .zen-leave-link {
display: block;
position: absolute;
top: 0;
}
input:checked ~ .zen-backdrop {
background-color: white;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 1031;
textarea {
border: none;
box-shadow: none;
border-radius: 0;
color: #000;
font-size: 20px;
line-height: 26px;
padding: 30px;
display: block;
outline: none;
resize: none;
height: 100vh;
max-width: 900px;
margin: 0 auto;
}
}
.zen-backdrop textarea::-webkit-input-placeholder {
color: white;
}
.zen-backdrop textarea:-moz-placeholder {
color: white;
}
.zen-backdrop textarea::-moz-placeholder {
color: white;
}
.zen-backdrop textarea:-ms-input-placeholder {
color: white;
}
input:checked ~ .zen-backdrop textarea::-webkit-input-placeholder {
color: #999;
}
input:checked ~ .zen-backdrop textarea:-moz-placeholder {
color: #999;
opacity: 1;
}
input:checked ~ .zen-backdrop textarea::-moz-placeholder {
color: #999;
opacity: 1;
}
input:checked ~ .zen-backdrop textarea:-ms-input-placeholder {
color: #999;
}
}
......@@ -148,6 +148,10 @@ $list-group-active-bg: $bg_primary;
color: #666;
}
.nav-compact > li > a {
padding: 6px 12px;
}
.nav-small > li > a {
padding: 3px 5px;
font-size: 12px;
......
......@@ -2,10 +2,10 @@ html {
overflow-y: scroll;
&.touch .tooltip { display: none !important; }
}
body {
padding-bottom: 20px;
body {
padding-top: 47px;
}
}
.container {
......@@ -17,3 +17,6 @@ body {
margin: 0 0;
}
.navless-container {
margin-top: 30px;
}
......@@ -65,10 +65,6 @@
max-width: 100%;
}
*:first-child {
margin-top: 0;
}
code { padding: 0 4px; }
h1 {
......
......@@ -44,6 +44,6 @@ $added: #63c363;
$deleted: #f77;
/**
*
* NProgress customize
*/
$nprogress-color: #3498db;
$nprogress-color: #c0392b;
......@@ -23,20 +23,6 @@
}
}
.dashboard {
.dash-filter {
width: 205px;
float: left;
height: inherit;
}
}
@media (max-width: 1200px) {
.dashboard .dash-filter {
width: 140px;
}
}
.dash-sidebar-tabs {
margin-bottom: 2px;
border: none;
......
......@@ -140,43 +140,17 @@
}
}
/**
* Event filter
*
*/
.event_filter {
position: absolute;
width: 40px;
margin-left: -55px;
.filter_icon {
a {
text-align:center;
background: $bg_primary;
margin-bottom: 10px;
float: left;
padding: 9px 6px;
font-size: 18px;
width: 40px;
color: #FFF;
@include border-radius(3px);
}
&.inactive {
a {
color: #DDD;
background: #f9f9f9;
}
}
}
}
/*
* Last push widget
*/
.event-last-push {
overflow: auto;
.event-last-push-text {
@include str-truncated(75%);
@include str-truncated(100%);
float:left;
margin-right: -150px;
padding-right: 150px;
line-height: 24px;
}
}
......@@ -203,3 +177,7 @@
}
}
}
.event_filter li a {
padding: 5px 10px;
}
......@@ -4,9 +4,11 @@
*/
header {
&.navbar-gitlab {
z-index: 100;
margin-bottom: 0;
min-height: 40px;
border: none;
width: 100%;
.navbar-inner {
filter: none;
......@@ -52,8 +54,6 @@ header {
border-width: 0;
font-size: 18px;
.app_logo { margin-left: -15px; }
.title {
@include str-truncated(70%);
}
......@@ -84,7 +84,10 @@ header {
}
}
z-index: 10;
.container {
width: 100% !important;
padding-left: 0px;
}
/**
*
......@@ -232,21 +235,6 @@ header {
color: #fff;
}
}
.app_logo {
.separator {
margin-left: 0;
margin-right: 0;
}
}
.separator {
float: left;
height: 46px;
width: 2px;
margin-left: 10px;
margin-right: 10px;
}
}
.search .search-input {
......
......@@ -162,3 +162,7 @@ form.edit-issue {
}
}
}
.issue-title {
margin-top: 0;
}
/* Login Page */
.login-page {
h1 {
font-size: 3em;
font-weight: 200;
.container {
max-width: 960px;
}
.login-box{
padding: 0 15px;
.navbar-gitlab .container {
max-width: none;
}
.login-heading h3 {
font-weight: 300;
line-height: 2;
}
.brand-holder {
font-size: 18px;
line-height: 1.5;
.login-footer {
margin-top: 10px;
p {
color: #888;
}
.btn {
padding: 12px !important;
@extend .btn-block;
h1:first-child {
font-weight: normal;
margin-bottom: 30px;
}
}
.brand-image {
img {
max-width: 100%;
margin-bottom: 20px;
margin-bottom: 30px;
}
&.default-brand-image {
margin: 0 80px;
a {
font-weight: bold;
}
}
.login-logo {
margin: 10px 0 30px 0;
display: block;
.login-box{
background: #fafafa;
border-radius: 10px;
box-shadow: 0 0px 2px #CCC;
padding: 15px;
.login-heading h3 {
font-weight: 300;
line-height: 1.5;
margin: 0;
display: none;
}
.login-footer {
margin-top: 10px;
}
a.forgot {
float: right;
padding-top: 6px
}
.nav .active a {
background: transparent;
}
}
.form-control {
background-color: #F5F5F5;
font-size: 16px;
padding: 14px 10px;
font-size: 14px;
padding: 10px 8px;
width: 100%;
height: auto;
......@@ -68,11 +86,6 @@
}
}
.login-box a.forgot {
float: right;
padding-top: 6px
}
.devise-errors {
h2 {
font-size: 14px;
......@@ -80,7 +93,19 @@
}
}
.brand-holder {
border-right: 1px solid #EEE;
.remember-me {
margin-top: -10px;
label {
font-weight: normal;
}
}
}
@media (max-width: $screen-xs-max) {
.login-page {
.col-sm-5.pull-right {
float: none !important;
}
}
}
.markdown-area {
background: #FFF;
border: 1px solid #ddd;
min-height: 100px;
padding: 5px;
font-size: 14px;
box-shadow: none;
width: 100%;
}
......@@ -11,25 +11,40 @@
}
}
.accept-group {
label {
margin: 5px;
.accept-merge-holder {
margin-top: 5px;
.accept-action {
display: inline-block;
.accept_merge_request {
padding: 10px 20px;
}
}
.accept-control {
display: inline-block;
margin-left: 20px;
padding: 10px 0;
line-height: 20px;
font-weight: bold;
.remove_source_checkbox {
margin: 0;
}
}
}
}
.merge-request .merge-request-tabs{
border-bottom: 2px solid $border_primary;
margin: 20px 0;
@media(min-width: $screen-sm-max) {
.merge-request .merge-request-tabs{
margin: 20px 0;
li {
a {
padding: 15px 40px;
font-size: 14px;
margin-bottom: -2px;
border-bottom: 2px solid $border_primary;
@include border-radius(0px);
li {
a {
padding: 15px 40px;
font-size: 14px;
}
}
}
}
......@@ -106,6 +121,7 @@
.mr-state-widget {
background: $box_bg;
margin-bottom: 20px;
color: #666;
@include box-shadow(0 1px 1px rgba(0, 0, 0, 0.09));
.ci_widget {
......@@ -150,7 +166,6 @@
padding: 10px 15px;
h4 {
font-size: 20px;
font-weight: normal;
}
......@@ -172,7 +187,3 @@
.merge-request-show-labels .label {
padding: 6px 10px;
}
.mr-commits .commit {
padding: 10px 15px;
}
.main-nav {
background: #f5f5f5;
margin: 20px 0;
margin-top: 0;
padding-top: 4px;
border-bottom: 1px solid #E9E9E9;
ul {
padding: 0;
margin: auto;
.count {
font-weight: normal;
display: inline-block;
height: 15px;
padding: 1px 6px;
height: auto;
font-size: 0.82em;
line-height: 14px;
text-align: center;
color: #777;
background: #eee;
@include border-radius(8px);
}
.label {
background: $hover;
text-shadow: none;
color: $style_color;
}
li {
list-style-type: none;
margin: 0;
display: table-cell;
width: 1%;
&.active {
a {
color: $link_color;
font-weight: bold;
border-bottom: 3px solid $link_color;
}
}
&:hover {
a {
color: $link_hover_color;
border-bottom: 3px solid $link_hover_color;
}
}
}
a {
display: block;
text-align: center;
font-weight: bold;
height: 42px;
line-height: 39px;
color: #777;
text-shadow: 0 1px 1px white;
text-decoration: none;
overflow: hidden;
margin-bottom: -1px;
}
}
@media (max-width: $screen-xs-max) {
font-size: 18px;
margin: 0;
max-height: none;
&, .container {
padding: 0;
border-top: 0;
}
ul {
height: auto;
li {
display: list-item;
width: auto;
padding: 5px 0;
&.active {
background-color: $link_hover_color;
a {
color: #fff;
font-weight: normal;
text-shadow: none;
border: none;
&:after { display: none; }
}
}
}
}
}
}
.page-with-sidebar {
background: #F5F5F5;
.sidebar-wrapper {
position: fixed;
top: 0;
left: 0;
height: 100%;
border-right: 1px solid #EAEAEA;
}
}
.sidebar-wrapper {
z-index: 99;
overflow-y: auto;
background: #F5F5F5;
}
.content-wrapper {
width: 100%;
padding: 15px;
background: #FFF;
}
.nav-sidebar {
margin: 0;
list-style: none;
&.navbar-collapse {
padding: 0px !important;
}
}
.nav-sidebar li a .count {
float: right;
background: #eee;
padding: 0px 8px;
@include border-radius(6px);
}
.nav-sidebar li {
&.active a {
color: #111;
background: #EEE;
font-weight: bold;
&.no-highlight {
background: none;
}
i {
color: #444;
}
}
}
.nav-sidebar li {
&.separate-item {
border-top: 1px solid #ddd;
padding-top: 10px;
margin-top: 10px;
}
a {
color: #555;
display: block;
text-decoration: none;
padding: 6px 15px;
font-size: 13px;
line-height: 20px;
text-shadow: 0 1px 2px #FFF;
padding-left: 20px;
&:hover {
text-decoration: none;
color: #333;
background: #DDD;
}
&:active, &:focus {
text-decoration: none;
}
i {
width: 20px;
color: #888;
margin-right: 23px;
}
}
}
.sidebar-subnav {
margin-left: 0px;
padding-left: 0px;
li {
list-style: none;
}
}
@mixin expanded-sidebar {
.page-with-sidebar {
padding-left: 250px;
}
.sidebar-wrapper {
width: 250px;
.nav-sidebar {
margin-top: 20px;
position: fixed;
top: 45px;
width: 250px;
}
}
.content-wrapper {
padding: 20px;
}
}
@mixin folded-sidebar {
.page-with-sidebar {
padding-left: 50px;
}
.sidebar-wrapper {
width: 52px;
overflow-x: hidden;
.nav-sidebar {
margin-top: 20px;
position: absolute;
top: 45px;
width: 52px;
li a {
padding-left: 18px;
font-size: 14px;
padding: 10px 15px;
text-align: center;
& > span {
display: none;
}
}
}
}
}
@media (max-width: $screen-md-max) {
@include folded-sidebar;
}
@media(min-width: $screen-md-max) {
@include expanded-sidebar;
}
/**
* Note Form
*/
.comment-btn {
@extend .btn-create;
}
.reply-btn {
@extend .btn-primary;
}
.diff-file .diff-content {
tr.line_holder:hover {
&> td.line_content {
background: $hover !important;
border-color: darken($hover, 10%) !important;
}
&> td.new_line,
&> td.old_line {
background: darken($hover, 4%) !important;
border-color: darken($hover, 10%) !important;
}
}
tr.line_holder:hover > td .line_note_link {
opacity: 1.0;
filter: alpha(opacity=100);
}
}
.diff-file,
.discussion {
.new_note {
margin: 0;
border: none;
}
}
.new_note {
display: none;
}
.new_note, .edit_note {
.buttons {
float: left;
margin-top: 8px;
}
.clearfix {
margin-bottom: 0;
}
.note-preview-holder {
> p {
overflow-x: auto;
}
}
.note_text {
width: 100%;
}
}
/* loading indicator */
.notes-busy {
margin: 18px;
}
.note-image-attach {
@extend .col-md-4;
@extend .thumbnail;
margin-left: 45px;
float: none;
}
.common-note-form {
margin: 0;
background: #F9F9F9;
padding: 5px;
border: 1px solid #DDD;
}
.note-form-actions {
background: #F9F9F9;
height: 45px;
.note-form-option {
margin-top: 8px;
margin-left: 30px;
@extend .pull-left;
}
.js-notify-commit-author {
float: left;
}
.write-preview-btn {
// makes the "absolute" position for links relative to this
position: relative;
// preview/edit buttons
> a {
position: absolute;
right: 5px;
top: 8px;
}
}
}
.note-edit-form {
display: none;
font-size: 13px;
.form-actions {
padding-left: 20px;
.btn-save {
float: left;
}
.note-form-option {
float: left;
padding: 2px 0 0 25px;
}
}
}
.js-note-attachment-delete {
display: none;
}
.parallel-comment {
padding: 6px;
}
.error-alert > .alert {
margin-top: 5px;
margin-bottom: 5px;
}
.discussion-body,
.diff-file {
.notes .note {
border-color: #ddd;
padding: 10px 15px;
}
.discussion-reply-holder {
background: #f9f9f9;
padding: 10px 15px;
border-top: 1px solid #DDD;
}
}
.discussion-notes-count {
font-size: 16px;
}
.edit_note {
.markdown-area {
min-height: 140px;
}
.note-form-actions {
background: transparent;
}
}
.comment-hints {
color: #999;
background: #FFF;
padding: 5px;
margin-top: -7px;
border: 1px solid #DDD;
font-size: 13px;
}
......@@ -62,6 +62,7 @@ ul.notes {
}
.note-body {
@include md-typography;
overflow: auto;
}
.note-header {
padding-bottom: 3px;
......@@ -155,19 +156,26 @@ ul.notes {
}
.add-diff-note {
background: image-url("diff_note_add.png") no-repeat left 0;
border: none;
height: 22px;
margin-left: -65px;
margin-top: -4px;
@include border-radius(40px);
background: #FFF;
padding: 4px;
font-size: 16px;
color: $link_color;
margin-left: -60px;
position: absolute;
width: 22px;
z-index: 10;
transition: all 0.2s ease;
// "hide" it by default
opacity: 0.0;
filter: alpha(opacity=0);
&:hover {
font-size: 24px;
background: $bg_primary;
color: #FFF;
@include show-add-diff-note;
}
}
......@@ -182,169 +190,3 @@ ul.notes {
}
}
/**
* Note Form
*/
.comment-btn {
@extend .btn-create;
}
.reply-btn {
@extend .btn-primary;
}
.diff-file .diff-content {
tr.line_holder:hover {
&> td.line_content {
background: $hover !important;
border-color: darken($hover, 10%) !important;
}
&> td.new_line,
&> td.old_line {
background: darken($hover, 4%) !important;
border-color: darken($hover, 10%) !important;
}
}
tr.line_holder:hover > td .line_note_link {
opacity: 1.0;
filter: alpha(opacity=100);
}
}
.diff-file,
.discussion {
.new_note {
margin: 0;
border: none;
}
}
.new_note {
display: none;
.buttons {
float: left;
margin-top: 8px;
}
.clearfix {
margin-bottom: 0;
}
.note_text {
background: #FFF;
border: 1px solid #ddd;
min-height: 100px;
padding: 5px;
font-size: 14px;
box-shadow: none;
}
.note-preview-holder {
> p {
overflow-x: auto;
}
}
.note_text {
width: 100%;
}
}
/* loading indicator */
.notes-busy {
margin: 18px;
}
.note-image-attach {
@extend .col-md-4;
@extend .thumbnail;
margin-left: 45px;
float: none;
}
.common-note-form {
margin: 0;
background: #F9F9F9;
padding: 5px;
border: 1px solid #DDD;
}
.note-form-actions {
background: #F9F9F9;
height: 45px;
.note-form-option {
margin-top: 8px;
margin-left: 30px;
@extend .pull-left;
}
.js-notify-commit-author {
float: left;
}
.write-preview-btn {
// makes the "absolute" position for links relative to this
position: relative;
// preview/edit buttons
> a {
position: absolute;
right: 5px;
top: 8px;
}
}
}
.note-edit-form {
display: none;
.note_text {
border: 1px solid #DDD;
box-shadow: none;
font-size: 14px;
height: 80px;
width: 100%;
}
.form-actions {
padding-left: 20px;
.btn-save {
float: left;
}
.note-form-option {
float: left;
padding: 2px 0 0 25px;
}
}
}
.js-note-attachment-delete {
display: none;
}
.parallel-comment {
padding: 6px;
}
.error-alert > .alert {
margin-top: 5px;
margin-bottom: 5px;
}
.discussion-body,
.diff-file {
.notes .note {
border-color: #ddd;
padding: 10px 15px;
}
.discussion-reply-holder {
background: #f9f9f9;
padding: 10px 15px;
border-top: 1px solid #DDD;
}
}
.discussion-notes-count {
font-size: 16px;
}
......@@ -111,20 +111,3 @@
height: 50px;
}
}
//CSS for password-strength indicator
#password-strength {
margin-bottom: 0;
}
.has-success input {
background-color: #D6F1D7 !important;
}
.has-error input {
background-color: #F3CECE !important;
}
.has-warning input {
background-color: #FFE9A4 !important;
}
......@@ -308,3 +308,10 @@ ul.nav.nav-projects-tabs {
display: none;
}
}
table.table.protected-branches-list tr.no-border {
th, td {
border: 0;
}
}
......@@ -17,19 +17,6 @@
@include border-radius(0);
tr {
td, th {
padding: 8px 10px;
line-height: 20px;
}
th {
font-weight: normal;
font-size: 15px;
border-bottom: 1px solid #CCC !important;
}
td {
border-color: #F1F1F1 !important;
border-bottom: 1px solid;
}
&:hover {
td {
background: $hover;
......
......@@ -37,13 +37,3 @@
margin: 0 8px;
}
.votes-holder {
float: right;
width: 250px;
@media (max-width: $screen-xs-max) {
width: 100%;
margin-top: 5px;
margin-bottom: 10px;
}
}
......@@ -9,17 +9,15 @@
.navbar-inner {
background: #F1F1F1;
border-bottom: 1px solid #DDD;
.app_logo {
background-color: #DDD;
}
.nav > li > a {
color: $style_color;
}
.separator {
background: #F9F9F9;
border-left: 1px solid #DDD;
}
}
}
}
.main-nav {
background: #FFF;
}
}
......@@ -23,9 +23,8 @@
background-color: #436;
}
}
.separator {
background: #436;
border-left: 1px solid #659;
.app_logo {
background-color: #325;
}
.nav > li > a {
color: #98C;
......
......@@ -23,9 +23,8 @@
background-color: #272727;
}
}
.separator {
background: #272727;
border-left: 1px solid #474747;
.app_logo {
background-color: #222;
}
}
}
......
......@@ -23,9 +23,8 @@
background-color: #373D47;
}
}
.separator {
background: #373D47;
border-left: 1px solid #575D67;
.app_logo {
background-color: #24272D;
}
.nav > li > a {
color: #979DA7;
......
......@@ -23,9 +23,8 @@
background-color: #018865;
}
}
.separator {
background: #018865;
border-left: 1px solid #11A885;
.app_logo {
background-color: #017855;
}
.nav > li > a {
color: #ADC;
......
class Admin::ApplicationSettingsController < Admin::ApplicationController
before_filter :set_application_setting
def show
end
def update
if @application_setting.update_attributes(application_setting_params)
redirect_to admin_application_settings_path,
notice: 'Application settings saved successfully'
else
render :show
end
end
private
def set_application_setting
@application_setting = ApplicationSetting.current
end
def application_setting_params
params.require(:application_setting).permit(
:default_projects_limit,
:signup_enabled,
:signin_enabled,
:gravatar_enabled,
:sign_in_text,
:home_page_url
)
end
end
class Admin::ApplicationsController < Admin::ApplicationController
before_action :set_application, only: [:show, :edit, :update, :destroy]
def index
@applications = Doorkeeper::Application.where("owner_id IS NULL")
end
def show
end
def new
@application = Doorkeeper::Application.new
end
def edit
end
def create
@application = Doorkeeper::Application.new(application_params)
if @application.save
flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create])
redirect_to admin_application_url(@application)
else
render :new
end
end
def update
if @application.update(application_params)
redirect_to admin_application_path(@application), notice: 'Application was successfully updated.'
else
render :edit
end
end
def destroy
@application.destroy
redirect_to admin_applications_url, notice: 'Application was successfully destroyed.'
end
private
def set_application
@application = Doorkeeper::Application.where("owner_id IS NULL").find(params[:id])
end
# Only allow a trusted parameter "white list" through.
def application_params
params[:doorkeeper_application].permit(:name, :redirect_uri)
end
end
......@@ -21,7 +21,7 @@ class Admin::GroupsController < Admin::ApplicationController
def create
@group = Group.new(group_params)
@group.path = @group.name.dup.parameterize if @group.name
@group.name = @group.path.dup unless @group.name
if @group.save
@group.add_owner(current_user)
......
class Admin::KeysController < Admin::ApplicationController
before_filter :user, only: [:show, :destroy]
def show
@key = user.keys.find(params[:id])
respond_to do |format|
format.html
format.js { render nothing: true }
end
end
def destroy
key = user.keys.find(params[:id])
respond_to do |format|
if key.destroy
format.html { redirect_to [:admin, user], notice: 'User key was successfully removed.' }
else
format.html { redirect_to [:admin, user], alert: 'Failed to remove user key.' }
end
end
end
protected
def user
@user ||= User.find_by!(username: params[:user_id])
end
def key_params
params.require(:user_id, :id)
end
end
......@@ -11,6 +11,7 @@ class Admin::UsersController < Admin::ApplicationController
def show
@personal_projects = user.personal_projects
@joined_projects = user.projects.joined(@user)
@keys = user.keys.order('id DESC')
end
def new
......@@ -118,7 +119,7 @@ class Admin::UsersController < Admin::ApplicationController
:email, :remember_me, :bio, :name, :username,
:skype, :linkedin, :twitter, :website_url, :color_scheme_id, :theme_id, :force_random_password,
:extern_uid, :provider, :password_expires_at, :avatar, :hide_no_ssh_key,
:projects_limit, :can_create_group, :admin
:projects_limit, :can_create_group, :admin, :key_id
)
end
end
require 'gon'
class ApplicationController < ActionController::Base
include Gitlab::CurrentSettings
before_filter :authenticate_user_from_token!
before_filter :authenticate_user!
before_filter :reject_blocked!
......@@ -13,7 +15,7 @@ class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
helper_method :abilities, :can?
helper_method :abilities, :can?, :current_application_settings
rescue_from Encoding::CompatibilityError do |exception|
log_exception(exception)
......@@ -46,6 +48,17 @@ class ApplicationController < ActionController::Base
end
end
def authenticate_user!(*args)
# If user is not signe-in and tries to access root_path - redirect him to landing page
if current_application_settings.home_page_url.present?
if current_user.nil? && controller_name == 'dashboard' && action_name == 'show'
redirect_to current_application_settings.home_page_url and return
end
end
super(*args)
end
def log_exception(exception)
application_trace = ActionDispatch::ExceptionWrapper.new(env, exception).application_trace
application_trace.map!{ |t| " #{t}\n" }
......@@ -239,4 +252,63 @@ class ApplicationController < ActionController::Base
redirect_to profile_path, notice: 'Please complete your profile with email address' and return
end
end
def set_filters_params
params[:sort] ||= 'newest'
params[:scope] = 'all' if params[:scope].blank?
params[:state] = 'opened' if params[:state].blank?
@filter_params = params.dup
if @project
@filter_params[:project_id] = @project.id
elsif @group
@filter_params[:group_id] = @group.id
else
# TODO: this filter ignore issues/mr created in public or
# internal repos where you are not a member. Enable this filter
# or improve current implementation to filter only issues you
# created or assigned or mentioned
#@filter_params[:authorized_only] = true
end
@filter_params
end
def set_filter_values(collection)
assignee_id = @filter_params[:assignee_id]
author_id = @filter_params[:author_id]
milestone_id = @filter_params[:milestone_id]
@sort = @filter_params[:sort].try(:humanize)
@assignees = User.where(id: collection.pluck(:assignee_id))
@authors = User.where(id: collection.pluck(:author_id))
@milestones = Milestone.where(id: collection.pluck(:milestone_id))
if assignee_id.present? && !assignee_id.to_i.zero?
@assignee = @assignees.find_by(id: assignee_id)
end
if author_id.present? && !author_id.to_i.zero?
@author = @authors.find_by(id: author_id)
end
if milestone_id.present? && !milestone_id.to_i.zero?
@milestone = @milestones.find_by(id: milestone_id)
end
end
def get_issues_collection
set_filters_params
issues = IssuesFinder.new.execute(current_user, @filter_params)
set_filter_values(issues)
issues
end
def get_merge_requests_collection
set_filters_params
merge_requests = MergeRequestsFinder.new.execute(current_user, @filter_params)
set_filter_values(merge_requests)
merge_requests
end
end
......@@ -3,8 +3,6 @@ class DashboardController < ApplicationController
before_filter :load_projects, except: [:projects]
before_filter :event_filter, only: :show
before_filter :default_filter, only: [:issues, :merge_requests]
def show
# Fetch only 30 projects.
......@@ -55,13 +53,13 @@ class DashboardController < ApplicationController
end
def merge_requests
@merge_requests = MergeRequestsFinder.new.execute(current_user, params)
@merge_requests = get_merge_requests_collection
@merge_requests = @merge_requests.page(params[:page]).per(20)
@merge_requests = @merge_requests.preload(:author, :target_project)
end
def issues
@issues = IssuesFinder.new.execute(current_user, params)
@issues = get_issues_collection
@issues = @issues.page(params[:page]).per(20)
@issues = @issues.preload(:author, :project)
......@@ -76,10 +74,4 @@ class DashboardController < ApplicationController
def load_projects
@projects = current_user.authorized_projects.sorted_by_activity.non_archived
end
def default_filter
params[:scope] = 'assigned-to-me' if params[:scope].blank?
params[:state] = 'opened' if params[:state].blank?
params[:authorized_only] = true
end
end
class GithubImportsController < ApplicationController
before_filter :github_auth, except: :callback
rescue_from Octokit::Unauthorized, with: :github_unauthorized
def callback
token = client.auth_code.get_token(params[:code]).token
current_user.github_access_token = token
current_user.save
redirect_to status_github_import_url
end
def status
@repos = octo_client.repos
octo_client.orgs.each do |org|
@repos += octo_client.repos(org.login)
end
@already_added_projects = current_user.created_projects.where(import_type: "github")
already_added_projects_names = @already_added_projects.pluck(:import_source)
@repos.reject!{|repo| already_added_projects_names.include? repo.full_name}
end
def create
@repo_id = params[:repo_id].to_i
repo = octo_client.repo(@repo_id)
target_namespace = params[:new_namespace].presence || repo.owner.login
existing_namespace = Namespace.find_by("path = ? OR name = ?", target_namespace, target_namespace)
if existing_namespace
if existing_namespace.owner == current_user
namespace = existing_namespace
else
@already_been_taken = true
@target_namespace = target_namespace
@project_name = repo.name
render and return
end
else
namespace = Group.create(name: target_namespace, path: target_namespace, owner: current_user)
namespace.add_owner(current_user)
end
Gitlab::Github::ProjectCreator.new(repo, namespace, current_user).execute
end
private
def client
@client ||= Gitlab::Github::Client.new.client
end
def octo_client
Octokit.auto_paginate = true
@octo_client ||= Octokit::Client.new(:access_token => current_user.github_access_token)
end
def github_auth
if current_user.github_access_token.blank?
go_to_gihub_for_permissions
end
end
def go_to_gihub_for_permissions
redirect_to client.auth_code.authorize_url({
redirect_uri: callback_github_import_url,
scope: "repo, user, user:email"
})
end
def github_unauthorized
go_to_gihub_for_permissions
end
end
......@@ -11,8 +11,6 @@ class GroupsController < ApplicationController
# Load group projects
before_filter :load_projects, except: [:new, :create, :projects, :edit, :update]
before_filter :default_filter, only: [:issues, :merge_requests]
layout :determine_layout
before_filter :set_title, only: [:new, :create]
......@@ -23,7 +21,7 @@ class GroupsController < ApplicationController
def create
@group = Group.new(group_params)
@group.path = @group.name.dup.parameterize if @group.name
@group.name = @group.path.dup unless @group.name
if @group.save
@group.add_owner(current_user)
......@@ -47,13 +45,13 @@ class GroupsController < ApplicationController
end
def merge_requests
@merge_requests = MergeRequestsFinder.new.execute(current_user, params)
@merge_requests = get_merge_requests_collection
@merge_requests = @merge_requests.page(params[:page]).per(20)
@merge_requests = @merge_requests.preload(:author, :target_project)
end
def issues
@issues = IssuesFinder.new.execute(current_user, params)
@issues = get_issues_collection
@issues = @issues.page(params[:page]).per(20)
@issues = @issues.preload(:author, :project)
......@@ -148,18 +146,6 @@ class GroupsController < ApplicationController
end
end
def default_filter
if params[:scope].blank?
if current_user
params[:scope] = 'assigned-to-me'
else
params[:scope] = 'all'
end
end
params[:state] = 'opened' if params[:state].blank?
params[:group_id] = @group.id
end
def group_params
params.require(:group).permit(:name, :description, :path, :avatar)
end
......
class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
before_filter :authenticate_user!
layout "profile"
def index
head :forbidden and return
end
def create
@application = Doorkeeper::Application.new(application_params)
@application.owner = current_user
if @application.save
flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create])
redirect_to oauth_application_url(@application)
else
render :new
end
end
def destroy
if @application.destroy
flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :destroy])
end
redirect_to applications_profile_url
end
private
def set_application
@application = current_user.oauth_applications.find(params[:id])
end
rescue_from ActiveRecord::RecordNotFound do |exception|
render "errors/not_found", layout: "errors", status: 404
end
end
class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
before_filter :authenticate_resource_owner!
layout "profile"
def new
if pre_auth.authorizable?
if skip_authorization? || matching_token?
auth = authorization.authorize
redirect_to auth.redirect_uri
else
render "doorkeeper/authorizations/new"
end
else
render "doorkeeper/authorizations/error"
end
end
# TODO: Handle raise invalid authorization
def create
redirect_or_render authorization.authorize
end
def destroy
redirect_or_render authorization.deny
end
private
def matching_token?
Doorkeeper::AccessToken.matching_token_for(pre_auth.client,
current_resource_owner.id,
pre_auth.scopes)
end
def redirect_or_render(auth)
if auth.redirectable?
redirect_to auth.redirect_uri
else
render json: auth.body, status: auth.status
end
end
def pre_auth
@pre_auth ||=
Doorkeeper::OAuth::PreAuthorization.new(Doorkeeper.configuration,
server.client_via_uid,
params)
end
def authorization
@authorization ||= strategy.request
end
def strategy
@strategy ||= server.authorization_request(pre_auth.response_type)
end
end
class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicationsController
layout "profile"
def destroy
Doorkeeper::AccessToken.revoke_all_for(params[:id], current_resource_owner)
redirect_to applications_profile_url, notice: I18n.t(:notice, scope: [:doorkeeper, :flash, :authorized_applications, :destroy])
end
end
......@@ -65,7 +65,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
redirect_to omniauth_error_path(oauth['provider'], error: error_message) and return
end
end
rescue ForbiddenAction => e
rescue Gitlab::OAuth::ForbiddenAction => e
flash[:notice] = e.message
redirect_to new_user_session_path
end
......
......@@ -13,6 +13,11 @@ class ProfilesController < ApplicationController
def design
end
def applications
@applications = current_user.oauth_applications
@authorized_tokens = current_user.oauth_authorized_tokens
end
def update
user_params.except!(:email) if @user.ldap_user?
......
......@@ -29,31 +29,4 @@ class Projects::ApplicationController < ApplicationController
redirect_to project_tree_path(@project, @ref), notice: "This action is not allowed unless you are on top of a branch"
end
end
def set_filter_variables(collection)
params[:sort] ||= 'newest'
params[:scope] = 'all' if params[:scope].blank?
params[:state] = 'opened' if params[:state].blank?
@sort = params[:sort].humanize
assignee_id = params[:assignee_id]
author_id = params[:author_id]
milestone_id = params[:milestone_id]
if assignee_id.present? && !assignee_id.to_i.zero?
@assignee = @project.team.find(assignee_id)
end
if author_id.present? && !author_id.to_i.zero?
@author = @project.team.find(assignee_id)
end
if milestone_id.present? && !milestone_id.to_i.zero?
@milestone = @project.milestones.find(milestone_id)
end
@assignees = User.where(id: collection.pluck(:assignee_id))
@authors = User.where(id: collection.pluck(:author_id))
end
end
......@@ -18,9 +18,7 @@ class Projects::IssuesController < Projects::ApplicationController
def index
terms = params['issue_search']
set_filter_variables(@project.issues)
@issues = IssuesFinder.new.execute(current_user, params.merge(project_id: @project.id))
@issues = get_issues_collection
@issues = @issues.full_search(terms) if terms.present?
@issues = @issues.page(params[:page]).per(20)
......
......@@ -17,9 +17,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
before_filter :authorize_modify_merge_request!, only: [:close, :edit, :update, :sort]
def index
set_filter_variables(@project.merge_requests)
@merge_requests = MergeRequestsFinder.new.execute(current_user, params.merge(project_id: @project.id))
@merge_requests = get_merge_requests_collection
@merge_requests = @merge_requests.page(params[:page]).per(20)
end
......@@ -29,6 +27,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
respond_to do |format|
format.html
format.json { render json: @merge_request }
format.diff { render text: @merge_request.to_diff(current_user) }
format.patch { render text: @merge_request.to_patch(current_user) }
end
......@@ -106,15 +105,15 @@ class Projects::MergeRequestsController < Projects::ApplicationController
if @merge_request.unchecked?
@merge_request.check_if_can_be_merged
end
render json: {merge_status: @merge_request.merge_status_name}
render json: { merge_status: @merge_request.merge_status_name }
end
def automerge
return access_denied! unless allowed_to_merge?
if @merge_request.open? && @merge_request.can_be_merged?
@merge_request.should_remove_source_branch = params[:should_remove_source_branch]
@merge_request.automerge!(current_user, params[:commit_message])
AutoMergeWorker.perform_async(@merge_request.id, current_user.id, params)
@status = true
else
@status = false
......
......@@ -11,7 +11,7 @@ class Projects::MilestonesController < Projects::ApplicationController
respond_to :html
def index
@milestones = case params[:f]
@milestones = case params[:state]
when 'all'; @project.milestones.order("state, due_date DESC")
when 'closed'; @project.milestones.closed.order("due_date DESC")
else @project.milestones.active.order("due_date ASC")
......
......@@ -15,6 +15,24 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController
redirect_to project_protected_branches_path(@project)
end
def update
protected_branch = @project.protected_branches.find(params[:id])
if protected_branch &&
protected_branch.update_attributes(
developers_can_push: params[:developers_can_push]
)
respond_to do |format|
format.json { render :json => protected_branch, status: :ok }
end
else
respond_to do |format|
format.json { render json: protected_branch.errors, status: :unprocessable_entity }
end
end
end
def destroy
@project.protected_branches.find(params[:id]).destroy
......@@ -27,6 +45,6 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController
private
def protected_branch_params
params.require(:protected_branch).permit(:name)
params.require(:protected_branch).permit(:name, :developers_can_push)
end
end
......@@ -24,8 +24,7 @@ class Projects::ServicesController < Projects::ApplicationController
end
def test
data = GitPushService.new.sample_data(project, current_user)
data = Gitlab::PushDataBuilder.build_sample(project, current_user)
@service.execute(data)
redirect_to :back
......@@ -42,7 +41,7 @@ class Projects::ServicesController < Projects::ApplicationController
:title, :token, :type, :active, :api_key, :subdomain,
:room, :recipients, :project_url, :webhook,
:user_key, :device, :priority, :sound, :bamboo_url, :username, :password,
:build_key, :server
:build_key, :server, :teamcity_url, :build_type
)
end
end
......@@ -13,6 +13,7 @@ class Projects::TagsController < Projects::ApplicationController
def create
result = CreateTagService.new(@project, current_user).
execute(params[:tag_name], params[:ref], params[:message])
if result[:status] == :success
@tag = result[:tag]
redirect_to project_tags_path(@project)
......
......@@ -44,6 +44,9 @@ class ProjectsController < ApplicationController
def transfer
::Projects::TransferService.new(project, current_user, project_params).execute
if @project.errors[:namespace_id].present?
flash[:alert] = @project.errors[:namespace_id].first
end
end
def show
......@@ -98,11 +101,20 @@ class ProjectsController < ApplicationController
def autocomplete_sources
note_type = params['type']
note_id = params['type_id']
autocomplete = ::Projects::AutocompleteService.new(@project)
participants = ::Projects::ParticipantsService.new(@project).execute(note_type, note_id)
emojis = Emoji.names.map do |e|
{
name: e,
path: view_context.image_url("emoji/#{e}.png")
}
end
@suggestions = {
emojis: Emoji.names.map { |e| { name: e, path: view_context.image_url("emoji/#{e}.png") } },
issues: @project.issues.select([:iid, :title, :description]),
mergerequests: @project.merge_requests.select([:iid, :title, :description]),
emojis: emojis,
issues: autocomplete.issues,
mergerequests: autocomplete.merge_requests,
members: participants
}
......
......@@ -26,7 +26,9 @@ class RegistrationsController < Devise::RegistrationsController
private
def signup_enabled?
redirect_to new_user_session_path unless Gitlab.config.gitlab.signup_enabled
unless current_application_settings.signup_enabled?
redirect_to(new_user_session_path)
end
end
def sign_up_params
......
class SessionsController < Devise::SessionsController
def new
redirect_path = if request.referer.present? && (params['redirect_to_referer'] == 'yes')
referer_uri = URI(request.referer)
if referer_uri.host == Gitlab.config.gitlab.host
referer_uri.path
else
request.fullpath
end
else
request.fullpath
end
redirect_path =
if request.referer.present? && (params['redirect_to_referer'] == 'yes')
referer_uri = URI(request.referer)
if referer_uri.host == Gitlab.config.gitlab.host
referer_uri.path
else
request.fullpath
end
else
request.fullpath
end
# Prevent a 'you are already signed in' message directly after signing:
# we should never redirect to '/users/sign_in' after signing in successfully.
......
......@@ -114,6 +114,10 @@ module ApplicationHelper
Gitlab::Theme.css_class_by_id(current_user.try(:theme_id))
end
def theme_type
Gitlab::Theme.type_css_class_by_id(current_user.try(:theme_id))
end
def user_color_scheme_class
COLOR_SCHEMES[current_user.try(:color_scheme_id)] if defined?(current_user)
end
......@@ -271,4 +275,34 @@ module ApplicationHelper
def promo_url
'https://' + promo_host
end
def page_filter_path(options={})
exist_opts = {
state: params[:state],
scope: params[:scope],
label_name: params[:label_name],
milestone_id: params[:milestone_id],
assignee_id: params[:assignee_id],
author_id: params[:author_id],
sort: params[:sort],
}
options = exist_opts.merge(options)
path = request.path
path << "?#{options.to_param}"
path
end
def outdated_browser?
browser.ie? && browser.version.to_i < 10
end
def path_to_key(key, admin = false)
if admin
admin_user_key_path(@user, key)
else
profile_key_path(key)
end
end
end
module ApplicationSettingsHelper
def gravatar_enabled?
current_application_settings.gravatar_enabled?
end
def signup_enabled?
current_application_settings.signup_enabled?
end
def signin_enabled?
current_application_settings.signin_enabled?
end
def extra_sign_in_text
current_application_settings.sign_in_text
end
end
module DashboardHelper
def filter_path(entity, options={})
exist_opts = {
state: params[:state],
scope: params[:scope],
project_id: params[:project_id],
}
options = exist_opts.merge(options)
path = request.path
path << "?#{options.to_param}"
path
end
def entities_per_project(project, entity)
case entity.to_sym
when :issue then @issues.where(project_id: project.id)
when :merge_request then @merge_requests.where(target_project_id: project.id)
else
[]
end.count
end
def projects_dashboard_filter_path(options={})
exist_opts = {
sort: params[:sort],
scope: params[:scope],
group: params[:group],
tag: params[:tag],
visibility_level: params[:visibility_level],
}
options = exist_opts.merge(options)
......@@ -36,32 +15,11 @@ module DashboardHelper
path
end
def assigned_entities_count(current_user, entity, scope = nil)
items = current_user.send('assigned_' + entity.pluralize)
get_count(items, scope)
def assigned_issues_dashboard_path
issues_dashboard_path(assignee_id: current_user.id)
end
def authored_entities_count(current_user, entity, scope = nil)
items = current_user.send(entity.pluralize)
get_count(items, scope)
end
def authorized_entities_count(current_user, entity, scope = nil)
items = entity.classify.constantize
get_count(items, scope, true, current_user)
end
protected
def get_count(items, scope, get_authorized = false, current_user = nil)
items = items.opened
if scope.kind_of?(Group)
items = items.of_group(scope)
elsif scope.kind_of?(Project)
items = items.of_projects(scope)
elsif get_authorized
items = items.of_projects(current_user.authorized_projects)
end
items.count
def assigned_mrs_dashboard_path
merge_requests_dashboard_path(assignee_id: current_user.id)
end
end
......@@ -117,4 +117,22 @@ module DiffHelper
[comments_left, comments_right]
end
def inline_diff_btn
params_copy = params.dup
params_copy[:view] = 'inline'
link_to url_for(params_copy), id: "commit-diff-viewtype", class: (params[:view] != 'parallel' ? 'btn active' : 'btn') do
'Inline'
end
end
def parallel_diff_btn
params_copy = params.dup
params_copy[:view] = 'parallel'
link_to url_for(params_copy), id: "commit-diff-viewtype", class: (params[:view] == 'parallel' ? 'btn active' : 'btn') do
'Side-by-side'
end
end
end
......@@ -21,15 +21,14 @@ module EventsHelper
def event_filter_link(key, tooltip)
key = key.to_s
inactive = if @event_filter.active? key
nil
else
'inactive'
end
active = if @event_filter.active? key
'active'
end
content_tag :div, class: "filter_icon #{inactive}" do
content_tag :li, class: "filter_icon #{active}" do
link_to request.path, class: 'has_tooltip event_filter_link', id: "#{key}_event_filter", 'data-original-title' => tooltip do
content_tag :i, nil, class: icon_for_event[key]
content_tag(:i, nil, class: icon_for_event[key]) +
content_tag(:span, ' ' + tooltip)
end
end
end
......
......@@ -6,7 +6,7 @@ module GroupsHelper
def leave_group_message(group)
"Are you sure you want to leave \"#{group}\" group?"
end
def should_user_see_group_roles?(user, group)
if user
user.is_admin? || group.members.exists?(user_id: user.id)
......@@ -33,15 +33,11 @@ module GroupsHelper
title
end
def group_filter_path(entity, options={})
exist_opts = {
status: params[:status]
}
options = exist_opts.merge(options)
path = request.path
path << "?#{options.to_param}"
path
def group_settings_page?
if current_controller?('groups')
current_action?('edit') || current_action?('projects')
else
false
end
end
end
module MilestonesHelper
def milestones_filter_path(opts = {})
if @project
project_milestones_path(@project, opts)
elsif @group
group_milestones_path(@group, opts)
end
end
end
......@@ -52,8 +52,11 @@ module NotesHelper
discussion_id: discussion_id
}
button_tag '', class: 'btn add-diff-note js-add-diff-note-button',
data: data, title: 'Add a comment to this line'
button_tag(class: 'btn add-diff-note js-add-diff-note-button',
data: data,
title: 'Add a comment to this line') do
content_tag :i, nil, class: 'fa fa-comment-o'
end
end
def link_to_reply_diff(note)
......
......@@ -14,6 +14,6 @@ module ProfileHelper
end
def show_profile_remove_tab?
gitlab_config.signup_enabled
signup_enabled?
end
end
......@@ -68,48 +68,6 @@ module ProjectsHelper
project_nav_tabs.include? name
end
def selected_label?(label_name)
params[:label_name].to_s.split(',').include?(label_name)
end
def labels_filter_path(label_name)
label_name =
if selected_label?(label_name)
params[:label_name].split(',').reject { |l| l == label_name }.join(',')
elsif params[:label_name].present?
"#{params[:label_name]},#{label_name}"
else
label_name
end
project_filter_path(label_name: label_name)
end
def label_filter_class(label_name)
if selected_label?(label_name)
'label-filter-item active'
else
'label-filter-item light'
end
end
def project_filter_path(options={})
exist_opts = {
state: params[:state],
scope: params[:scope],
label_name: params[:label_name],
milestone_id: params[:milestone_id],
assignee_id: params[:assignee_id],
sort: params[:sort],
}
options = exist_opts.merge(options)
path = request.path
path << "?#{options.to_param}"
path
end
def project_active_milestones
@project.milestones.active.order("due_date, title ASC")
end
......@@ -279,4 +237,25 @@ module ProjectsHelper
result.password = '*****' if result.password.present?
result
end
def project_wiki_path_with_version(proj, page, version, is_newest)
url_params = is_newest ? {} : { version_id: version }
project_wiki_path(proj, page, url_params)
end
def project_status_css_class(status)
case status
when "started"
"active"
when "failed"
"danger"
when "finished"
"success"
end
end
def github_import_enabled?
Gitlab.config.omniauth.enabled && enabled_oauth_providers.include?(:github)
end
end
......@@ -17,4 +17,13 @@ module SelectsHelper
project_id = opts[:project_id] || @project.id
hidden_field_tag(id, value, class: css_class, 'data-placeholder' => placeholder, 'data-project-id' => project_id)
end
def groups_select_tag(id, opts = {})
css_class = "ajax-groups-select "
css_class << "multiselect " if opts[:multiple]
css_class << (opts[:class] || '')
value = opts[:selected] || ''
hidden_field_tag(id, value, class: css_class)
end
end
......@@ -28,6 +28,10 @@ module TabHelper
# nav_link(controller: [:tree, :refs]) { "Hello" }
# # => '<li class="active">Hello</li>'
#
# # Several paths
# nav_link(path: ['tree#show', 'profile#show']) { "Hello" }
# # => '<li class="active">Hello</li>'
#
# # Shorthand path
# nav_link(path: 'tree#show') { "Hello" }
# # => '<li class="active">Hello</li>'
......@@ -38,25 +42,7 @@ module TabHelper
#
# Returns a list item element String
def nav_link(options = {}, &block)
if path = options.delete(:path)
if path.respond_to?(:each)
c = path.map { |p| p.split('#').first }
a = path.map { |p| p.split('#').last }
else
c, a, _ = path.split('#')
end
else
c = options.delete(:controller)
a = options.delete(:action)
end
if c && a
# When given both options, make sure BOTH are active
klass = current_controller?(*c) && current_action?(*a) ? 'active' : ''
else
# Otherwise check EITHER option
klass = current_controller?(*c) || current_action?(*a) ? 'active' : ''
end
klass = active_nav_link?(options) ? 'active' : ''
# Add our custom class into the html_options, which may or may not exist
# and which may or may not already have a :class key
......@@ -72,6 +58,34 @@ module TabHelper
end
end
def active_nav_link?(options)
if path = options.delete(:path)
unless path.respond_to?(:each)
path = [path]
end
path.any? do |single_path|
current_path?(single_path)
end
else
c = options.delete(:controller)
a = options.delete(:action)
if c && a
# When given both options, make sure BOTH are true
current_controller?(*c) && current_action?(*a)
else
# Otherwise check EITHER option
current_controller?(*c) || current_action?(*a)
end
end
end
def current_path?(path)
c, a, _ = path.split('#')
current_controller?(c) && current_action?(a)
end
def project_tab_class
return "active" if current_page?(controller: "/projects", action: :edit, id: @project)
......
......@@ -53,13 +53,41 @@ module TreeHelper
File.join(*args)
end
def allowed_tree_edit?
return false unless @repository.branch_names.include?(@ref)
def allowed_tree_edit?(project = nil, ref = nil)
project ||= @project
ref ||= @ref
return false unless project.repository.branch_names.include?(ref)
if @project.protected_branch? @ref
can?(current_user, :push_code_to_protected_branches, @project)
if project.protected_branch? ref
can?(current_user, :push_code_to_protected_branches, project)
else
can?(current_user, :push_code, @project)
can?(current_user, :push_code, project)
end
end
def edit_blob_link(project, ref, path, options = {})
blob =
begin
project.repository.blob_at(ref, path)
rescue
nil
end
if blob && blob.text?
text = 'Edit'
after = options[:after] || ''
from_mr = options[:from_merge_request_id]
link_opts = {}
link_opts[:from_merge_request_id] = from_mr if from_mr
cls = 'btn btn-small'
if allowed_tree_edit?(project, ref)
link_to text, project_edit_tree_path(project, tree_join(ref, path),
link_opts), class: cls
else
content_tag :span, text, class: cls + ' disabled'
end + after.html_safe
else
''
end
end
......@@ -85,6 +113,16 @@ module TreeHelper
tree_join(@ref, file)
end
# returns the relative path of the first subdir that doesn't have only one directory descendand
def flatten_tree(tree)
subtree = Gitlab::Git::Tree.where(@repository, @commit.id, tree.path)
if subtree.count == 1 && subtree.first.dir?
return tree_join(tree.name, flatten_tree(subtree.first))
else
return tree.name
end
end
def leave_edit_message
"Leave edit mode?\nAll unsaved changes will be lost."
end
......
class ApplicationSetting < ActiveRecord::Base
validates :home_page_url, allow_blank: true,
format: { with: URI::regexp(%w(http https)), message: "should be a valid url" },
if: :home_page_url_column_exist
def self.current
ApplicationSetting.last
end
def self.create_from_defaults
create(
default_projects_limit: Settings.gitlab['default_projects_limit'],
signup_enabled: Settings.gitlab['signup_enabled'],
signin_enabled: Settings.gitlab['signin_enabled'],
gravatar_enabled: Settings.gravatar['enabled'],
sign_in_text: Settings.extra['sign_in_text'],
)
end
def home_page_url_column_exist
ActiveRecord::Base.connection.column_exists?(:application_settings, :home_page_url)
end
end
......@@ -174,7 +174,7 @@ class Event < ActiveRecord::Base
def valid_push?
data[:ref] && ref_name.present?
rescue => ex
rescue
false
end
......
......@@ -21,7 +21,7 @@ class Group < Namespace
has_many :users, through: :group_members
validate :avatar_type, if: ->(user) { user.avatar_changed? }
validates :avatar, file_size: { maximum: 100.kilobytes.to_i }
validates :avatar, file_size: { maximum: 200.kilobytes.to_i }
mount_uploader :avatar, AttachmentUploader
......
......@@ -48,7 +48,7 @@ class WebHook < ActiveRecord::Base
verify: false,
basic_auth: auth)
end
rescue SocketError, Errno::ECONNREFUSED => e
rescue SocketError, Errno::ECONNREFUSED, Net::OpenTimeout => e
logger.error("WebHook Error => #{e}")
false
end
......
......@@ -189,7 +189,9 @@ class MergeRequest < ActiveRecord::Base
end
def automerge!(current_user, commit_message = nil)
MergeRequests::AutoMergeService.new.execute(self, current_user, commit_message)
MergeRequests::AutoMergeService.
new(target_project, current_user).
execute(self, commit_message)
end
def open?
......
......@@ -296,6 +296,7 @@ class Note < ActiveRecord::Base
# If not - its outdated diff
def active?
return true unless self.diff
return false unless noteable
noteable.diffs.each do |mr_diff|
next unless mr_diff.new_path == self.diff.new_path
......
......@@ -6,12 +6,13 @@ class Notification
N_PARTICIPATING = 1
N_WATCH = 2
N_GLOBAL = 3
N_MENTION = 4
attr_accessor :target
class << self
def notification_levels
[N_DISABLED, N_PARTICIPATING, N_WATCH]
[N_DISABLED, N_PARTICIPATING, N_WATCH, N_MENTION]
end
def options_with_labels
......@@ -19,12 +20,13 @@ class Notification
disabled: N_DISABLED,
participating: N_PARTICIPATING,
watch: N_WATCH,
mention: N_MENTION,
global: N_GLOBAL
}
end
def project_notification_levels
[N_DISABLED, N_PARTICIPATING, N_WATCH, N_GLOBAL]
[N_DISABLED, N_PARTICIPATING, N_WATCH, N_GLOBAL, N_MENTION]
end
end
......@@ -48,6 +50,10 @@ class Notification
target.notification_level == N_GLOBAL
end
def mention?
target.notification_level == N_MENTION
end
def level
target.notification_level
end
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
File mode changed from 100755 to 100644
This diff is collapsed.
File mode changed from 100755 to 100644
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
File mode changed from 100755 to 100644
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