Commit 3be2c3f5 authored by Lin Jen-Shin's avatar Lin Jen-Shin

Merge branch 'master' into artifacts-from-ref-and-build-name

* master: (335 commits)
  Disable transaction when adding index for Ci::Pipeline
  added changelog
  allow empty repos on import/export
  Modify test for Build tabs
  Add tests to project builds for pending tab
  Remove unused .js-running-count class
  Add test for new pending tab and update tests for running tab
  Add Pending Tab to Admin Builds
  added changelog
  limit project expor retry to only 3
  use method in validates statement
  Fix spec to set import_url before attempting to create import_data
  Allow a project import URL to be blank to prevent false positives preventing settings from being saved
  Refactor gitlab_ci_yaml_processor variables tests
  Fix CI yaml example
  Align cancel and retry buttons
  Remove deploy to production button
  Remove irrelevant comments
  Fix gitlab_ci_yaml_processor_spec.rb
  Fix AddWhenAndYamlVariablesToCiBuilds migration
  ...
parents e51d4a05 4e898b9b
# Prefer single quotes
StringLiterals:
EnforcedStyle: single_quotes
Enabled: true
This diff is collapsed.
This diff is collapsed.
stage:
before:
- cp config/gitlab.teatro.yml config/gitlab.yml
- mkdir /apps/gitlab-satellites
- mkdir /apps/repositories
database:
- RAILS_ENV=development force=yes bundle exec rake db:create gitlab:setup
\ No newline at end of file
Please view this file on the master branch, on stable branches it's out of date.
v 8.10.0 (unreleased)
- Expose {should,force}_remove_source_branch (Ben Boeckel)
- Disable PostgreSQL statement timeout during migrations
- Fix projects dropdown loading performance with a simplified api cal. !5113 (tiagonbotelho)
- Fix commit builds API, return all builds for all pipelines for given commit. !4849
- Replace Haml with Hamlit to make view rendering faster. !3666
- Refresh the branch cache after `git gc` runs
- Refactor repository paths handling to allow multiple git mount points
- Optimize system note visibility checking by memoizing the visible reference count !5070
- Add Application Setting to configure default Repository Path for new projects
- Delete award emoji when deleting a user
- Remove pinTo from Flash and make inline flash messages look nicer !4854 (winniehell)
- Wrap code blocks on Activies and Todos page. !4783 (winniehell)
- Align flash messages with left side of page content !4959 (winniehell)
- Display tooltip for "Copy to Clipboard" button !5164 (winniehell)
- Use default cursor for table header of project files !5165 (winniehell)
- Store when and yaml variables in builds table
- Display last commit of deleted branch in push events !4699 (winniehell)
- Escape file extension when parsing search results !5141 (winniehell)
- Apply the trusted_proxies config to the rack request object for use with rack_attack
- Upgrade to Rails 4.2.7. !5236
- Add Sidekiq queue duration to transaction metrics.
- Add a new column `artifacts_size` to table `ci_builds` !4964
- Let Workhorse serve format-patch diffs
- Display tooltip for mentioned users and groups !5261 (winniehell)
- Added day name to contribution calendar tooltips
- Make images fit to the size of the viewport !4810
- Fix check for New Branch button on Issue page !4630 (winniehell)
- Fix MR-auto-close text added to description. !4836
- Support U2F devices in Firefox. !5177
- Fix issue, preventing users w/o push access to sort tags !5105 (redetection)
- Add Spring EmojiOne updates.
- Fix fetching LFS objects for private CI projects
- Add syntax for multiline blockquote using `>>>` fence !3954
- Fix viewing notification settings when a project is pending deletion
- Updated compare dropdown menus to use GL dropdown
- Eager load award emoji on notes
- Fix pagination when sorting by columns with lots of ties (like priority)
- The Markdown reference parsers now re-use query results to prevent running the same queries multiple times !5020
- Updated project header design
- Issuable collapsed assignee tooltip is now the users name
- Exclude email check from the standard health check
- Updated layout for Projects, Groups, Users on Admin area !4424
- Fix changing issue state columns in milestone view
- Update health_check gem to version 2.1.0
- Add notification settings dropdown for groups
- Render inline diffs for multiple changed lines following eachother
- Wildcards for protected branches. !4665
- Allow importing from Github using Personal Access Tokens. (Eric K Idema)
- API: Expose `due_date` for issues (Robert Schilling)
- API: Todos !3188 (Robert Schilling)
- API: Expose shared groups for projects and shared projects for groups !5050 (Robert Schilling)
- Add "Enabled Git access protocols" to Application Settings
- Diffs will create button/diff form on demand no on server side
- Reduce size of HTML used by diff comment forms
- Fix user creation with stronger minimum password requirements !4054 (nathan-pmt)
- Only show New Snippet button to users that can create snippets.
- PipelinesFinder uses git cache data
- Track a user who created a pipeline
- Actually render old and new sections of parallel diff next to each other
- Throttle the update of `project.pushes_since_gc` to 1 minute.
- Allow expanding and collapsing files in diff view (!4990)
- Collapse large diffs by default (!4990)
- Fix mentioned users list on diff notes
- Fix creation of deployment on build that is retried, redeployed or rollback
- Check for conflicts with existing Project's wiki path when creating a new project.
- Show last push widget in upstream after push to fork
- Fix stage status shown for pipelines
- Cache todos pending/done dashboard query counts.
- Don't instantiate a git tree on Projects show default view
- Bump Rinku to 2.0.0
- Remove unused front-end variable -> default_issues_tracker
- ObjectRenderer retrieve renderer content using Rails.cache.read_multi
- Better caching of git calls on ProjectsController#show.
- Avoid to retrieve MR closes_issues as much as possible.
- Add API endpoint for a group issues !4520 (mahcsig)
- Add Bugzilla integration !4930 (iamtjg)
- Instrument Rinku usage
- Be explicit to define merge request discussion variables
- Metrics for Rouge::Plugins::Redcarpet and Rouge::Formatters::HTMLGitlab
- RailsCache metris now includes fetch_hit/fetch_miss and read_hit/read_miss info.
- Allow [ci skip] to be in any case and allow [skip ci]. !4785 (simon_w)
......@@ -55,14 +88,47 @@ v 8.10.0 (unreleased)
- Add basic system information like memory and disk usage to the admin panel
- Don't garbage collect commits that have related DB records like comments
- More descriptive message for git hooks and file locks
- Aliases of award emoji should be stored as original name. !5060 (dixpac)
- Handle custom Git hook result in GitLab UI
- Allow to access Container Registry for Public and Internal projects
- Allow '?', or '&' for label names
- Support redirected blobs for Container Registry integration
- Fix importer for GitHub Pull Requests when a branch was reused across Pull Requests
- Add date when user joined the team on the member page
- Fix 404 redirect after validation fails importing a GitLab project
- Added setting to set new users by default as external !4545 (Dravere)
- Add min value for project limit field on user's form !3622 (jastkand)
- Reset project pushes_since_gc when we enqueue the git gc call
- Add reminder to not paste private SSH keys !4399 (Ingo Blechschmidt)
- Remove duplicate `description` field in `MergeRequest` entities (Ben Boeckel)
- Style of import project buttons were fixed in the new project page. !5183 (rdemirbay)
- Fix GitHub client requests when rate limit is disabled
- Optimistic locking for Issues and Merge Requests (Title and description overriding prevention)
- Redesign Builds and Pipelines pages
- Change status color and icon for running builds
- Fix markdown rendering for: consecutive labels references, label references that begin with a digit or contains `.`
- Project export filename now includes the project and namespace path
- Fix last update timestamp on issues not preserved on gitlab.com and project imports
- Fix issues importing projects from EE to CE
- Fix creating group with space in group path
- Create Todos for Issue author when assign or mention himself (Katarzyna Kobierska)
- Limit the number of retries on error to 3 for exporting projects
- Allow empty repositories on project import/export
v 8.9.6
- Fix importing of events under notes for GitLab projects. !5154
- Fix log statements in import/export. !5129
- Fix commit avatar alignment in compare view. !5128
- Fix broken migration in MySQL. !5005
- Overwrite Host and X-Forwarded-Host headers in NGINX !5213
- Keeps issue number when importing from Gitlab.com
- Add Pending tab for Builds (Katarzyna Kobierska, Urszula Budziszewska)
v 8.9.7 (unreleased)
- Fix import_data wrongly saved as a result of an invalid import_url
v 8.9.6
- Fix importing of events under notes for GitLab projects
v 8.9.5
- Add more debug info to import/export and memory killer. !5108
......@@ -223,6 +289,7 @@ v 8.9.0
- Add rake task 'gitlab:db:configure' for conditionally seeding or migrating the database
- Changed the Slack build message to use the singular duration if necessary (Aran Koning)
- Fix race condition on merge when build succeeds
- Added shortcut to focus filter search fields and added documentation #18120
- Links from a wiki page to other wiki pages should be rewritten as expected
- Add option to project to only allow merge requests to be merged if the build succeeds (Rui Santos)
- Added navigation shortcuts to the project pipelines, milestones, builds and forks page. !4393
......@@ -2184,8 +2251,6 @@ v 7.7.0
- Fixes for edit comments: drag-n-drop images, preview mode, selecting images, save & update
- Remove password strength indicator
v 7.6.0
- Fork repository to groups
- New rugged version
......@@ -2611,13 +2676,13 @@ v 6.5.0
- Files API supports base64 encoded content (sponsored by O'Reilly Media)
- Added support for Go's repository retrieval (Bruno Albuquerque)
v6.4.3
v 6.4.3
- Don't use unicorn worker killer if PhusionPassenger is defined
v6.4.2
v 6.4.2
- Fixed wrong behaviour of script/upgrade.rb
v6.4.1
v 6.4.1
- Fixed bug with repository rename
- Fixed bug with project transfer
......
......@@ -145,7 +145,8 @@ might be edited to make them small and simple.
You are encouraged to use the template below for feature proposals.
```
## Description including problem, use cases, benefits, and/or goals
## Description
Include problem, use cases, benefits, and/or goals
## Proposal
......
source 'https://rubygems.org'
gem 'rails', '4.2.6'
gem 'rails', '4.2.7'
gem 'rails-deprecated_sanitizer', '~> 1.0.3'
# Responders respond_to and respond_with
......@@ -61,7 +61,7 @@ gem 'gitlab_omniauth-ldap', '~> 1.2.1', require: 'omniauth-ldap'
# Git Wiki
# Required manually in config/initializers/gollum.rb to control load order
gem 'gollum-lib', '~> 4.1.0', require: false
gem 'gollum-lib', '~> 4.2', require: false
gem 'gollum-rugged_adapter', '~> 0.4.2', require: false
# Language detection
......@@ -105,7 +105,7 @@ gem 'seed-fu', '~> 2.3.5'
# Markdown and HTML processing
gem 'html-pipeline', '~> 1.11.0'
gem 'task_list', '~> 1.0.2', require: 'task_list/railtie'
gem 'github-markup', '~> 1.3.1'
gem 'github-markup', '~> 1.4'
gem 'redcarpet', '~> 3.3.3'
gem 'RedCloth', '~> 4.3.2'
gem 'rdoc', '~>3.6'
......@@ -113,7 +113,7 @@ gem 'org-ruby', '~> 0.9.12'
gem 'creole', '~> 0.5.0'
gem 'wikicloth', '0.8.1'
gem 'asciidoctor', '~> 1.5.2'
gem 'rouge', '~> 1.11'
gem 'rouge', '~> 2.0'
# See https://groups.google.com/forum/#!topic/ruby-security-ann/aSbgDiwb24s
# and https://groups.google.com/forum/#!topic/ruby-security-ann/Dy7YiKb_pMM
......@@ -299,7 +299,7 @@ group :development, :test do
gem 'spring-commands-spinach', '~> 1.1.0'
gem 'spring-commands-teaspoon', '~> 0.0.2'
gem 'rubocop', '~> 0.40.0', require: false
gem 'rubocop', '~> 0.41.2', require: false
gem 'rubocop-rspec', '~> 1.5.0', require: false
gem 'scss_lint', '~> 0.47.0', require: false
gem 'simplecov', '~> 0.11.0', require: false
......@@ -344,7 +344,7 @@ gem 'oauth2', '~> 1.2.0'
gem 'paranoia', '~> 2.0'
# Health check
gem 'health_check', '~> 1.5.1'
gem 'health_check', '~> 2.1.0'
# System information
gem 'vmstat', '~> 2.1.0'
......
......@@ -3,34 +3,34 @@ GEM
specs:
RedCloth (4.3.2)
ace-rails-ap (4.0.2)
actionmailer (4.2.6)
actionpack (= 4.2.6)
actionview (= 4.2.6)
activejob (= 4.2.6)
actionmailer (4.2.7)
actionpack (= 4.2.7)
actionview (= 4.2.7)
activejob (= 4.2.7)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 1.0, >= 1.0.5)
actionpack (4.2.6)
actionview (= 4.2.6)
activesupport (= 4.2.6)
actionpack (4.2.7)
actionview (= 4.2.7)
activesupport (= 4.2.7)
rack (~> 1.6)
rack-test (~> 0.6.2)
rails-dom-testing (~> 1.0, >= 1.0.5)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
actionview (4.2.6)
activesupport (= 4.2.6)
actionview (4.2.7)
activesupport (= 4.2.7)
builder (~> 3.1)
erubis (~> 2.7.0)
rails-dom-testing (~> 1.0, >= 1.0.5)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
activejob (4.2.6)
activesupport (= 4.2.6)
activejob (4.2.7)
activesupport (= 4.2.7)
globalid (>= 0.3.0)
activemodel (4.2.6)
activesupport (= 4.2.6)
activemodel (4.2.7)
activesupport (= 4.2.7)
builder (~> 3.1)
activerecord (4.2.6)
activemodel (= 4.2.6)
activesupport (= 4.2.6)
activerecord (4.2.7)
activemodel (= 4.2.7)
activesupport (= 4.2.7)
arel (~> 6.0)
activerecord-session_store (1.0.0)
actionpack (>= 4.0, < 5.1)
......@@ -38,7 +38,7 @@ GEM
multi_json (~> 1.11, >= 1.11.2)
rack (>= 1.5.2, < 3)
railties (>= 4.0, < 5.1)
activesupport (4.2.6)
activesupport (4.2.7)
i18n (~> 0.7)
json (~> 1.7, >= 1.7.7)
minitest (~> 5.1)
......@@ -58,7 +58,7 @@ GEM
faraday_middleware-multi_json (~> 0.0)
oauth2 (~> 1.0)
asciidoctor (1.5.3)
ast (2.2.0)
ast (2.3.0)
attr_encrypted (3.0.1)
encryptor (~> 3.0.0)
attr_required (1.0.0)
......@@ -264,7 +264,7 @@ GEM
escape_utils (~> 1.1.0)
mime-types (>= 1.19)
rugged (>= 0.23.0b)
github-markup (1.3.3)
github-markup (1.4.0)
gitlab-flowdock-git-hook (1.0.1)
flowdock (~> 0.7)
gitlab-grit (>= 2.4.1)
......@@ -287,13 +287,13 @@ GEM
rubyntlm (~> 0.3)
globalid (0.3.6)
activesupport (>= 4.1.0)
gollum-grit_adapter (1.0.0)
gollum-grit_adapter (1.0.1)
gitlab-grit (~> 2.7, >= 2.7.1)
gollum-lib (4.1.0)
github-markup (~> 1.3.3)
gollum-lib (4.2.1)
github-markup (~> 1.4.0)
gollum-grit_adapter (~> 1.0)
nokogiri (~> 1.6.4)
rouge (~> 1.9)
rouge (~> 2.0)
sanitize (~> 2.1.0)
stringex (~> 2.5.1)
gollum-rugged_adapter (0.4.2)
......@@ -322,8 +322,8 @@ GEM
thor
tilt
hashie (3.4.3)
health_check (1.5.1)
rails (>= 2.3.0)
health_check (2.1.0)
rails (>= 4.0)
hipchat (1.5.2)
httparty
mimemagic
......@@ -473,7 +473,7 @@ GEM
orm_adapter (0.5.0)
paranoia (2.1.4)
activerecord (~> 4.0)
parser (2.3.1.0)
parser (2.3.1.2)
ast (~> 2.2)
pg (0.18.4)
pkg-config (1.1.7)
......@@ -515,16 +515,16 @@ GEM
rack
rack-test (0.6.3)
rack (>= 1.0)
rails (4.2.6)
actionmailer (= 4.2.6)
actionpack (= 4.2.6)
actionview (= 4.2.6)
activejob (= 4.2.6)
activemodel (= 4.2.6)
activerecord (= 4.2.6)
activesupport (= 4.2.6)
rails (4.2.7)
actionmailer (= 4.2.7)
actionpack (= 4.2.7)
actionview (= 4.2.7)
activejob (= 4.2.7)
activemodel (= 4.2.7)
activerecord (= 4.2.7)
activesupport (= 4.2.7)
bundler (>= 1.3.0, < 2.0)
railties (= 4.2.6)
railties (= 4.2.7)
sprockets-rails
rails-deprecated_sanitizer (1.0.3)
activesupport (>= 4.2.0.alpha)
......@@ -534,9 +534,9 @@ GEM
rails-deprecated_sanitizer (>= 1.0.1)
rails-html-sanitizer (1.0.3)
loofah (~> 2.0)
railties (4.2.6)
actionpack (= 4.2.6)
activesupport (= 4.2.6)
railties (4.2.7)
actionpack (= 4.2.7)
activesupport (= 4.2.7)
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
rainbow (2.1.0)
......@@ -578,7 +578,7 @@ GEM
railties (>= 4.2.0, < 5.1)
rinku (2.0.0)
rotp (2.1.2)
rouge (1.11.0)
rouge (2.0.3)
rqrcode (0.7.0)
chunky_png
rqrcode-rails3 (0.1.7)
......@@ -606,8 +606,8 @@ GEM
rspec-retry (0.4.5)
rspec-core
rspec-support (3.5.0)
rubocop (0.40.0)
parser (>= 2.3.1.0, < 3.0)
rubocop (0.41.2)
parser (>= 2.3.1.1, < 3.0)
powerpack (~> 0.1)
rainbow (>= 1.99.1, < 3.0)
ruby-progressbar (~> 1.7)
......@@ -697,7 +697,7 @@ GEM
spring (>= 0.9.1)
spring-commands-teaspoon (0.0.2)
spring (>= 0.9.1)
sprockets (3.6.2)
sprockets (3.6.3)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
sprockets-rails (3.1.1)
......@@ -758,7 +758,7 @@ GEM
unf (0.1.4)
unf_ext
unf_ext (0.0.7.2)
unicode-display_width (1.0.5)
unicode-display_width (1.1.0)
unicorn (4.9.0)
kgio (~> 2.6)
rack
......@@ -859,18 +859,18 @@ DEPENDENCIES
gemnasium-gitlab-service (~> 0.2)
gemojione (~> 2.6)
github-linguist (~> 4.7.0)
github-markup (~> 1.3.1)
github-markup (~> 1.4)
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab_git (~> 10.2)
gitlab_meta (= 7.0)
gitlab_omniauth-ldap (~> 1.2.1)
gollum-lib (~> 4.1.0)
gollum-lib (~> 4.2)
gollum-rugged_adapter (~> 0.4.2)
gon (~> 6.0.1)
grape (~> 0.13.0)
grape-entity (~> 0.4.2)
hamlit (~> 2.5)
health_check (~> 1.5.1)
health_check (~> 2.1.0)
hipchat (~> 1.5.0)
html-pipeline (~> 1.11.0)
httparty (~> 0.13.3)
......@@ -920,7 +920,7 @@ DEPENDENCIES
rack-attack (~> 4.3.1)
rack-cors (~> 0.4.0)
rack-oauth2 (~> 1.2.1)
rails (= 4.2.6)
rails (= 4.2.7)
rails-deprecated_sanitizer (~> 1.0.3)
rainbow (~> 2.1.0)
rblineprof (~> 0.3.6)
......@@ -933,11 +933,11 @@ DEPENDENCIES
request_store (~> 1.3.0)
rerun (~> 0.11.0)
responders (~> 2.0)
rouge (~> 1.11)
rouge (~> 2.0)
rqrcode-rails3 (~> 0.1.7)
rspec-rails (~> 3.5.0)
rspec-retry (~> 0.4.5)
rubocop (~> 0.40.0)
rubocop (~> 0.41.2)
rubocop-rspec (~> 1.5.0)
ruby-fogbugz (~> 0.2.1)
sanitize (~> 2.0)
......
......@@ -3,7 +3,7 @@
groupPath: "/api/:version/groups/:id.json"
namespacesPath: "/api/:version/namespaces.json"
groupProjectsPath: "/api/:version/groups/:id/projects.json"
projectsPath: "/api/:version/projects.json"
projectsPath: "/api/:version/projects.json?simple=true"
labelsPath: "/api/:version/projects/:id/labels"
licensePath: "/api/:version/licenses/:key"
gitignorePath: "/api/:version/gitignores/:key"
......
......@@ -47,14 +47,12 @@
#= require date.format
#= require_directory ./behaviors
#= require_directory ./blob
#= require_directory ./ci
#= require_directory ./commit
#= require_directory ./extensions
#= require_directory ./lib/utils
#= require_directory ./u2f
#= require_directory .
#= require fuzzaldrin-plus
#= require u2f
window.slugify = (text) ->
text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase()
......
class @CiBuild
class @Build
@interval: null
@state: null
constructor: (@page_url, @build_url, @build_status, @state) ->
clearInterval(CiBuild.interval)
clearInterval(Build.interval)
# Init breakpoint checker
@bp = Breakpoints.get()
......@@ -40,7 +40,7 @@ class @CiBuild
# Check for new build output if user still watching build page
# Only valid for runnig build when output changes during time
#
CiBuild.interval = setInterval =>
Build.interval = setInterval =>
if window.location.href.split("#").first() is @page_url
@getBuildTrace()
, 4000
......
#= require pager
#= require jquery_nested_form
#= require_tree .
$(document).on 'click', '.assign-all-runner', ->
$(this).replaceWith('<i class="fa fa-refresh fa-spin"></i> Assign in progress..')
window.unbindEvents = ->
$(document).unbind('scroll')
$(document).off('scroll')
document.addEventListener("page:fetch", unbindEvents)
$(document).on 'click', '.badge-codes-toggle', ->
$('.badge-codes-block').toggleClass("hide")
return false
class @CompareAutocomplete
constructor: ->
@initDropdown()
initDropdown: ->
$('.js-compare-dropdown').each ->
$dropdown = $(@)
selected = $dropdown.data('selected')
$dropdown.glDropdown(
data: (term, callback) ->
$.ajax(
url: $dropdown.data('refs-url')
data:
ref: $dropdown.data('ref')
).done (refs) ->
callback(refs)
selectable: true
filterable: true
filterByText: true
fieldName: $dropdown.attr('name')
filterInput: 'input[type="text"]'
renderRow: (ref) ->
if ref.header?
$('<li />')
.addClass('dropdown-header')
.text(ref.header)
else
link = $('<a />')
.attr('href', '#')
.addClass(if ref is selected then 'is-active' else '')
.text(ref)
.attr('data-ref', escape(ref))
$('<li />')
.append(link)
id: (obj, $el) ->
$el.attr('data-ref')
toggleLabel: (obj, $el) ->
$el.text().trim()
)
class @Diff
UNFOLD_COUNT = 20
constructor: ->
$('.files .diff-file').singleFileDiff()
@filesCommentButton = $('.files .diff-file').filesCommentButton()
$(document).off('click', '.js-unfold')
$(document).on('click', '.js-unfold', (event) =>
target = $(event.target)
......@@ -36,7 +39,7 @@ class @Diff
# see https://gitlab.com/gitlab-org/gitlab-ce/issues/707
indent: 1
$.get(link, params, (response) =>
$.get(link, params, (response) ->
target.parent().replaceWith(response)
)
)
......
......@@ -137,6 +137,8 @@ class Dispatcher
new Project()
new ProjectAvatar()
switch path[1]
when 'compare'
new CompareAutocomplete()
when 'edit'
shortcut_handler = new ShortcutsNavigation()
new ProjectNew()
......
class @FilesCommentButton
COMMENT_BUTTON_CLASS = '.add-diff-note'
COMMENT_BUTTON_TEMPLATE = _.template '<button name="button" type="submit" class="btn <%- COMMENT_BUTTON_CLASS %> js-add-diff-note-button" title="Add a comment to this line"><i class="fa fa-comment-o"></i></button>'
LINE_HOLDER_CLASS = '.line_holder'
LINE_NUMBER_CLASS = 'diff-line-num'
LINE_CONTENT_CLASS = 'line_content'
UNFOLDABLE_LINE_CLASS = 'js-unfold'
EMPTY_CELL_CLASS = 'empty-cell'
OLD_LINE_CLASS = 'old_line'
NEW_CLASS = 'new'
LINE_COLUMN_CLASSES = ".#{LINE_NUMBER_CLASS}, .line_content"
TEXT_FILE_SELECTOR = '.text-file'
DEBOUNCE_TIMEOUT_DURATION = 100
constructor: (@filesContainerElement) ->
@VIEW_TYPE = $('input#view[type=hidden]').val()
debounce = _.debounce @render, DEBOUNCE_TIMEOUT_DURATION
$(document)
.on 'mouseover', LINE_COLUMN_CLASSES, debounce
.on 'mouseleave', LINE_COLUMN_CLASSES, @destroy
render: (e) =>
$currentTarget = $(e.currentTarget)
buttonParentElement = @getButtonParent $currentTarget
return unless @shouldRender e, buttonParentElement
textFileElement = @getTextFileElement $currentTarget
lineContentElement = @getLineContent $currentTarget
buttonParentElement.append @buildButton
noteableType: textFileElement.attr 'data-noteable-type'
noteableID: textFileElement.attr 'data-noteable-id'
commitID: textFileElement.attr 'data-commit-id'
noteType: lineContentElement.attr 'data-note-type'
position: lineContentElement.attr 'data-position'
lineType: lineContentElement.attr 'data-line-type'
discussionID: lineContentElement.attr 'data-discussion-id'
lineCode: lineContentElement.attr 'data-line-code'
return
destroy: (e) =>
return if @isMovingToSameType e
$(COMMENT_BUTTON_CLASS, @getButtonParent $(e.currentTarget)).remove()
return
buildButton: (buttonAttributes) ->
initializedButtonTemplate = COMMENT_BUTTON_TEMPLATE
COMMENT_BUTTON_CLASS: COMMENT_BUTTON_CLASS.substr 1
$(initializedButtonTemplate).attr
'data-noteable-type': buttonAttributes.noteableType
'data-noteable-id': buttonAttributes.noteableID
'data-commit-id': buttonAttributes.commitID
'data-note-type': buttonAttributes.noteType
'data-line-code': buttonAttributes.lineCode
'data-position': buttonAttributes.position
'data-discussion-id': buttonAttributes.discussionID
'data-line-type': buttonAttributes.lineType
getTextFileElement: (hoveredElement) ->
$(hoveredElement.closest TEXT_FILE_SELECTOR)
getLineContent: (hoveredElement) ->
return hoveredElement if hoveredElement.hasClass LINE_CONTENT_CLASS
$(".#{LINE_CONTENT_CLASS + @diffTypeClass hoveredElement}", hoveredElement.parent())
getButtonParent: (hoveredElement) ->
if @VIEW_TYPE is 'inline'
return hoveredElement if hoveredElement.hasClass OLD_LINE_CLASS
$(".#{OLD_LINE_CLASS}", hoveredElement.parent())
else
return hoveredElement if hoveredElement.hasClass LINE_NUMBER_CLASS
$(".#{LINE_NUMBER_CLASS + @diffTypeClass hoveredElement}", hoveredElement.parent())
diffTypeClass: (hoveredElement) ->
if hoveredElement.hasClass(NEW_CLASS) then '.new' else '.old'
isMovingToSameType: (e) ->
newButtonParent = @getButtonParent $(e.toElement)
return false unless newButtonParent
newButtonParent.is @getButtonParent $(e.currentTarget)
shouldRender: (e, buttonParentElement) ->
(not buttonParentElement.hasClass(EMPTY_CELL_CLASS) and \
not buttonParentElement.hasClass(UNFOLDABLE_LINE_CLASS) and \
$(COMMENT_BUTTON_CLASS, buttonParentElement).length is 0)
$.fn.filesCommentButton = ->
return unless this and @parent().data('can-create-note')?
@each ->
unless $.data this, 'filesCommentButton'
$.data this, 'filesCommentButton', new FilesCommentButton $(this)
class @Flash
constructor: (message, type = 'alert')->
@flash = $(".flash-container")
@flash.html("")
hideFlash = -> $(@).fadeOut()
innerDiv = $('<div/>',
constructor: (message, type = 'alert', parent = null)->
if parent
@flashContainer = parent.find('.flash-container')
else
@flashContainer = $('.flash-container-page')
@flashContainer.html('')
flash = $('<div/>',
class: "flash-#{type}"
)
innerDiv.appendTo(".flash-container")
flash.on 'click', hideFlash
textDiv = $("<div/>",
class: "flash-text",
textDiv = $('<div/>',
class: 'flash-text',
text: message
)
textDiv.appendTo(innerDiv)
textDiv.appendTo(flash)
if @flash.parent().hasClass('content-wrapper')
if @flashContainer.parent().hasClass('content-wrapper')
textDiv.addClass('container-fluid container-limited')
@flash.click -> $(@).fadeOut()
@flash.show()
flash.appendTo(@flashContainer)
@flashContainer.show()
pinTo: (selector) ->
@flash.detach().appendTo(selector)
......@@ -456,6 +456,8 @@ class GitLabDropdown
rowClicked: (el) ->
fieldName = @options.fieldName
isInput = $(@el).is('input')
if @renderedData
groupName = el.data('group')
if groupName
......@@ -466,10 +468,19 @@ class GitLabDropdown
selectedObject = @renderedData[selectedIndex]
value = if @options.id then @options.id(selectedObject, el) else selectedObject.id
field = @dropdown.parent().find("input[name='#{fieldName}'][value='#{value}']")
if isInput
field = $(@el)
else
field = @dropdown.parent().find("input[name='#{fieldName}'][value='#{value}']")
if el.hasClass(ACTIVE_CLASS)
el.removeClass(ACTIVE_CLASS)
field.remove()
if isInput
field.val('')
else
field.remove()
# Toggle the dropdown label
if @options.toggleLabel
......@@ -490,7 +501,9 @@ class GitLabDropdown
else
if not @options.multiSelect or el.hasClass('dropdown-clear-active')
@dropdown.find(".#{ACTIVE_CLASS}").removeClass ACTIVE_CLASS
@dropdown.parent().find("input[name='#{fieldName}']").remove()
unless isInput
@dropdown.parent().find("input[name='#{fieldName}']").remove()
if !value?
field.remove()
......@@ -505,7 +518,9 @@ class GitLabDropdown
if !field.length and fieldName
@addInput(fieldName, value)
else
field.val value
field
.val value
.trigger 'change'
return selectedObject
......
......@@ -32,13 +32,11 @@ issuable_created = false
$search = $('#issue_search')
$form = $('.js-filter-form')
$input = $("input[name='#{$search.attr('name')}']", $form)
if $input.length is 0
$form.append "<input type='hidden' name='#{$search.attr('name')}' value='#{_.escape($search.val())}'/>"
else
$input.val $search.val()
Issuable.filterResults $form
Issuable.filterResults $form if $search.val() isnt ''
, 500)
initLabelFilterRemove: ->
......
......@@ -2,10 +2,14 @@
w.gl ?= {}
w.gl.utils ?= {}
w.gl.utils.days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
w.gl.utils.formatDate = (datetime) ->
dateFormat(datetime, 'mmm d, yyyy h:MMtt Z')
w.gl.utils.getDayName = (date) ->
this.days[date.getDay()]
w.gl.utils.localTimeAgo = ($timeagoEls, setTimeago = true) ->
$timeagoEls.each( ->
$el = $(@)
......
......@@ -153,17 +153,18 @@ class @MergeRequestTabs
loadDiff: (source) ->
return if @diffsLoaded
@_get
url: "#{source}.json" + @_location.search
success: (data) =>
$('#diffs').html data.html
gl.utils.localTimeAgo($('.js-timeago', 'div#diffs'))
$('#diffs .js-syntax-highlight').syntaxHighlight()
$('#diffs .diff-file').singleFileDiff()
@expandViewContainer() if @diffViewType() is 'parallel'
@diffsLoaded = true
@scrollToElement("#diffs")
@highlighSelectedLine()
@filesCommentButton = $('.files .diff-file').filesCommentButton()
$(document)
.off 'click', '.diff-line-num a'
......
......@@ -194,8 +194,7 @@ class @Notes
renderNote: (note) ->
unless note.valid
if note.award
flash = new Flash('You have already awarded this emoji!', 'alert')
flash.pinTo('.header-content')
new Flash('You have already awarded this emoji!', 'alert')
return
if note.award
......@@ -325,6 +324,8 @@ class @Notes
form.find("#note_position").remove()
form.find("#note_type").remove()
@parentTimeline = form.parents('.timeline')
###
General note form setup.
......@@ -357,8 +358,7 @@ class @Notes
@renderNote(note)
addNoteError: (xhr, note, status) =>
flash = new Flash('Your comment could not be submitted! Please check your network connection and try again.', 'alert')
flash.pinTo('.md-area')
new Flash('Your comment could not be submitted! Please check your network connection and try again.', 'alert', @parentTimeline)
###
Called in response to the new note form being submitted
......
......@@ -5,13 +5,12 @@
this.initPagination()
initSearch: ->
@timer = null
$(".projects-list-filter").on('keyup', ->
clearTimeout(@timer)
@timer = setTimeout(ProjectsList.filterResults, 500)
)
projectsListFilter = $('.projects-list-filter')
debounceFilter = _.debounce ProjectsList.filterResults, 500
projectsListFilter.on 'keyup', (e) ->
debounceFilter() if projectsListFilter.val() isnt ''
filterResults: =>
filterResults: ->
$('.projects-list-holder').fadeTo(250, 0.5)
form = null
......
......@@ -2,9 +2,10 @@ class @Shortcuts
constructor: (skipResetBindings) ->
@enabledHelp = []
Mousetrap.reset() if not skipResetBindings
Mousetrap.bind('?', @onToggleHelp)
Mousetrap.bind('s', Shortcuts.focusSearch)
Mousetrap.bind(['ctrl+shift+p', 'command+shift+p'], @toggleMarkdownPreview)
Mousetrap.bind '?', @onToggleHelp
Mousetrap.bind 's', Shortcuts.focusSearch
Mousetrap.bind 'f', (e) => @focusFilter e
Mousetrap.bind ['ctrl+shift+p', 'command+shift+p'], @toggleMarkdownPreview
Mousetrap.bind('t', -> Turbolinks.visit(findFileURL)) if findFileURL?
onToggleHelp: (e) =>
......@@ -32,10 +33,16 @@ class @Shortcuts
$('.js-more-help-button').remove()
)
focusFilter: (e) ->
@filterInput ?= $('input[type=search]', '.nav-controls')
@filterInput.focus()
e.preventDefault()
@focusSearch: (e) ->
$('#search').focus()
e.preventDefault()
$(document).on 'click.more_help', '.js-more-help-button', (e) ->
$(@).remove()
$('.hidden-shortcut').show()
......
class @SingleFileDiff
WRAPPER = '<div class="diff-content diff-wrap-lines"></div>'
LOADING_HTML = '<i class="fa fa-spinner fa-spin"></i>'
ERROR_HTML = '<div class="nothing-here-block"><i class="fa fa-warning"></i> Could not load diff</div>'
COLLAPSED_HTML = '<div class="nothing-here-block diff-collapsed">This diff is collapsed. Click to expand it.</div>'
constructor: (@file) ->
@content = $('.diff-content', @file)
@diffForPath = @content.find('[data-diff-for-path]').data 'diff-for-path'
@isOpen = !@diffForPath
if @diffForPath
@collapsedContent = @content
@loadingContent = $(WRAPPER).addClass('loading').html(LOADING_HTML).hide()
@content = null
@collapsedContent.after(@loadingContent)
else
@collapsedContent = $(WRAPPER).html(COLLAPSED_HTML).hide()
@content.after(@collapsedContent)
@collapsedContent.on 'click', @toggleDiff
$('.file-title > a', @file).on 'click', @toggleDiff
toggleDiff: (e) =>
@isOpen = !@isOpen
if not @isOpen and not @hasError
@content.hide()
@collapsedContent.show()
else if @content
@collapsedContent.hide()
@content.show()
else
@getContentHTML()
getContentHTML: ->
@collapsedContent.hide()
@loadingContent.show()
$.get @diffForPath, (data) =>
@loadingContent.hide()
if data.html
@content = $(data.html)
@content.syntaxHighlight()
else
@hasError = true
@content = $(ERROR_HTML)
@collapsedContent.after(@content)
return
$.fn.singleFileDiff = ->
return @each ->
if not $.data this, 'singleFileDiff'
$.data this, 'singleFileDiff', new SingleFileDiff this
......@@ -6,8 +6,20 @@
class @U2FAuthenticate
constructor: (@container, u2fParams) ->
@appId = u2fParams.app_id
@challenges = u2fParams.challenges
@signRequests = u2fParams.sign_requests
@challenge = u2fParams.challenge
# The U2F Javascript API v1.1 requires a single challenge, with
# _no challenges per-request_. The U2F Javascript API v1.0 requires a
# challenge per-request, which is done by copying the single challenge
# into every request.
#
# In either case, we don't need the per-request challenges that the server
# has generated, so we can remove them.
#
# Note: The server library fixes this behaviour in (unreleased) version 1.0.0.
# This can be removed once we upgrade.
# https://github.com/castle/ruby-u2f/commit/103f428071a81cd3d5f80c2e77d522d5029946a4
@signRequests = u2fParams.sign_requests.map (request) -> _(request).omit('challenge')
start: () =>
if U2FUtil.isU2FSupported()
......@@ -16,7 +28,7 @@ class @U2FAuthenticate
@renderNotSupported()
authenticate: () =>
u2f.sign(@appId, @challenges, @signRequests, (response) =>
u2f.sign(@appId, @challenge, @signRequests, (response) =>
if response.errorCode
error = new U2FError(response.errorCode)
@renderError(error);
......
class @U2FUtil
@isU2FSupported: ->
window.u2f
# Helper class for U2F (universal 2nd factor) device registration and authentication.
class @U2FUtil
@isU2FSupported: ->
if @testMode
true
else
gon.u2f.browser_supports_u2f
@enableTestMode: ->
@testMode = true
<% if Rails.env.test? %>
U2FUtil.enableTestMode();
<% end %>
......@@ -87,14 +87,15 @@ class @Calendar
.attr 'width', @daySize
.attr 'height', @daySize
.attr 'title', (stamp) =>
date = new Date(stamp.date)
contribText = 'No contributions'
if stamp.count > 0
contribText = "#{stamp.count} contribution#{if stamp.count > 1 then 's' else ''}"
date = dateFormat(stamp.date, 'mmm d, yyyy')
dateText = dateFormat(date, 'mmm d, yyyy')
"#{contribText}<br />#{date}"
"#{contribText}<br />#{gl.utils.getDayName(date)} #{dateText}"
.attr 'class', 'user-contrib-cell js-tooltip'
.attr 'fill', (stamp) =>
if stamp.count isnt 0
......
......@@ -56,6 +56,11 @@ class @UsersSelect
username: ''
avatar: ''
$value.html(assigneeTemplate(user))
$collapsedSidebar
.attr('title', user.name)
.tooltip('fixTitle')
$collapsedSidebar.html(collapsedAssigneeTemplate(user))
......@@ -63,7 +68,6 @@ class @UsersSelect
'<% if( avatar ) { %>
<a class="author_link" href="/u/<%- username %>">
<img width="24" class="avatar avatar-inline s24" alt="" src="<%- avatar %>">
<span class="author">Toni Boehm</span>
</a>
<% } else { %>
<i class="fa fa-user"></i>
......
......@@ -20,6 +20,7 @@
}
&.s16 { width: 16px; height: 16px; margin-right: 6px; }
&.s20 { width: 20px; height: 20px; margin-right: 7px; }
&.s24 { width: 24px; height: 24px; margin-right: 8px; }
&.s26 { width: 26px; height: 26px; margin-right: 8px; }
&.s32 { width: 32px; height: 32px; margin-right: 10px; }
......
......@@ -20,9 +20,12 @@
.blank-state-icon {
padding-bottom: 20px;
color: $gray-darkest;
font-size: 56px;
path {
fill: $gray-darkest;
path,
polygon {
fill: currentColor;
}
}
......@@ -37,6 +40,10 @@
margin-top: 0;
margin-bottom: $gl-padding;
font-size: 15px;
> strong {
font-weight: 600;
}
}
.blank-state-welcome-title {
......
......@@ -16,6 +16,15 @@
font-weight: normal;
font-size: 16px;
line-height: 36px;
&.diff-collapsed {
padding: 5px;
cursor: pointer;
&:hover {
background-color: $row-hover;
}
}
}
.row-content-block {
......
......@@ -270,21 +270,6 @@ table {
}
}
.dashboard-intro-icon {
float: left;
text-align: center;
font-size: 32px;
color: #aaa;
width: 60px;
}
.dashboard-intro-text {
display: inline-block;
margin-left: -60px;
padding-left: 60px;
width: 100%;
}
.btn-sign-in {
text-shadow: none;
......
.flash-container {
cursor: pointer;
margin: 0;
margin-bottom: $gl-padding;
font-size: 14px;
width: 100%;
z-index: 100;
.flash-notice {
......@@ -18,9 +18,27 @@
}
.flash-notice, .flash-alert {
.container-fluid.flash-text {
border-radius: $border-radius-default;
.container-fluid.container-limited.flash-text {
background: transparent;
}
}
&.flash-container-page {
margin-bottom: 0;
.flash-notice, .flash-alert {
border-radius: 0;
}
}
}
@media (max-width: $screen-md-min) {
ul.notes {
.flash-container.timeline-content {
margin-left: 0;
}
}
}
......@@ -11,7 +11,6 @@
.toggle-nav-collapse,
.pin-nav-btn {
color: $color-light;
background: $color;
&:hover {
color: $white-light;
......
......@@ -3,6 +3,12 @@
padding-bottom: 25px;
transition: padding $sidebar-transition-duration;
&.page-sidebar-pinned {
.sidebar-wrapper {
@include box-shadow(none);
}
}
.sidebar-wrapper {
position: fixed;
top: 0;
......@@ -11,6 +17,7 @@
height: 100%;
overflow: hidden;
transition: width $sidebar-transition-duration;
@include box-shadow(2px 0 16px 0 $black-transparent);
}
}
......@@ -48,10 +55,6 @@
overflow-y: auto;
overflow-x: hidden;
@media (min-width: $sidebar-breakpoint) {
bottom: 50px;
}
&.navbar-collapse {
padding: 0 !important;
}
......@@ -69,7 +72,7 @@
}
a {
padding: 7px 15px 7px 12px;
padding: 7px $gl-sidebar-padding;
font-size: $gl-font-size;
line-height: 24px;
display: block;
......@@ -101,7 +104,7 @@
}
}
.toggle-nav-collapse {
.sidebar-action-buttons {
width: $sidebar_width;
position: absolute;
top: 0;
......@@ -110,12 +113,37 @@
padding: 5px 0;
font-size: 18px;
line-height: 30px;
.toggle-nav-collapse {
left: 0;
}
.pin-nav-btn {
right: 0;
display: none;
@media (min-width: $sidebar-breakpoint) {
display: block;
}
.fa {
transition: transform .15s;
}
&.is-active {
.fa {
transform: rotate(90deg);
}
}
}
}
.nav-header-btn {
padding: 10px 5px;
padding: 10px $gl-sidebar-padding;
color: inherit;
transition-duration: .3s;
position: absolute;
top: 0;
&:hover,
&:focus {
......@@ -124,30 +152,6 @@
}
}
.pin-nav-btn {
display: none;
position: absolute;
left: 0;
bottom: 0;
height: 50px;
width: $sidebar_width;
line-height: 30px;
@media (min-width: $sidebar-breakpoint) {
display: block;
}
.fa {
transition: transform .15s;
}
&.is-active {
.fa {
transform: rotate(90deg);
}
}
}
.page-sidebar-collapsed {
padding-left: 0;
......
......@@ -17,6 +17,7 @@ $focus-border-color: #3aabf0;
$table-border-color: #f0f0f0;
$background-color: #fafafa;
$dark-background-color: #f7f7f7;
$table-text-gray: #8f8f8f;
/*
* Text
......@@ -63,6 +64,7 @@ $gl-btn-padding: 10px;
$gl-input-padding: 10px;
$gl-vert-padding: 6px;
$gl-padding-top: 10px;
$gl-sidebar-padding: 22px;
/*
* Misc
......
......@@ -88,13 +88,7 @@
.user-name {
display: inline-block;
font-weight: bold;
}
.controls {
> .btn, > .dropdown {
margin-left: 5px;
}
font-weight: 600;
}
.dropdown {
......
......@@ -83,14 +83,6 @@
}
}
table.builds {
.build-link {
a {
color: $gl-dark-link-color;
}
}
}
.build-trace {
background: $ci-output-bg;
color: $ci-text-color;
......
......@@ -61,7 +61,7 @@
font-size: 0;
}
.btn-transparent {
.btn-clipboard, .btn-transparent {
padding-left: 0;
padding-right: 0;
}
......
......@@ -38,33 +38,6 @@
margin-right: 15px;
}
}
&.group-admin {
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
.group-avatar, .group-details, .group-controls {
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
}
.group-details {
flex: 1 1 auto;
flex-direction: column;
min-width: 0;
}
.group-controls {
align-items: center;
a {
margin-left: 5px;
}
}
}
}
.ldap-group-links {
......
......@@ -63,8 +63,8 @@ form.edit-issue {
.merge-request,
.issue {
&.today {
background: #efe;
border-color: #cec;
background: #f8feef;
border-color: #e1e8d5;
}
&.closed {
......
......@@ -162,9 +162,15 @@
}
.filtered-labels {
font-size: 0;
padding: 12px 16px;
.label-row {
margin-top: 4px;
margin-bottom: 4px;
&:not(:last-child) {
margin-right: 5px;
margin-right: 8px;
}
}
......
......@@ -73,11 +73,14 @@
color: #888;
}
&.ci-pending,
&.ci-running {
&.ci-pending {
color: $gl-warning;
}
&.ci-running {
color: $blue-normal;
}
&.ci-failed,
&.ci-error {
color: $gl-danger;
......
.pipelines {
.stage {
max-width: 100px;
max-width: 80px;
width: 80px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.duration, .finished_at {
margin: 4px 0;
}
.commit-title {
margin: 0;
}
......@@ -22,3 +19,156 @@
margin: 4px;
}
}
.content-list {
&.pipelines,
&.builds-content-list {
width: 100%;
overflow: auto;
}
}
.table.builds {
min-width: 1100px;
tr {
th {
padding: 16px;
border: none;
}
}
tbody {
border-top-width: 1px;
}
.commit-link {
a:hover {
text-decoration: none;
}
}
.branch-commit {
.branch-name {
margin-left: 8px;
font-weight: bold;
max-width: 180px;
overflow: hidden;
display: inline-block;
white-space: nowrap;
vertical-align: top;
text-overflow: ellipsis;
}
svg {
margin: 0 6px;
height: 14px;
width: auto;
vertical-align: middle;
}
.commit-id {
color: $gl-link-color;
margin-right: 8px;
}
.commit-title {
margin-top: 4px;
max-width: 320px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.avatar {
margin-left: 0;
}
.label {
margin-right: 4px;
}
.label-container {
font-size: 0;
.label {
margin-top: 5px;
}
}
}
.duration,
.finished-at {
color: $table-text-gray;
margin: 4px 0;
.fa {
font-size: 12px;
}
svg {
height: 12px;
width: auto;
vertical-align: middle;
}
.fa,
svg {
margin-right: 5px;
}
}
.pipeline-actions {
.btn {
margin: 0;
color: $table-text-gray;
}
.cancel-retry-btns {
vertical-align: middle;
.btn:not(:first-child) {
margin-left: 8px;
}
}
.dropdown-toggle,
.dropdown-menu {
color: $table-text-gray;
.fa {
color: $table-text-gray;
margin-right: 6px;
font-size: 14px;
}
}
.btn-remove {
color: $white-light;
}
.btn-group {
&.open {
.btn-default {
background-color: $white-normal;
border-color: $border-white-normal;
}
}
}
}
.build-link {
a {
color: $gl-dark-link-color;
}
}
.btn-group.open .dropdown-toggle {
box-shadow: none;
}
}
......@@ -340,23 +340,30 @@ a.deploy-project-label {
.project-import {
.form-group {
margin-bottom: 0;
margin-bottom: 5px;
}
.import-buttons {
padding-left: 0;
display: -webkit-flex;
display: flex;
-webkit-flex-wrap: wrap;
flex-wrap: wrap;
.btn {
margin-right: 10px;
padding: 8px 12px;
margin: 0 10px 10px 0;
padding: 8px;
}
&> div {
margin-bottom: 14px;
> div {
padding-left: 0;
&:last-child {
margin-bottom: 0;
.btn {
margin-right: 0;
}
}
}
}
......@@ -475,6 +482,10 @@ pre.light-well {
a:hover {
text-decoration: none;
}
> span {
margin-left: 10px;
}
}
}
......@@ -632,3 +643,9 @@ pre.light-well {
width: 300px;
}
}
.compare-form-group {
.dropdown-menu {
width: 300px;
}
}
......@@ -185,7 +185,7 @@
padding-right: $gl-padding + 15px;
}
.btn-search {
.btn-search, .btn-new {
width: 100%;
margin-top: 5px;
......
......@@ -32,11 +32,15 @@
border-color: $gl-gray;
}
&.ci-pending,
&.ci-running {
&.ci-pending {
color: $gl-warning;
border-color: $gl-warning;
}
&.ci-running {
color: $blue-normal;
border-color: $blue-normal;
}
}
.ci-status-icon-success {
......@@ -45,10 +49,12 @@
.ci-status-icon-failed {
color: $gl-danger;
}
.ci-status-icon-running,
.ci-status-icon-pending {
color: $gl-warning;
}
.ci-status-icon-running {
color: $blue-normal;
}
.ci-status-icon-canceled,
.ci-status-icon-disabled,
.ci-status-icon-not-found,
......
......@@ -23,12 +23,11 @@
}
&:hover {
cursor: pointer;
td {
background-color: $row-hover;
border-top: 1px solid $row-hover-border;
border-bottom: 1px solid $row-hover-border;
cursor: pointer;
}
}
......
......@@ -5,8 +5,10 @@ class Admin::BuildsController < Admin::ApplicationController
@builds = @all_builds.order('created_at DESC')
@builds =
case @scope
when 'pending'
@builds.pending.reverse_order
when 'running'
@builds.running_or_pending.reverse_order
@builds.running.reverse_order
when 'finished'
@builds.finished
else
......
......@@ -344,10 +344,6 @@ class ApplicationController < ActionController::Base
session[:skip_tfa] && session[:skip_tfa] > Time.current
end
def browser_supports_u2f?
browser.chrome? && browser.version.to_i >= 41 && !browser.device.mobile?
end
def redirect_to_home_page_url?
# If user is not signed-in and tries to access root_path - redirect him to landing page
# Don't redirect to the default URL to prevent endless redirections
......
......@@ -57,7 +57,7 @@ module AuthenticatesWithTwoFactor
# Authenticate using the response from a U2F (universal 2nd factor) device
def authenticate_with_two_factor_via_u2f(user)
if U2fRegistration.authenticate(user, u2f_app_id, user_params[:device_response], session[:challenges])
if U2fRegistration.authenticate(user, u2f_app_id, user_params[:device_response], session[:challenge])
# Remove any lingering user data from login
session.delete(:otp_user_id)
session.delete(:challenges)
......@@ -77,11 +77,9 @@ module AuthenticatesWithTwoFactor
if key_handles.present?
sign_requests = u2f.authentication_requests(key_handles)
challenges = sign_requests.map(&:challenge)
session[:challenges] = challenges
gon.push(u2f: { challenges: challenges, app_id: u2f_app_id,
sign_requests: sign_requests,
browser_supports_u2f: browser_supports_u2f? })
session[:challenge] ||= u2f.challenge
gon.push(u2f: { challenge: session[:challenge], app_id: u2f_app_id,
sign_requests: sign_requests })
end
end
end
module DiffForPath
extend ActiveSupport::Concern
def render_diff_for_path(diffs, diff_refs, project)
diff_file = safe_diff_files(diffs, diff_refs: diff_refs, repository: project.repository).find do |diff|
diff.old_path == params[:old_path] && diff.new_path == params[:new_path]
end
return render_404 unless diff_file
diff_commit = commit_for_diff(diff_file)
blob = diff_file.blob(diff_commit)
@expand_all_diffs = true
locals = {
diff_file: diff_file,
diff_commit: diff_commit,
diff_refs: diff_refs,
blob: blob,
project: project
}
render json: { html: view_to_html_string('projects/diffs/_content', locals) }
end
end
class Dashboard::TodosController < Dashboard::ApplicationController
include TodosHelper
before_action :find_todos, only: [:index, :destroy_all]
def index
......@@ -13,7 +11,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController
respond_to do |format|
format.html { redirect_to dashboard_todos_path, notice: 'Todo was successfully marked as done.' }
format.js { head :ok }
format.json { render json: { count: todos_pending_count, done_count: todos_done_count } }
format.json { render json: todos_counts }
end
end
......@@ -23,7 +21,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController
respond_to do |format|
format.html { redirect_to dashboard_todos_path, notice: 'All todos were marked as done.' }
format.js { head :ok }
format.json { render json: { count: todos_pending_count, done_count: todos_done_count } }
format.json { render json: todos_counts }
end
end
......@@ -36,4 +34,11 @@ class Dashboard::TodosController < Dashboard::ApplicationController
def find_todos
@todos ||= TodosFinder.new(current_user, params).execute
end
def todos_counts
{
count: TodosFinder.new(current_user, state: :pending).execute.count,
done_count: TodosFinder.new(current_user, state: :done).execute.count
}
end
end
......@@ -12,13 +12,12 @@ class HelpController < ApplicationController
end
def show
@category = clean_path_info(path_params[:category])
@file = path_params[:file]
@path = clean_path_info(path_params[:path])
respond_to do |format|
format.any(:markdown, :md, :html) do
# Note: We are purposefully NOT using `Rails.root.join`
path = File.join(Rails.root, 'doc', @category, "#{@file}.md")
path = File.join(Rails.root, 'doc', "#{@path}.md")
if File.exist?(path)
@markdown = File.read(path)
......@@ -33,7 +32,7 @@ class HelpController < ApplicationController
# Allow access to images in the doc folder
format.any(:png, :gif, :jpeg) do
# Note: We are purposefully NOT using `Rails.root.join`
path = File.join(Rails.root, 'doc', @category, "#{@file}.#{params[:format]}")
path = File.join(Rails.root, 'doc', "#{@path}.#{params[:format]}")
if File.exist?(path)
send_file(path, disposition: 'inline')
......@@ -57,8 +56,7 @@ class HelpController < ApplicationController
private
def path_params
params.require(:category)
params.require(:file)
params.require(:path)
params
end
......
......@@ -107,7 +107,11 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
# Only allow properly saved users to login.
if @user.persisted? && @user.valid?
log_audit_event(@user, with: oauth['provider'])
sign_in_and_redirect(@user)
if @user.two_factor_enabled?
prompt_for_two_factor(@user)
else
sign_in_and_redirect(@user)
end
else
error_message = @user.errors.full_messages.to_sentence
......
......@@ -100,7 +100,6 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
gon.push(u2f: { challenges: session[:challenges], app_id: u2f_app_id,
register_requests: registration_requests,
sign_requests: sign_requests,
browser_supports_u2f: browser_supports_u2f? })
sign_requests: sign_requests })
end
end
......@@ -23,10 +23,9 @@ class Projects::ArtifactsController < Projects::ApplicationController
entry = build.artifacts_metadata_entry(params[:path])
if entry.exists?
render json: { archive: build.artifacts_file.path,
entry: Base64.encode64(entry.path) }
send_artifacts_entry(build, entry)
else
render json: {}, status: 404
render_404
end
end
......
......@@ -10,8 +10,10 @@ class Projects::BuildsController < Projects::ApplicationController
@builds = @all_builds.order('created_at DESC')
@builds =
case @scope
when 'pending'
@builds.pending.reverse_order
when 'running'
@builds.running_or_pending.reverse_order
@builds.running.reverse_order
when 'finished'
@builds.finished
else
......
......@@ -3,6 +3,7 @@
# Not to be confused with CommitsController, plural.
class Projects::CommitController < Projects::ApplicationController
include CreatesCommit
include DiffForPath
include DiffHelper
# Authorize
......@@ -11,29 +12,14 @@ class Projects::CommitController < Projects::ApplicationController
before_action :authorize_update_build!, only: [:cancel_builds, :retry_builds]
before_action :authorize_read_commit_status!, only: [:builds]
before_action :commit
before_action :define_show_vars, only: [:show, :builds]
before_action :define_commit_vars, only: [:show, :diff_for_path, :builds]
before_action :define_status_vars, only: [:show, :builds]
before_action :define_note_vars, only: [:show, :diff_for_path]
before_action :authorize_edit_tree!, only: [:revert, :cherry_pick]
def show
apply_diff_view_cookie!
@grouped_diff_notes = commit.notes.grouped_diff_notes
@notes = commit.notes.non_diff_notes.fresh
Banzai::NoteRenderer.render(
@grouped_diff_notes.values.flatten + @notes,
@project,
current_user,
)
@note = @project.build_commit_note(commit)
@noteable = @commit
@comments_target = {
noteable_type: 'Commit',
commit_id: @commit.id
}
respond_to do |format|
format.html
format.diff { render text: @commit.to_diff }
......@@ -41,6 +27,10 @@ class Projects::CommitController < Projects::ApplicationController
end
end
def diff_for_path
render_diff_for_path(@diffs, @commit.diff_refs, @project)
end
def builds
end
......@@ -114,7 +104,7 @@ class Projects::CommitController < Projects::ApplicationController
@ci_builds ||= Ci::Build.where(pipeline: pipelines)
end
def define_show_vars
def define_commit_vars
return git_not_found! unless commit
opts = diff_options
......@@ -122,7 +112,28 @@ class Projects::CommitController < Projects::ApplicationController
@diffs = commit.diffs(opts)
@notes_count = commit.notes.count
end
def define_note_vars
@grouped_diff_notes = commit.notes.grouped_diff_notes
@notes = commit.notes.non_diff_notes.fresh
Banzai::NoteRenderer.render(
@grouped_diff_notes.values.flatten + @notes,
@project,
current_user,
)
@note = @project.build_commit_note(commit)
@noteable = @commit
@comments_target = {
noteable_type: 'Commit',
commit_id: @commit.id
}
end
def define_status_vars
@statuses = CommitStatus.where(pipeline: pipelines)
@builds = Ci::Build.where(pipeline: pipelines)
end
......
require 'addressable/uri'
class Projects::CompareController < Projects::ApplicationController
include DiffForPath
include DiffHelper
# Authorize
before_action :require_non_empty_project
before_action :authorize_download_code!
before_action :assign_ref_vars, only: [:index, :show]
before_action :define_ref_vars, only: [:index, :show, :diff_for_path]
before_action :define_diff_vars, only: [:show, :diff_for_path]
before_action :merge_request, only: [:index, :show]
def index
end
def show
compare = CompareService.new.
execute(@project, @head_ref, @project, @start_ref, diff_options)
end
def diff_for_path
return render_404 unless @compare
render_diff_for_path(@diffs, @diff_refs, @project)
end
def create
redirect_to namespace_project_compare_path(@project.namespace, @project,
params[:from], params[:to])
end
private
if compare
@commits = Commit.decorate(compare.commits, @project)
def define_ref_vars
@start_ref = Addressable::URI.unescape(params[:from])
@ref = @head_ref = Addressable::URI.unescape(params[:to])
end
def define_diff_vars
@compare = CompareService.new.execute(@project, @head_ref, @project, @start_ref)
if @compare
@commits = Commit.decorate(@compare.commits, @project)
@start_commit = @project.commit(@start_ref)
@commit = @project.commit(@head_ref)
@base_commit = @project.merge_base_commit(@start_ref, @head_ref)
@diffs = compare.diffs(diff_options)
@diffs = @compare.diffs(diff_options)
@diff_refs = Gitlab::Diff::DiffRefs.new(
base_sha: @base_commit.try(:sha),
start_sha: @start_commit.try(:sha),
......@@ -35,18 +57,6 @@ class Projects::CompareController < Projects::ApplicationController
end
end
def create
redirect_to namespace_project_compare_path(@project.namespace, @project,
params[:from], params[:to])
end
private
def assign_ref_vars
@start_ref = Addressable::URI.unescape(params[:from])
@ref = @head_ref = Addressable::URI.unescape(params[:to])
end
def merge_request
@merge_request ||= @project.merge_requests.opened.
find_by(source_project: @project, source_branch: @head_ref, target_branch: @start_ref)
......
class Projects::MergeRequestsController < Projects::ApplicationController
include ToggleSubscriptionAction
include DiffForPath
include DiffHelper
include IssuableActions
include ToggleAwardEmoji
......@@ -12,6 +13,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
before_action :validates_merge_request, only: [:show, :diffs, :commits, :builds]
before_action :define_show_vars, only: [:show, :diffs, :commits, :builds]
before_action :define_widget_vars, only: [:merge, :cancel_merge_when_build_succeeds, :merge_check]
before_action :define_commit_vars, only: [:diffs]
before_action :define_diff_comment_vars, only: [:diffs]
before_action :ensure_ref_fetched, only: [:show, :diffs, :commits, :builds]
# Allow read any merge_request
......@@ -53,8 +56,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def show
respond_to do |format|
format.html
format.html { define_discussion_vars }
format.json do
render json: @merge_request
end
......@@ -78,35 +81,38 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request_diff = @merge_request.merge_request_diff
@commit = @merge_request.diff_head_commit
@base_commit = @merge_request.diff_base_commit || @merge_request.likely_diff_base_commit
@comments_target = {
noteable_type: 'MergeRequest',
noteable_id: @merge_request.id
}
@use_legacy_diff_notes = !@merge_request.support_new_diff_notes?
@grouped_diff_notes = @merge_request.notes.grouped_diff_notes
Banzai::NoteRenderer.render(
@grouped_diff_notes.values.flatten,
@project,
current_user,
@path,
@project_wiki,
@ref
)
respond_to do |format|
format.html
format.html { define_discussion_vars }
format.json { render json: { html: view_to_html_string("projects/merge_requests/show/_diffs") } }
end
end
# With an ID param, loads the MR at that ID. Otherwise, accepts the same params as #new
# and uses that (unsaved) MR.
#
def diff_for_path
if params[:id]
merge_request
define_diff_comment_vars
else
build_merge_request
@diff_notes_disabled = true
@grouped_diff_notes = {}
end
define_commit_vars
diffs = @merge_request.diffs(diff_options)
render_diff_for_path(diffs, @merge_request.diff_refs, @merge_request.project)
end
def commits
respond_to do |format|
format.html { render 'show' }
format.html do
define_discussion_vars
render 'show'
end
format.json do
# Get commits from repository
# or from cache if already merged
......@@ -121,14 +127,17 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def builds
respond_to do |format|
format.html { render 'show' }
format.html do
define_discussion_vars
render 'show'
end
format.json { render json: { html: view_to_html_string('projects/merge_requests/show/_builds') } }
end
end
def new
params[:merge_request] ||= ActionController::Parameters.new(source_project: @project)
@merge_request = MergeRequests::BuildService.new(project, current_user, merge_request_params).execute
build_merge_request
@noteable = @merge_request
@target_branches = if @merge_request.target_project
......@@ -352,14 +361,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request.unlock_mr
@merge_request.close
end
if request.format == :html || action_name == 'show'
define_show_html_vars
end
end
# Discussion tab data is only required on html requests
def define_show_html_vars
# Discussion tab data is rendered on html responses of actions
# :show, :diff, :commits, :builds. but not when request the data through AJAX
def define_discussion_vars
# Build a note object for comment form
@note = @project.notes.new(noteable: @noteable)
......@@ -384,6 +390,30 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@pipelines = [@pipeline].compact
end
def define_commit_vars
@commit = @merge_request.diff_head_commit
@base_commit = @merge_request.diff_base_commit || @merge_request.likely_diff_base_commit
end
def define_diff_comment_vars
@comments_target = {
noteable_type: 'MergeRequest',
noteable_id: @merge_request.id
}
@use_legacy_diff_notes = !@merge_request.support_new_diff_notes?
@grouped_diff_notes = @merge_request.notes.grouped_diff_notes
Banzai::NoteRenderer.render(
@grouped_diff_notes.values.flatten,
@project,
current_user,
@path,
@project_wiki,
@ref
)
end
def invalid_mr
# Render special view for MR with removed source or target branch
render 'invalid'
......@@ -412,4 +442,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController
params[:merge_when_build_succeeds].present? &&
@merge_request.pipeline && @merge_request.pipeline.active?
end
def build_merge_request
params[:merge_request] ||= ActionController::Parameters.new(source_project: @project)
@merge_request = MergeRequests::BuildService.new(project, current_user, merge_request_params).execute
end
end
......@@ -5,7 +5,7 @@ class Projects::TodosController < Projects::ApplicationController
todo = TodoService.new.mark_todo(issuable, current_user)
render json: {
count: current_user.todos_pending_count,
count: TodosFinder.new(current_user, state: :pending).execute.count,
delete_path: dashboard_todo_path(todo)
}
end
......
......@@ -8,7 +8,7 @@
# action_id: integer
# author_id: integer
# project_id; integer
# state: 'pending' or 'done'
# state: 'pending' (default) or 'done'
# type: 'Issue' or 'MergeRequest'
#
......@@ -37,7 +37,7 @@ class TodosFinder
private
def action_id?
action_id.present? && [Todo::ASSIGNED, Todo::MENTIONED, Todo::BUILD_FAILED, Todo::MARKED].include?(action_id.to_i)
action_id.present? && Todo::ACTION_NAMES.has_key?(action_id.to_i)
end
def action_id
......
......@@ -31,7 +31,7 @@ module AppearancesHelper
end
end
def navbar_icon(icon_name, size: 16)
def custom_icon(icon_name, size: 16)
render "shared/icons/#{icon_name}.svg", size: size
end
end
module BlobHelper
def highlighter(blob_name, blob_content, repository: nil, nowrap: false)
Gitlab::Highlight.new(blob_name, blob_content, nowrap: nowrap, repository: repository)
end
def highlight(blob_name, blob_content, repository: nil, nowrap: false, plain: false)
Gitlab::Highlight.highlight(blob_name, blob_content, nowrap: nowrap, plain: plain, repository: repository)
def highlight(blob_name, blob_content, repository: nil, plain: false)
highlighted = Gitlab::Highlight.highlight(blob_name, blob_content, plain: plain, repository: repository)
raw %(<pre class="code highlight"><code>#{highlighted}</code></pre>)
end
def no_highlight_files
......
......@@ -15,29 +15,13 @@ module ButtonHelper
#
# See http://clipboardjs.com/#usage
def clipboard_button(data = {})
data = { toggle: 'tooltip', placement: 'bottom', container: 'body' }.merge(data)
content_tag :button,
icon('clipboard'),
class: "btn btn-clipboard",
data: data,
type: :button
end
# Output a "Copy to Clipboard" button with a custom CSS class
#
# data - Data attributes passed to `content_tag`
# css_class - Class passed to the `content_tag`
#
# Examples:
#
# # Define the target element
# clipboard_button_with_class({clipboard_target: "div#foo"}, css_class: "btn-clipboard")
# # => "<button class='btn btn-clipboard' data-clipboard-target='div#foo'>...</button>"
def clipboard_button_with_class(data = {}, css_class: 'btn-clipboard')
content_tag :button,
icon('clipboard'),
class: "btn #{css_class}",
data: data,
type: :button
type: :button,
title: "Copy to Clipboard"
end
def http_clone_button(project, placement = 'right', append_link: true)
......
......@@ -29,8 +29,10 @@ module CiStatusHelper
'check'
when 'failed'
'close'
when 'running', 'pending'
when 'pending'
'clock-o'
when 'running'
'spinner'
else
'circle'
end
......
......@@ -8,6 +8,10 @@ module DiffHelper
[marked_old_line, marked_new_line]
end
def expand_all_diffs?
@expand_all_diffs || params[:expand_all_diffs].present?
end
def diff_view
diff_views = %w(inline parallel)
......@@ -18,16 +22,14 @@ module DiffHelper
end
end
def diff_hard_limit_enabled?
params[:force_show_diff].present?
end
def diff_options
options = { ignore_whitespace_change: hide_whitespace? }
if diff_hard_limit_enabled?
options.merge!(Commit.max_diff_options)
default_options = Commit.max_diff_options
if action_name == 'diff_for_path'
default_options[:paths] = params.values_at(:old_path, :new_path)
end
options
default_options.merge(ignore_whitespace_change: hide_whitespace?)
end
def safe_diff_files(diffs, diff_refs: nil, repository: nil)
......@@ -35,7 +37,7 @@ module DiffHelper
end
def unfold_bottom_class(bottom)
bottom ? 'js-unfold-bottom' : ''
bottom ? 'js-unfold js-unfold-bottom' : ''
end
def unfold_class(unfold)
......@@ -90,7 +92,7 @@ module DiffHelper
def commit_for_diff(diff_file)
return diff_file.content_commit if diff_file.content_commit
if diff_file.deleted_file
@base_commit || @commit.parent || @commit
else
......
......@@ -61,7 +61,7 @@ module IssuablesHelper
output = content_tag :strong, "#{text} #{issuable.to_reference}", class: "identifier"
output << " opened #{time_ago_with_tooltip(issuable.created_at)} by ".html_safe
output << content_tag(:strong) do
author_output = link_to_member(project, issuable.author, size: 24, mobile_classes: "hidden-xs")
author_output = link_to_member(project, issuable.author, size: 24, mobile_classes: "hidden-xs", tooltip: true)
author_output << link_to_member(project, issuable.author, size: 24, by_username: true, avatar: false, mobile_classes: "hidden-sm hidden-md hidden-lg")
end
end
......
......@@ -96,4 +96,8 @@ module MergeRequestsHelper
["#{source_path}:#{source_branch}", "#{target_path}:#{target_branch}"]
end
end
def merge_request_button_visibility(merge_request, closed)
return 'hidden' if merge_request.closed? == closed || (merge_request.merged? == closed && !merge_request.closed?)
end
end
......@@ -24,7 +24,15 @@ module NotesHelper
}.to_json
end
def link_to_new_diff_note(line_code, position, line_type = nil)
def diff_view_data
return {} unless @comments_target
@comments_target.slice(:noteable_id, :noteable_type, :commit_id)
end
def diff_view_line_data(line_code, position, line_type)
return if @diff_notes_disabled
use_legacy_diff_note = @use_legacy_diff_notes
# If the controller doesn't force the use of legacy diff notes, we
# determine this on a line-by-line basis by seeing if there already exist
......@@ -41,11 +49,8 @@ module NotesHelper
end
data = {
noteable_type: @comments_target[:noteable_type],
noteable_id: @comments_target[:noteable_id],
commit_id: @comments_target[:commit_id],
line_type: line_type,
line_code: line_code
line_code: line_code,
line_type: line_type,
}
if use_legacy_diff_note
......@@ -73,11 +78,7 @@ module NotesHelper
)
end
button_tag(class: 'btn add-diff-note js-add-diff-note-button',
data: data,
title: 'Add a comment to this line') do
icon('comment-o')
end
data
end
def link_to_reply_discussion(note, line_type = nil)
......
......@@ -19,7 +19,7 @@ module ProjectsHelper
end
def link_to_member(project, author, opts = {}, &block)
default_opts = { avatar: true, name: true, size: 16, author_class: 'author', title: ":name" }
default_opts = { avatar: true, name: true, size: 16, author_class: 'author', title: ":name", tooltip: false }
opts = default_opts.merge(opts)
return "(deleted)" unless author
......@@ -33,7 +33,8 @@ module ProjectsHelper
if opts[:by_username]
author_html << content_tag(:span, sanitize("@#{author.username}"), class: opts[:author_class]) if opts[:name]
else
author_html << content_tag(:span, sanitize(author.name), class: opts[:author_class]) if opts[:name]
tooltip_data = { placement: 'top' }
author_html << content_tag(:span, sanitize(author.name), class: [opts[:author_class], ('has-tooltip' if opts[:tooltip])], title: (author.to_reference if opts[:tooltip]), data: (tooltip_data if opts[:tooltip])) if opts[:name]
end
author_html << capture(&block) if block
......@@ -60,7 +61,7 @@ module ProjectsHelper
project_link = link_to simple_sanitize(project.name), project_path(project), { class: "project-item-select-holder" }
if current_user
project_link << icon("chevron-down", class: "dropdown-toggle-caret js-projects-dropdown-toggle", data: { target: ".js-dropdown-menu-projects", toggle: "dropdown" })
project_link << icon("chevron-down", class: "dropdown-toggle-caret js-projects-dropdown-toggle", aria: { label: "Toggle switch project dropdown" }, data: { target: ".js-dropdown-menu-projects", toggle: "dropdown" })
end
full_title = "#{namespace_link} / #{project_link}".html_safe
......
......@@ -43,15 +43,15 @@ module SearchHelper
# Autocomplete results for internal help pages
def help_autocomplete
[
{ category: "Help", label: "API Help", url: help_page_path("api", "README") },
{ category: "Help", label: "Markdown Help", url: help_page_path("markdown", "markdown") },
{ category: "Help", label: "Permissions Help", url: help_page_path("permissions", "permissions") },
{ category: "Help", label: "Public Access Help", url: help_page_path("public_access", "public_access") },
{ category: "Help", label: "Rake Tasks Help", url: help_page_path("raketasks", "README") },
{ category: "Help", label: "SSH Keys Help", url: help_page_path("ssh", "README") },
{ category: "Help", label: "System Hooks Help", url: help_page_path("system_hooks", "system_hooks") },
{ category: "Help", label: "Webhooks Help", url: help_page_path("web_hooks", "web_hooks") },
{ category: "Help", label: "Workflow Help", url: help_page_path("workflow", "README") },
{ category: "Help", label: "API Help", url: help_page_path("api/README") },
{ category: "Help", label: "Markdown Help", url: help_page_path("markdown/markdown") },
{ category: "Help", label: "Permissions Help", url: help_page_path("user/permissions") },
{ category: "Help", label: "Public Access Help", url: help_page_path("public_access/public_access") },
{ category: "Help", label: "Rake Tasks Help", url: help_page_path("raketasks/README") },
{ category: "Help", label: "SSH Keys Help", url: help_page_path("ssh/README") },
{ category: "Help", label: "System Hooks Help", url: help_page_path("system_hooks/system_hooks") },
{ category: "Help", label: "Webhooks Help", url: help_page_path("web_hooks/web_hooks") },
{ category: "Help", label: "Workflow Help", url: help_page_path("workflow/README") },
]
end
......
module TodosHelper
def todos_pending_count
TodosFinder.new(current_user, state: :pending).execute.count
@todos_pending_count ||= TodosFinder.new(current_user, state: :pending).execute.count
end
def todos_done_count
TodosFinder.new(current_user, state: :done).execute.count
@todos_done_count ||= TodosFinder.new(current_user, state: :done).execute.count
end
def todo_action_name(todo)
......@@ -13,6 +13,7 @@ module TodosHelper
when Todo::MENTIONED then 'mentioned you on'
when Todo::BUILD_FAILED then 'The build failed for your'
when Todo::MARKED then 'added a todo for'
when Todo::APPROVAL_REQUIRED then 'set you as an approver for'
end
end
......
module U2fHelper
def inject_u2f_api?
browser.chrome? && browser.version.to_i >= 41 && !browser.device.mobile?
end
end
......@@ -28,4 +28,10 @@ module WorkhorseHelper
headers.store(*Gitlab::Workhorse.send_git_archive(repository, ref: ref, format: format))
head :ok
end
# Send an entry from artifacts through Workhorse
def send_artifacts_entry(build, entry)
headers.store(*Gitlab::Workhorse.send_artifacts_entry(build, entry))
head :ok
end
end
......@@ -29,7 +29,8 @@ module Emails
# used in notify layout
@target_url = @message.target_url
@project = Project.find(project_id)
@diff_notes_disabled = true
add_project_headers
headers['X-GitLab-Author'] = @message.author_username
......
......@@ -204,7 +204,8 @@ class Ability
:download_code,
:fork_project,
:read_commit_status,
:read_pipeline
:read_pipeline,
:read_container_image
]
end
......
......@@ -5,6 +5,7 @@ module Ci
belongs_to :erased_by, class_name: 'User'
serialize :options
serialize :yaml_variables
validates :coverage, numericality: true, allow_blank: true
validates_presence_of :ref
......@@ -52,7 +53,10 @@ module Ci
new_build.stage = build.stage
new_build.stage_idx = build.stage_idx
new_build.trigger_request = build.trigger_request
new_build.yaml_variables = build.yaml_variables
new_build.when = build.when
new_build.user = user
new_build.environment = build.environment
new_build.save
MergeRequests::AddTodoWhenBuildFailsService.new(build.project, nil).close(new_build)
new_build
......@@ -117,7 +121,12 @@ module Ci
end
def variables
predefined_variables + yaml_variables + project_variables + trigger_variables
variables = []
variables += predefined_variables
variables += yaml_variables if yaml_variables
variables += project_variables
variables += trigger_variables
variables
end
def merge_request
......@@ -394,30 +403,6 @@ module Ci
self.update(erased_by: user, erased_at: Time.now, artifacts_expire_at: nil)
end
def yaml_variables
global_yaml_variables + job_yaml_variables
end
def global_yaml_variables
if pipeline.config_processor
pipeline.config_processor.global_variables.map do |key, value|
{ key: key, value: value, public: true }
end
else
[]
end
end
def job_yaml_variables
if pipeline.config_processor
pipeline.config_processor.job_variables(name).map do |key, value|
{ key: key, value: value, public: true }
end
else
[]
end
end
def project_variables
project.variables.map do |variable|
{ key: variable.key, value: variable.value, public: false }
......
......@@ -6,6 +6,8 @@ module Ci
self.table_name = 'ci_commits'
belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id
belongs_to :user
has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id
has_many :builds, class_name: 'Ci::Build', foreign_key: :commit_id
has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest', foreign_key: :commit_id
......@@ -223,6 +225,8 @@ module Ci
end
def keep_around_commits
return unless project
project.repository.keep_around(self.sha)
project.repository.keep_around(self.before_sha)
end
......
......@@ -65,8 +65,7 @@ module Awardable
def create_award_emoji(name, current_user)
return unless emoji_awardable?
award_emoji.create(name: name, user: current_user)
award_emoji.create(name: normalize_name(name), user: current_user)
end
def remove_award_emoji(name, current_user)
......@@ -80,4 +79,10 @@ module Awardable
create_award_emoji(emoji_name, current_user)
end
end
private
def normalize_name(name)
Gitlab::AwardEmoji.normalize_emoji_name(name)
end
end
......@@ -14,14 +14,14 @@ module Mentionable
attr = attr.to_s
mentionable_attrs << [attr, options]
end
end
included do
# Accessor for attributes marked mentionable.
def mentionable_attrs
@mentionable_attrs ||= []
cattr_accessor :mentionable_attrs, instance_accessor: false do
[]
end
end
included do
if self < Participable
participant -> (user, ext) { all_references(user, extractor: ext) }
end
......
......@@ -41,9 +41,12 @@ module Participable
def participant(attr)
participant_attrs << attr
end
end
def participant_attrs
@participant_attrs ||= []
included do
# Accessor for participant attributes.
cattr_accessor :participant_attrs, instance_accessor: false do
[]
end
end
......
......@@ -52,14 +52,17 @@ class Label < ActiveRecord::Base
# This pattern supports cross-project references.
#
def self.reference_pattern
# NOTE: The id pattern only matches when all characters on the expression
# are digits, so it will match ~2 but not ~2fa because that's probably a
# label name and we want it to be matched as such.
@reference_pattern ||= %r{
(#{Project.reference_pattern})?
#{Regexp.escape(reference_prefix)}
(?:
(?<label_id>\d+) | # Integer-based label ID, or
(?<label_id>\d+(?!\S\w)\b) | # Integer-based label ID, or
(?<label_name>
[A-Za-z0-9_\-\?&]+ | # String-based single-word label title, or
"[^,]+" # String-based multi-word label surrounded in quotes
[A-Za-z0-9_\-\?\.&]+ | # String-based single-word label title, or
".+?" # String-based multi-word label surrounded in quotes
)
)
}x
......
......@@ -38,7 +38,7 @@ class LegacyDiffNote < Note
end
def diff_line
@diff_line ||= diff_file.line_for_line_code(self.line_code)
@diff_line ||= diff_file.line_for_line_code(self.line_code) if diff_file
end
def for_line?(line)
......@@ -55,7 +55,7 @@ class LegacyDiffNote < Note
def active?
return @active if defined?(@active)
return true if for_commit?
return true unless self.diff
return true unless diff_line
return false unless noteable
noteable_diff = find_noteable_diff
......
......@@ -19,7 +19,7 @@ class MergeRequest < ActiveRecord::Base
after_create :create_merge_request_diff, unless: :importing?
after_update :update_merge_request_diff
delegate :commits, :diffs, :real_size, to: :merge_request_diff, prefix: nil
delegate :commits, :real_size, to: :merge_request_diff, prefix: nil
# When this attribute is true some MR validation is ignored
# It allows us to close or modify broken merge requests
......@@ -164,6 +164,10 @@ class MergeRequest < ActiveRecord::Base
merge_request_diff ? merge_request_diff.first_commit : compare_commits.first
end
def diffs(*args)
merge_request_diff ? merge_request_diff.diffs(*args) : compare.diffs(*args)
end
def diff_size
merge_request_diff.size
end
......
......@@ -46,7 +46,8 @@ class MergeRequestDiff < ActiveRecord::Base
compare.diffs(options)
end
else
@diffs ||= load_diffs(st_diffs, options)
@diffs ||= {}
@diffs[options] ||= load_diffs(st_diffs, options)
end
end
......@@ -144,6 +145,12 @@ class MergeRequestDiff < ActiveRecord::Base
def load_diffs(raw, options)
if raw.respond_to?(:each)
if paths = options[:paths]
raw = raw.select do |diff|
paths.include?(diff[:old_path]) || paths.include?(diff[:new_path])
end
end
Gitlab::Git::DiffCollection.new(raw, options)
else
Gitlab::Git::DiffCollection.new([])
......
......@@ -10,6 +10,10 @@ class Note < ActiveRecord::Base
# Banzai::ObjectRenderer.
attr_accessor :note_html
# An Array containing the number of visible references as generated by
# Banzai::ObjectRenderer
attr_accessor :user_visible_reference_count
default_value_for :system, false
attr_mentionable :note, pipeline: :note
......@@ -193,7 +197,15 @@ class Note < ActiveRecord::Base
end
def cross_reference_not_visible_for?(user)
cross_reference? && referenced_mentionables(user).empty?
cross_reference? && !has_referenced_mentionables?(user)
end
def has_referenced_mentionables?(user)
if user_visible_reference_count.present?
user_visible_reference_count > 0
else
referenced_mentionables(user).any?
end
end
def award_emoji?
......@@ -217,8 +229,7 @@ class Note < ActiveRecord::Base
end
def award_emoji_name
original_name = note.match(Banzai::Filter::EmojiFilter.emoji_pattern)[1]
Gitlab::AwardEmoji.normalize_emoji_name(original_name)
note.match(Banzai::Filter::EmojiFilter.emoji_pattern)[1]
end
private
......
......@@ -470,8 +470,8 @@ class Project < ActiveRecord::Base
return super(value) unless Gitlab::UrlSanitizer.valid?(value)
import_url = Gitlab::UrlSanitizer.new(value)
create_or_update_import_data(credentials: import_url.credentials)
super(import_url.sanitized_url)
create_or_update_import_data(credentials: import_url.credentials)
end
def import_url
......@@ -483,7 +483,13 @@ class Project < ActiveRecord::Base
end
end
def valid_import_url?
valid? || errors.messages[:import_url].nil?
end
def create_or_update_import_data(data: nil, credentials: nil)
return unless import_url.present? && valid_import_url?
project_import_data = import_data || build_import_data
if data
project_import_data.data ||= {}
......@@ -1038,8 +1044,8 @@ class Project < ActiveRecord::Base
pipelines.order(id: :desc).find_by(sha: sha, ref: ref)
end
def ensure_pipeline(sha, ref)
pipeline(sha, ref) || pipelines.create(sha: sha, ref: ref)
def ensure_pipeline(sha, ref, current_user = nil)
pipeline(sha, ref) || pipelines.create(sha: sha, ref: ref, user: current_user)
end
def enable_ci
......
......@@ -72,6 +72,19 @@ class SentNotification < ActiveRecord::Base
end
end
def position=(new_position)
if new_position.is_a?(String)
new_position = JSON.parse(new_position) rescue nil
end
if new_position.is_a?(Hash)
new_position = new_position.with_indifferent_access
new_position = Gitlab::Diff::Position.new(new_position)
end
super(new_position)
end
def to_param
self.reply_key
end
......
class Todo < ActiveRecord::Base
ASSIGNED = 1
MENTIONED = 2
BUILD_FAILED = 3
MARKED = 4
ASSIGNED = 1
MENTIONED = 2
BUILD_FAILED = 3
MARKED = 4
APPROVAL_REQUIRED = 5 # This is an EE-only feature
ACTION_NAMES = {
ASSIGNED => :assigned,
MENTIONED => :mentioned,
BUILD_FAILED => :build_failed,
MARKED => :marked
MARKED => :marked,
APPROVAL_REQUIRED => :approval_required
}
belongs_to :author, class_name: "User"
......
......@@ -85,9 +85,10 @@ class User < ActiveRecord::Base
has_one :abuse_report, dependent: :destroy
has_many :spam_logs, dependent: :destroy
has_many :builds, dependent: :nullify, class_name: 'Ci::Build'
has_many :pipelines, dependent: :nullify, class_name: 'Ci::Pipeline'
has_many :todos, dependent: :destroy
has_many :notification_settings, dependent: :destroy
has_many :award_emoji, as: :awardable, dependent: :destroy
has_many :award_emoji, dependent: :destroy
#
# Validations
......
......@@ -36,7 +36,9 @@ module Ci
:allow_failure,
:stage,
:stage_idx,
:environment)
:environment,
:when,
:yaml_variables)
build_attrs.merge!(pipeline: @pipeline,
ref: @pipeline.ref,
......
......@@ -2,6 +2,7 @@ module Ci
class CreatePipelineService < BaseService
def execute
pipeline = project.pipelines.new(params)
pipeline.user = current_user
unless ref_names.include?(params[:ref])
pipeline.errors.add(:base, 'Reference not found')
......
......@@ -3,7 +3,7 @@ require 'securerandom'
# Compare 2 branches for one repo or between repositories
# and return Gitlab::Git::Compare object that responds to commits and diffs
class CompareService
def execute(source_project, source_branch, target_project, target_branch, diff_options = {})
def execute(source_project, source_branch, target_project, target_branch)
source_commit = source_project.commit(source_branch)
return unless source_commit
......
......@@ -14,7 +14,13 @@ class CreateCommitBuildsService
return false
end
@pipeline = Ci::Pipeline.new(project: project, sha: sha, ref: ref, before_sha: before_sha, tag: tag)
@pipeline = Ci::Pipeline.new(
project: project,
sha: sha,
ref: ref,
before_sha: before_sha,
tag: tag,
user: user)
##
# Skip creating pipeline if no gitlab-ci.yml is found
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment