mirror of
https://github.com/wahyd4/gitlabhq.git
synced 2026-08-15 23:56:09 +10:00
Merge commit '79190fe06b' into merge-79190fe06b-7.3.0-pre
Conflicts: Gemfile README.md VERSION db/schema.rb doc/install/installation.md lib/gitlab/git_access.rb lib/gitlab/ldap/user.rb spec/lib/gitlab/ldap/access_spec.rb
This commit is contained in:
@@ -5,6 +5,8 @@ targets:
|
||||
debian-7: &wheezy
|
||||
build_dependencies:
|
||||
- libicu-dev
|
||||
- cmake
|
||||
- pkg-config
|
||||
dependencies:
|
||||
- libicu48
|
||||
- libpcre3
|
||||
@@ -13,6 +15,8 @@ targets:
|
||||
ubuntu-14.04:
|
||||
build_dependencies:
|
||||
- libicu-dev
|
||||
- cmake
|
||||
- pkg-config
|
||||
dependencies:
|
||||
- libicu52
|
||||
- libpcre3
|
||||
@@ -20,6 +24,8 @@ targets:
|
||||
centos-6:
|
||||
build_dependencies:
|
||||
- libicu-devel
|
||||
- cmake
|
||||
- pkgconfig
|
||||
dependencies:
|
||||
- libicu
|
||||
- pcre
|
||||
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
language: ruby
|
||||
env:
|
||||
global:
|
||||
- TRAVIS=true
|
||||
matrix:
|
||||
- TASK=spinach_project DB=mysql
|
||||
- TASK=spinach_other DB=mysql
|
||||
- TASK=spec:api DB=mysql
|
||||
- TASK=spec:feature DB=mysql
|
||||
- TASK=spec:other DB=mysql
|
||||
- TASK=jasmine:ci DB=mysql
|
||||
- TASK=spinach_project DB=postgresql
|
||||
- TASK=spinach_other DB=postgresql
|
||||
- TASK=spec:api DB=postgresql
|
||||
- TASK=spec:feature DB=postgresql
|
||||
- TASK=spec:other DB=postgresql
|
||||
- TASK=jasmine:ci DB=postgresql
|
||||
before_install:
|
||||
- sudo apt-get install libicu-dev -y
|
||||
install:
|
||||
- "travis_retry bundle install --deployment --without production --retry 5"
|
||||
branches:
|
||||
only:
|
||||
- 'master'
|
||||
rvm:
|
||||
- 2.0.0
|
||||
services:
|
||||
- redis-server
|
||||
before_script:
|
||||
- "cp config/database.yml.$DB config/database.yml"
|
||||
- "cp config/gitlab.yml.example config/gitlab.yml"
|
||||
- "bundle exec rake db:setup"
|
||||
- "bundle exec rake db:seed_fu"
|
||||
script: "bundle exec rake $TASK --trace"
|
||||
notifications:
|
||||
email: false
|
||||
@@ -1,3 +1,39 @@
|
||||
v 7.3.0
|
||||
- Always set the 'origin' remote in satellite actions
|
||||
- Write authorized_keys in tmp/ during tests
|
||||
- Expire Rack sessions after 1 week
|
||||
- Cleaner signin/signup pages
|
||||
- Improved comments UI
|
||||
- Better search with filtering, pagination etc
|
||||
- Added a checkbox to toggle line wrapping in diff (Yuriy Glukhov)
|
||||
- Prevent project stars duplication when fork project
|
||||
- Use the default Unicorn socket backlog value of 1024
|
||||
- Support Unix domain sockets for Redis
|
||||
- Store session Redis keys in 'session:gitlab:' namespace
|
||||
- Deprecate LDAP account takeover based on partial LDAP email / GitLab username match
|
||||
- Use /bin/sh instead of Bash in bin/web, bin/background_jobs (Pavel Novitskiy)
|
||||
- Keyboard shortcuts for productivity (Robert Schilling)
|
||||
- API: filter issues by state (Julien Bianchi)
|
||||
- API: filter issues by labels (Julien Bianchi)
|
||||
- Add system hook for ssh key changes
|
||||
- Add blob permalink link (Ciro Santilli)
|
||||
- Create annotated tags through UI and API (Sean Edge)
|
||||
- Snippets search (Charles Bushong)
|
||||
- Comment new push to existing MR
|
||||
- Add 'ci' to the blacklist of forbidden names
|
||||
- Improve text filtering on issues page
|
||||
- Comment & Close button
|
||||
- Process git push --all much faster
|
||||
- Don't allow edit of system notes
|
||||
- Project wiki search (Ralf Seidler)
|
||||
- Enabled Shibboleth authentication support (Matus Banas)
|
||||
- Zen mode (fullscreen) for issues/MR/notes (Robert Schilling)
|
||||
- Add ability to configure webhook timeout via gitlab.yml (Wes Gurney)
|
||||
|
||||
v 7.2.1
|
||||
- Delete orphaned labels during label migration (James Brooks)
|
||||
- Security: prevent XSS with stricter MIME types for raw repo files
|
||||
|
||||
v 7.2.0
|
||||
- Explore page
|
||||
- Add project stars (Ciro Santilli)
|
||||
|
||||
@@ -62,6 +62,7 @@ If you can, please submit a merge request with the fix or improvements including
|
||||
1. Create a feature branch
|
||||
1. Write [tests](README.md#run-the-tests) and code
|
||||
1. Add your changes to the [CHANGELOG](CHANGELOG)
|
||||
1. If you are changing the README, some documentation or other things which have no effect on the tests, add `[ci skip]` somewhere in the commit message
|
||||
1. If you have multiple commits please combine them into one commit by [squashing them](http://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits)
|
||||
1. Push the commit to your fork
|
||||
1. Submit a merge request (MR) to the master branch
|
||||
|
||||
@@ -1 +1 @@
|
||||
1.9.7
|
||||
2.0.0
|
||||
|
||||
@@ -27,6 +27,7 @@ gem 'omniauth', "~> 1.1.3"
|
||||
gem 'omniauth-google-oauth2'
|
||||
gem 'omniauth-twitter'
|
||||
gem 'omniauth-github'
|
||||
gem 'omniauth-shibboleth'
|
||||
|
||||
# Extracting information from a git repository
|
||||
# Provide access to Gitlab::Git library
|
||||
@@ -36,7 +37,7 @@ gem "gitlab_git", '~> 6.0'
|
||||
gem 'gitlab-grack', '~> 2.0.0.pre', require: 'grack'
|
||||
|
||||
# LDAP Auth
|
||||
gem 'gitlab_omniauth-ldap', '1.0.4', require: "omniauth-ldap"
|
||||
gem 'gitlab_omniauth-ldap', '1.1.0', require: "omniauth-ldap"
|
||||
gem 'net-ldap'
|
||||
|
||||
# Git Wiki
|
||||
@@ -83,7 +84,7 @@ gem "seed-fu"
|
||||
gem "github-markup"
|
||||
|
||||
# Required markup gems by github-markdown
|
||||
gem 'redcarpet', '~> 2.2.2'
|
||||
gem 'redcarpet', '~> 3.1.2'
|
||||
gem 'RedCloth'
|
||||
gem 'rdoc', '~>3.6'
|
||||
gem 'org-ruby'
|
||||
@@ -157,6 +158,9 @@ gem "rack-attack"
|
||||
# Ace editor
|
||||
gem 'ace-rails-ap'
|
||||
|
||||
# Keyboard shortcuts
|
||||
gem 'mousetrap-rails'
|
||||
|
||||
# Semantic UI Sass for Sidebar
|
||||
gem 'semantic-ui-sass', '~> 0.16.1.0'
|
||||
|
||||
@@ -232,7 +236,7 @@ group :development, :test do
|
||||
|
||||
gem 'jasmine', '2.0.2'
|
||||
|
||||
gem "spring", '1.1.1'
|
||||
gem "spring", '1.1.3'
|
||||
gem "spring-commands-rspec", '1.0.1'
|
||||
gem "spring-commands-spinach", '1.0.0'
|
||||
end
|
||||
|
||||
+14
-9
@@ -168,7 +168,7 @@ GEM
|
||||
multi_json
|
||||
gitlab-grack (2.0.0.pre)
|
||||
rack (~> 1.5.1)
|
||||
gitlab-grit (2.6.10)
|
||||
gitlab-grit (2.6.11)
|
||||
charlock_holmes (~> 0.6)
|
||||
diff-lcs (~> 1.1)
|
||||
mime-types (~> 1.15)
|
||||
@@ -186,8 +186,8 @@ GEM
|
||||
gitlab-linguist (~> 3.0)
|
||||
rugged (~> 0.21.0)
|
||||
gitlab_meta (7.0)
|
||||
gitlab_omniauth-ldap (1.0.4)
|
||||
net-ldap (~> 0.3.1)
|
||||
gitlab_omniauth-ldap (1.1.0)
|
||||
net-ldap (~> 0.7.0)
|
||||
omniauth (~> 1.0)
|
||||
pyu-ruby-sasl (~> 0.0.3.1)
|
||||
rubyntlm (~> 0.1.1)
|
||||
@@ -287,11 +287,12 @@ GEM
|
||||
mime-types (1.25.1)
|
||||
mini_portile (0.6.0)
|
||||
minitest (5.3.5)
|
||||
mousetrap-rails (1.4.6)
|
||||
multi_json (1.10.1)
|
||||
multi_xml (0.5.5)
|
||||
multipart-post (1.2.0)
|
||||
mysql2 (0.3.16)
|
||||
net-ldap (0.3.1)
|
||||
net-ldap (0.7.0)
|
||||
net-scp (1.1.2)
|
||||
net-ssh (>= 2.6.5)
|
||||
net-ssh (2.8.0)
|
||||
@@ -320,6 +321,8 @@ GEM
|
||||
omniauth-oauth2 (1.1.1)
|
||||
oauth2 (~> 0.8.0)
|
||||
omniauth (~> 1.0)
|
||||
omniauth-shibboleth (1.1.1)
|
||||
omniauth (>= 1.0.0)
|
||||
omniauth-twitter (1.0.1)
|
||||
multi_json (~> 1.3)
|
||||
omniauth-oauth (~> 1.0)
|
||||
@@ -391,7 +394,7 @@ GEM
|
||||
ffi (>= 0.5.0)
|
||||
rdoc (3.12.2)
|
||||
json (~> 1.4)
|
||||
redcarpet (2.2.2)
|
||||
redcarpet (3.1.2)
|
||||
redis (3.0.6)
|
||||
redis-actionpack (4.0.0)
|
||||
actionpack (~> 4)
|
||||
@@ -489,7 +492,7 @@ GEM
|
||||
capybara (>= 2.0.0)
|
||||
railties (>= 3)
|
||||
spinach (>= 0.4)
|
||||
spring (1.1.1)
|
||||
spring (1.1.3)
|
||||
spring-commands-rspec (1.0.1)
|
||||
spring (>= 0.9.1)
|
||||
spring-commands-spinach (1.0.0)
|
||||
@@ -615,7 +618,7 @@ DEPENDENCIES
|
||||
gitlab_emoji (~> 0.0.1.1)
|
||||
gitlab_git (~> 6.0)
|
||||
gitlab_meta (= 7.0)
|
||||
gitlab_omniauth-ldap (= 1.0.4)
|
||||
gitlab_omniauth-ldap (= 1.1.0)
|
||||
gollum-lib (~> 3.0.0)
|
||||
gon (~> 5.0.0)
|
||||
grape (~> 0.6.1)
|
||||
@@ -636,12 +639,14 @@ DEPENDENCIES
|
||||
launchy
|
||||
letter_opener
|
||||
minitest (~> 5.3.0)
|
||||
mousetrap-rails
|
||||
mysql2
|
||||
net-ldap
|
||||
nprogress-rails
|
||||
omniauth (~> 1.1.3)
|
||||
omniauth-github
|
||||
omniauth-google-oauth2
|
||||
omniauth-shibboleth
|
||||
omniauth-twitter
|
||||
org-ruby
|
||||
pg
|
||||
@@ -658,7 +663,7 @@ DEPENDENCIES
|
||||
rb-fsevent
|
||||
rb-inotify
|
||||
rdoc (~> 3.6)
|
||||
redcarpet (~> 2.2.2)
|
||||
redcarpet (~> 3.1.2)
|
||||
redis-rails
|
||||
request_store
|
||||
rspec-rails
|
||||
@@ -677,7 +682,7 @@ DEPENDENCIES
|
||||
slack-notifier (~> 0.3.2)
|
||||
slim
|
||||
spinach-rails
|
||||
spring (= 1.1.1)
|
||||
spring (= 1.1.3)
|
||||
spring-commands-rspec (= 1.0.1)
|
||||
spring-commands-spinach (= 1.0.0)
|
||||
stamp
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# A sample Guardfile
|
||||
# More info at https://github.com/guard/guard#readme
|
||||
|
||||
guard 'rspec', cmd: "spring rspec", version: 2, all_on_start: false, all_after_pass: false do
|
||||
guard 'rspec', cmd: "spring rspec", all_on_start: false, all_after_pass: false do
|
||||
watch(%r{^spec/.+_spec\.rb$})
|
||||
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
||||
watch(%r{^lib/api/(.+)\.rb$}) { |m| "spec/requests/api/#{m[1]}_spec.rb" }
|
||||
@@ -19,7 +19,7 @@ guard 'rspec', cmd: "spring rspec", version: 2, all_on_start: false, all_after_p
|
||||
watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/requests/#{m[1]}_spec.rb" }
|
||||
end
|
||||
|
||||
guard 'spinach' do
|
||||
guard 'spinach', command_prefix: 'spring' do
|
||||
watch(%r|^features/(.*)\.feature|)
|
||||
watch(%r|^features/steps/(.*)([^/]+)\.rb|) do |m|
|
||||
"features/#{m[1]}#{m[2]}.feature"
|
||||
|
||||
+1
-1
@@ -87,7 +87,7 @@ Please use ``` to format console output, logs, and code as it's very hard to rea
|
||||
|
||||
### Issue fixed in newer version
|
||||
|
||||
Thanks for the issue report. This issue has already been fixed in newer versions of GitLab. Due to the size of this project and our limited resources we are only able to support the latest stable release as outlined in our \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker). In order to get this bug fix and enjoy many new features please \[upgrade\]\(https://github.com/gitlabhq/gitlabhq/tree/master/doc/update). If you still experience issues at that time please open a new issue following our issue tracker guidelines found in the \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker-guidelines).
|
||||
Thanks for the issue report. This issue has already been fixed in newer versions of GitLab. Due to the size of this project and our limited resources we are only able to support the latest stable release as outlined in our \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker). In order to get this bug fix and enjoy many new features please \[upgrade\]\(https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update). If you still experience issues at that time please open a new issue following our issue tracker guidelines found in the \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker-guidelines).
|
||||
|
||||
### Improperly formatted merge request
|
||||
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
# GitLab
|
||||
#  GitLab
|
||||
|
||||
## Open source software to collaborate on code
|
||||
|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
- Manage Git repositories with fine grained access controls that keep your code secure
|
||||
- Perform code reviews and enhance collaboration with merge requests
|
||||
@@ -21,7 +19,9 @@
|
||||
|
||||
- [](https://ci.gitlab.org/projects/1?ref=master) on ci.gitlab.org (master branch)
|
||||
|
||||
- [](https://codeclimate.com/github/gitlabhq/gitlabhq)
|
||||
- [](https://semaphoreapp.com/gitlabhq/gitlabhq)
|
||||
|
||||
- [](https://codeclimate.com/github/gitlabhq/gitlabhq)
|
||||
|
||||
- [](https://coveralls.io/r/gitlabhq/gitlabhq)
|
||||
|
||||
@@ -29,14 +29,14 @@
|
||||
|
||||
## Website
|
||||
|
||||
On [www.gitlab.com](https://www.gitlab.com/) you can find more information about:
|
||||
On [about.gitlab.com](https://about.gitlab.com/) you can find more information about:
|
||||
|
||||
- [Subscriptions](https://www.gitlab.com/subscription/)
|
||||
- [Consultancy](https://www.gitlab.com/consultancy/)
|
||||
- [Community](https://www.gitlab.com/community/)
|
||||
- [Hosted GitLab.com](https://www.gitlab.com/gitlab-com/) use GitLab as a free service
|
||||
- [GitLab Enterprise Edition](https://www.gitlab.com/gitlab-ee/) with additional features aimed at larger organizations.
|
||||
- [GitLab CI](https://www.gitlab.com/gitlab-ci/) a continuous integration (CI) server that is easy to integrate with GitLab.
|
||||
- [Subscriptions](https://about.gitlab.com/subscription/)
|
||||
- [Consultancy](https://about.gitlab.com/consultancy/)
|
||||
- [Community](https://about.gitlab.com/community/)
|
||||
- [Hosted GitLab.com](https://about.gitlab.com/gitlab-com/) use GitLab as a free service
|
||||
- [GitLab Enterprise Edition](https://about.gitlab.com/gitlab-ee/) with additional features aimed at larger organizations.
|
||||
- [GitLab CI](https://about.gitlab.com/gitlab-ci/) a continuous integration (CI) server that is easy to integrate with GitLab.
|
||||
|
||||
## Third-party applications
|
||||
|
||||
@@ -61,15 +61,11 @@ These applications are maintained by contributors, GitLab B.V. does not offer su
|
||||
|
||||
## Installation
|
||||
|
||||
We recommend the [GitLab Enterprise Edition packages](https://gitlab.com/subscribers/gitlab-ee/blob/master/doc/install/packages.md).
|
||||
|
||||
You can also use the [Chef cookbook](https://gitlab.com/gitlab-org/cookbook-gitlab/blob/master/README.md) or the [manual installation](doc/install/installation.md).
|
||||
|
||||
Other options are listed on the [installation page on the GitLab website](https://www.gitlab.com/installation/) but most of these assume GitLab Community Edition.
|
||||
Please see [the installation page on the GitLab website](https://about.gitlab.com/installation/).
|
||||
|
||||
### New versions
|
||||
|
||||
Since 2011 a minor or major version of GitLab is released on the 22nd of every month. Patch and security releases come out when needed. New features are detailed on the [blog](https://www.gitlab.com/blog/) and in the [changelog](CHANGELOG). For more information about the release process see the release [documentation](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/release). Features that will likely be in the next releases can be found on the [feature request forum](http://feedback.gitlab.com/forums/176466-general) with the status [started](http://feedback.gitlab.com/forums/176466-general/status/796456) and [completed](http://feedback.gitlab.com/forums/176466-general/status/796457).
|
||||
Since 2011 a minor or major version of GitLab is released on the 22nd of every month. Patch and security releases come out when needed. New features are detailed on the [blog](https://about.gitlab.com/blog/) and in the [changelog](CHANGELOG). For more information about the release process see the release [documentation](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/release). Features that will likely be in the next releases can be found on the [feature request forum](http://feedback.gitlab.com/forums/176466-general) with the status [started](http://feedback.gitlab.com/forums/176466-general/status/796456) and [completed](http://feedback.gitlab.com/forums/176466-general/status/796457).
|
||||
|
||||
### Upgrading
|
||||
|
||||
@@ -89,7 +85,8 @@ Please login with `root` / `5iveL!fe`
|
||||
|
||||
## Install a development environment
|
||||
|
||||
We recommend setting up your development environment with [the cookbook](https://gitlab.com/gitlab-org/cookbook-gitlab/blob/master/README.md#installation). If you do not use the cookbook you might need to copy the example development unicorn configuration file
|
||||
We recommend setting up your development environment with [the GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit).
|
||||
If you do not use the development kit you might need to copy the example development unicorn configuration file
|
||||
|
||||
cp config/unicorn.rb.example.development config/unicorn.rb
|
||||
|
||||
@@ -130,7 +127,7 @@ All documentation can be found on [doc.gitlab.com/ee/](http://doc.gitlab.com/ee/
|
||||
|
||||
## Getting help
|
||||
|
||||
Please see [Getting help for GitLab](https://www.gitlab.com/getting-help/) on our website for the many options to get help.
|
||||
Please see [Getting help for GitLab](https://about.gitlab.com/getting-help/) on our website for the many options to get help.
|
||||
|
||||
## Is it any good?
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#= require jquery.atwho
|
||||
#= require jquery.scrollTo
|
||||
#= require jquery.blockUI
|
||||
#= require jquery.sticky
|
||||
#= require turbolinks
|
||||
#= require jquery.turbolinks
|
||||
#= require bootstrap
|
||||
@@ -25,12 +26,20 @@
|
||||
#= require branch-graph
|
||||
#= require highlight.pack
|
||||
#= require ace/ace
|
||||
#= require ace/ext-searchbox
|
||||
#= require d3
|
||||
#= require underscore
|
||||
#= require nprogress
|
||||
#= require nprogress-turbolinks
|
||||
#= require dropzone
|
||||
#= require semantic-ui/sidebar
|
||||
#= require mousetrap
|
||||
#= require mousetrap/pause
|
||||
#= require shortcuts
|
||||
#= require shortcuts_navigation
|
||||
#= require shortcuts_dashboard_navigation
|
||||
#= require shortcuts_issueable
|
||||
#= require shortcuts_network
|
||||
#= require_tree .
|
||||
|
||||
window.slugify = (text) ->
|
||||
@@ -117,6 +126,13 @@ $ ->
|
||||
# Initialize select2 selects
|
||||
$('select.select2').select2(width: 'resolve', dropdownAutoWidth: true)
|
||||
|
||||
# Close select2 on escape
|
||||
$('.js-select2').bind 'select2-close', ->
|
||||
setTimeout ( ->
|
||||
$('.select2-container-active').removeClass('select2-container-active')
|
||||
$(':focus').blur()
|
||||
), 1
|
||||
|
||||
# Initialize tooltips
|
||||
$('.has_tooltip').tooltip()
|
||||
|
||||
@@ -149,20 +165,6 @@ $ ->
|
||||
# Show/Hide the profile menu when hovering the account box
|
||||
$('.account-box').hover -> $(@).toggleClass('hover')
|
||||
|
||||
# Focus search field by pressing 's' key
|
||||
$(document).keypress (e) ->
|
||||
# Don't do anything if typing in an input
|
||||
return if $(e.target).is(":input")
|
||||
|
||||
switch e.which
|
||||
when 115
|
||||
$("#search").focus()
|
||||
e.preventDefault()
|
||||
when 63
|
||||
new Shortcuts()
|
||||
e.preventDefault()
|
||||
|
||||
|
||||
# Commit show suppressed diff
|
||||
$(".diff-content").on "click", ".supp_diff_link", ->
|
||||
$(@).next('table').show()
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
$ ->
|
||||
# Toggle line wrapping in diff.
|
||||
#
|
||||
# %div.diff-file
|
||||
# %input.js-toggle-diff-line-wrap
|
||||
# %td.line_content
|
||||
#
|
||||
$("body").on "click", ".js-toggle-diff-line-wrap", (e) ->
|
||||
diffFile = $(@).closest(".diff-file")
|
||||
if $(@).is(":checked")
|
||||
diffFile.addClass("diff-wrap-lines")
|
||||
else
|
||||
diffFile.removeClass("diff-wrap-lines")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
class BranchGraph
|
||||
class @BranchGraph
|
||||
constructor: (@element, @options) ->
|
||||
@preparedCommits = {}
|
||||
@mtime = 0
|
||||
@@ -120,23 +120,32 @@ class BranchGraph
|
||||
@top.toFront()
|
||||
|
||||
bindEvents: ->
|
||||
drag = {}
|
||||
element = @element
|
||||
|
||||
$(element).scroll (event) =>
|
||||
@renderPartialGraph()
|
||||
|
||||
$(window).on
|
||||
keydown: (event) =>
|
||||
# left
|
||||
element.scrollLeft element.scrollLeft() - 50 if event.keyCode is 37
|
||||
# top
|
||||
element.scrollTop element.scrollTop() - 50 if event.keyCode is 38
|
||||
# right
|
||||
element.scrollLeft element.scrollLeft() + 50 if event.keyCode is 39
|
||||
# bottom
|
||||
element.scrollTop element.scrollTop() + 50 if event.keyCode is 40
|
||||
@renderPartialGraph()
|
||||
scrollDown: =>
|
||||
@element.scrollTop @element.scrollTop() + 50
|
||||
@renderPartialGraph()
|
||||
|
||||
scrollUp: =>
|
||||
@element.scrollTop @element.scrollTop() - 50
|
||||
@renderPartialGraph()
|
||||
|
||||
scrollLeft: =>
|
||||
@element.scrollLeft @element.scrollLeft() - 50
|
||||
@renderPartialGraph()
|
||||
|
||||
scrollRight: =>
|
||||
@element.scrollLeft @element.scrollLeft() + 50
|
||||
@renderPartialGraph()
|
||||
|
||||
scrollBottom: =>
|
||||
@element.scrollTop @element.find('svg').height()
|
||||
|
||||
scrollTop: =>
|
||||
@element.scrollTop 0
|
||||
|
||||
appendLabel: (x, y, commit) ->
|
||||
return unless commit.refs
|
||||
@@ -325,5 +334,3 @@ Raphael::textWrap = (t, width) ->
|
||||
b = t.getBBox()
|
||||
h = Math.abs(b.y2) - Math.abs(b.y) + 1
|
||||
t.attr y: b.y + h
|
||||
|
||||
@BranchGraph = BranchGraph
|
||||
|
||||
@@ -34,7 +34,8 @@ class Diff
|
||||
$.get(link, params, (response) =>
|
||||
target.parent().replaceWith(response)
|
||||
)
|
||||
)
|
||||
).ready =>
|
||||
$(".diff-header").sticky {responsiveWidth:true, getWidthFrom: ".diff-file"}
|
||||
|
||||
lineNumbers: (line) ->
|
||||
return ([0, 0]) unless line.children().length
|
||||
|
||||
@@ -15,50 +15,88 @@ class Dispatcher
|
||||
return false
|
||||
|
||||
path = page.split(':')
|
||||
shortcut_handler = null
|
||||
|
||||
switch page
|
||||
when 'projects:issues:index'
|
||||
Issues.init()
|
||||
shortcut_handler = new ShortcutsNavigation()
|
||||
when 'projects:issues:show'
|
||||
new Issue()
|
||||
shortcut_handler = new ShortcutsIssueable()
|
||||
new ZenMode()
|
||||
when 'projects:milestones:show'
|
||||
new Milestone()
|
||||
when 'projects:issues:new'
|
||||
when 'projects:issues:new','projects:issues:edit'
|
||||
GitLab.GfmAutoComplete.setup()
|
||||
when 'projects:merge_requests:new'
|
||||
shortcut_handler = new ShortcutsNavigation()
|
||||
new ZenMode()
|
||||
when 'projects:merge_requests:new', 'projects:merge_requests:edit'
|
||||
GitLab.GfmAutoComplete.setup()
|
||||
new Diff()
|
||||
shortcut_handler = new ShortcutsNavigation()
|
||||
new ZenMode()
|
||||
when 'projects:merge_requests:show'
|
||||
new Diff()
|
||||
shortcut_handler = new ShortcutsIssueable()
|
||||
new ZenMode()
|
||||
when "projects:merge_requests:diffs"
|
||||
new Diff()
|
||||
when 'projects:merge_requests:index'
|
||||
shortcut_handler = new ShortcutsNavigation()
|
||||
when 'dashboard:show'
|
||||
new Dashboard()
|
||||
new Activities()
|
||||
when 'projects:commit:show'
|
||||
new Commit()
|
||||
new Diff()
|
||||
shortcut_handler = new ShortcutsNavigation()
|
||||
when 'projects:commits:show'
|
||||
shortcut_handler = new ShortcutsNavigation()
|
||||
when 'groups:show', 'projects:show'
|
||||
new Activities()
|
||||
when 'projects:new', 'projects:edit'
|
||||
shortcut_handler = new ShortcutsNavigation()
|
||||
when 'projects:new'
|
||||
new Project()
|
||||
when 'projects:edit'
|
||||
new Project()
|
||||
shortcut_handler = new ShortcutsNavigation()
|
||||
when 'projects:teams:members:index'
|
||||
new TeamMembers()
|
||||
when 'groups:members'
|
||||
new GroupMembers()
|
||||
when 'projects:tree:show'
|
||||
new TreeView()
|
||||
shortcut_handler = new ShortcutsNavigation()
|
||||
when 'projects:blob:show'
|
||||
new BlobView()
|
||||
shortcut_handler = new ShortcutsNavigation()
|
||||
when 'projects:labels:new', 'projects:labels:edit'
|
||||
new Labels()
|
||||
when 'projects:network:show'
|
||||
# Ensure we don't create a particular shortcut handler here. This is
|
||||
# already created, where the network graph is created.
|
||||
shortcut_handler = true
|
||||
|
||||
switch path.first()
|
||||
when 'admin' then new Admin()
|
||||
when 'dashboard'
|
||||
shortcut_handler = new ShortcutsDashboardNavigation()
|
||||
when 'projects'
|
||||
new Wikis() if path[1] == 'wikis'
|
||||
switch path[1]
|
||||
when 'wikis'
|
||||
new Wikis()
|
||||
shortcut_handler = new ShortcutsNavigation()
|
||||
when 'snippets', 'labels', 'graphs'
|
||||
shortcut_handler = new ShortcutsNavigation()
|
||||
when 'team_members', 'deploy_keys', 'hooks', 'services', 'protected_branches'
|
||||
shortcut_handler = new ShortcutsNavigation()
|
||||
|
||||
|
||||
# If we haven't installed a custom shortcut handler, install the default one
|
||||
if not shortcut_handler
|
||||
new Shortcuts()
|
||||
|
||||
initSearch: ->
|
||||
opts = $('.search-autocomplete-opts')
|
||||
path = opts.data('autocomplete-path')
|
||||
|
||||
@@ -43,25 +43,31 @@
|
||||
|
||||
$(".selected_issue").bind "change", Issues.checkChanged
|
||||
|
||||
|
||||
# Make sure we trigger ajax request only after user stop typing
|
||||
initSearch: ->
|
||||
form = $("#issue_search_form")
|
||||
last_terms = ""
|
||||
@timer = null
|
||||
$("#issue_search").keyup ->
|
||||
terms = $(this).val()
|
||||
unless terms is last_terms
|
||||
last_terms = terms
|
||||
if terms.length >= 2 or terms.length is 0
|
||||
$.ajax
|
||||
type: "GET"
|
||||
url: location.href
|
||||
data: "issue_search=" + terms
|
||||
complete: ->
|
||||
$(".loading").hide()
|
||||
success: (data) ->
|
||||
$('.issues-holder').html(data.html)
|
||||
Issues.reload()
|
||||
dataType: "json"
|
||||
clearTimeout(@timer);
|
||||
@timer = setTimeout(Issues.filterResults, 500)
|
||||
|
||||
filterResults: =>
|
||||
form = $("#issue_search_form")
|
||||
search = $("#issue_search").val()
|
||||
$('.issues-holder').css("opacity", '0.5')
|
||||
issues_url = form.attr('action') + '? '+ form.serialize()
|
||||
|
||||
$.ajax
|
||||
type: "GET"
|
||||
url: form.attr('action')
|
||||
data: form.serialize()
|
||||
complete: ->
|
||||
$('.issues-holder').css("opacity", '1.0')
|
||||
success: (data) ->
|
||||
$('.issues-holder').html(data.html)
|
||||
# Change url so if user reload a page - search results are saved
|
||||
History.replaceState {page: issues_url}, document.title, issues_url
|
||||
Issues.reload()
|
||||
dataType: "json"
|
||||
|
||||
checkChanged: ->
|
||||
checked_issues = $(".selected_issue:checked")
|
||||
|
||||
@@ -27,7 +27,7 @@ $(document).ready ->
|
||||
dropzone = $(".div-dropzone").dropzone(
|
||||
url: project_image_path_upload
|
||||
dictDefaultMessage: ""
|
||||
clickable: true
|
||||
clickable: false
|
||||
paramName: "markdown_img"
|
||||
maxFilesize: 10
|
||||
uploadMultiple: false
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
class Network
|
||||
class @Network
|
||||
constructor: (opts) ->
|
||||
$("#filter_ref").click ->
|
||||
$(this).closest('form').submit()
|
||||
|
||||
branch_graph = new BranchGraph($(".network-graph"), opts)
|
||||
@branch_graph = new BranchGraph($(".network-graph"), opts)
|
||||
|
||||
vph = $(window).height() - 250
|
||||
$('.network-graph').css 'height': (vph + 'px')
|
||||
|
||||
@Network = Network
|
||||
|
||||
@@ -16,13 +16,19 @@ class Notes
|
||||
$(document).on "ajax:success", ".js-main-target-form", @addNote
|
||||
$(document).on "ajax:success", ".js-discussion-note-form", @addDiscussionNote
|
||||
|
||||
# change note in UI after update
|
||||
# change note in UI after update
|
||||
$(document).on "ajax:success", "form.edit_note", @updateNote
|
||||
|
||||
# Edit note link
|
||||
$(document).on "click", ".js-note-edit", @showEditForm
|
||||
$(document).on "click", ".note-edit-cancel", @cancelEdit
|
||||
|
||||
# Reopen and close actions for Issue/MR combined with note form submit
|
||||
$(document).on "click", ".js-note-target-reopen", @targetReopen
|
||||
$(document).on "click", ".js-note-target-close", @targetClose
|
||||
$(document).on "click", ".js-comment-button", @updateCloseButton
|
||||
$(document).on "keyup", ".js-note-text", @updateTargetButtons
|
||||
|
||||
# remove a note (in general)
|
||||
$(document).on "click", ".js-note-delete", @removeNote
|
||||
|
||||
@@ -78,7 +84,9 @@ class Notes
|
||||
$(document).off "click", ".js-add-diff-note-button"
|
||||
$(document).off "visibilitychange"
|
||||
$(document).off "keypress", @notes_forms
|
||||
|
||||
$(document).off "keyup", ".js-note-text"
|
||||
$(document).off "click", ".js-note-target-reopen"
|
||||
$(document).off "click", ".js-note-target-close"
|
||||
|
||||
initRefresh: ->
|
||||
clearInterval(Notes.interval)
|
||||
@@ -406,30 +414,6 @@ class Notes
|
||||
form.find(".js-note-text").focus()
|
||||
form.addClass "js-discussion-note-form"
|
||||
|
||||
###
|
||||
General note form setup.
|
||||
|
||||
deactivates the submit button when text is empty
|
||||
hides the preview button when text is empty
|
||||
setup GFM auto complete
|
||||
show the form
|
||||
###
|
||||
setupNoteForm: (form) =>
|
||||
disableButtonIfEmptyField form.find(".js-note-text"), form.find(".js-comment-button")
|
||||
form.removeClass "js-new-note-form"
|
||||
form.removeClass "js-new-note-form"
|
||||
GitLab.GfmAutoComplete.setup()
|
||||
|
||||
# setup preview buttons
|
||||
previewButton = form.find(".js-note-preview-button")
|
||||
form.find(".js-note-text").on "input", ->
|
||||
if $(this).val().trim() isnt ""
|
||||
previewButton.removeClass("turn-off").addClass "turn-on"
|
||||
else
|
||||
previewButton.removeClass("turn-on").addClass "turn-off"
|
||||
|
||||
form.show()
|
||||
|
||||
###
|
||||
Called when clicking on the "add a comment" button on the side of a diff line.
|
||||
|
||||
@@ -502,4 +486,33 @@ class Notes
|
||||
visibilityChange: =>
|
||||
@refresh()
|
||||
|
||||
targetReopen: (e) =>
|
||||
@submitNoteForm($(e.target).parents('form'))
|
||||
|
||||
targetClose: (e) =>
|
||||
@submitNoteForm($(e.target).parents('form'))
|
||||
|
||||
submitNoteForm: (form) =>
|
||||
noteText = form.find(".js-note-text").val()
|
||||
if noteText.trim().length > 0
|
||||
form.submit()
|
||||
|
||||
updateCloseButton: (e) =>
|
||||
textarea = $(e.target)
|
||||
form = textarea.parents('form')
|
||||
form.find('.js-note-target-close').text('Close')
|
||||
|
||||
updateTargetButtons: (e) =>
|
||||
textarea = $(e.target)
|
||||
form = textarea.parents('form')
|
||||
|
||||
if textarea.val().trim().length > 0
|
||||
form.find('.js-note-target-reopen').text('Comment & reopen')
|
||||
form.find('.js-note-target-close').text('Comment & close')
|
||||
else
|
||||
form.find('.js-note-target-reopen').text('Reopen')
|
||||
form.find('.js-note-target-close').text('Close')
|
||||
|
||||
|
||||
|
||||
@Notes = Notes
|
||||
|
||||
@@ -55,7 +55,7 @@ $ ->
|
||||
$(@).parents('.no-ssh-key-message').hide()
|
||||
e.preventDefault()
|
||||
|
||||
$('.project-side .star').on 'ajax:success', (e, data, status, xhr) ->
|
||||
$('.project-home-panel .star').on 'ajax:success', (e, data, status, xhr) ->
|
||||
$(@).toggleClass('on').find('.count').html(data.star_count)
|
||||
.on 'ajax:error', (e, xhr, status, error) ->
|
||||
new Flash('Star toggle failed. Try again later.', 'alert')
|
||||
|
||||
@@ -1,11 +1,30 @@
|
||||
class Shortcuts
|
||||
class @Shortcuts
|
||||
constructor: ->
|
||||
@enabledHelp = []
|
||||
Mousetrap.reset()
|
||||
Mousetrap.bind('?', @selectiveHelp)
|
||||
Mousetrap.bind('s', Shortcuts.focusSearch)
|
||||
|
||||
selectiveHelp: (e) =>
|
||||
Shortcuts.showHelp(e, @enabledHelp)
|
||||
|
||||
@showHelp: (e, location) ->
|
||||
if $('#modal-shortcuts').length > 0
|
||||
$('#modal-shortcuts').modal('show')
|
||||
else
|
||||
$.ajax(
|
||||
url: '/help/shortcuts',
|
||||
dataType: "script"
|
||||
dataType: 'script',
|
||||
success: (e) ->
|
||||
if location and location.length > 0
|
||||
for l in location
|
||||
$(l).show()
|
||||
else
|
||||
$('.hidden-shortcut').show()
|
||||
$('.js-more-help-button').remove()
|
||||
)
|
||||
e.preventDefault()
|
||||
|
||||
@Shortcuts = Shortcuts
|
||||
@focusSearch: (e) ->
|
||||
$('#search').focus()
|
||||
e.preventDefault()
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
#= require shortcuts
|
||||
|
||||
class @ShortcutsDashboardNavigation extends Shortcuts
|
||||
constructor: ->
|
||||
super()
|
||||
Mousetrap.bind('g a', -> ShortcutsDashboardNavigation.findAndollowLink('.shortcuts-activity'))
|
||||
Mousetrap.bind('g p', -> ShortcutsDashboardNavigation.findAndollowLink('.shortcuts-projects'))
|
||||
Mousetrap.bind('g i', -> ShortcutsDashboardNavigation.findAndollowLink('.shortcuts-issues'))
|
||||
Mousetrap.bind('g m', -> ShortcutsDashboardNavigation.findAndollowLink('.shortcuts-merge_requests'))
|
||||
|
||||
@findAndollowLink: (selector) ->
|
||||
link = $(selector).attr('href')
|
||||
if link
|
||||
window.location = link
|
||||
@@ -0,0 +1,19 @@
|
||||
#= require shortcuts_navigation
|
||||
|
||||
class @ShortcutsIssueable extends ShortcutsNavigation
|
||||
constructor: (isMergeRequest) ->
|
||||
super()
|
||||
Mousetrap.bind('a', ->
|
||||
$('.js-assignee').select2('open')
|
||||
return false
|
||||
)
|
||||
Mousetrap.bind('m', ->
|
||||
$('.js-milestone').select2('open')
|
||||
return false
|
||||
)
|
||||
|
||||
if isMergeRequest
|
||||
@enabledHelp.push('.hidden-shortcut.merge_reuests')
|
||||
else
|
||||
@enabledHelp.push('.hidden-shortcut.issues')
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
#= require shortcuts
|
||||
|
||||
class @ShortcutsNavigation extends Shortcuts
|
||||
constructor: ->
|
||||
super()
|
||||
Mousetrap.bind('g p', -> ShortcutsNavigation.findAndollowLink('.shortcuts-project'))
|
||||
Mousetrap.bind('g f', -> ShortcutsNavigation.findAndollowLink('.shortcuts-tree'))
|
||||
Mousetrap.bind('g c', -> ShortcutsNavigation.findAndollowLink('.shortcuts-commits'))
|
||||
Mousetrap.bind('g n', -> ShortcutsNavigation.findAndollowLink('.shortcuts-network'))
|
||||
Mousetrap.bind('g g', -> ShortcutsNavigation.findAndollowLink('.shortcuts-graphs'))
|
||||
Mousetrap.bind('g i', -> ShortcutsNavigation.findAndollowLink('.shortcuts-issues'))
|
||||
Mousetrap.bind('g m', -> ShortcutsNavigation.findAndollowLink('.shortcuts-merge_requests'))
|
||||
Mousetrap.bind('g w', -> ShortcutsNavigation.findAndollowLink('.shortcuts-wiki'))
|
||||
Mousetrap.bind('g s', -> ShortcutsNavigation.findAndollowLink('.shortcuts-snippets'))
|
||||
@enabledHelp.push('.hidden-shortcut.project')
|
||||
|
||||
@findAndollowLink: (selector) ->
|
||||
link = $(selector).attr('href')
|
||||
if link
|
||||
window.location = link
|
||||
@@ -0,0 +1,12 @@
|
||||
#= require shortcuts_navigation
|
||||
|
||||
class @ShortcutsNetwork extends ShortcutsNavigation
|
||||
constructor: (@graph) ->
|
||||
super()
|
||||
Mousetrap.bind(['left', 'h'], @graph.scrollLeft)
|
||||
Mousetrap.bind(['right', 'l'], @graph.scrollRight)
|
||||
Mousetrap.bind(['up', 'k'], @graph.scrollUp)
|
||||
Mousetrap.bind(['down', 'j'], @graph.scrollDown)
|
||||
Mousetrap.bind(['shift+up', 'shift+k'], @graph.scrollTop)
|
||||
Mousetrap.bind(['shift+down', 'shift+j'], @graph.scrollBottom)
|
||||
@enabledHelp.push('.hidden-shortcut.network')
|
||||
@@ -0,0 +1,51 @@
|
||||
class @ZenMode
|
||||
@fullscreen_prefix = 'fullscreen_'
|
||||
@ESC = 27
|
||||
|
||||
constructor: ->
|
||||
@active_zen_area = null
|
||||
@active_checkbox = null
|
||||
|
||||
$('body').on 'change', '.zennable input[type=checkbox]', (e) =>
|
||||
checkbox = e.currentTarget;
|
||||
if checkbox.checked
|
||||
Mousetrap.pause()
|
||||
@udpateActiveZenArea(checkbox)
|
||||
else
|
||||
@exitZenMode()
|
||||
|
||||
$(document).on 'keydown', (e) =>
|
||||
console.log("esc")
|
||||
if e.keyCode is ZenMode.ESC
|
||||
@exitZenMode()
|
||||
|
||||
$(window).on 'hashchange', @updateZenModeFromLocationHash
|
||||
|
||||
udpateActiveZenArea: (checkbox) =>
|
||||
@active_checkbox = $(checkbox)
|
||||
@active_checkbox.prop('checked', true)
|
||||
@active_zen_area = @active_checkbox.parent().find('textarea')
|
||||
@active_zen_area.focus()
|
||||
window.location.hash = ZenMode.fullscreen_prefix + @active_checkbox.prop('id')
|
||||
|
||||
exitZenMode: =>
|
||||
if @active_zen_area isnt null
|
||||
Mousetrap.unpause()
|
||||
@active_checkbox.prop('checked', false)
|
||||
@active_zen_area = null
|
||||
@active_checkbox = null
|
||||
window.location.hash = ''
|
||||
|
||||
checkboxFromLocationHash: (e) ->
|
||||
id = $.trim(window.location.hash.replace('#' + ZenMode.fullscreen_prefix, ''))
|
||||
if id
|
||||
return $('.zennable input[type=checkbox]#' + id)[0]
|
||||
else
|
||||
return null
|
||||
|
||||
updateZenModeFromLocationHash: (e) =>
|
||||
checkbox = @checkboxFromLocationHash()
|
||||
if checkbox
|
||||
@udpateActiveZenArea(checkbox)
|
||||
else
|
||||
@exitZenMode()
|
||||
@@ -128,7 +128,7 @@ p.time {
|
||||
}
|
||||
|
||||
.highlight_word {
|
||||
border-bottom: 2px solid #F90;
|
||||
background: #fafe3d;
|
||||
}
|
||||
|
||||
.thin_area{
|
||||
|
||||
@@ -83,3 +83,140 @@ label {
|
||||
.form-control {
|
||||
@include box-shadow(none);
|
||||
}
|
||||
|
||||
.issuable-description {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,6 +88,10 @@
|
||||
|
||||
.description {
|
||||
padding: 0 15px 10px 15px;
|
||||
|
||||
code {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
}
|
||||
|
||||
.title, .context, .description {
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
|
||||
&:hover {
|
||||
background: $hover;
|
||||
border-bottom: 1px solid #ADF;
|
||||
border-bottom: 1px solid darken($hover, 10%);
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
.timeline {
|
||||
list-style: none;
|
||||
padding: 20px 0 20px;
|
||||
position: relative;
|
||||
|
||||
&:before {
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
position: absolute;
|
||||
content: " ";
|
||||
width: 3px;
|
||||
background-color: #eeeeee;
|
||||
margin-left: 29px;
|
||||
}
|
||||
|
||||
.timeline-entry {
|
||||
position: relative;
|
||||
margin-top: 5px;
|
||||
margin-left: 30px;
|
||||
margin-bottom: 10px;
|
||||
clear: both;
|
||||
|
||||
|
||||
&:target {
|
||||
.timeline-entry-inner .timeline-content {
|
||||
-webkit-animation:target-note 2s linear;
|
||||
background: $hover;
|
||||
}
|
||||
}
|
||||
|
||||
.timeline-entry-inner {
|
||||
position: relative;
|
||||
margin-left: -20px;
|
||||
|
||||
&:before, &:after {
|
||||
content: " ";
|
||||
display: table;
|
||||
}
|
||||
|
||||
.timeline-icon {
|
||||
margin-top: 2px;
|
||||
background: #fff;
|
||||
color: #737881;
|
||||
float: left;
|
||||
@include border-radius(40px);
|
||||
@include box-shadow(0 0 0 3px #EEE);
|
||||
overflow: hidden;
|
||||
|
||||
.avatar {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.timeline-content {
|
||||
position: relative;
|
||||
background: #f5f5f6;
|
||||
padding: 10px 15px;
|
||||
margin-left: 60px;
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-style: solid;
|
||||
border-width: 9px 9px 9px 0;
|
||||
border-color: transparent #f5f5f6 transparent transparent;
|
||||
left: 0;
|
||||
top: 10px;
|
||||
margin-left: -9px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ a {
|
||||
outline: none;
|
||||
color: $link_color;
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
text-decoration: underline;
|
||||
color: $link_hover_color;
|
||||
}
|
||||
|
||||
@@ -89,6 +89,8 @@ a:focus {
|
||||
.wiki {
|
||||
@include md-typography;
|
||||
|
||||
word-wrap: break-word;
|
||||
|
||||
/* Link to current header. */
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
position: relative;
|
||||
|
||||
@@ -16,3 +16,4 @@ body {
|
||||
.container .content {
|
||||
margin: 0 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
* General Colors
|
||||
*/
|
||||
$style_color: #474D57;
|
||||
$hover: #D9EDF7;
|
||||
$hover: #FFECDB;
|
||||
|
||||
/*
|
||||
* Link colors
|
||||
*/
|
||||
$link_color: #446e9b;
|
||||
$link_hover_color: #2FA0BB;
|
||||
$link_hover_color: darken($link-color, 10%);
|
||||
|
||||
$btn-border: 1px solid #ccc;
|
||||
|
||||
|
||||
@@ -60,12 +60,13 @@
|
||||
}
|
||||
|
||||
.project-row, .group-row {
|
||||
padding: 8px 15px !important;
|
||||
padding: 0 !important;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
|
||||
a {
|
||||
display: block;
|
||||
padding: 8px 15px;
|
||||
}
|
||||
|
||||
.project-name, .group-name {
|
||||
@@ -99,14 +100,9 @@
|
||||
margin-right: 15px;
|
||||
font-size: 20px;
|
||||
margin-bottom: 15px;
|
||||
border: 1px solid #EEE;
|
||||
padding: 8px 12px;
|
||||
border-radius: 50px;
|
||||
background: #f5f5f5;
|
||||
text-align: center;
|
||||
|
||||
i {
|
||||
color: #BBB;
|
||||
color: #888;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -125,8 +125,6 @@
|
||||
}
|
||||
.line_content {
|
||||
display: block;
|
||||
white-space: pre;
|
||||
height: 18px;
|
||||
margin: 0px;
|
||||
padding: 0px 0.5em;
|
||||
border: none;
|
||||
@@ -341,3 +339,12 @@
|
||||
margin: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.diff-file .line_content {
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.diff-wrap-lines .line_content {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,3 +17,56 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.shortcut-mappings {
|
||||
font-size: 12px;
|
||||
color: #555;
|
||||
|
||||
tbody:first-child tr:first-child {
|
||||
padding-top: 0
|
||||
}
|
||||
|
||||
th {
|
||||
padding-top: 15px;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
color: #333;
|
||||
text-align: left
|
||||
}
|
||||
|
||||
td {
|
||||
padding-top: 3px;
|
||||
padding-bottom: 3px;
|
||||
vertical-align: top;
|
||||
line-height: 20px
|
||||
}
|
||||
|
||||
.shortcut {
|
||||
padding-right: 10px;
|
||||
color: #999;
|
||||
text-align: right;
|
||||
white-space: nowrap
|
||||
}
|
||||
|
||||
.key {
|
||||
@extend .label;
|
||||
@extend .label-inverse;
|
||||
font: 11px Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
||||
padding: 3px 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
position: relative;
|
||||
overflow-y: auto;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
body.modal-open {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.modal .modal-dialog {
|
||||
width: 860px;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,21 @@
|
||||
}
|
||||
|
||||
.login-box{
|
||||
padding: 0 15px;
|
||||
|
||||
.login-heading h3 {
|
||||
font-weight: 300;
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
.login-footer {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 12px !important;
|
||||
@extend .btn-block;
|
||||
}
|
||||
}
|
||||
|
||||
.brand-image {
|
||||
@@ -19,7 +34,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.login-logo{
|
||||
.login-logo {
|
||||
margin: 10px 0 30px 0;
|
||||
display: block;
|
||||
}
|
||||
@@ -64,4 +79,8 @@
|
||||
color: #a00;
|
||||
}
|
||||
}
|
||||
|
||||
.brand-holder {
|
||||
border-right: 1px solid #EEE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,39 +35,31 @@
|
||||
width: 1%;
|
||||
&.active {
|
||||
a {
|
||||
color: #333;
|
||||
color: $link_color;
|
||||
font-weight: bold;
|
||||
&:after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: relative;
|
||||
bottom: 8px;
|
||||
left: 50%;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-color: transparent transparent #333 transparent;
|
||||
bottom: -1px;
|
||||
border-color: $link_color;
|
||||
border-style: solid;
|
||||
border-width: 6px;
|
||||
margin-left: -6px;
|
||||
border-width: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
a {
|
||||
color: $link_color;
|
||||
color: $link_hover_color;
|
||||
&:after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: relative;
|
||||
bottom: 8px;
|
||||
left: 50%;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-color: transparent transparent $link_color transparent;
|
||||
bottom: -1px;
|
||||
border-color: $link_hover_color;
|
||||
border-style: solid;
|
||||
border-width: 6px;
|
||||
margin-left: -6px;
|
||||
border-width: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -90,7 +82,6 @@
|
||||
line-height: 34px;
|
||||
color: #777;
|
||||
text-shadow: 0 1px 1px white;
|
||||
padding: 0 10px;
|
||||
text-decoration: none;
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ ul.notes {
|
||||
.discussion-header,
|
||||
.note-header {
|
||||
@extend .cgray;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 15px;
|
||||
|
||||
.avatar {
|
||||
@@ -43,34 +42,19 @@ ul.notes {
|
||||
}
|
||||
|
||||
.discussion {
|
||||
padding: 10px 0;
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
position:relative;
|
||||
border-bottom: 1px solid #EEE;
|
||||
|
||||
.discussion-body {
|
||||
margin-left: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
.note {
|
||||
padding: 8px 0;
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
position:relative;
|
||||
border-bottom: 1px solid #eee;
|
||||
p { color: $style_color; }
|
||||
|
||||
.avatar {
|
||||
margin-top: 3px;
|
||||
}
|
||||
.attachment {
|
||||
font-size: 14px;
|
||||
}
|
||||
.note-body {
|
||||
@include md-typography;
|
||||
margin-left: 43px;
|
||||
}
|
||||
.note-header {
|
||||
padding-bottom: 3px;
|
||||
@@ -80,11 +64,6 @@ ul.notes {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
.note:target {
|
||||
-webkit-animation:target-note 2s linear;
|
||||
background: #fffff0;
|
||||
}
|
||||
}
|
||||
|
||||
.diff-file .notes_holder {
|
||||
@@ -99,7 +78,7 @@ ul.notes {
|
||||
&.notes_line {
|
||||
text-align: center;
|
||||
padding: 10px 0;
|
||||
background: #eee;
|
||||
background: #FFF;
|
||||
}
|
||||
&.notes_line2 {
|
||||
text-align: center;
|
||||
@@ -111,6 +90,9 @@ ul.notes {
|
||||
border-width: 1px 0;
|
||||
padding-top: 0;
|
||||
vertical-align: top;
|
||||
&.parallel{
|
||||
border-width: 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -362,3 +344,7 @@ ul.notes {
|
||||
border-top: 1px solid #DDD;
|
||||
}
|
||||
}
|
||||
|
||||
.discussion-notes-count {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
@@ -15,62 +15,64 @@
|
||||
}
|
||||
|
||||
.project-home-panel {
|
||||
border-bottom: 1px solid #DDD;
|
||||
padding-bottom: 15px;
|
||||
margin-bottom: 30px;
|
||||
margin-bottom: 15px;
|
||||
|
||||
&.empty-project {
|
||||
border-bottom: 0px;
|
||||
padding-bottom: 15px;
|
||||
margin-bottom: 0px;
|
||||
border-bottom: 0px;
|
||||
padding-bottom: 15px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.project-home-title {
|
||||
font-size: 18px;
|
||||
color: #444;
|
||||
margin: 0;
|
||||
line-height: 32px;
|
||||
}
|
||||
.project-home-dropdown {
|
||||
margin-left: 10px;
|
||||
float: right;
|
||||
}
|
||||
.project-home-extra {
|
||||
margin-top: 15px;
|
||||
|
||||
.project-home-row {
|
||||
@extend .clearfix;
|
||||
margin-bottom: 15px;
|
||||
|
||||
.project-home-desc {
|
||||
float: left;
|
||||
color: #777;
|
||||
margin-bottom: 10px;
|
||||
color: #666;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.project-home-links {
|
||||
.star-fork-buttons {
|
||||
float: right;
|
||||
a {
|
||||
margin-left: 10px;
|
||||
font-weight: 500;
|
||||
width: 200px;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
|
||||
.star-buttons, .fork-buttons {
|
||||
float: right;
|
||||
margin-left: 20px;
|
||||
|
||||
.count {
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.visibility-level-label {
|
||||
font-size: 17px;
|
||||
background: #f1f1f1;
|
||||
border-radius: 4px;
|
||||
color: #444;
|
||||
position: absolute;
|
||||
margin-left: -55px;
|
||||
text-shadow: 0 1px 1px #FFF;
|
||||
width: 40px;
|
||||
text-align: center;
|
||||
padding: 6px;
|
||||
|
||||
color: #555;
|
||||
font-weight: bold;
|
||||
i {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.project-home-links {
|
||||
padding: 10px 0px;
|
||||
float: right;
|
||||
a {
|
||||
margin-left: 10px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.git-clone-holder {
|
||||
.project-home-dropdown + & {
|
||||
margin-right: 45px;
|
||||
@@ -159,6 +161,7 @@ ul.nav.nav-projects-tabs {
|
||||
li {
|
||||
.project-info {
|
||||
margin-bottom: 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.access-icon {
|
||||
@@ -195,8 +198,8 @@ ul.nav.nav-projects-tabs {
|
||||
white-space: normal;
|
||||
text-align: left;
|
||||
padding: 10px 15px;
|
||||
background-color: #F1f1f1;
|
||||
border-color: #EEE;
|
||||
background-color: #F9F9F9;
|
||||
border-color: #DDD;
|
||||
|
||||
&:hover {
|
||||
background-color: #eee;
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
.search-results {
|
||||
.search-result-row {
|
||||
border-bottom: 1px solid #EEE;
|
||||
padding-bottom: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
@@ -17,9 +17,17 @@ class Projects::BranchesController < Projects::ApplicationController
|
||||
end
|
||||
|
||||
def create
|
||||
@branch = CreateBranchService.new.execute(project, params[:branch_name], params[:ref], current_user)
|
||||
|
||||
redirect_to project_tree_path(@project, @branch.name)
|
||||
result = CreateBranchService.new.execute(project,
|
||||
params[:branch_name],
|
||||
params[:ref],
|
||||
current_user)
|
||||
if result[:status] == :success
|
||||
@branch = result[:branch]
|
||||
redirect_to project_tree_path(@project, @branch.name)
|
||||
else
|
||||
@error = result[:message]
|
||||
render action: 'new'
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
|
||||
@@ -31,7 +31,7 @@ class Projects::EditTreeController < Projects::BaseTreeController
|
||||
|
||||
diffy = Diffy::Diff.new(@blob.data, @content, diff: '-U 3',
|
||||
include_diff_info: true)
|
||||
@diff = Gitlab::DiffParser.new(diffy.diff.scan(/.*\n/))
|
||||
@diff_lines = Gitlab::Diff::Parser.new.parse(diffy.diff.scan(/.*\n/))
|
||||
|
||||
render layout: false
|
||||
end
|
||||
|
||||
@@ -20,7 +20,7 @@ class Projects::IssuesController < Projects::ApplicationController
|
||||
terms = params['issue_search']
|
||||
|
||||
@issues = issues_filtered
|
||||
@issues = @issues.where("title LIKE ? OR description LIKE ?", "%#{terms}%", "%#{terms}%") if terms.present?
|
||||
@issues = @issues.full_search(terms) if terms.present?
|
||||
@issues = @issues.page(params[:page]).per(20)
|
||||
|
||||
assignee_id, milestone_id = params[:assignee_id], params[:milestone_id]
|
||||
|
||||
@@ -52,7 +52,7 @@ class Projects::LabelsController < Projects::ApplicationController
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to project_labels_path(@project), notice: 'Label was removed' }
|
||||
format.js { render nothing: true }
|
||||
format.js
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -30,8 +30,10 @@ class Projects::NotesController < Projects::ApplicationController
|
||||
end
|
||||
|
||||
def update
|
||||
note.update_attributes(note_params)
|
||||
note.reset_events_cache
|
||||
if note.editable?
|
||||
note.update_attributes(note_params)
|
||||
note.reset_events_cache
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.json { render_note_json(note) }
|
||||
@@ -40,8 +42,10 @@ class Projects::NotesController < Projects::ApplicationController
|
||||
end
|
||||
|
||||
def destroy
|
||||
note.destroy
|
||||
note.reset_events_cache
|
||||
if note.editable?
|
||||
note.destroy
|
||||
note.reset_events_cache
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.js { render nothing: true }
|
||||
|
||||
@@ -29,12 +29,10 @@ class Projects::RawController < Projects::ApplicationController
|
||||
private
|
||||
|
||||
def get_blob_type
|
||||
if @blob.mime_type =~ /html|javascript/
|
||||
if @blob.text?
|
||||
'text/plain; charset=utf-8'
|
||||
elsif @blob.name =~ /(?:msi|exe|rar|r0\d|7z|7zip|zip)$/
|
||||
'application/octet-stream'
|
||||
else
|
||||
@blob.mime_type
|
||||
'application/octet-stream'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -63,7 +63,7 @@ class Projects::SnippetsController < Projects::ApplicationController
|
||||
def raw
|
||||
send_data(
|
||||
@snippet.content,
|
||||
type: "text/plain",
|
||||
type: 'text/plain; charset=utf-8',
|
||||
disposition: 'inline',
|
||||
filename: @snippet.file_name
|
||||
)
|
||||
|
||||
@@ -13,10 +13,16 @@ class Projects::TagsController < Projects::ApplicationController
|
||||
end
|
||||
|
||||
def create
|
||||
@tag = CreateTagService.new.execute(@project, params[:tag_name],
|
||||
params[:ref], current_user)
|
||||
|
||||
redirect_to project_tags_path(@project)
|
||||
result = CreateTagService.new.execute(@project, params[:tag_name],
|
||||
params[:ref], params[:message],
|
||||
current_user)
|
||||
if result[:status] == :success
|
||||
@tag = result[:tag]
|
||||
redirect_to project_tags_path(@project)
|
||||
else
|
||||
@error = result[:message]
|
||||
render action: 'new'
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
@@ -28,7 +34,7 @@ class Projects::TagsController < Projects::ApplicationController
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to project_tags_path }
|
||||
format.js { render nothing: true }
|
||||
format.js
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -103,7 +103,15 @@ class ProjectsController < ApplicationController
|
||||
::Projects::DestroyService.new(@project, current_user, {}).execute
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to root_path }
|
||||
format.html do
|
||||
flash[:alert] = "Project deleted."
|
||||
|
||||
if request.referer.include?("/admin")
|
||||
redirect_to admin_projects_path
|
||||
else
|
||||
redirect_to projects_dashboard_path
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -4,14 +4,33 @@ class SearchController < ApplicationController
|
||||
def show
|
||||
@project = Project.find_by(id: params[:project_id]) if params[:project_id].present?
|
||||
@group = Group.find_by(id: params[:group_id]) if params[:group_id].present?
|
||||
@scope = params[:scope]
|
||||
@show_snippets = params[:snippets].eql? 'true'
|
||||
|
||||
if @project
|
||||
return access_denied! unless can?(current_user, :download_code, @project)
|
||||
@search_results = if @project
|
||||
return access_denied! unless can?(current_user, :download_code, @project)
|
||||
|
||||
@search_results = Search::ProjectService.new(@project, current_user, params).execute
|
||||
else
|
||||
@search_results = Search::GlobalService.new(current_user, params).execute
|
||||
end
|
||||
unless %w(blobs notes issues merge_requests wiki_blobs).
|
||||
include?(@scope)
|
||||
@scope = 'blobs'
|
||||
end
|
||||
|
||||
Search::ProjectService.new(@project, current_user, params).execute
|
||||
elsif @show_snippets
|
||||
unless %w(snippet_blobs snippet_titles).include?(@scope)
|
||||
@scope = 'snippet_blobs'
|
||||
end
|
||||
|
||||
Search::SnippetService.new(current_user, params).execute
|
||||
else
|
||||
unless %w(projects issues merge_requests).include?(@scope)
|
||||
@scope = 'projects'
|
||||
end
|
||||
|
||||
Search::GlobalService.new(current_user, params).execute
|
||||
end
|
||||
|
||||
@objects = @search_results.objects(@scope, params[:page])
|
||||
end
|
||||
|
||||
def autocomplete
|
||||
|
||||
@@ -86,7 +86,7 @@ class SnippetsController < ApplicationController
|
||||
def raw
|
||||
send_data(
|
||||
@snippet.content,
|
||||
type: "text/plain",
|
||||
type: 'text/plain; charset=utf-8',
|
||||
disposition: 'inline',
|
||||
filename: @snippet.file_name
|
||||
)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# BaseFinder
|
||||
# IssuableFinder
|
||||
#
|
||||
# Used to filter Issues and MergeRequests collections by set of params
|
||||
#
|
||||
@@ -16,7 +16,9 @@
|
||||
# label_name: string
|
||||
# sort: string
|
||||
#
|
||||
class BaseFinder
|
||||
require_relative 'projects_finder'
|
||||
|
||||
class IssuableFinder
|
||||
attr_accessor :current_user, :params
|
||||
|
||||
def execute(current_user, params)
|
||||
@@ -15,7 +15,7 @@
|
||||
# label_name: string
|
||||
# sort: string
|
||||
#
|
||||
class IssuesFinder < BaseFinder
|
||||
class IssuesFinder < IssuableFinder
|
||||
def klass
|
||||
Issue
|
||||
end
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
# label_name: string
|
||||
# sort: string
|
||||
#
|
||||
class MergeRequestsFinder < BaseFinder
|
||||
class MergeRequestsFinder < IssuableFinder
|
||||
def klass
|
||||
MergeRequest
|
||||
end
|
||||
|
||||
@@ -178,6 +178,8 @@ module ApplicationHelper
|
||||
def search_placeholder
|
||||
if @project && @project.persisted?
|
||||
"Search in this project"
|
||||
elsif @snippet || @snippets || @show_snippets
|
||||
'Search snippets'
|
||||
elsif @group && @group.persisted?
|
||||
"Search in this group"
|
||||
else
|
||||
|
||||
@@ -16,38 +16,6 @@ module CommitsHelper
|
||||
commit_person_link(commit, options.merge(source: :committer))
|
||||
end
|
||||
|
||||
def each_diff_line(diff, index)
|
||||
Gitlab::DiffParser.new(diff.diff.lines.to_a, diff.new_path)
|
||||
.each do |full_line, type, line_code, line_new, line_old|
|
||||
yield(full_line, type, line_code, line_new, line_old)
|
||||
end
|
||||
end
|
||||
|
||||
def each_diff_line_near(diff, index, expected_line_code)
|
||||
max_number_of_lines = 16
|
||||
|
||||
prev_match_line = nil
|
||||
prev_lines = []
|
||||
|
||||
each_diff_line(diff, index) do |full_line, type, line_code, line_new, line_old|
|
||||
line = [full_line, type, line_code, line_new, line_old]
|
||||
if line_code != expected_line_code
|
||||
if type == "match"
|
||||
prev_lines.clear
|
||||
prev_match_line = line
|
||||
else
|
||||
prev_lines.push(line)
|
||||
prev_lines.shift if prev_lines.length >= max_number_of_lines
|
||||
end
|
||||
else
|
||||
yield(prev_match_line) if !prev_match_line.nil?
|
||||
prev_lines.each { |ln| yield(ln) }
|
||||
yield(line)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def image_diff_class(diff)
|
||||
if diff.deleted_file
|
||||
"deleted"
|
||||
@@ -63,14 +31,6 @@ module CommitsHelper
|
||||
escape_javascript(render "projects/commits/#{template}", commit: commit, project: project) unless commit.nil?
|
||||
end
|
||||
|
||||
def diff_line_content(line)
|
||||
if line.blank?
|
||||
" "
|
||||
else
|
||||
line
|
||||
end
|
||||
end
|
||||
|
||||
# Breadcrumb links for a Project and, if applicable, a tree path
|
||||
def commits_breadcrumbs
|
||||
return unless @project && @ref
|
||||
@@ -105,82 +65,6 @@ module CommitsHelper
|
||||
branches.sort.map { |branch| link_to(branch, project_tree_path(project, branch)) }.join(", ").html_safe
|
||||
end
|
||||
|
||||
def parallel_diff_lines(project, commit, diff, file)
|
||||
old_file = project.repository.blob_at(commit.parent_id, diff.old_path) if commit.parent_id
|
||||
deleted_lines = {}
|
||||
added_lines = {}
|
||||
each_diff_line(diff, 0) do |line, type, line_code, line_new, line_old|
|
||||
if type == "old"
|
||||
deleted_lines[line_old] = { line_code: line_code, type: type, line: line }
|
||||
elsif type == "new"
|
||||
added_lines[line_new] = { line_code: line_code, type: type, line: line }
|
||||
end
|
||||
end
|
||||
max_length = old_file ? [old_file.loc, file.loc].max : file.loc
|
||||
|
||||
offset1 = 0
|
||||
offset2 = 0
|
||||
old_lines = []
|
||||
new_lines = []
|
||||
|
||||
max_length.times do |line_index|
|
||||
line_index1 = line_index - offset1
|
||||
line_index2 = line_index - offset2
|
||||
deleted_line = deleted_lines[line_index1 + 1]
|
||||
added_line = added_lines[line_index2 + 1]
|
||||
old_line = old_file.lines[line_index1] if old_file
|
||||
new_line = file.lines[line_index2]
|
||||
|
||||
if deleted_line && added_line
|
||||
elsif deleted_line
|
||||
new_line = nil
|
||||
offset2 += 1
|
||||
elsif added_line
|
||||
old_line = nil
|
||||
offset1 += 1
|
||||
end
|
||||
|
||||
old_lines[line_index] = DiffLine.new
|
||||
new_lines[line_index] = DiffLine.new
|
||||
|
||||
# old
|
||||
if line_index == 0 && diff.new_file
|
||||
old_lines[line_index].type = :file_created
|
||||
old_lines[line_index].content = 'File was created'
|
||||
elsif deleted_line
|
||||
old_lines[line_index].type = :deleted
|
||||
old_lines[line_index].content = old_line
|
||||
old_lines[line_index].num = line_index1 + 1
|
||||
old_lines[line_index].code = deleted_line[:line_code]
|
||||
elsif old_line
|
||||
old_lines[line_index].type = :no_change
|
||||
old_lines[line_index].content = old_line
|
||||
old_lines[line_index].num = line_index1 + 1
|
||||
else
|
||||
old_lines[line_index].type = :added
|
||||
end
|
||||
|
||||
# new
|
||||
if line_index == 0 && diff.deleted_file
|
||||
new_lines[line_index].type = :file_deleted
|
||||
new_lines[line_index].content = "File was deleted"
|
||||
elsif added_line
|
||||
new_lines[line_index].type = :added
|
||||
new_lines[line_index].num = line_index2 + 1
|
||||
new_lines[line_index].content = new_line
|
||||
new_lines[line_index].code = added_line[:line_code]
|
||||
elsif new_line
|
||||
new_lines[line_index].type = :no_change
|
||||
new_lines[line_index].num = line_index2 + 1
|
||||
new_lines[line_index].content = new_line
|
||||
else
|
||||
new_lines[line_index].type = :deleted
|
||||
end
|
||||
end
|
||||
|
||||
return old_lines, new_lines
|
||||
end
|
||||
|
||||
def link_to_browse_code(project, commit)
|
||||
if current_controller?(:projects, :commits)
|
||||
if @repo.blob_at(commit.id, @path)
|
||||
@@ -229,14 +113,6 @@ module CommitsHelper
|
||||
end
|
||||
end
|
||||
|
||||
def diff_file_mode_changed?(diff)
|
||||
diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode
|
||||
end
|
||||
|
||||
def unfold_bottom_class(bottom)
|
||||
(bottom) ? 'js-unfold-bottom' : ''
|
||||
end
|
||||
|
||||
def view_file_btn(commit_sha, diff, project)
|
||||
link_to project_blob_path(project, tree_join(commit_sha, diff.new_path)),
|
||||
class: 'btn btn-small view-file js-view-file' do
|
||||
|
||||
+103
-5
@@ -1,14 +1,20 @@
|
||||
module DiffHelper
|
||||
def safe_diff_files(diffs)
|
||||
def allowed_diff_size
|
||||
if diff_hard_limit_enabled?
|
||||
diffs.first(Commit::DIFF_HARD_LIMIT_FILES)
|
||||
Commit::DIFF_HARD_LIMIT_FILES
|
||||
else
|
||||
diffs.first(Commit::DIFF_SAFE_FILES)
|
||||
Commit::DIFF_SAFE_FILES
|
||||
end
|
||||
end
|
||||
|
||||
def show_diff_size_warninig?(diffs)
|
||||
safe_diff_files(diffs).size < diffs.size
|
||||
def safe_diff_files(diffs)
|
||||
diffs.first(allowed_diff_size).map do |diff|
|
||||
Gitlab::Diff::File.new(diff)
|
||||
end
|
||||
end
|
||||
|
||||
def show_diff_size_warning?(diffs)
|
||||
diffs.size > allowed_diff_size
|
||||
end
|
||||
|
||||
def diff_hard_limit_enabled?
|
||||
@@ -19,4 +25,96 @@ module DiffHelper
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def generate_line_code(file_path, line)
|
||||
Gitlab::Diff::LineCode.generate(file_path, line.new_pos, line.old_pos)
|
||||
end
|
||||
|
||||
def parallel_diff(diff_file, index)
|
||||
lines = []
|
||||
skip_next = false
|
||||
|
||||
# Building array of lines
|
||||
#
|
||||
# [
|
||||
# left_type, left_line_number, left_line_content, left_line_code,
|
||||
# right_line_type, right_line_number, right_line_content, right_line_code
|
||||
# ]
|
||||
#
|
||||
diff_file.diff_lines.each do |line|
|
||||
|
||||
full_line = line.text
|
||||
type = line.type
|
||||
line_code = generate_line_code(diff_file.file_path, line)
|
||||
line_new = line.new_pos
|
||||
line_old = line.old_pos
|
||||
|
||||
next_line = diff_file.next_line(line.index)
|
||||
|
||||
if next_line
|
||||
next_line_code = generate_line_code(diff_file.file_path, next_line)
|
||||
next_type = next_line.type
|
||||
next_line = next_line.text
|
||||
end
|
||||
|
||||
if type == 'match' || type.nil?
|
||||
# line in the right panel is the same as in the left one
|
||||
line = [type, line_old, full_line, line_code, type, line_new, full_line, line_code]
|
||||
lines.push(line)
|
||||
elsif type == 'old'
|
||||
if next_type == 'new'
|
||||
# Left side has text removed, right side has text added
|
||||
line = [type, line_old, full_line, line_code, next_type, line_new, next_line, next_line_code]
|
||||
lines.push(line)
|
||||
skip_next = true
|
||||
elsif next_type == 'old' || next_type.nil?
|
||||
# Left side has text removed, right side doesn't have any change
|
||||
# No next line code, no new line number, no new line text
|
||||
line = [type, line_old, full_line, line_code, next_type, nil, " ", nil]
|
||||
lines.push(line)
|
||||
end
|
||||
elsif type == 'new'
|
||||
if skip_next
|
||||
# Change has been already included in previous line so no need to do it again
|
||||
skip_next = false
|
||||
next
|
||||
else
|
||||
# Change is only on the right side, left side has no change
|
||||
line = [nil, nil, " ", line_code, type, line_new, full_line, line_code]
|
||||
lines.push(line)
|
||||
end
|
||||
end
|
||||
end
|
||||
lines
|
||||
end
|
||||
|
||||
def unfold_bottom_class(bottom)
|
||||
(bottom) ? 'js-unfold-bottom' : ''
|
||||
end
|
||||
|
||||
def diff_line_content(line)
|
||||
if line.blank?
|
||||
" "
|
||||
else
|
||||
line
|
||||
end
|
||||
end
|
||||
|
||||
def line_comments
|
||||
@line_comments ||= @line_notes.group_by(&:line_code)
|
||||
end
|
||||
|
||||
def organize_comments(type_left, type_right, line_code_left, line_code_right)
|
||||
comments_left = comments_right = nil
|
||||
|
||||
unless type_left.nil? && type_right == 'new'
|
||||
comments_left = line_comments[line_code_left]
|
||||
end
|
||||
|
||||
unless type_left.nil? && type_right.nil?
|
||||
comments_right = line_comments[line_code_right]
|
||||
end
|
||||
|
||||
[comments_left, comments_right]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -52,6 +52,8 @@ module EventsHelper
|
||||
"#{event.author_name} #{event.push_action_name} #{event.ref_type} #{event.ref_name} at #{event.project_name}"
|
||||
elsif event.membership_changed?
|
||||
"#{event.author_name} #{event.action_name} #{event.project_name}"
|
||||
elsif event.note? && event.note_commit?
|
||||
"#{event.author_name} commented on #{event.note_target_type} #{event.note_short_commit_id} at #{event.project_name}"
|
||||
elsif event.note?
|
||||
"#{event.author_name} commented on #{event.note_target_type} ##{truncate event.note_target_iid} at #{event.project_name}"
|
||||
else
|
||||
@@ -64,6 +66,8 @@ module EventsHelper
|
||||
project_issue_url(event.project, event.issue)
|
||||
elsif event.merge_request?
|
||||
project_merge_request_url(event.project, event.merge_request)
|
||||
elsif event.note? && event.note_commit?
|
||||
project_commit_url(event.project, event.note_target)
|
||||
elsif event.note?
|
||||
if event.note_target
|
||||
if event.note_commit?
|
||||
@@ -94,6 +98,8 @@ module EventsHelper
|
||||
render "events/event_push", event: event
|
||||
elsif event.merge_request?
|
||||
render "events/event_merge_request", merge_request: event.merge_request
|
||||
elsif event.push?
|
||||
render "events/event_push", event: event
|
||||
elsif event.note?
|
||||
render "events/event_note", note: event.note
|
||||
end
|
||||
|
||||
@@ -123,7 +123,7 @@ module ProjectsHelper
|
||||
end
|
||||
|
||||
def link_to_toggle_star(title, starred, signed_in)
|
||||
cls = 'btn btn-block'
|
||||
cls = 'star-btn'
|
||||
cls += ' disabled' unless signed_in
|
||||
|
||||
toggle_html = content_tag('span', class: 'toggle') do
|
||||
@@ -151,7 +151,7 @@ module ProjectsHelper
|
||||
|
||||
content_tag 'span', class: starred ? 'turn-on' : 'turn-off' do
|
||||
link_to toggle_star_project_path(@project), link_opts do
|
||||
toggle_html + count_html
|
||||
toggle_html + ' ' + count_html
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -261,4 +261,10 @@ module ProjectsHelper
|
||||
project_blob_path(project, tree_join(project.default_branch, project.repository.contribution_guide.name))
|
||||
end
|
||||
end
|
||||
|
||||
def hidden_pass_url(original_url)
|
||||
result = URI(original_url)
|
||||
result.password = '*****' if result.password.present?
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
@@ -91,4 +91,21 @@ module SearchHelper
|
||||
def search_result_sanitize(str)
|
||||
Sanitize.clean(str)
|
||||
end
|
||||
|
||||
def search_filter_path(options={})
|
||||
exist_opts = {
|
||||
search: params[:search],
|
||||
project_id: params[:project_id],
|
||||
group_id: params[:group_id],
|
||||
scope: params[:scope]
|
||||
}
|
||||
|
||||
options = exist_opts.merge(options)
|
||||
search_path(options)
|
||||
end
|
||||
|
||||
# Sanitize html generated after parsing markdown from issue description or comment
|
||||
def search_md_sanitize(html)
|
||||
sanitize(html, tags: %w(a p ol ul li pre code))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -49,6 +49,10 @@ module Issuable
|
||||
where("LOWER(title) like :query", query: "%#{query.downcase}%")
|
||||
end
|
||||
|
||||
def full_search(query)
|
||||
where("LOWER(title) like :query OR LOWER(description) like :query", query: "%#{query.downcase}%")
|
||||
end
|
||||
|
||||
def sort(method)
|
||||
case method.to_s
|
||||
when 'newest' then reorder("#{table_name}.created_at DESC")
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
# milestone_id :integer
|
||||
# state :string(255)
|
||||
# iid :integer
|
||||
# attachment :string(255)
|
||||
#
|
||||
|
||||
require 'carrierwave/orm/activerecord'
|
||||
|
||||
@@ -31,7 +31,9 @@ class Key < ActiveRecord::Base
|
||||
|
||||
after_create :add_to_shell
|
||||
after_create :notify_user
|
||||
after_create :post_create_hook
|
||||
after_destroy :remove_from_shell
|
||||
after_destroy :post_destroy_hook
|
||||
|
||||
def strip_white_space
|
||||
self.key = key.strip unless key.blank?
|
||||
@@ -58,6 +60,10 @@ class Key < ActiveRecord::Base
|
||||
NotificationService.new.new_key(self)
|
||||
end
|
||||
|
||||
def post_create_hook
|
||||
SystemHooksService.new.execute_hooks_for(self, :create)
|
||||
end
|
||||
|
||||
def remove_from_shell
|
||||
GitlabShellWorker.perform_async(
|
||||
:remove_key,
|
||||
@@ -66,6 +72,10 @@ class Key < ActiveRecord::Base
|
||||
)
|
||||
end
|
||||
|
||||
def post_destroy_hook
|
||||
SystemHooksService.new.execute_hooks_for(self, :destroy)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def generate_fingerpint
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: labels
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# title :string(255)
|
||||
# color :string(255)
|
||||
# project_id :integer
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
#
|
||||
|
||||
class Label < ActiveRecord::Base
|
||||
DEFAULT_COLOR = '#428BCA'
|
||||
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: label_links
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# label_id :integer
|
||||
# target_id :integer
|
||||
# target_type :string(255)
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
#
|
||||
|
||||
class LabelLink < ActiveRecord::Base
|
||||
belongs_to :target, polymorphic: true
|
||||
belongs_to :label
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
# target_project_id :integer not null
|
||||
# iid :integer
|
||||
# description :text
|
||||
# position :integer default(0)
|
||||
#
|
||||
|
||||
require Rails.root.join("app/models/commit")
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# Table name: merge_request_diffs
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# state :string(255) default("collected"), not null
|
||||
# state :string(255)
|
||||
# st_commits :text
|
||||
# st_diffs :text
|
||||
# merge_request_id :integer not null
|
||||
|
||||
@@ -178,12 +178,6 @@ module Network
|
||||
space = find_free_space(time_range, 2, space_base)
|
||||
leaves.each do |l|
|
||||
l.spaces << space
|
||||
# Also add space to parent
|
||||
l.parents(@map).each do |parent|
|
||||
if 0 < parent.space && parent.space < space
|
||||
parent.spaces << space
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# and mark it as reserved
|
||||
|
||||
+72
-6
@@ -117,6 +117,25 @@ class Note < ActiveRecord::Base
|
||||
})
|
||||
end
|
||||
|
||||
def create_new_commits_note(noteable, project, author, commits)
|
||||
commits_text = ActionController::Base.helpers.pluralize(commits.size, 'new commit')
|
||||
body = "Added #{commits_text}:\n\n"
|
||||
|
||||
commits.each do |commit|
|
||||
message = "* #{commit.short_id} - #{commit.title}"
|
||||
body << message
|
||||
body << "\n"
|
||||
end
|
||||
|
||||
create(
|
||||
noteable: noteable,
|
||||
project: project,
|
||||
author: author,
|
||||
note: body,
|
||||
system: true
|
||||
)
|
||||
end
|
||||
|
||||
def discussions_from_notes(notes)
|
||||
discussion_ids = []
|
||||
discussions = []
|
||||
@@ -146,6 +165,10 @@ class Note < ActiveRecord::Base
|
||||
def cross_reference_exists?(noteable, mentioner)
|
||||
where(noteable_id: noteable.id, system: true, note: "_mentioned in #{mentioner.gfm_reference}_").any?
|
||||
end
|
||||
|
||||
def search(query)
|
||||
where("note like :query", query: "%#{query}%")
|
||||
end
|
||||
end
|
||||
|
||||
def commit_author
|
||||
@@ -186,9 +209,10 @@ class Note < ActiveRecord::Base
|
||||
noteable.diffs.each do |mr_diff|
|
||||
next unless mr_diff.new_path == self.diff.new_path
|
||||
|
||||
Gitlab::DiffParser.new(mr_diff.diff.lines.to_a, mr_diff.new_path).
|
||||
each do |full_line, type, line_code, line_new, line_old|
|
||||
if full_line == diff_line
|
||||
lines = Gitlab::Diff::Parser.new.parse(mr_diff.diff.lines.to_a)
|
||||
|
||||
lines.each do |line|
|
||||
if line.text == diff_line
|
||||
return true
|
||||
end
|
||||
end
|
||||
@@ -209,6 +233,14 @@ class Note < ActiveRecord::Base
|
||||
diff.new_path if diff
|
||||
end
|
||||
|
||||
def file_path
|
||||
if diff.new_path.present?
|
||||
diff.new_path
|
||||
elsif diff.old_path.present?
|
||||
diff.old_path
|
||||
end
|
||||
end
|
||||
|
||||
def diff_old_line
|
||||
line_code.split('_')[1].to_i
|
||||
end
|
||||
@@ -217,19 +249,49 @@ class Note < ActiveRecord::Base
|
||||
line_code.split('_')[2].to_i
|
||||
end
|
||||
|
||||
def generate_line_code(line)
|
||||
Gitlab::Diff::LineCode.generate(file_path, line.new_pos, line.old_pos)
|
||||
end
|
||||
|
||||
def diff_line
|
||||
return @diff_line if @diff_line
|
||||
|
||||
if diff
|
||||
Gitlab::DiffParser.new(diff.diff.lines.to_a, diff.new_path)
|
||||
.each do |full_line, type, line_code, line_new, line_old|
|
||||
@diff_line = full_line if line_code == self.line_code
|
||||
diff_lines.each do |line|
|
||||
if generate_line_code(line) == self.line_code
|
||||
@diff_line = line.text
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@diff_line
|
||||
end
|
||||
|
||||
def truncated_diff_lines
|
||||
max_number_of_lines = 16
|
||||
prev_match_line = nil
|
||||
prev_lines = []
|
||||
|
||||
diff_lines.each do |line|
|
||||
if generate_line_code(line) != self.line_code
|
||||
if line.type == "match"
|
||||
prev_lines.clear
|
||||
prev_match_line = line
|
||||
else
|
||||
prev_lines.push(line)
|
||||
prev_lines.shift if prev_lines.length >= max_number_of_lines
|
||||
end
|
||||
else
|
||||
prev_lines << line
|
||||
return prev_lines
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def diff_lines
|
||||
@diff_lines ||= Gitlab::Diff::Parser.new.parse(diff.diff.lines.to_a)
|
||||
end
|
||||
|
||||
def discussion_id
|
||||
@discussion_id ||= Note.build_discussion_id(noteable_type, noteable_id || commit_id, line_code)
|
||||
end
|
||||
@@ -333,4 +395,8 @@ class Note < ActiveRecord::Base
|
||||
def set_references
|
||||
notice_added_references(project, author)
|
||||
end
|
||||
|
||||
def editable?
|
||||
!system
|
||||
end
|
||||
end
|
||||
|
||||
+25
-7
@@ -22,7 +22,8 @@
|
||||
# visibility_level :integer default(0), not null
|
||||
# archived :boolean default(FALSE), not null
|
||||
# import_status :string(255)
|
||||
# star_count :integer
|
||||
# repository_size :float default(0.0)
|
||||
# star_count :integer default(0), not null
|
||||
#
|
||||
|
||||
class Project < ActiveRecord::Base
|
||||
@@ -73,7 +74,7 @@ class Project < ActiveRecord::Base
|
||||
has_many :merge_requests, dependent: :destroy, foreign_key: "target_project_id"
|
||||
# Merge requests from source project should be kept when source project was removed
|
||||
has_many :fork_merge_requests, foreign_key: "source_project_id", class_name: MergeRequest
|
||||
has_many :issues, -> { order "state DESC, created_at DESC" }, dependent: :destroy
|
||||
has_many :issues, -> { order 'issues.state DESC, issues.created_at DESC' }, dependent: :destroy
|
||||
has_many :labels, dependent: :destroy
|
||||
has_many :services, dependent: :destroy
|
||||
has_many :events, dependent: :destroy
|
||||
@@ -183,11 +184,11 @@ class Project < ActiveRecord::Base
|
||||
joins(:issues, :notes, :merge_requests).order("issues.created_at, notes.created_at, merge_requests.created_at DESC")
|
||||
end
|
||||
|
||||
def search query
|
||||
def search(query)
|
||||
joins(:namespace).where("projects.archived = ?", false).where("projects.name LIKE :query OR projects.path LIKE :query OR namespaces.name LIKE :query OR projects.description LIKE :query", query: "%#{query}%")
|
||||
end
|
||||
|
||||
def search_by_title query
|
||||
def search_by_title(query)
|
||||
where("projects.archived = ?", false).where("LOWER(projects.name) LIKE :query", query: "%#{query.downcase}%")
|
||||
end
|
||||
|
||||
@@ -414,18 +415,35 @@ class Project < ActiveRecord::Base
|
||||
def update_merge_requests(oldrev, newrev, ref, user)
|
||||
return true unless ref =~ /heads/
|
||||
branch_name = ref.gsub("refs/heads/", "")
|
||||
c_ids = self.repository.commits_between(oldrev, newrev).map(&:id)
|
||||
commits = self.repository.commits_between(oldrev, newrev)
|
||||
c_ids = commits.map(&:id)
|
||||
|
||||
# Close merge requests
|
||||
mrs = self.merge_requests.opened.where(target_branch: branch_name).to_a
|
||||
mrs = mrs.select(&:last_commit).select { |mr| c_ids.include?(mr.last_commit.id) }
|
||||
mrs.each { |merge_request| MergeRequests::MergeService.new.execute(merge_request, user, nil) }
|
||||
|
||||
mrs.uniq.each do |merge_request|
|
||||
MergeRequests::MergeService.new.execute(merge_request, user, nil)
|
||||
end
|
||||
|
||||
# Update code for merge requests into project between project branches
|
||||
mrs = self.merge_requests.opened.by_branch(branch_name).to_a
|
||||
# Update code for merge requests between project and project fork
|
||||
mrs += self.fork_merge_requests.opened.by_branch(branch_name).to_a
|
||||
mrs.each { |merge_request| merge_request.reload_code; merge_request.mark_as_unchecked }
|
||||
|
||||
mrs.uniq.each do |merge_request|
|
||||
merge_request.reload_code
|
||||
merge_request.mark_as_unchecked
|
||||
end
|
||||
|
||||
# Add comment about pushing new commits to merge requests
|
||||
mrs = self.merge_requests.opened.where(source_branch: branch_name).to_a
|
||||
mrs += self.fork_merge_requests.opened.where(source_branch: branch_name).to_a
|
||||
|
||||
mrs.uniq.each do |merge_request|
|
||||
Note.create_new_commits_note(merge_request, merge_request.project,
|
||||
user, commits)
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
@@ -5,21 +5,17 @@
|
||||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# token :string(255)
|
||||
# project_id :integer not null
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# project_url :string(255)
|
||||
# subdomain :string(255)
|
||||
# room :string(255)
|
||||
# recipients :text
|
||||
# api_key :string(255)
|
||||
# properties :text
|
||||
#
|
||||
|
||||
class AssemblaService < Service
|
||||
include HTTParty
|
||||
|
||||
prop_accessor :token, :subdomain
|
||||
validates :token, presence: true, if: :activated?
|
||||
|
||||
def title
|
||||
|
||||
@@ -5,19 +5,15 @@
|
||||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# token :string(255)
|
||||
# project_id :integer not null
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# project_url :string(255)
|
||||
# subdomain :string(255)
|
||||
# room :string(255)
|
||||
# recipients :text
|
||||
# api_key :string(255)
|
||||
# properties :text
|
||||
#
|
||||
|
||||
class CampfireService < Service
|
||||
prop_accessor :token, :subdomain, :room
|
||||
validates :token, presence: true, if: :activated?
|
||||
|
||||
def title
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: services
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# project_id :integer not null
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# properties :text
|
||||
#
|
||||
|
||||
# Base class for CI services
|
||||
# List methods you need to implement to get your CI service
|
||||
# working with GitLab Merge Requests
|
||||
|
||||
@@ -5,19 +5,15 @@
|
||||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# token :string(255)
|
||||
# project_id :integer not null
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# project_url :string(255)
|
||||
# subdomain :string(255)
|
||||
# room :string(255)
|
||||
# recipients :text
|
||||
# api_key :string(255)
|
||||
# properties :text
|
||||
#
|
||||
|
||||
class EmailsOnPushService < Service
|
||||
prop_accessor :recipients
|
||||
validates :recipients, presence: true, if: :activated?
|
||||
|
||||
def title
|
||||
|
||||
@@ -5,21 +5,17 @@
|
||||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# token :string(255)
|
||||
# project_id :integer not null
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# project_url :string(255)
|
||||
# subdomain :string(255)
|
||||
# room :string(255)
|
||||
# recipients :text
|
||||
# api_key :string(255)
|
||||
# properties :text
|
||||
#
|
||||
|
||||
require "flowdock-git-hook"
|
||||
|
||||
class FlowdockService < Service
|
||||
prop_accessor :token
|
||||
validates :token, presence: true, if: :activated?
|
||||
|
||||
def title
|
||||
|
||||
@@ -5,21 +5,17 @@
|
||||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# token :string(255)
|
||||
# project_id :integer not null
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# project_url :string(255)
|
||||
# subdomain :string(255)
|
||||
# room :string(255)
|
||||
# recipients :text
|
||||
# api_key :string(255)
|
||||
# properties :text
|
||||
#
|
||||
|
||||
require "gemnasium/gitlab_service"
|
||||
|
||||
class GemnasiumService < Service
|
||||
prop_accessor :token, :api_key
|
||||
validates :token, :api_key, presence: true, if: :activated?
|
||||
|
||||
def title
|
||||
|
||||
@@ -5,19 +5,15 @@
|
||||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# token :string(255)
|
||||
# project_id :integer not null
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# project_url :string(255)
|
||||
# subdomain :string(255)
|
||||
# room :string(255)
|
||||
# recipients :text
|
||||
# api_key :string(255)
|
||||
# property :text
|
||||
#
|
||||
|
||||
class GitlabCiService < CiService
|
||||
prop_accessor :project_url, :token
|
||||
validates :project_url, presence: true, if: :activated?
|
||||
validates :token, presence: true, if: :activated?
|
||||
|
||||
|
||||
@@ -5,21 +5,17 @@
|
||||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# token :string(255)
|
||||
# project_id :integer not null
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# project_url :string(255)
|
||||
# subdomain :string(255)
|
||||
# room :string(255)
|
||||
# recipients :text
|
||||
# api_key :string(255)
|
||||
# properties :text
|
||||
#
|
||||
|
||||
class HipchatService < Service
|
||||
MAX_COMMITS = 3
|
||||
|
||||
prop_accessor :token, :room
|
||||
validates :token, presence: true, if: :activated?
|
||||
|
||||
def title
|
||||
|
||||
@@ -5,21 +5,17 @@
|
||||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# token :string(255)
|
||||
# project_id :integer not null
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# project_url :string(255)
|
||||
# subdomain :string(255)
|
||||
# room :string(255)
|
||||
# recipients :text
|
||||
# api_key :string(255)
|
||||
# properties :text
|
||||
#
|
||||
|
||||
class PivotaltrackerService < Service
|
||||
include HTTParty
|
||||
|
||||
prop_accessor :token
|
||||
validates :token, presence: true, if: :activated?
|
||||
|
||||
def title
|
||||
|
||||
@@ -5,19 +5,15 @@
|
||||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# token :string(255)
|
||||
# project_id :integer not null
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# project_url :string(255)
|
||||
# subdomain :string(255)
|
||||
# room :string(255)
|
||||
# recipients :text
|
||||
# api_key :string(255)
|
||||
# properties :text
|
||||
#
|
||||
|
||||
class SlackService < Service
|
||||
prop_accessor :room, :subdomain, :token
|
||||
validates :room, presence: true, if: :activated?
|
||||
validates :subdomain, presence: true, if: :activated?
|
||||
validates :token, presence: true, if: :activated?
|
||||
|
||||
@@ -2,8 +2,9 @@ class ProjectWiki
|
||||
include Gitlab::ShellAdapter
|
||||
|
||||
MARKUPS = {
|
||||
"Markdown" => :markdown,
|
||||
"RDoc" => :rdoc
|
||||
'Markdown' => :markdown,
|
||||
'RDoc' => :rdoc,
|
||||
'AsciiDoc' => :asciidoc
|
||||
}
|
||||
|
||||
class CouldNotCreateWikiError < StandardError; end
|
||||
|
||||
@@ -64,10 +64,10 @@ class Repository
|
||||
gitlab_shell.add_branch(path_with_namespace, branch_name, ref)
|
||||
end
|
||||
|
||||
def add_tag(tag_name, ref)
|
||||
def add_tag(tag_name, ref, message = nil)
|
||||
Rails.cache.delete(cache_key(:tag_names))
|
||||
|
||||
gitlab_shell.add_tag(path_with_namespace, tag_name, ref)
|
||||
gitlab_shell.add_tag(path_with_namespace, tag_name, ref, message)
|
||||
end
|
||||
|
||||
def rm_branch(branch_name)
|
||||
|
||||
+25
-7
@@ -5,23 +5,21 @@
|
||||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# token :string(255)
|
||||
# project_id :integer not null
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# project_url :string(255)
|
||||
# subdomain :string(255)
|
||||
# room :string(255)
|
||||
# recipients :text
|
||||
# api_key :string(255)
|
||||
#
|
||||
# properties :text
|
||||
|
||||
# To add new service you should build a class inherited from Service
|
||||
# and implement a set of methods
|
||||
class Service < ActiveRecord::Base
|
||||
serialize :properties, JSON
|
||||
|
||||
default_value_for :active, false
|
||||
|
||||
after_initialize :initialize_properties
|
||||
|
||||
belongs_to :project
|
||||
has_one :service_hook
|
||||
|
||||
@@ -35,6 +33,10 @@ class Service < ActiveRecord::Base
|
||||
:common
|
||||
end
|
||||
|
||||
def initialize_properties
|
||||
self.properties = {} if properties.nil?
|
||||
end
|
||||
|
||||
def title
|
||||
# implement inside child
|
||||
end
|
||||
@@ -63,4 +65,20 @@ class Service < ActiveRecord::Base
|
||||
def can_test?
|
||||
!project.empty_repo?
|
||||
end
|
||||
|
||||
# Provide convenient accessor methods
|
||||
# for each serialized property.
|
||||
def self.prop_accessor(*args)
|
||||
args.each do |arg|
|
||||
class_eval %{
|
||||
def #{arg}
|
||||
properties['#{arg}']
|
||||
end
|
||||
|
||||
def #{arg}=(value)
|
||||
self.properties['#{arg}'] = value
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -65,4 +65,18 @@ class Snippet < ActiveRecord::Base
|
||||
def expired?
|
||||
expires_at && expires_at < Time.current
|
||||
end
|
||||
|
||||
class << self
|
||||
def search(query)
|
||||
where('(title LIKE :query OR file_name LIKE :query)', query: "%#{query}%")
|
||||
end
|
||||
|
||||
def search_code(query)
|
||||
where('(content LIKE :query)', query: "%#{query}%")
|
||||
end
|
||||
|
||||
def accessible_to(user)
|
||||
where('private = ? OR author_id = ?', false, user)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
#
|
||||
# Table name: users_star_projects
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# starrer_id :integer not null
|
||||
# project_id :integer not null
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# id :integer not null, primary key
|
||||
# project_id :integer not null
|
||||
# user_id :integer not null
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
#
|
||||
|
||||
class UsersStarProject < ActiveRecord::Base
|
||||
|
||||
@@ -23,7 +23,7 @@ class WebHook < ActiveRecord::Base
|
||||
default_value_for :merge_requests_events, false
|
||||
|
||||
# HTTParty timeout
|
||||
default_timeout 10
|
||||
default_timeout Gitlab.config.gitlab.webhook_timeout
|
||||
|
||||
validates :url, presence: true,
|
||||
format: { with: URI::regexp(%w(http https)), message: "should be a valid url" }
|
||||
|
||||
@@ -4,8 +4,8 @@ class CompareService
|
||||
def execute(current_user, source_project, source_branch, target_project, target_branch)
|
||||
# Try to compare branches to get commits list and diffs
|
||||
#
|
||||
# Note: Use satellite only when need to compare between to repos
|
||||
# because satellites are slower then operations on bare repo
|
||||
# Note: Use satellite only when need to compare between two repos
|
||||
# because satellites are slower than operations on bare repo
|
||||
if target_project == source_project
|
||||
Gitlab::CompareResult.new(
|
||||
Gitlab::Git::Compare.new(
|
||||
|
||||
@@ -1,13 +1,38 @@
|
||||
class CreateBranchService
|
||||
def execute(project, branch_name, ref, current_user)
|
||||
valid_branch = Gitlab::GitRefValidator.validate(branch_name)
|
||||
if valid_branch == false
|
||||
return error('Branch name invalid')
|
||||
end
|
||||
|
||||
repository = project.repository
|
||||
existing_branch = repository.find_branch(branch_name)
|
||||
if existing_branch
|
||||
return error('Branch already exists')
|
||||
end
|
||||
|
||||
repository.add_branch(branch_name, ref)
|
||||
new_branch = repository.find_branch(branch_name)
|
||||
|
||||
if new_branch
|
||||
Event.create_ref_event(project, current_user, new_branch, 'add')
|
||||
return success(new_branch)
|
||||
else
|
||||
return error('Invalid reference name')
|
||||
end
|
||||
end
|
||||
|
||||
new_branch
|
||||
def error(message)
|
||||
{
|
||||
message: message,
|
||||
status: :error
|
||||
}
|
||||
end
|
||||
|
||||
def success(branch)
|
||||
{
|
||||
branch: branch,
|
||||
status: :success
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,13 +1,42 @@
|
||||
class CreateTagService
|
||||
def execute(project, tag_name, ref, current_user)
|
||||
def execute(project, tag_name, ref, message, current_user)
|
||||
valid_tag = Gitlab::GitRefValidator.validate(tag_name)
|
||||
if valid_tag == false
|
||||
return error('Tag name invalid')
|
||||
end
|
||||
|
||||
repository = project.repository
|
||||
repository.add_tag(tag_name, ref)
|
||||
existing_tag = repository.find_tag(tag_name)
|
||||
if existing_tag
|
||||
return error('Tag already exists')
|
||||
end
|
||||
|
||||
if message
|
||||
message.gsub!(/^\s+|\s+$/, '')
|
||||
end
|
||||
|
||||
repository.add_tag(tag_name, ref, message)
|
||||
new_tag = repository.find_tag(tag_name)
|
||||
|
||||
if new_tag
|
||||
Event.create_ref_event(project, current_user, new_tag, 'add', 'refs/tags')
|
||||
return success(new_tag)
|
||||
else
|
||||
return error('Invalid reference name')
|
||||
end
|
||||
end
|
||||
|
||||
new_tag
|
||||
def error(message)
|
||||
{
|
||||
message: message,
|
||||
status: :error
|
||||
}
|
||||
end
|
||||
|
||||
def success(branch)
|
||||
{
|
||||
tag: branch,
|
||||
status: :success
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -5,21 +5,21 @@ class DeleteBranchService
|
||||
|
||||
# No such branch
|
||||
unless branch
|
||||
return error('No such branch')
|
||||
return error('No such branch', 404)
|
||||
end
|
||||
|
||||
if branch_name == repository.root_ref
|
||||
return error('Cannot remove HEAD branch')
|
||||
return error('Cannot remove HEAD branch', 405)
|
||||
end
|
||||
|
||||
# Dont allow remove of protected branch
|
||||
if project.protected_branch?(branch_name)
|
||||
return error('Protected branch cant be removed')
|
||||
return error('Protected branch cant be removed', 405)
|
||||
end
|
||||
|
||||
# Dont allow user to remove branch if he is not allowed to push
|
||||
unless current_user.can?(:push_code, project)
|
||||
return error('You dont have push access to repo')
|
||||
return error('You dont have push access to repo', 405)
|
||||
end
|
||||
|
||||
if repository.rm_branch(branch_name)
|
||||
@@ -30,9 +30,10 @@ class DeleteBranchService
|
||||
end
|
||||
end
|
||||
|
||||
def error(message)
|
||||
def error(message, return_code = 400)
|
||||
{
|
||||
message: message,
|
||||
return_code: return_code,
|
||||
state: :error
|
||||
}
|
||||
end
|
||||
|
||||
@@ -7,7 +7,12 @@ module Projects
|
||||
end
|
||||
|
||||
def execute
|
||||
project = @from_project.dup
|
||||
project_params = {
|
||||
visibility_level: @from_project.visibility_level,
|
||||
description: @from_project.description,
|
||||
}
|
||||
|
||||
project = Project.new(project_params)
|
||||
project.name = @from_project.name
|
||||
project.path = @from_project.path
|
||||
project.namespace = current_user.namespace
|
||||
|
||||
@@ -7,30 +7,12 @@ module Search
|
||||
end
|
||||
|
||||
def execute
|
||||
query = params[:search]
|
||||
query = Shellwords.shellescape(query) if query.present?
|
||||
return result unless query.present?
|
||||
|
||||
group = Group.find_by(id: params[:group_id]) if params[:group_id].present?
|
||||
projects = ProjectsFinder.new.execute(current_user)
|
||||
projects = projects.where(namespace_id: group.id) if group
|
||||
project_ids = projects.pluck(:id)
|
||||
|
||||
result[:projects] = projects.search(query).limit(20)
|
||||
result[:merge_requests] = MergeRequest.in_projects(project_ids).search(query).order('updated_at DESC').limit(20)
|
||||
result[:issues] = Issue.where(project_id: project_ids).search(query).order('updated_at DESC').limit(20)
|
||||
result[:total_results] = %w(projects issues merge_requests).sum { |items| result[items.to_sym].size }
|
||||
result
|
||||
end
|
||||
|
||||
def result
|
||||
@result ||= {
|
||||
projects: [],
|
||||
merge_requests: [],
|
||||
issues: [],
|
||||
notes: [],
|
||||
total_results: 0,
|
||||
}
|
||||
Gitlab::SearchResults.new(project_ids, params[:search])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -7,39 +7,9 @@ module Search
|
||||
end
|
||||
|
||||
def execute
|
||||
query = params[:search]
|
||||
query = Shellwords.shellescape(query) if query.present?
|
||||
return result unless query.present?
|
||||
|
||||
if params[:search_code].present?
|
||||
if !@project.empty_repo?
|
||||
blobs = project.repository.search_files(query,
|
||||
params[:repository_ref])
|
||||
else
|
||||
blobs = Array.new
|
||||
end
|
||||
|
||||
blobs = Kaminari.paginate_array(blobs).page(params[:page]).per(20)
|
||||
result[:blobs] = blobs
|
||||
result[:total_results] = blobs.total_count
|
||||
else
|
||||
result[:merge_requests] = project.merge_requests.search(query).order('updated_at DESC').limit(20)
|
||||
result[:issues] = project.issues.where("title like :query OR description like :query ", query: "%#{query}%").order('updated_at DESC').limit(20)
|
||||
result[:notes] = Note.where(noteable_type: 'issue').where(project_id: project.id).where("note like :query", query: "%#{query}%").order('updated_at DESC').limit(20)
|
||||
result[:total_results] = %w(issues merge_requests notes).sum { |items| result[items.to_sym].size }
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
def result
|
||||
@result ||= {
|
||||
merge_requests: [],
|
||||
issues: [],
|
||||
blobs: [],
|
||||
notes: [],
|
||||
total_results: 0,
|
||||
}
|
||||
Gitlab::ProjectSearchResults.new(project.id,
|
||||
params[:search],
|
||||
params[:repository_ref])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
module Search
|
||||
class SnippetService
|
||||
attr_accessor :current_user, :params
|
||||
|
||||
def initialize(user, params)
|
||||
@current_user, @params = user, params.dup
|
||||
end
|
||||
|
||||
def execute
|
||||
snippet_ids = Snippet.accessible_to(current_user).pluck(:id)
|
||||
Gitlab::SnippetSearchResults.new(snippet_ids, params[:search])
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -22,6 +22,16 @@ class SystemHooksService
|
||||
}
|
||||
|
||||
case model
|
||||
when Key
|
||||
data.merge!(
|
||||
key: model.key,
|
||||
id: model.id
|
||||
)
|
||||
if model.user
|
||||
data.merge!(
|
||||
username: model.user.username
|
||||
)
|
||||
end
|
||||
when Project
|
||||
owner = model.owner
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
%span.light.pull-right
|
||||
= Milestone.count
|
||||
%p
|
||||
Monthly active users
|
||||
Active users last 30 days
|
||||
%span.light.pull-right
|
||||
= User.where("current_sign_in_at > ?", 30.days.ago).count
|
||||
.col-md-4
|
||||
|
||||
@@ -70,6 +70,14 @@
|
||||
%strong.cred
|
||||
No
|
||||
|
||||
%li
|
||||
%span.light Current sign-in at:
|
||||
%strong
|
||||
- if @user.current_sign_in_at
|
||||
= @user.current_sign_in_at.stamp("Nov 12, 2031")
|
||||
- else
|
||||
never
|
||||
|
||||
%li
|
||||
%span.light Last sign-in at:
|
||||
%strong
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
.panel-heading.clearfix
|
||||
= search_field_tag :filter_group, nil, placeholder: 'Filter by name', class: 'dash-filter form-control'
|
||||
- if current_user.can_create_group?
|
||||
%span.pull-right
|
||||
= link_to new_group_path, class: "btn btn-new" do
|
||||
%i.icon-plus
|
||||
New group
|
||||
= link_to new_group_path, class: "btn btn-new pull-right" do
|
||||
%i.icon-plus
|
||||
New group
|
||||
%ul.well-list.dash-list
|
||||
- groups.each do |group|
|
||||
%li.group-row
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user