Imported Upstream version 8.9.0+dfsg
This commit is contained in:
parent
bcb40d0491
commit
0f42b0dbae
500 changed files with 23391 additions and 2691 deletions
89
CHANGELOG
89
CHANGELOG
|
@ -1,19 +1,29 @@
|
||||||
Please view this file on the master branch, on stable branches it's out of date.
|
Please view this file on the master branch, on stable branches it's out of date.
|
||||||
|
|
||||||
v 8.9.0 (unreleased)
|
v 8.9.0 (unreleased)
|
||||||
|
- Fix builds API response not including commit data
|
||||||
|
- Fix error when CI job variables key specified but not defined
|
||||||
|
- Fix pipeline status when there are no builds in pipeline
|
||||||
- Fix Error 500 when using closes_issues API with an external issue tracker
|
- Fix Error 500 when using closes_issues API with an external issue tracker
|
||||||
- Add more information into RSS feed for issues (Alexander Matyushentsev)
|
- Add more information into RSS feed for issues (Alexander Matyushentsev)
|
||||||
- Bulk assign/unassign labels to issues.
|
- Bulk assign/unassign labels to issues.
|
||||||
- Ability to prioritize labels !4009 / !3205 (Thijs Wouters)
|
- Ability to prioritize labels !4009 / !3205 (Thijs Wouters)
|
||||||
|
- Show Star and Fork buttons on mobile.
|
||||||
|
- Performance improvements on RelativeLinkFilter
|
||||||
- Fix endless redirections when accessing user OAuth applications when they are disabled
|
- Fix endless redirections when accessing user OAuth applications when they are disabled
|
||||||
- Allow enabling wiki page events from Webhook management UI
|
- Allow enabling wiki page events from Webhook management UI
|
||||||
- Bump rouge to 1.11.0
|
- Bump rouge to 1.11.0
|
||||||
- Fix issue with arrow keys not working in search autocomplete dropdown
|
- Fix issue with arrow keys not working in search autocomplete dropdown
|
||||||
- Fix an issue where note polling stopped working if a window was in the
|
- Fix an issue where note polling stopped working if a window was in the
|
||||||
background during a refresh.
|
background during a refresh.
|
||||||
|
- Pre-processing Markdown now only happens when needed
|
||||||
- Make EmailsOnPushWorker use Sidekiq mailers queue
|
- Make EmailsOnPushWorker use Sidekiq mailers queue
|
||||||
|
- Redesign all Devise emails. !4297
|
||||||
|
- Don't show 'Leave Project' to group members
|
||||||
- Fix wiki page events' webhook to point to the wiki repository
|
- Fix wiki page events' webhook to point to the wiki repository
|
||||||
|
- Add a border around images to differentiate them from the background.
|
||||||
- Don't show tags for revert and cherry-pick operations
|
- Don't show tags for revert and cherry-pick operations
|
||||||
|
- Show image ID on registry page
|
||||||
- Fix issue todo not remove when leave project !4150 (Long Nguyen)
|
- Fix issue todo not remove when leave project !4150 (Long Nguyen)
|
||||||
- Allow customisable text on the 'nearly there' page after a user signs up
|
- Allow customisable text on the 'nearly there' page after a user signs up
|
||||||
- Bump recaptcha gem to 3.0.0 to remove deprecated stoken support
|
- Bump recaptcha gem to 3.0.0 to remove deprecated stoken support
|
||||||
|
@ -22,12 +32,18 @@ v 8.9.0 (unreleased)
|
||||||
- Added descriptions to notification settings dropdown
|
- Added descriptions to notification settings dropdown
|
||||||
- Improve note validation to prevent errors when creating invalid note via API
|
- Improve note validation to prevent errors when creating invalid note via API
|
||||||
- Reduce number of fog gem dependencies
|
- Reduce number of fog gem dependencies
|
||||||
|
- Add number of merge requests for a given milestone to the milestones view.
|
||||||
|
- Implement a fair usage of shared runners
|
||||||
- Remove project notification settings associated with deleted projects
|
- Remove project notification settings associated with deleted projects
|
||||||
- Fix 404 page when viewing TODOs that contain milestones or labels in different projects
|
- Fix 404 page when viewing TODOs that contain milestones or labels in different projects
|
||||||
- Add a metric for the number of new Redis connections created by a transaction
|
- Add a metric for the number of new Redis connections created by a transaction
|
||||||
- Fix Error 500 when viewing a blob with binary characters after the 1024-byte mark
|
- Fix Error 500 when viewing a blob with binary characters after the 1024-byte mark
|
||||||
- Redesign navigation for project pages
|
- Redesign navigation for project pages
|
||||||
|
- Fix images in sign-up confirmation email
|
||||||
|
- Added shortcut 'y' for copying a files content hash URL #14470
|
||||||
- Fix groups API to list only user's accessible projects
|
- Fix groups API to list only user's accessible projects
|
||||||
|
- Fix horizontal scrollbar for long commit message.
|
||||||
|
- GitLab Performance Monitoring now tracks the total method execution time and call count per method
|
||||||
- Add Environments and Deployments
|
- Add Environments and Deployments
|
||||||
- Redesign account and email confirmation emails
|
- Redesign account and email confirmation emails
|
||||||
- Don't fail builds for projects that are deleted
|
- Don't fail builds for projects that are deleted
|
||||||
|
@ -35,30 +51,47 @@ v 8.9.0 (unreleased)
|
||||||
- `git clone https://host/namespace/project` now works, in addition to using the `.git` suffix
|
- `git clone https://host/namespace/project` now works, in addition to using the `.git` suffix
|
||||||
- Bump nokogiri to 1.6.8
|
- Bump nokogiri to 1.6.8
|
||||||
- Use gitlab-shell v3.0.0
|
- Use gitlab-shell v3.0.0
|
||||||
|
- Fixed alignment of download dropdown in merge requests
|
||||||
- Upgrade to jQuery 2
|
- Upgrade to jQuery 2
|
||||||
|
- Adds selected branch name to the dropdown toggle
|
||||||
|
- Add API endpoint for Sidekiq Metrics !4653
|
||||||
|
- Refactoring Award Emoji with API support for Issues and MergeRequests
|
||||||
- Use Knapsack to evenly distribute tests across multiple nodes
|
- Use Knapsack to evenly distribute tests across multiple nodes
|
||||||
- Add `sha` parameter to MR merge API, to ensure only reviewed changes are merged
|
- Add `sha` parameter to MR merge API, to ensure only reviewed changes are merged
|
||||||
- Don't allow MRs to be merged when commits were added since the last review / page load
|
- Don't allow MRs to be merged when commits were added since the last review / page load
|
||||||
- Add DB index on users.state
|
- Add DB index on users.state
|
||||||
|
- Limit email on push diff size to 30 files / 150 KB
|
||||||
- Add rake task 'gitlab:db:configure' for conditionally seeding or migrating the database
|
- 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)
|
- Changed the Slack build message to use the singular duration if necessary (Aran Koning)
|
||||||
- Links from a wiki page to other wiki pages should be rewritten as expected
|
- 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)
|
- 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
|
||||||
- Fix issues filter when ordering by milestone
|
- Fix issues filter when ordering by milestone
|
||||||
|
- Disable SAML account unlink feature
|
||||||
- Added artifacts:when to .gitlab-ci.yml - this requires GitLab Runner 1.3
|
- Added artifacts:when to .gitlab-ci.yml - this requires GitLab Runner 1.3
|
||||||
- Bamboo Service: Fix missing credentials & URL handling when base URL contains a path (Benjamin Schmid)
|
- Bamboo Service: Fix missing credentials & URL handling when base URL contains a path (Benjamin Schmid)
|
||||||
- TeamCity Service: Fix URL handling when base URL contains a path
|
- TeamCity Service: Fix URL handling when base URL contains a path
|
||||||
- Todos will display target state if issuable target is 'Closed' or 'Merged'
|
- Todos will display target state if issuable target is 'Closed' or 'Merged'
|
||||||
|
- Validate only and except regexp
|
||||||
- Fix bug when sorting issues by milestone due date and filtering by two or more labels
|
- Fix bug when sorting issues by milestone due date and filtering by two or more labels
|
||||||
|
- POST to API /projects/:id/runners/:runner_id would give 409 if the runner was already enabled for this project
|
||||||
- Add support for using Yubikeys (U2F) for two-factor authentication
|
- Add support for using Yubikeys (U2F) for two-factor authentication
|
||||||
- Link to blank group icon doesn't throw a 404 anymore
|
- Link to blank group icon doesn't throw a 404 anymore
|
||||||
- Remove 'main language' feature
|
- Remove 'main language' feature
|
||||||
|
- Toggle whitespace button now available for compare branches diffs #17881
|
||||||
- Pipelines can be canceled only when there are running builds
|
- Pipelines can be canceled only when there are running builds
|
||||||
|
- Allow authentication using personal access tokens
|
||||||
- Use downcased path to container repository as this is expected path by Docker
|
- Use downcased path to container repository as this is expected path by Docker
|
||||||
|
- Allow to use CI token to fetch LFS objects
|
||||||
|
- Custom notification settings
|
||||||
- Projects pending deletion will render a 404 page
|
- Projects pending deletion will render a 404 page
|
||||||
- Measure queue duration between gitlab-workhorse and Rails
|
- Measure queue duration between gitlab-workhorse and Rails
|
||||||
|
- Added Gfm autocomplete for labels
|
||||||
|
- Added edit note 'up' shortcut documentation to the help panel and docs screenshot #18114
|
||||||
- Make Omniauth providers specs to not modify global configuration
|
- Make Omniauth providers specs to not modify global configuration
|
||||||
|
- Remove unused JiraIssue class and replace references with ExternalIssue. !4659 (Ilan Shamir)
|
||||||
- Make authentication service for Container Registry to be compatible with < Docker 1.11
|
- Make authentication service for Container Registry to be compatible with < Docker 1.11
|
||||||
|
- Make it possible to lock a runner from being enabled for other projects
|
||||||
- Add Application Setting to configure Container Registry token expire delay (default 5min)
|
- Add Application Setting to configure Container Registry token expire delay (default 5min)
|
||||||
- Cache assigned issue and merge request counts in sidebar nav
|
- Cache assigned issue and merge request counts in sidebar nav
|
||||||
- Use Knapsack only in CI environment
|
- Use Knapsack only in CI environment
|
||||||
|
@ -74,8 +107,11 @@ v 8.9.0 (unreleased)
|
||||||
- Replace Colorize with Rainbow for coloring console output in Rake tasks.
|
- Replace Colorize with Rainbow for coloring console output in Rake tasks.
|
||||||
- Add workhorse controller and API helpers
|
- Add workhorse controller and API helpers
|
||||||
- An indicator is now displayed at the top of the comment field for confidential issues.
|
- An indicator is now displayed at the top of the comment field for confidential issues.
|
||||||
|
- Show categorised search queries in the search autocomplete
|
||||||
- RepositoryCheck::SingleRepositoryWorker public and private methods are now instrumented
|
- RepositoryCheck::SingleRepositoryWorker public and private methods are now instrumented
|
||||||
|
- Dropdown for `.gitlab-ci.yml` templates
|
||||||
- Improve issuables APIs performance when accessing notes !4471
|
- Improve issuables APIs performance when accessing notes !4471
|
||||||
|
- Add sorting dropdown to tags page !4423
|
||||||
- External links now open in a new tab
|
- External links now open in a new tab
|
||||||
- Prevent default actions of disabled buttons and links
|
- Prevent default actions of disabled buttons and links
|
||||||
- Markdown editor now correctly resets the input value on edit cancellation !4175
|
- Markdown editor now correctly resets the input value on edit cancellation !4175
|
||||||
|
@ -83,6 +119,7 @@ v 8.9.0 (unreleased)
|
||||||
- Improved UX of date pickers on issue & milestone forms
|
- Improved UX of date pickers on issue & milestone forms
|
||||||
- Cache on the database if a project has an active external issue tracker.
|
- Cache on the database if a project has an active external issue tracker.
|
||||||
- Put project Labels and Milestones pages links under Issues and Merge Requests tabs as subnav
|
- Put project Labels and Milestones pages links under Issues and Merge Requests tabs as subnav
|
||||||
|
- GitLab project import and export functionality
|
||||||
- All classes in the Banzai::ReferenceParser namespace are now instrumented
|
- All classes in the Banzai::ReferenceParser namespace are now instrumented
|
||||||
- Remove deprecated issues_tracker and issues_tracker_id from project model
|
- Remove deprecated issues_tracker and issues_tracker_id from project model
|
||||||
- Allow users to create confidential issues in private projects
|
- Allow users to create confidential issues in private projects
|
||||||
|
@ -108,9 +145,37 @@ v 8.8.5 (unreleased)
|
||||||
- Fix importer for GitHub comments on diff
|
- Fix importer for GitHub comments on diff
|
||||||
- Disable Webhooks before proceeding with the GitHub import
|
- Disable Webhooks before proceeding with the GitHub import
|
||||||
- Fix incremental trace upload API when using multi-byte UTF-8 chars in trace
|
- Fix incremental trace upload API when using multi-byte UTF-8 chars in trace
|
||||||
|
- Set inverse_of for Project/Service association to reduce the number of queries
|
||||||
|
- Update tanuki logo highlight/loading colors
|
||||||
|
- Remove explicit Gitlab::Metrics.action assignments, are already automatic.
|
||||||
|
- Use Git cached counters for branches and tags on project page
|
||||||
|
- Cache participable participants in an instance variable.
|
||||||
|
- Filter parameters for request_uri value on instrumented transactions.
|
||||||
|
- Remove duplicated keys add UNIQUE index to keys fingerprint column
|
||||||
|
- ExtractsPath get ref_names from repository cache, if not there access git.
|
||||||
|
- Cache user todo counts from TodoService
|
||||||
|
- Ensure Todos counters doesn't count Todos for projects pending delete
|
||||||
|
- Add tooltip to pin/unpin navbar
|
||||||
|
- Add left/right arrows horizontal navigation
|
||||||
|
- Add new sub nav style to Wiki and Graphs sub navigation
|
||||||
|
|
||||||
|
v 8.8.5
|
||||||
|
- Import GitHub repositories respecting the API rate limit !4166
|
||||||
|
- Fix todos page throwing errors when you have a project pending deletion !4300
|
||||||
|
- Disable Webhooks before proceeding with the GitHub import !4470
|
||||||
|
- Fix importer for GitHub comments on diff !4488
|
||||||
|
- Adjust the SAML control flow to allow LDAP identities to be added to an existing SAML user !4498
|
||||||
|
- Fix incremental trace upload API when using multi-byte UTF-8 chars in trace !4541
|
||||||
|
- Prevent unauthorized access for projects build traces
|
||||||
|
- Forbid scripting for wiki files
|
||||||
|
- Only show notes through JSON on confidential issues that the user has access to
|
||||||
|
- Banzai::Filter::UploadLinkFilter use XPath instead CSS expressions
|
||||||
|
- Banzai::Filter::ExternalLinkFilter use XPath instead CSS expressions
|
||||||
|
|
||||||
v 8.8.4
|
v 8.8.4
|
||||||
- Fix LDAP-based login for users with 2FA enabled. !4493
|
- Fix LDAP-based login for users with 2FA enabled. !4493
|
||||||
|
- Added descriptions to notification settings dropdown
|
||||||
|
- Due date can be removed from milestones
|
||||||
|
|
||||||
v 8.8.3
|
v 8.8.3
|
||||||
- Fix 404 page when viewing TODOs that contain milestones or labels in different projects. !4312
|
- Fix 404 page when viewing TODOs that contain milestones or labels in different projects. !4312
|
||||||
|
@ -226,6 +291,9 @@ v 8.8.0
|
||||||
|
|
||||||
v 8.7.7
|
v 8.7.7
|
||||||
- Fix import by `Any Git URL` broken if the URL contains a space
|
- Fix import by `Any Git URL` broken if the URL contains a space
|
||||||
|
- Prevent unauthorized access to other projects build traces
|
||||||
|
- Forbid scripting for wiki files
|
||||||
|
- Only show notes through JSON on confidential issues that the user has access to
|
||||||
|
|
||||||
v 8.7.6
|
v 8.7.6
|
||||||
- Fix links on wiki pages for relative url setups. !4131 (Artem Sidorenko)
|
- Fix links on wiki pages for relative url setups. !4131 (Artem Sidorenko)
|
||||||
|
@ -388,6 +456,11 @@ v 8.7.0
|
||||||
- Add RAW build trace output and button on build page
|
- Add RAW build trace output and button on build page
|
||||||
- Add incremental build trace update into CI API
|
- Add incremental build trace update into CI API
|
||||||
|
|
||||||
|
v 8.6.9
|
||||||
|
- Prevent unauthorized access to other projects build traces
|
||||||
|
- Forbid scripting for wiki files
|
||||||
|
- Only show notes through JSON on confidential issues that the user has access to
|
||||||
|
|
||||||
v 8.6.8
|
v 8.6.8
|
||||||
- Prevent privilege escalation via "impersonate" feature
|
- Prevent privilege escalation via "impersonate" feature
|
||||||
- Prevent privilege escalation via notes API
|
- Prevent privilege escalation via notes API
|
||||||
|
@ -542,6 +615,10 @@ v 8.6.0
|
||||||
- Trigger a todo for mentions on commits page
|
- Trigger a todo for mentions on commits page
|
||||||
- Let project owners and admins soft delete issues and merge requests
|
- Let project owners and admins soft delete issues and merge requests
|
||||||
|
|
||||||
|
v 8.5.13
|
||||||
|
- Prevent unauthorized access to other projects build traces
|
||||||
|
- Forbid scripting for wiki files
|
||||||
|
|
||||||
v 8.5.12
|
v 8.5.12
|
||||||
- Prevent privilege escalation via "impersonate" feature
|
- Prevent privilege escalation via "impersonate" feature
|
||||||
- Prevent privilege escalation via notes API
|
- Prevent privilege escalation via notes API
|
||||||
|
@ -703,6 +780,10 @@ v 8.5.0
|
||||||
- Show label row when filtering issues or merge requests by label (Nuttanart Pornprasitsakul)
|
- Show label row when filtering issues or merge requests by label (Nuttanart Pornprasitsakul)
|
||||||
- Add Todos
|
- Add Todos
|
||||||
|
|
||||||
|
v 8.4.11
|
||||||
|
- Prevent unauthorized access to other projects build traces
|
||||||
|
- Forbid scripting for wiki files
|
||||||
|
|
||||||
v 8.4.10
|
v 8.4.10
|
||||||
- Prevent privilege escalation via "impersonate" feature
|
- Prevent privilege escalation via "impersonate" feature
|
||||||
- Prevent privilege escalation via notes API
|
- Prevent privilege escalation via notes API
|
||||||
|
@ -839,6 +920,10 @@ v 8.4.0
|
||||||
- Add IP check against DNSBLs at account sign-up
|
- Add IP check against DNSBLs at account sign-up
|
||||||
- Added cache:key to .gitlab-ci.yml allowing to fine tune the caching
|
- Added cache:key to .gitlab-ci.yml allowing to fine tune the caching
|
||||||
|
|
||||||
|
v 8.3.10
|
||||||
|
- Prevent unauthorized access to other projects build traces
|
||||||
|
- Forbid scripting for wiki files
|
||||||
|
|
||||||
v 8.3.9
|
v 8.3.9
|
||||||
- Prevent privilege escalation via "impersonate" feature
|
- Prevent privilege escalation via "impersonate" feature
|
||||||
- Prevent privilege escalation via notes API
|
- Prevent privilege escalation via notes API
|
||||||
|
@ -957,6 +1042,10 @@ v 8.3.0
|
||||||
- Expose Git's version in the admin area
|
- Expose Git's version in the admin area
|
||||||
- Show "New Merge Request" buttons on canonical repos when you have a fork (Josh Frye)
|
- Show "New Merge Request" buttons on canonical repos when you have a fork (Josh Frye)
|
||||||
|
|
||||||
|
v 8.2.6
|
||||||
|
- Prevent unauthorized access to other projects build traces
|
||||||
|
- Forbid scripting for wiki files
|
||||||
|
|
||||||
v 8.2.5
|
v 8.2.5
|
||||||
- Prevent privilege escalation via "impersonate" feature
|
- Prevent privilege escalation via "impersonate" feature
|
||||||
- Prevent privilege escalation via notes API
|
- Prevent privilege escalation via notes API
|
||||||
|
|
9
Gemfile
9
Gemfile
|
@ -48,11 +48,11 @@ gem 'attr_encrypted', '~> 3.0.0'
|
||||||
gem 'u2f', '~> 0.2.1'
|
gem 'u2f', '~> 0.2.1'
|
||||||
|
|
||||||
# Browser detection
|
# Browser detection
|
||||||
gem "browser", '~> 2.0.3'
|
gem "browser", '~> 2.2'
|
||||||
|
|
||||||
# Extracting information from a git repository
|
# Extracting information from a git repository
|
||||||
# Provide access to Gitlab::Git library
|
# Provide access to Gitlab::Git library
|
||||||
gem "gitlab_git", '~> 10.0'
|
gem "gitlab_git", '~> 10.2'
|
||||||
|
|
||||||
# LDAP Auth
|
# LDAP Auth
|
||||||
# GitLab fork with several improvements to original library. For full list of changes
|
# GitLab fork with several improvements to original library. For full list of changes
|
||||||
|
@ -221,13 +221,12 @@ gem 'jquery-turbolinks', '~> 2.1.0'
|
||||||
|
|
||||||
gem 'addressable', '~> 2.3.8'
|
gem 'addressable', '~> 2.3.8'
|
||||||
gem 'bootstrap-sass', '~> 3.3.0'
|
gem 'bootstrap-sass', '~> 3.3.0'
|
||||||
gem 'font-awesome-rails', '~> 4.2'
|
gem 'font-awesome-rails', '~> 4.6.1'
|
||||||
gem 'gitlab_emoji', '~> 0.3.0'
|
gem 'gitlab_emoji', '~> 0.3.0'
|
||||||
gem 'gon', '~> 6.0.1'
|
gem 'gon', '~> 6.0.1'
|
||||||
gem 'jquery-atwho-rails', '~> 1.3.2'
|
gem 'jquery-atwho-rails', '~> 1.3.2'
|
||||||
gem 'jquery-rails', '~> 4.1.0'
|
gem 'jquery-rails', '~> 4.1.0'
|
||||||
gem 'jquery-ui-rails', '~> 5.0.0'
|
gem 'jquery-ui-rails', '~> 5.0.0'
|
||||||
gem 'raphael-rails', '~> 2.1.2'
|
|
||||||
gem 'request_store', '~> 1.3.0'
|
gem 'request_store', '~> 1.3.0'
|
||||||
gem 'select2-rails', '~> 3.5.9'
|
gem 'select2-rails', '~> 3.5.9'
|
||||||
gem 'virtus', '~> 1.0.1'
|
gem 'virtus', '~> 1.0.1'
|
||||||
|
@ -331,7 +330,7 @@ gem "newrelic_rpm", '~> 3.14'
|
||||||
|
|
||||||
gem 'octokit', '~> 4.3.0'
|
gem 'octokit', '~> 4.3.0'
|
||||||
|
|
||||||
gem "mail_room", "~> 0.7"
|
gem "mail_room", "~> 0.8"
|
||||||
|
|
||||||
gem 'email_reply_parser', '~> 0.5.8'
|
gem 'email_reply_parser', '~> 0.5.8'
|
||||||
|
|
||||||
|
|
18
Gemfile.lock
18
Gemfile.lock
|
@ -98,7 +98,7 @@ GEM
|
||||||
autoprefixer-rails (>= 5.2.1)
|
autoprefixer-rails (>= 5.2.1)
|
||||||
sass (>= 3.3.4)
|
sass (>= 3.3.4)
|
||||||
brakeman (3.3.2)
|
brakeman (3.3.2)
|
||||||
browser (2.0.3)
|
browser (2.2.0)
|
||||||
builder (3.2.2)
|
builder (3.2.2)
|
||||||
bullet (5.0.0)
|
bullet (5.0.0)
|
||||||
activesupport (>= 3.0.0)
|
activesupport (>= 3.0.0)
|
||||||
|
@ -246,7 +246,7 @@ GEM
|
||||||
fog-xml (0.1.2)
|
fog-xml (0.1.2)
|
||||||
fog-core
|
fog-core
|
||||||
nokogiri (~> 1.5, >= 1.5.11)
|
nokogiri (~> 1.5, >= 1.5.11)
|
||||||
font-awesome-rails (4.5.0.1)
|
font-awesome-rails (4.6.1.0)
|
||||||
railties (>= 3.2, < 5.1)
|
railties (>= 3.2, < 5.1)
|
||||||
foreman (0.78.0)
|
foreman (0.78.0)
|
||||||
thor (~> 0.19.1)
|
thor (~> 0.19.1)
|
||||||
|
@ -277,7 +277,7 @@ GEM
|
||||||
posix-spawn (~> 0.3)
|
posix-spawn (~> 0.3)
|
||||||
gitlab_emoji (0.3.1)
|
gitlab_emoji (0.3.1)
|
||||||
gemojione (~> 2.2, >= 2.2.1)
|
gemojione (~> 2.2, >= 2.2.1)
|
||||||
gitlab_git (10.1.3)
|
gitlab_git (10.2.0)
|
||||||
activesupport (~> 4.0)
|
activesupport (~> 4.0)
|
||||||
charlock_holmes (~> 0.7.3)
|
charlock_holmes (~> 0.7.3)
|
||||||
github-linguist (~> 4.7.0)
|
github-linguist (~> 4.7.0)
|
||||||
|
@ -398,7 +398,7 @@ GEM
|
||||||
systemu (~> 2.6.2)
|
systemu (~> 2.6.2)
|
||||||
mail (2.6.4)
|
mail (2.6.4)
|
||||||
mime-types (>= 1.16, < 4)
|
mime-types (>= 1.16, < 4)
|
||||||
mail_room (0.7.0)
|
mail_room (0.8.0)
|
||||||
method_source (0.8.2)
|
method_source (0.8.2)
|
||||||
mime-types (2.99.2)
|
mime-types (2.99.2)
|
||||||
mimemagic (0.3.0)
|
mimemagic (0.3.0)
|
||||||
|
@ -556,7 +556,6 @@ GEM
|
||||||
rainbow (2.1.0)
|
rainbow (2.1.0)
|
||||||
raindrops (0.15.0)
|
raindrops (0.15.0)
|
||||||
rake (10.5.0)
|
rake (10.5.0)
|
||||||
raphael-rails (2.1.2)
|
|
||||||
rb-fsevent (0.9.6)
|
rb-fsevent (0.9.6)
|
||||||
rb-inotify (0.9.5)
|
rb-inotify (0.9.5)
|
||||||
ffi (>= 0.5.0)
|
ffi (>= 0.5.0)
|
||||||
|
@ -834,7 +833,7 @@ DEPENDENCIES
|
||||||
binding_of_caller (~> 0.7.2)
|
binding_of_caller (~> 0.7.2)
|
||||||
bootstrap-sass (~> 3.3.0)
|
bootstrap-sass (~> 3.3.0)
|
||||||
brakeman (~> 3.3.0)
|
brakeman (~> 3.3.0)
|
||||||
browser (~> 2.0.3)
|
browser (~> 2.2)
|
||||||
bullet
|
bullet
|
||||||
bundler-audit
|
bundler-audit
|
||||||
byebug
|
byebug
|
||||||
|
@ -867,7 +866,7 @@ DEPENDENCIES
|
||||||
fog-google (~> 0.3)
|
fog-google (~> 0.3)
|
||||||
fog-local (~> 0.3)
|
fog-local (~> 0.3)
|
||||||
fog-openstack (~> 0.1)
|
fog-openstack (~> 0.1)
|
||||||
font-awesome-rails (~> 4.2)
|
font-awesome-rails (~> 4.6.1)
|
||||||
foreman
|
foreman
|
||||||
fuubar (~> 2.0.0)
|
fuubar (~> 2.0.0)
|
||||||
gemnasium-gitlab-service (~> 0.2)
|
gemnasium-gitlab-service (~> 0.2)
|
||||||
|
@ -875,7 +874,7 @@ DEPENDENCIES
|
||||||
github-markup (~> 1.3.1)
|
github-markup (~> 1.3.1)
|
||||||
gitlab-flowdock-git-hook (~> 1.0.1)
|
gitlab-flowdock-git-hook (~> 1.0.1)
|
||||||
gitlab_emoji (~> 0.3.0)
|
gitlab_emoji (~> 0.3.0)
|
||||||
gitlab_git (~> 10.0)
|
gitlab_git (~> 10.2)
|
||||||
gitlab_meta (= 7.0)
|
gitlab_meta (= 7.0)
|
||||||
gitlab_omniauth-ldap (~> 1.2.1)
|
gitlab_omniauth-ldap (~> 1.2.1)
|
||||||
gollum-lib (~> 4.1.0)
|
gollum-lib (~> 4.1.0)
|
||||||
|
@ -900,7 +899,7 @@ DEPENDENCIES
|
||||||
license_finder
|
license_finder
|
||||||
licensee (~> 8.0.0)
|
licensee (~> 8.0.0)
|
||||||
loofah (~> 2.0.3)
|
loofah (~> 2.0.3)
|
||||||
mail_room (~> 0.7)
|
mail_room (~> 0.8)
|
||||||
method_source (~> 0.8)
|
method_source (~> 0.8)
|
||||||
minitest (~> 5.7.0)
|
minitest (~> 5.7.0)
|
||||||
mousetrap-rails (~> 1.4.6)
|
mousetrap-rails (~> 1.4.6)
|
||||||
|
@ -938,7 +937,6 @@ DEPENDENCIES
|
||||||
rails (= 4.2.6)
|
rails (= 4.2.6)
|
||||||
rails-deprecated_sanitizer (~> 1.0.3)
|
rails-deprecated_sanitizer (~> 1.0.3)
|
||||||
rainbow (~> 2.1.0)
|
rainbow (~> 2.1.0)
|
||||||
raphael-rails (~> 2.1.2)
|
|
||||||
rblineprof
|
rblineprof
|
||||||
rdoc (~> 3.6)
|
rdoc (~> 3.6)
|
||||||
recaptcha (~> 3.0)
|
recaptcha (~> 3.0)
|
||||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
8.9.0-rc4
|
8.9.0
|
||||||
|
|
|
@ -27,6 +27,11 @@ class @LabelManager
|
||||||
$btn = $(e.currentTarget)
|
$btn = $(e.currentTarget)
|
||||||
$label = $("##{$btn.data('domId')}")
|
$label = $("##{$btn.data('domId')}")
|
||||||
action = if $btn.parents('.js-prioritized-labels').length then 'remove' else 'add'
|
action = if $btn.parents('.js-prioritized-labels').length then 'remove' else 'add'
|
||||||
|
|
||||||
|
# Make sure tooltip will hide
|
||||||
|
$tooltip = $ "##{$btn.find('.has-tooltip:visible').attr('aria-describedby')}"
|
||||||
|
$tooltip.tooltip 'destroy'
|
||||||
|
|
||||||
_this.toggleLabelPriority($label, action)
|
_this.toggleLabelPriority($label, action)
|
||||||
|
|
||||||
toggleLabelPriority: ($label, action, persistState = true) ->
|
toggleLabelPriority: ($label, action, persistState = true) ->
|
||||||
|
@ -42,10 +47,10 @@ class @LabelManager
|
||||||
$from = @prioritizedLabels
|
$from = @prioritizedLabels
|
||||||
|
|
||||||
if $from.find('li').length is 1
|
if $from.find('li').length is 1
|
||||||
$from.find('.empty-message').show()
|
$from.find('.empty-message').removeClass('hidden')
|
||||||
|
|
||||||
if not $target.find('li').length
|
if not $target.find('li').length
|
||||||
$target.find('.empty-message').hide()
|
$target.find('.empty-message').addClass('hidden')
|
||||||
|
|
||||||
$label.detach().appendTo($target)
|
$label.detach().appendTo($target)
|
||||||
|
|
||||||
|
@ -54,6 +59,9 @@ class @LabelManager
|
||||||
|
|
||||||
if action is 'remove'
|
if action is 'remove'
|
||||||
xhr = $.ajax url: url, type: 'DELETE'
|
xhr = $.ajax url: url, type: 'DELETE'
|
||||||
|
|
||||||
|
# Restore empty message
|
||||||
|
$from.find('.empty-message').removeClass('hidden') unless $from.find('li').length
|
||||||
else
|
else
|
||||||
xhr = @savePrioritySort($label, action)
|
xhr = @savePrioritySort($label, action)
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
labelsPath: "/api/:version/projects/:id/labels"
|
labelsPath: "/api/:version/projects/:id/labels"
|
||||||
licensePath: "/api/:version/licenses/:key"
|
licensePath: "/api/:version/licenses/:key"
|
||||||
gitignorePath: "/api/:version/gitignores/:key"
|
gitignorePath: "/api/:version/gitignores/:key"
|
||||||
|
gitlabCiYmlPath: "/api/:version/gitlab_ci_ymls/:key"
|
||||||
|
|
||||||
group: (group_id, callback) ->
|
group: (group_id, callback) ->
|
||||||
url = Api.buildUrl(Api.groupPath)
|
url = Api.buildUrl(Api.groupPath)
|
||||||
|
@ -110,6 +111,12 @@
|
||||||
$.get url, (gitignore) ->
|
$.get url, (gitignore) ->
|
||||||
callback(gitignore)
|
callback(gitignore)
|
||||||
|
|
||||||
|
gitlabCiYml: (key, callback) ->
|
||||||
|
url = Api.buildUrl(Api.gitlabCiYmlPath).replace(':key', key)
|
||||||
|
|
||||||
|
$.get url, (file) ->
|
||||||
|
callback(file)
|
||||||
|
|
||||||
buildUrl: (url) ->
|
buildUrl: (url) ->
|
||||||
url = gon.relative_url_root + url if gon.relative_url_root?
|
url = gon.relative_url_root + url if gon.relative_url_root?
|
||||||
return url.replace(':version', gon.api_version)
|
return url.replace(':version', gon.api_version)
|
||||||
|
|
|
@ -32,10 +32,6 @@
|
||||||
#= require bootstrap/tooltip
|
#= require bootstrap/tooltip
|
||||||
#= require bootstrap/popover
|
#= require bootstrap/popover
|
||||||
#= require select2
|
#= require select2
|
||||||
#= require raphael
|
|
||||||
#= require g.raphael
|
|
||||||
#= require g.bar
|
|
||||||
#= require branch-graph
|
|
||||||
#= require ace/ace
|
#= require ace/ace
|
||||||
#= require ace/ext-searchbox
|
#= require ace/ext-searchbox
|
||||||
#= require underscore
|
#= require underscore
|
||||||
|
@ -128,7 +124,7 @@ $ ->
|
||||||
gl.utils.preventDisabledButtons()
|
gl.utils.preventDisabledButtons()
|
||||||
bootstrapBreakpoint = bp.getBreakpointSize()
|
bootstrapBreakpoint = bp.getBreakpointSize()
|
||||||
|
|
||||||
$(".nicescroll").niceScroll(cursoropacitymax: '0.4', cursorcolor: '#FFF', cursorborder: "1px solid #FFF")
|
$(".nav-sidebar").niceScroll(cursoropacitymax: '0.4', cursorcolor: '#FFF', cursorborder: "1px solid #FFF")
|
||||||
|
|
||||||
# Click a .js-select-on-focus field, select the contents
|
# Click a .js-select-on-focus field, select the contents
|
||||||
$(".js-select-on-focus").on "focusin", ->
|
$(".js-select-on-focus").on "focusin", ->
|
||||||
|
@ -258,3 +254,47 @@ $ ->
|
||||||
gl.awardsHandler = new AwardsHandler()
|
gl.awardsHandler = new AwardsHandler()
|
||||||
checkInitialSidebarSize()
|
checkInitialSidebarSize()
|
||||||
new Aside()
|
new Aside()
|
||||||
|
|
||||||
|
# Sidenav pinning
|
||||||
|
if $(window).width() < 1440 and $.cookie('pin_nav') is 'true'
|
||||||
|
$.cookie('pin_nav', 'false', { path: '/' })
|
||||||
|
$('.page-with-sidebar')
|
||||||
|
.toggleClass('page-sidebar-collapsed page-sidebar-expanded')
|
||||||
|
.removeClass('page-sidebar-pinned')
|
||||||
|
$('.navbar-fixed-top').removeClass('header-pinned-nav')
|
||||||
|
|
||||||
|
$(document)
|
||||||
|
.off 'click', '.js-nav-pin'
|
||||||
|
.on 'click', '.js-nav-pin', (e) ->
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
|
$pinBtn = $(e.currentTarget)
|
||||||
|
$page = $ '.page-with-sidebar'
|
||||||
|
$topNav = $ '.navbar-fixed-top'
|
||||||
|
$tooltip = $ "##{$pinBtn.attr('aria-describedby')}"
|
||||||
|
doPinNav = not $page.is('.page-sidebar-pinned')
|
||||||
|
tooltipText = 'Pin navigation'
|
||||||
|
|
||||||
|
$(this).toggleClass 'is-active'
|
||||||
|
|
||||||
|
if doPinNav
|
||||||
|
$page.addClass('page-sidebar-pinned')
|
||||||
|
$topNav.addClass('header-pinned-nav')
|
||||||
|
else
|
||||||
|
$tooltip.remove() # Remove it immediately when collapsing the sidebar
|
||||||
|
$page.removeClass('page-sidebar-pinned')
|
||||||
|
.toggleClass('page-sidebar-collapsed page-sidebar-expanded')
|
||||||
|
$topNav.removeClass('header-pinned-nav')
|
||||||
|
.toggleClass('header-collapsed header-expanded')
|
||||||
|
|
||||||
|
# Save settings
|
||||||
|
$.cookie 'pin_nav', doPinNav, { path: '/' }
|
||||||
|
|
||||||
|
if $.cookie('pin_nav') is 'true' or doPinNav
|
||||||
|
tooltipText = 'Unpin navigation'
|
||||||
|
|
||||||
|
# Update tooltip text immediately
|
||||||
|
$tooltip.find('.tooltip-inner').text(tooltipText)
|
||||||
|
|
||||||
|
# Persist tooltip title
|
||||||
|
$pinBtn.attr('title', tooltipText).tooltip('fixTitle')
|
||||||
|
|
|
@ -40,7 +40,7 @@ class @AwardsHandler
|
||||||
$menu = $ '.emoji-menu'
|
$menu = $ '.emoji-menu'
|
||||||
|
|
||||||
if $addBtn.hasClass 'js-note-emoji'
|
if $addBtn.hasClass 'js-note-emoji'
|
||||||
$addBtn.parents('.note').find('.js-awards-block').addClass 'current'
|
$addBtn.closest('.note').find('.js-awards-block').addClass 'current'
|
||||||
else
|
else
|
||||||
$addBtn.closest('.js-awards-block').addClass 'current'
|
$addBtn.closest('.js-awards-block').addClass 'current'
|
||||||
|
|
||||||
|
|
23
app/assets/javascripts/blob/blob_ci_yaml.js.coffee
Normal file
23
app/assets/javascripts/blob/blob_ci_yaml.js.coffee
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
#= require blob/template_selector
|
||||||
|
|
||||||
|
class @BlobCiYamlSelector extends TemplateSelector
|
||||||
|
requestFile: (query) ->
|
||||||
|
Api.gitlabCiYml query.name, @requestFileSuccess.bind(@)
|
||||||
|
|
||||||
|
class @BlobCiYamlSelectors
|
||||||
|
constructor: (opts) ->
|
||||||
|
{
|
||||||
|
@$dropdowns = $('.js-gitlab-ci-yml-selector')
|
||||||
|
@editor
|
||||||
|
} = opts
|
||||||
|
|
||||||
|
@$dropdowns.each (i, dropdown) =>
|
||||||
|
$dropdown = $(dropdown)
|
||||||
|
|
||||||
|
new BlobCiYamlSelector(
|
||||||
|
pattern: /(.gitlab-ci.yml)/,
|
||||||
|
data: $dropdown.data('data'),
|
||||||
|
wrapper: $dropdown.closest('.js-gitlab-ci-yml-selector-wrap'),
|
||||||
|
dropdown: $dropdown,
|
||||||
|
editor: @editor
|
||||||
|
)
|
|
@ -1,58 +1,5 @@
|
||||||
class @BlobGitignoreSelector
|
#= require blob/template_selector
|
||||||
constructor: (opts) ->
|
|
||||||
{
|
|
||||||
@dropdown
|
|
||||||
@editor
|
|
||||||
@$wrapper = @dropdown.closest('.gitignore-selector')
|
|
||||||
@$filenameInput = $('#file_name')
|
|
||||||
@data = @dropdown.data('filenames')
|
|
||||||
} = opts
|
|
||||||
|
|
||||||
@dropdown.glDropdown(
|
class @BlobGitignoreSelector extends TemplateSelector
|
||||||
data: @data,
|
requestFile: (query) ->
|
||||||
filterable: true,
|
Api.gitignoreText query.name, @requestFileSuccess.bind(@)
|
||||||
selectable: true,
|
|
||||||
search:
|
|
||||||
fields: ['name']
|
|
||||||
clicked: @onClick
|
|
||||||
text: (gitignore) ->
|
|
||||||
gitignore.name
|
|
||||||
)
|
|
||||||
|
|
||||||
@toggleGitignoreSelector()
|
|
||||||
@bindEvents()
|
|
||||||
|
|
||||||
bindEvents: ->
|
|
||||||
@$filenameInput
|
|
||||||
.on 'keyup blur', (e) =>
|
|
||||||
@toggleGitignoreSelector()
|
|
||||||
|
|
||||||
toggleGitignoreSelector: ->
|
|
||||||
filename = @$filenameInput.val() or $('.editor-file-name').text().trim()
|
|
||||||
@$wrapper.toggleClass 'hidden', filename isnt '.gitignore'
|
|
||||||
|
|
||||||
onClick: (item, el, e) =>
|
|
||||||
e.preventDefault()
|
|
||||||
@requestIgnoreFile(item.name)
|
|
||||||
|
|
||||||
requestIgnoreFile: (name) ->
|
|
||||||
Api.gitignoreText name, @requestIgnoreFileSuccess.bind(@)
|
|
||||||
|
|
||||||
requestIgnoreFileSuccess: (gitignore) ->
|
|
||||||
@editor.setValue(gitignore.content, 1)
|
|
||||||
@editor.focus()
|
|
||||||
|
|
||||||
class @BlobGitignoreSelectors
|
|
||||||
constructor: (opts) ->
|
|
||||||
{
|
|
||||||
@$dropdowns = $('.js-gitignore-selector')
|
|
||||||
@editor
|
|
||||||
} = opts
|
|
||||||
|
|
||||||
@$dropdowns.each (i, dropdown) =>
|
|
||||||
$dropdown = $(dropdown)
|
|
||||||
|
|
||||||
new BlobGitignoreSelector(
|
|
||||||
dropdown: $dropdown,
|
|
||||||
editor: @editor
|
|
||||||
)
|
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
class @BlobGitignoreSelectors
|
||||||
|
constructor: (opts) ->
|
||||||
|
{
|
||||||
|
@$dropdowns = $('.js-gitignore-selector')
|
||||||
|
@editor
|
||||||
|
} = opts
|
||||||
|
|
||||||
|
@$dropdowns.each (i, dropdown) =>
|
||||||
|
$dropdown = $(dropdown)
|
||||||
|
|
||||||
|
new BlobGitignoreSelector(
|
||||||
|
pattern: /(.gitignore)/,
|
||||||
|
data: $dropdown.data('data'),
|
||||||
|
wrapper: $dropdown.closest('.js-gitignore-selector-wrap'),
|
||||||
|
dropdown: $dropdown,
|
||||||
|
editor: @editor
|
||||||
|
)
|
|
@ -1,30 +1,9 @@
|
||||||
class @BlobLicenseSelector
|
#= require blob/template_selector
|
||||||
licenseRegex: /^(.+\/)?(licen[sc]e|copying)($|\.)/i
|
|
||||||
|
|
||||||
constructor: (editor) ->
|
class @BlobLicenseSelector extends TemplateSelector
|
||||||
@$licenseSelector = $('.js-license-selector')
|
requestFile: (query) ->
|
||||||
$fileNameInput = $('#file_name')
|
data =
|
||||||
|
project: @dropdown.data('project')
|
||||||
|
fullname: @dropdown.data('fullname')
|
||||||
|
|
||||||
initialFileNameValue = if $fileNameInput.length
|
Api.licenseText query.id, data, @requestFileSuccess.bind(@)
|
||||||
$fileNameInput.val()
|
|
||||||
else if $('.editor-file-name').length
|
|
||||||
$('.editor-file-name').text().trim()
|
|
||||||
|
|
||||||
@toggleLicenseSelector(initialFileNameValue)
|
|
||||||
|
|
||||||
if $fileNameInput
|
|
||||||
$fileNameInput.on 'keyup blur', (e) =>
|
|
||||||
@toggleLicenseSelector($(e.target).val())
|
|
||||||
|
|
||||||
$('select.license-select').on 'change', (e) ->
|
|
||||||
data =
|
|
||||||
project: $(this).data('project')
|
|
||||||
fullname: $(this).data('fullname')
|
|
||||||
Api.licenseText $(this).val(), data, (license) ->
|
|
||||||
editor.setValue(license.content, -1)
|
|
||||||
|
|
||||||
toggleLicenseSelector: (fileName) =>
|
|
||||||
if @licenseRegex.test(fileName)
|
|
||||||
@$licenseSelector.show()
|
|
||||||
else
|
|
||||||
@$licenseSelector.hide()
|
|
||||||
|
|
17
app/assets/javascripts/blob/blob_license_selectors.js.coffee
Normal file
17
app/assets/javascripts/blob/blob_license_selectors.js.coffee
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
class @BlobLicenseSelectors
|
||||||
|
constructor: (opts) ->
|
||||||
|
{
|
||||||
|
@$dropdowns = $('.js-license-selector')
|
||||||
|
@editor
|
||||||
|
} = opts
|
||||||
|
|
||||||
|
@$dropdowns.each (i, dropdown) =>
|
||||||
|
$dropdown = $(dropdown)
|
||||||
|
|
||||||
|
new BlobLicenseSelector(
|
||||||
|
pattern: /^(.+\/)?(licen[sc]e|copying)($|\.)/i,
|
||||||
|
data: $dropdown.data('data'),
|
||||||
|
wrapper: $dropdown.closest('.js-license-selector-wrap'),
|
||||||
|
dropdown: $dropdown,
|
||||||
|
editor: @editor
|
||||||
|
)
|
|
@ -12,8 +12,10 @@ class @EditBlob
|
||||||
$("#file-content").val(@editor.getValue())
|
$("#file-content").val(@editor.getValue())
|
||||||
|
|
||||||
@initModePanesAndLinks()
|
@initModePanesAndLinks()
|
||||||
new BlobLicenseSelector(@editor)
|
|
||||||
new BlobGitignoreSelectors(editor: @editor)
|
new BlobLicenseSelectors { @editor }
|
||||||
|
new BlobGitignoreSelectors { @editor }
|
||||||
|
new BlobCiYamlSelectors { @editor }
|
||||||
|
|
||||||
initModePanesAndLinks: ->
|
initModePanesAndLinks: ->
|
||||||
@$editModePanes = $(".js-edit-mode-pane")
|
@$editModePanes = $(".js-edit-mode-pane")
|
||||||
|
|
56
app/assets/javascripts/blob/template_selector.js.coffee
Normal file
56
app/assets/javascripts/blob/template_selector.js.coffee
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
class @TemplateSelector
|
||||||
|
constructor: (opts = {}) ->
|
||||||
|
{
|
||||||
|
@dropdown,
|
||||||
|
@data,
|
||||||
|
@pattern,
|
||||||
|
@wrapper,
|
||||||
|
@editor,
|
||||||
|
@fileEndpoint,
|
||||||
|
@$input = $('#file_name')
|
||||||
|
} = opts
|
||||||
|
|
||||||
|
@buildDropdown()
|
||||||
|
@bindEvents()
|
||||||
|
@onFilenameUpdate()
|
||||||
|
|
||||||
|
buildDropdown: ->
|
||||||
|
@dropdown.glDropdown(
|
||||||
|
data: @data,
|
||||||
|
filterable: true,
|
||||||
|
selectable: true,
|
||||||
|
search:
|
||||||
|
fields: ['name']
|
||||||
|
clicked: @onClick
|
||||||
|
text: (item) ->
|
||||||
|
item.name
|
||||||
|
)
|
||||||
|
|
||||||
|
bindEvents: ->
|
||||||
|
@$input.on('keyup blur', (e) =>
|
||||||
|
@onFilenameUpdate()
|
||||||
|
)
|
||||||
|
|
||||||
|
onFilenameUpdate: ->
|
||||||
|
return unless @$input.length
|
||||||
|
|
||||||
|
filenameMatches = @pattern.test(@$input.val().trim())
|
||||||
|
|
||||||
|
if not filenameMatches
|
||||||
|
@wrapper.addClass('hidden')
|
||||||
|
return
|
||||||
|
|
||||||
|
@wrapper.removeClass('hidden')
|
||||||
|
|
||||||
|
onClick: (item, el, e) =>
|
||||||
|
e.preventDefault()
|
||||||
|
@requestFile(item)
|
||||||
|
|
||||||
|
requestFile: (item) ->
|
||||||
|
# To be implemented on the extending class
|
||||||
|
# e.g.
|
||||||
|
# Api.gitignoreText item.name, @requestFileSuccess.bind(@)
|
||||||
|
|
||||||
|
requestFileSuccess: (file) ->
|
||||||
|
@editor.setValue(file.content, 1)
|
||||||
|
@editor.focus()
|
|
@ -29,6 +29,7 @@ class Dispatcher
|
||||||
new Todos()
|
new Todos()
|
||||||
when 'projects:milestones:new', 'projects:milestones:edit'
|
when 'projects:milestones:new', 'projects:milestones:edit'
|
||||||
new ZenMode()
|
new ZenMode()
|
||||||
|
new DueDateSelect()
|
||||||
new GLForm($('.milestone-form'))
|
new GLForm($('.milestone-form'))
|
||||||
when 'groups:milestones:new'
|
when 'groups:milestones:new'
|
||||||
new ZenMode()
|
new ZenMode()
|
||||||
|
@ -72,13 +73,12 @@ class Dispatcher
|
||||||
new Diff()
|
new Diff()
|
||||||
new ZenMode()
|
new ZenMode()
|
||||||
shortcut_handler = new ShortcutsNavigation()
|
shortcut_handler = new ShortcutsNavigation()
|
||||||
when 'projects:commits:show'
|
when 'projects:commits:show', 'projects:activity'
|
||||||
shortcut_handler = new ShortcutsNavigation()
|
|
||||||
when 'projects:activity'
|
|
||||||
shortcut_handler = new ShortcutsNavigation()
|
shortcut_handler = new ShortcutsNavigation()
|
||||||
when 'projects:show'
|
when 'projects:show'
|
||||||
shortcut_handler = new ShortcutsNavigation()
|
shortcut_handler = new ShortcutsNavigation()
|
||||||
|
|
||||||
|
new NotificationsForm()
|
||||||
new TreeView() if $('#tree-slider').length
|
new TreeView() if $('#tree-slider').length
|
||||||
when 'groups:activity'
|
when 'groups:activity'
|
||||||
new Activities()
|
new Activities()
|
||||||
|
@ -100,6 +100,7 @@ class Dispatcher
|
||||||
when 'projects:blob:show', 'projects:blame:show'
|
when 'projects:blob:show', 'projects:blame:show'
|
||||||
new LineHighlighter()
|
new LineHighlighter()
|
||||||
shortcut_handler = new ShortcutsNavigation()
|
shortcut_handler = new ShortcutsNavigation()
|
||||||
|
new ShortcutsBlob true
|
||||||
when 'projects:labels:new', 'projects:labels:edit'
|
when 'projects:labels:new', 'projects:labels:edit'
|
||||||
new Labels()
|
new Labels()
|
||||||
when 'projects:labels:index'
|
when 'projects:labels:index'
|
||||||
|
@ -129,19 +130,21 @@ class Dispatcher
|
||||||
shortcut_handler = new ShortcutsDashboardNavigation()
|
shortcut_handler = new ShortcutsDashboardNavigation()
|
||||||
when 'profiles'
|
when 'profiles'
|
||||||
new Profile()
|
new Profile()
|
||||||
|
new NotificationsForm()
|
||||||
|
new NotificationsDropdown()
|
||||||
when 'projects'
|
when 'projects'
|
||||||
new Project()
|
new Project()
|
||||||
new ProjectAvatar()
|
new ProjectAvatar()
|
||||||
switch path[1]
|
switch path[1]
|
||||||
when 'compare'
|
|
||||||
shortcut_handler = new ShortcutsNavigation()
|
|
||||||
when 'edit'
|
when 'edit'
|
||||||
shortcut_handler = new ShortcutsNavigation()
|
shortcut_handler = new ShortcutsNavigation()
|
||||||
new ProjectNew()
|
new ProjectNew()
|
||||||
when 'new'
|
when 'new'
|
||||||
new ProjectNew()
|
new ProjectNew()
|
||||||
when 'show'
|
when 'show'
|
||||||
|
new ProjectNew()
|
||||||
new ProjectShow()
|
new ProjectShow()
|
||||||
|
new NotificationsDropdown()
|
||||||
when 'wikis'
|
when 'wikis'
|
||||||
new Wikis()
|
new Wikis()
|
||||||
shortcut_handler = new ShortcutsNavigation()
|
shortcut_handler = new ShortcutsNavigation()
|
||||||
|
@ -150,9 +153,9 @@ class Dispatcher
|
||||||
when 'snippets'
|
when 'snippets'
|
||||||
shortcut_handler = new ShortcutsNavigation()
|
shortcut_handler = new ShortcutsNavigation()
|
||||||
new ZenMode() if path[2] == 'show'
|
new ZenMode() if path[2] == 'show'
|
||||||
when 'labels', 'graphs'
|
when 'labels', 'graphs', 'compare', 'pipelines', 'forks', \
|
||||||
shortcut_handler = new ShortcutsNavigation()
|
'milestones', 'project_members', 'deploy_keys', 'builds', \
|
||||||
when 'project_members', 'deploy_keys', 'hooks', 'services', 'protected_branches'
|
'hooks', 'services', 'protected_branches'
|
||||||
shortcut_handler = new ShortcutsNavigation()
|
shortcut_handler = new ShortcutsNavigation()
|
||||||
|
|
||||||
# If we haven't installed a custom shortcut handler, install the default one
|
# If we haven't installed a custom shortcut handler, install the default one
|
||||||
|
|
|
@ -1,5 +1,21 @@
|
||||||
class @DueDateSelect
|
class @DueDateSelect
|
||||||
constructor: ->
|
constructor: ->
|
||||||
|
# Milestone edit/new form
|
||||||
|
$datePicker = $('.datepicker')
|
||||||
|
|
||||||
|
if $datePicker.length
|
||||||
|
$dueDate = $('#milestone_due_date')
|
||||||
|
$datePicker.datepicker
|
||||||
|
dateFormat: 'yy-mm-dd'
|
||||||
|
onSelect: (dateText, inst) ->
|
||||||
|
$dueDate.val(dateText)
|
||||||
|
.datepicker('setDate', $.datepicker.parseDate('yy-mm-dd', $dueDate.val()))
|
||||||
|
|
||||||
|
$('.js-clear-due-date').on 'click', (e) ->
|
||||||
|
e.preventDefault()
|
||||||
|
$.datepicker._clearDate($datePicker)
|
||||||
|
|
||||||
|
# Issuable sidebar
|
||||||
$loading = $('.js-issuable-update .due_date')
|
$loading = $('.js-issuable-update .due_date')
|
||||||
.find('.block-loading')
|
.find('.block-loading')
|
||||||
.hide()
|
.hide()
|
||||||
|
@ -32,7 +48,7 @@ class @DueDateSelect
|
||||||
date = new Date value.replace(new RegExp('-', 'g'), ',')
|
date = new Date value.replace(new RegExp('-', 'g'), ',')
|
||||||
mediumDate = $.datepicker.formatDate 'M d, yy', date
|
mediumDate = $.datepicker.formatDate 'M d, yy', date
|
||||||
else
|
else
|
||||||
mediumDate = 'None'
|
mediumDate = 'No due date'
|
||||||
|
|
||||||
data = {}
|
data = {}
|
||||||
data[abilityName] = {}
|
data[abilityName] = {}
|
||||||
|
@ -50,7 +66,8 @@ class @DueDateSelect
|
||||||
$selectbox.hide()
|
$selectbox.hide()
|
||||||
$value.css('display', '')
|
$value.css('display', '')
|
||||||
|
|
||||||
$valueContent.html(mediumDate)
|
cssClass = if Date.parse(mediumDate) then 'bold' else 'no-value'
|
||||||
|
$valueContent.html("<span class='#{cssClass}'>#{mediumDate}</span>")
|
||||||
$sidebarValue.html(mediumDate)
|
$sidebarValue.html(mediumDate)
|
||||||
|
|
||||||
if value isnt ''
|
if value isnt ''
|
||||||
|
|
|
@ -15,6 +15,9 @@ GitLab.GfmAutoComplete =
|
||||||
Members:
|
Members:
|
||||||
template: '<li>${username} <small>${title}</small></li>'
|
template: '<li>${username} <small>${title}</small></li>'
|
||||||
|
|
||||||
|
Labels:
|
||||||
|
template: '<li><span class="dropdown-label-box" style="background: ${color}"></span> ${title}</li>'
|
||||||
|
|
||||||
# Issues and MergeRequests
|
# Issues and MergeRequests
|
||||||
Issues:
|
Issues:
|
||||||
template: '<li><small>${id}</small> ${title}</li>'
|
template: '<li><small>${id}</small> ${title}</li>'
|
||||||
|
@ -176,6 +179,25 @@ GitLab.GfmAutoComplete =
|
||||||
title: sanitize(m.title)
|
title: sanitize(m.title)
|
||||||
search: "#{m.iid} #{m.title}"
|
search: "#{m.iid} #{m.title}"
|
||||||
|
|
||||||
|
@input.atwho
|
||||||
|
at: '~'
|
||||||
|
alias: 'labels'
|
||||||
|
searchKey: 'search'
|
||||||
|
displayTpl: @Labels.template
|
||||||
|
insertTpl: '${atwho-at}${title}'
|
||||||
|
callbacks:
|
||||||
|
beforeSave: (merges) ->
|
||||||
|
sanitizeLabelTitle = (title)->
|
||||||
|
if /\w+\s+\w+/g.test(title)
|
||||||
|
"\"#{sanitize(title)}\""
|
||||||
|
else
|
||||||
|
sanitize(title)
|
||||||
|
|
||||||
|
$.map merges, (m) ->
|
||||||
|
title: sanitizeLabelTitle(m.title)
|
||||||
|
color: m.color
|
||||||
|
search: "#{m.title}"
|
||||||
|
|
||||||
destroyAtWho: ->
|
destroyAtWho: ->
|
||||||
@input.atwho('destroy')
|
@input.atwho('destroy')
|
||||||
|
|
||||||
|
@ -195,6 +217,8 @@ GitLab.GfmAutoComplete =
|
||||||
@input.atwho 'load', 'mergerequests', data.mergerequests
|
@input.atwho 'load', 'mergerequests', data.mergerequests
|
||||||
# load emojis
|
# load emojis
|
||||||
@input.atwho 'load', ':', data.emojis
|
@input.atwho 'load', ':', data.emojis
|
||||||
|
# load labels
|
||||||
|
@input.atwho 'load', '~', data.labels
|
||||||
|
|
||||||
# This trigger at.js again
|
# This trigger at.js again
|
||||||
# otherwise we would be stuck with loading until the user types
|
# otherwise we would be stuck with loading until the user types
|
||||||
|
|
|
@ -58,7 +58,7 @@ class GitLabDropdownFilter
|
||||||
filter: (search_text) ->
|
filter: (search_text) ->
|
||||||
data = @options.data()
|
data = @options.data()
|
||||||
|
|
||||||
if data?
|
if data? and not @options.filterByText
|
||||||
results = data
|
results = data
|
||||||
|
|
||||||
if search_text isnt ''
|
if search_text isnt ''
|
||||||
|
@ -102,10 +102,11 @@ class GitLabDropdownFilter
|
||||||
$el = $(@)
|
$el = $(@)
|
||||||
matches = fuzzaldrinPlus.match($el.text().trim(), search_text)
|
matches = fuzzaldrinPlus.match($el.text().trim(), search_text)
|
||||||
|
|
||||||
if matches.length
|
unless $el.is('.dropdown-header')
|
||||||
$el.show()
|
if matches.length
|
||||||
else
|
$el.show()
|
||||||
$el.hide()
|
else
|
||||||
|
$el.hide()
|
||||||
else
|
else
|
||||||
elements.show()
|
elements.show()
|
||||||
|
|
||||||
|
@ -191,6 +192,7 @@ class GitLabDropdown
|
||||||
if @options.filterable
|
if @options.filterable
|
||||||
@filter = new GitLabDropdownFilter @filterInput,
|
@filter = new GitLabDropdownFilter @filterInput,
|
||||||
filterInputBlur: @filterInputBlur
|
filterInputBlur: @filterInputBlur
|
||||||
|
filterByText: @options.filterByText
|
||||||
remote: @options.filterRemote
|
remote: @options.filterRemote
|
||||||
query: @options.data
|
query: @options.data
|
||||||
keys: searchFields
|
keys: searchFields
|
||||||
|
@ -302,6 +304,9 @@ class GitLabDropdown
|
||||||
if @options.setIndeterminateIds
|
if @options.setIndeterminateIds
|
||||||
@options.setIndeterminateIds.call(@)
|
@options.setIndeterminateIds.call(@)
|
||||||
|
|
||||||
|
if @options.setActiveIds
|
||||||
|
@options.setActiveIds.call(@)
|
||||||
|
|
||||||
# Makes indeterminate items effective
|
# Makes indeterminate items effective
|
||||||
if @fullData and @dropdown.find('.dropdown-menu-toggle').hasClass('js-filter-bulk-update')
|
if @fullData and @dropdown.find('.dropdown-menu-toggle').hasClass('js-filter-bulk-update')
|
||||||
@parseData @fullData
|
@parseData @fullData
|
||||||
|
|
|
@ -39,7 +39,7 @@ class @LabelsSelect
|
||||||
</a>
|
</a>
|
||||||
<% }); %>'
|
<% }); %>'
|
||||||
)
|
)
|
||||||
labelNoneHTMLTemplate = _.template('<div class="light">None</div>')
|
labelNoneHTMLTemplate = '<span class="no-value">None</span>'
|
||||||
|
|
||||||
if newLabelField.length
|
if newLabelField.length
|
||||||
|
|
||||||
|
@ -145,7 +145,7 @@ class @LabelsSelect
|
||||||
template = labelHTMLTemplate(data)
|
template = labelHTMLTemplate(data)
|
||||||
labelCount = data.labels.length
|
labelCount = data.labels.length
|
||||||
else
|
else
|
||||||
template = labelNoneHTMLTemplate()
|
template = labelNoneHTMLTemplate
|
||||||
$value
|
$value
|
||||||
.removeAttr('style')
|
.removeAttr('style')
|
||||||
.html(template)
|
.html(template)
|
||||||
|
@ -210,9 +210,21 @@ class @LabelsSelect
|
||||||
|
|
||||||
if $dropdown.hasClass('js-filter-bulk-update')
|
if $dropdown.hasClass('js-filter-bulk-update')
|
||||||
indeterminate = instance.indeterminateIds
|
indeterminate = instance.indeterminateIds
|
||||||
|
active = instance.activeIds
|
||||||
|
|
||||||
if indeterminate.indexOf(label.id) isnt -1
|
if indeterminate.indexOf(label.id) isnt -1
|
||||||
selectedClass.push 'is-indeterminate'
|
selectedClass.push 'is-indeterminate'
|
||||||
|
|
||||||
|
if active.indexOf(label.id) isnt -1
|
||||||
|
# Remove is-indeterminate class if the item will be marked as active
|
||||||
|
i = selectedClass.indexOf 'is-indeterminate'
|
||||||
|
selectedClass.splice i, 1 unless i is -1
|
||||||
|
|
||||||
|
selectedClass.push 'is-active'
|
||||||
|
|
||||||
|
# Add input manually
|
||||||
|
instance.addInput @fieldName, label.id
|
||||||
|
|
||||||
if $form.find("input[type='hidden']\
|
if $form.find("input[type='hidden']\
|
||||||
[name='#{$dropdown.data('fieldName')}']\
|
[name='#{$dropdown.data('fieldName')}']\
|
||||||
[value='#{this.id(label)}']").length
|
[value='#{this.id(label)}']").length
|
||||||
|
@ -328,6 +340,10 @@ class @LabelsSelect
|
||||||
setIndeterminateIds: ->
|
setIndeterminateIds: ->
|
||||||
if @dropdown.find('.dropdown-menu-toggle').hasClass('js-filter-bulk-update')
|
if @dropdown.find('.dropdown-menu-toggle').hasClass('js-filter-bulk-update')
|
||||||
@indeterminateIds = _this.getIndeterminateIds()
|
@indeterminateIds = _this.getIndeterminateIds()
|
||||||
|
|
||||||
|
setActiveIds: ->
|
||||||
|
if @dropdown.find('.dropdown-menu-toggle').hasClass('js-filter-bulk-update')
|
||||||
|
@activeIds = _this.getActiveIds()
|
||||||
)
|
)
|
||||||
|
|
||||||
@bindEvents()
|
@bindEvents()
|
||||||
|
@ -352,3 +368,12 @@ class @LabelsSelect
|
||||||
label_ids.push $("#issue_#{issue_id}").data('labels')
|
label_ids.push $("#issue_#{issue_id}").data('labels')
|
||||||
|
|
||||||
_.flatten(label_ids)
|
_.flatten(label_ids)
|
||||||
|
|
||||||
|
getActiveIds: ->
|
||||||
|
label_ids = []
|
||||||
|
|
||||||
|
$('.selected_issue:checked').each (i, el) ->
|
||||||
|
issue_id = $(el).data('id')
|
||||||
|
label_ids.push $("#issue_#{issue_id}").data('labels')
|
||||||
|
|
||||||
|
_.intersection.apply _, label_ids
|
||||||
|
|
|
@ -1,7 +1,45 @@
|
||||||
((w) ->
|
((w) ->
|
||||||
|
|
||||||
window.gl or= {}
|
w.gl or= {}
|
||||||
window.gl.utils or= {}
|
w.gl.utils or= {}
|
||||||
|
|
||||||
|
w.gl.utils.isInGroupsPage = ->
|
||||||
|
|
||||||
|
return $('body').data('page').split(':')[0] is 'groups'
|
||||||
|
|
||||||
|
|
||||||
|
w.gl.utils.isInProjectPage = ->
|
||||||
|
|
||||||
|
return $('body').data('page').split(':')[0] is 'projects'
|
||||||
|
|
||||||
|
|
||||||
|
w.gl.utils.getProjectSlug = ->
|
||||||
|
|
||||||
|
return if @isInProjectPage() then $('body').data 'project' else null
|
||||||
|
|
||||||
|
|
||||||
|
w.gl.utils.getGroupSlug = ->
|
||||||
|
|
||||||
|
return if @isInGroupsPage() then $('body').data 'group' else null
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
gl.utils.updateTooltipTitle = ($tooltipEl, newTitle) ->
|
||||||
|
|
||||||
|
$tooltipEl
|
||||||
|
.tooltip 'destroy'
|
||||||
|
.attr 'title', newTitle
|
||||||
|
.tooltip 'fixTitle'
|
||||||
|
|
||||||
|
|
||||||
|
gl.utils.preventDisabledButtons = ->
|
||||||
|
|
||||||
|
$('.btn').click (e) ->
|
||||||
|
if $(this).hasClass 'disabled'
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopImmediatePropagation()
|
||||||
|
return false
|
||||||
|
|
||||||
|
|
||||||
jQuery.timefor = (time, suffix, expiredLabel) ->
|
jQuery.timefor = (time, suffix, expiredLabel) ->
|
||||||
|
|
||||||
|
@ -24,20 +62,4 @@
|
||||||
|
|
||||||
return timefor
|
return timefor
|
||||||
|
|
||||||
|
|
||||||
gl.utils.updateTooltipTitle = ($tooltipEl, newTitle) ->
|
|
||||||
|
|
||||||
$tooltipEl
|
|
||||||
.tooltip 'destroy'
|
|
||||||
.attr 'title', newTitle
|
|
||||||
.tooltip 'fixTitle'
|
|
||||||
|
|
||||||
gl.utils.preventDisabledButtons = ->
|
|
||||||
|
|
||||||
$('.btn').click (e) ->
|
|
||||||
if $(this).hasClass 'disabled'
|
|
||||||
e.preventDefault()
|
|
||||||
e.stopImmediatePropagation()
|
|
||||||
return false
|
|
||||||
|
|
||||||
) window
|
) window
|
||||||
|
|
|
@ -88,7 +88,7 @@ class @MergeRequestTabs
|
||||||
|
|
||||||
scrollToElement: (container) ->
|
scrollToElement: (container) ->
|
||||||
if window.location.hash
|
if window.location.hash
|
||||||
navBarHeight = $('.navbar-gitlab').outerHeight()
|
navBarHeight = $('.navbar-gitlab').outerHeight() + $('.layout-nav').outerHeight()
|
||||||
|
|
||||||
$el = $("#{container} #{window.location.hash}:not(.match)")
|
$el = $("#{container} #{window.location.hash}:not(.match)")
|
||||||
$.scrollTo("#{container} #{window.location.hash}:not(.match)", offset: -navBarHeight) if $el.length
|
$.scrollTo("#{container} #{window.location.hash}:not(.match)", offset: -navBarHeight) if $el.length
|
||||||
|
|
|
@ -24,14 +24,10 @@ class @MilestoneSelect
|
||||||
|
|
||||||
if issueUpdateURL
|
if issueUpdateURL
|
||||||
milestoneLinkTemplate = _.template(
|
milestoneLinkTemplate = _.template(
|
||||||
'<a href="/<%= namespace %>/<%= path %>/milestones/<%= iid %>">
|
'<a href="/<%= namespace %>/<%= path %>/milestones/<%= iid %>" class="bold has-tooltip" data-container="body" title="<%= remaining %>"><%= _.escape(title) %></a>'
|
||||||
<span class="has-tooltip" data-container="body" title="<%= remaining %>">
|
|
||||||
<%= _.escape(title) %>
|
|
||||||
</span>
|
|
||||||
</a>'
|
|
||||||
)
|
)
|
||||||
|
|
||||||
milestoneLinkNoneTemplate = '<div class="light">None</div>'
|
milestoneLinkNoneTemplate = '<span class="no-value">None</span>'
|
||||||
|
|
||||||
collapsedSidebarLabelTemplate = _.template(
|
collapsedSidebarLabelTemplate = _.template(
|
||||||
'<span class="has-tooltip" data-container="body" title="<%= remaining %>" data-placement="left">
|
'<span class="has-tooltip" data-container="body" title="<%= remaining %>" data-placement="left">
|
||||||
|
|
20
app/assets/javascripts/network/application.js.coffee
Normal file
20
app/assets/javascripts/network/application.js.coffee
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# This is a manifest file that'll be compiled into including all the files listed below.
|
||||||
|
# Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
|
||||||
|
# be included in the compiled file accessible from http://example.com/assets/application.js
|
||||||
|
# It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
||||||
|
# the compiled file.
|
||||||
|
#
|
||||||
|
#= require raphael
|
||||||
|
#= require g.raphael
|
||||||
|
#= require g.bar
|
||||||
|
#= require_tree .
|
||||||
|
|
||||||
|
$ ->
|
||||||
|
network_graph = new Network({
|
||||||
|
url: $(".network-graph").attr('data-url'),
|
||||||
|
commit_url: $(".network-graph").attr('data-commit-url'),
|
||||||
|
ref: $(".network-graph").attr('data-ref'),
|
||||||
|
commit_id: $(".network-graph").attr('data-commit-id')
|
||||||
|
})
|
||||||
|
|
||||||
|
new ShortcutsNetwork(network_graph.branch_graph)
|
|
@ -102,12 +102,15 @@ class @Notes
|
||||||
|
|
||||||
keydownNoteText: (e) ->
|
keydownNoteText: (e) ->
|
||||||
$this = $(this)
|
$this = $(this)
|
||||||
if $this.val() is '' and e.which is 38 #aka the up key
|
if $this.val() is '' and e.which is 38 and not isMetaKey e
|
||||||
myLastNote = $("li.note[data-author-id='#{gon.current_user_id}'][data-editable]:last")
|
myLastNote = $("li.note[data-author-id='#{gon.current_user_id}'][data-editable]:last")
|
||||||
if myLastNote.length
|
if myLastNote.length
|
||||||
myLastNoteEditBtn = myLastNote.find('.js-note-edit')
|
myLastNoteEditBtn = myLastNote.find('.js-note-edit')
|
||||||
myLastNoteEditBtn.trigger('click', [true, myLastNote])
|
myLastNoteEditBtn.trigger('click', [true, myLastNote])
|
||||||
|
|
||||||
|
isMetaKey = (e) ->
|
||||||
|
(e.metaKey or e.ctrlKey or e.altKey or e.shiftKey)
|
||||||
|
|
||||||
initRefresh: ->
|
initRefresh: ->
|
||||||
clearInterval(Notes.interval)
|
clearInterval(Notes.interval)
|
||||||
Notes.interval = setInterval =>
|
Notes.interval = setInterval =>
|
||||||
|
|
25
app/assets/javascripts/notifications_dropdown.js.coffee
Normal file
25
app/assets/javascripts/notifications_dropdown.js.coffee
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
class @NotificationsDropdown
|
||||||
|
constructor: ->
|
||||||
|
$(document)
|
||||||
|
.off 'click', '.update-notification'
|
||||||
|
.on 'click', '.update-notification', (e) ->
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
|
return if $(this).is('.is-active') and $(this).data('notification-level') is 'custom'
|
||||||
|
|
||||||
|
notificationLevel = $(@).data 'notification-level'
|
||||||
|
label = $(@).data 'notification-title'
|
||||||
|
form = $(this).parents('.notification-form:first')
|
||||||
|
form.find('.js-notification-loading').toggleClass 'fa-bell fa-spin fa-spinner'
|
||||||
|
form.find('#notification_setting_level').val(notificationLevel)
|
||||||
|
form.submit()
|
||||||
|
|
||||||
|
$(document)
|
||||||
|
.off 'ajax:success', '.notification-form'
|
||||||
|
.on 'ajax:success', '.notification-form', (e, data) ->
|
||||||
|
if data.saved
|
||||||
|
$(e.currentTarget)
|
||||||
|
.closest('.notification-dropdown')
|
||||||
|
.replaceWith(data.html)
|
||||||
|
else
|
||||||
|
new Flash('Failed to save new settings', 'alert')
|
49
app/assets/javascripts/notifications_form.js.coffee
Normal file
49
app/assets/javascripts/notifications_form.js.coffee
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
class @NotificationsForm
|
||||||
|
constructor: ->
|
||||||
|
@removeEventListeners()
|
||||||
|
@initEventListeners()
|
||||||
|
|
||||||
|
removeEventListeners: ->
|
||||||
|
$(document).off 'change', '.js-custom-notification-event'
|
||||||
|
|
||||||
|
initEventListeners: ->
|
||||||
|
$(document).on 'change', '.js-custom-notification-event', @toggleCheckbox
|
||||||
|
|
||||||
|
toggleCheckbox: (e) =>
|
||||||
|
$checkbox = $(e.currentTarget)
|
||||||
|
$parent = $checkbox.closest('.checkbox')
|
||||||
|
@saveEvent($checkbox, $parent)
|
||||||
|
|
||||||
|
showCheckboxLoadingSpinner: ($parent) ->
|
||||||
|
$parent
|
||||||
|
.addClass 'is-loading'
|
||||||
|
.find '.custom-notification-event-loading'
|
||||||
|
.removeClass 'fa-check'
|
||||||
|
.addClass 'fa-spin fa-spinner'
|
||||||
|
.removeClass 'is-done'
|
||||||
|
|
||||||
|
saveEvent: ($checkbox, $parent) ->
|
||||||
|
form = $parent.parents('form:first')
|
||||||
|
|
||||||
|
$.ajax(
|
||||||
|
url: form.attr('action')
|
||||||
|
method: form.attr('method')
|
||||||
|
dataType: 'json'
|
||||||
|
data: form.serialize()
|
||||||
|
|
||||||
|
beforeSend: =>
|
||||||
|
@showCheckboxLoadingSpinner($parent)
|
||||||
|
).done (data) ->
|
||||||
|
$checkbox.enable()
|
||||||
|
|
||||||
|
if data.saved
|
||||||
|
$parent
|
||||||
|
.find '.custom-notification-event-loading'
|
||||||
|
.toggleClass 'fa-spin fa-spinner fa-check is-done'
|
||||||
|
|
||||||
|
setTimeout(->
|
||||||
|
$parent
|
||||||
|
.removeClass 'is-loading'
|
||||||
|
.find '.custom-notification-event-loading'
|
||||||
|
.toggleClass 'fa-spin fa-spinner fa-check is-done'
|
||||||
|
, 2000)
|
|
@ -8,6 +8,10 @@ class @Profile
|
||||||
$('.js-preferences-form').on 'change.preference', 'input[type=radio]', ->
|
$('.js-preferences-form').on 'change.preference', 'input[type=radio]', ->
|
||||||
$(this).parents('form').submit()
|
$(this).parents('form').submit()
|
||||||
|
|
||||||
|
# Automatically submit email form when it changes
|
||||||
|
$('#user_notification_email').on 'change', ->
|
||||||
|
$(this).parents('form').submit()
|
||||||
|
|
||||||
$('.update-username').on 'ajax:before', ->
|
$('.update-username').on 'ajax:before', ->
|
||||||
$('.loading-username').show()
|
$('.loading-username').show()
|
||||||
$(this).find('.update-success').hide()
|
$(this).find('.update-success').hide()
|
||||||
|
|
|
@ -19,6 +19,7 @@ class @Project
|
||||||
$('.clone').text(url)
|
$('.clone').text(url)
|
||||||
|
|
||||||
# Ref switcher
|
# Ref switcher
|
||||||
|
@initRefSwitcher()
|
||||||
$('.project-refs-select').on 'change', ->
|
$('.project-refs-select').on 'change', ->
|
||||||
$(@).parents('form').submit()
|
$(@).parents('form').submit()
|
||||||
|
|
||||||
|
@ -34,23 +35,6 @@ class @Project
|
||||||
$(@).parents('.no-password-message').remove()
|
$(@).parents('.no-password-message').remove()
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
$('.update-notification').on 'click', (e) ->
|
|
||||||
e.preventDefault()
|
|
||||||
notification_level = $(@).data 'notification-level'
|
|
||||||
label = $(@).data 'notification-title'
|
|
||||||
$('#notification_setting_level').val(notification_level)
|
|
||||||
$('#notification-form').submit()
|
|
||||||
$('#notifications-button').empty().append("<i class='fa fa-bell'></i>" + label + "<i class='fa fa-angle-down'></i>")
|
|
||||||
$(@).parents('ul').find('li.active').removeClass 'active'
|
|
||||||
$(@).parent().addClass 'active'
|
|
||||||
|
|
||||||
$('#notification-form').on 'ajax:success', (e, data) ->
|
|
||||||
if data.saved
|
|
||||||
new Flash("Notification settings saved", "notice")
|
|
||||||
else
|
|
||||||
new Flash("Failed to save new settings", "alert")
|
|
||||||
|
|
||||||
|
|
||||||
@projectSelectDropdown()
|
@projectSelectDropdown()
|
||||||
|
|
||||||
projectSelectDropdown: ->
|
projectSelectDropdown: ->
|
||||||
|
@ -66,3 +50,39 @@ class @Project
|
||||||
|
|
||||||
changeProject: (url) ->
|
changeProject: (url) ->
|
||||||
window.location = url
|
window.location = url
|
||||||
|
|
||||||
|
initRefSwitcher: ->
|
||||||
|
$('.js-project-refs-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: 'ref'
|
||||||
|
renderRow: (ref) ->
|
||||||
|
if ref.header?
|
||||||
|
"<li class='dropdown-header'>#{ref.header}</li>"
|
||||||
|
else
|
||||||
|
isActiveClass = if ref is selected then 'is-active' else ''
|
||||||
|
|
||||||
|
"<li>
|
||||||
|
<a href='#' data-ref='#{escape(ref)}' class='#{isActiveClass}'>
|
||||||
|
#{ref}
|
||||||
|
</a>
|
||||||
|
</li>"
|
||||||
|
id: (obj, $el) ->
|
||||||
|
$el.data('ref')
|
||||||
|
toggleLabel: (obj, $el) ->
|
||||||
|
$el.text().trim()
|
||||||
|
clicked: (e) ->
|
||||||
|
$dropdown.closest('form').submit()
|
||||||
|
)
|
||||||
|
|
|
@ -51,15 +51,19 @@ class @Sidebar
|
||||||
$this = $(e.currentTarget)
|
$this = $(e.currentTarget)
|
||||||
$todoLoading = $('.js-issuable-todo-loading')
|
$todoLoading = $('.js-issuable-todo-loading')
|
||||||
$btnText = $('.js-issuable-todo-text', $this)
|
$btnText = $('.js-issuable-todo-text', $this)
|
||||||
ajaxType = if $this.attr('data-id') then 'PATCH' else 'POST'
|
ajaxType = if $this.attr('data-delete-path') then 'DELETE' else 'POST'
|
||||||
ajaxUrlExtra = if $this.attr('data-id') then "/#{$this.attr('data-id')}" else ''
|
|
||||||
|
if $this.attr('data-delete-path')
|
||||||
|
url = "#{$this.attr('data-delete-path')}"
|
||||||
|
else
|
||||||
|
url = "#{$this.data('url')}"
|
||||||
|
|
||||||
$.ajax(
|
$.ajax(
|
||||||
url: "#{$this.data('url')}#{ajaxUrlExtra}"
|
url: url
|
||||||
type: ajaxType
|
type: ajaxType
|
||||||
dataType: 'json'
|
dataType: 'json'
|
||||||
data:
|
data:
|
||||||
issuable_id: $this.data('issuable')
|
issuable_id: $this.data('issuable-id')
|
||||||
issuable_type: $this.data('issuable-type')
|
issuable_type: $this.data('issuable-type')
|
||||||
beforeSend: =>
|
beforeSend: =>
|
||||||
@beforeTodoSend($this, $todoLoading)
|
@beforeTodoSend($this, $todoLoading)
|
||||||
|
@ -82,15 +86,15 @@ class @Sidebar
|
||||||
else
|
else
|
||||||
$todoPendingCount.removeClass 'hidden'
|
$todoPendingCount.removeClass 'hidden'
|
||||||
|
|
||||||
if data.todo?
|
if data.delete_path?
|
||||||
$btn
|
$btn
|
||||||
.attr 'aria-label', $btn.data('mark-text')
|
.attr 'aria-label', $btn.data('mark-text')
|
||||||
.attr 'data-id', data.todo.id
|
.attr 'data-delete-path', data.delete_path
|
||||||
$btnText.text $btn.data('mark-text')
|
$btnText.text $btn.data('mark-text')
|
||||||
else
|
else
|
||||||
$btn
|
$btn
|
||||||
.attr 'aria-label', $btn.data('todo-text')
|
.attr 'aria-label', $btn.data('todo-text')
|
||||||
.removeAttr 'data-id'
|
.removeAttr 'data-delete-path'
|
||||||
$btnText.text $btn.data('todo-text')
|
$btnText.text $btn.data('todo-text')
|
||||||
|
|
||||||
sidebarDropdownLoading: (e) ->
|
sidebarDropdownLoading: (e) ->
|
||||||
|
|
|
@ -67,8 +67,12 @@ class @SearchAutocomplete
|
||||||
getData: (term, callback) ->
|
getData: (term, callback) ->
|
||||||
_this = @
|
_this = @
|
||||||
|
|
||||||
# Do not trigger request if input is empty
|
unless term
|
||||||
return if @searchInput.val() is ''
|
if contents = @getCategoryContents()
|
||||||
|
@searchInput.data('glDropdown').filter.options.callback contents
|
||||||
|
@enableAutocomplete()
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
# Prevent multiple ajax calls
|
# Prevent multiple ajax calls
|
||||||
return if @loadingSuggestions
|
return if @loadingSuggestions
|
||||||
|
@ -122,6 +126,37 @@ class @SearchAutocomplete
|
||||||
).always ->
|
).always ->
|
||||||
_this.loadingSuggestions = false
|
_this.loadingSuggestions = false
|
||||||
|
|
||||||
|
|
||||||
|
getCategoryContents: ->
|
||||||
|
|
||||||
|
userId = gon.current_user_id
|
||||||
|
{ utils, projectOptions, groupOptions, dashboardOptions } = gl
|
||||||
|
|
||||||
|
if utils.isInGroupsPage() and groupOptions
|
||||||
|
options = groupOptions[utils.getGroupSlug()]
|
||||||
|
|
||||||
|
else if utils.isInProjectPage() and projectOptions
|
||||||
|
options = projectOptions[utils.getProjectSlug()]
|
||||||
|
|
||||||
|
else if dashboardOptions
|
||||||
|
options = dashboardOptions
|
||||||
|
|
||||||
|
{ issuesPath, mrPath, name } = options
|
||||||
|
|
||||||
|
items = [
|
||||||
|
{ header: "#{name}" }
|
||||||
|
{ text: 'Issues assigned to me', url: "#{issuesPath}/?assignee_id=#{userId}" }
|
||||||
|
{ text: "Issues I've created", url: "#{issuesPath}/?author_id=#{userId}" }
|
||||||
|
'separator'
|
||||||
|
{ text: 'Merge requests assigned to me', url: "#{mrPath}/?assignee_id=#{userId}" }
|
||||||
|
{ text: "Merge requests I've created", url: "#{mrPath}/?author_id=#{userId}" }
|
||||||
|
]
|
||||||
|
|
||||||
|
items.splice 0, 1 unless name
|
||||||
|
|
||||||
|
return items
|
||||||
|
|
||||||
|
|
||||||
serializeState: ->
|
serializeState: ->
|
||||||
{
|
{
|
||||||
# Search Criteria
|
# Search Criteria
|
||||||
|
@ -209,6 +244,12 @@ class @SearchAutocomplete
|
||||||
@isFocused = true
|
@isFocused = true
|
||||||
@wrap.addClass('search-active')
|
@wrap.addClass('search-active')
|
||||||
|
|
||||||
|
@getData() if @getValue() is ''
|
||||||
|
|
||||||
|
|
||||||
|
getValue: -> return @searchInput.val()
|
||||||
|
|
||||||
|
|
||||||
onClearInputClick: (e) =>
|
onClearInputClick: (e) =>
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
@searchInput.val('').focus()
|
@searchInput.val('').focus()
|
||||||
|
@ -229,6 +270,10 @@ class @SearchAutocomplete
|
||||||
@locationBadgeEl.text(badgeText).show()
|
@locationBadgeEl.text(badgeText).show()
|
||||||
@wrap.addClass('has-location-badge')
|
@wrap.addClass('has-location-badge')
|
||||||
|
|
||||||
|
|
||||||
|
hasLocationBadge: -> return @wrap.is '.has-location-badge'
|
||||||
|
|
||||||
|
|
||||||
restoreOriginalState: ->
|
restoreOriginalState: ->
|
||||||
inputs = Object.keys @originalState
|
inputs = Object.keys @originalState
|
||||||
|
|
||||||
|
@ -257,13 +302,14 @@ class @SearchAutocomplete
|
||||||
|
|
||||||
@getElement("##{input}").val('')
|
@getElement("##{input}").val('')
|
||||||
|
|
||||||
|
|
||||||
removeLocationBadge: ->
|
removeLocationBadge: ->
|
||||||
|
|
||||||
@locationBadgeEl.hide()
|
@locationBadgeEl.hide()
|
||||||
|
|
||||||
# Reset state
|
|
||||||
@resetSearchState()
|
@resetSearchState()
|
||||||
|
|
||||||
@wrap.removeClass('has-location-badge')
|
@wrap.removeClass('has-location-badge')
|
||||||
|
@disableAutocomplete()
|
||||||
|
|
||||||
|
|
||||||
disableAutocomplete: ->
|
disableAutocomplete: ->
|
||||||
@searchInput.addClass('disabled')
|
@searchInput.addClass('disabled')
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
class @Shortcuts
|
class @Shortcuts
|
||||||
constructor: ->
|
constructor: (skipResetBindings) ->
|
||||||
@enabledHelp = []
|
@enabledHelp = []
|
||||||
Mousetrap.reset()
|
Mousetrap.reset() if not skipResetBindings
|
||||||
Mousetrap.bind('?', @onToggleHelp)
|
Mousetrap.bind('?', @onToggleHelp)
|
||||||
Mousetrap.bind('s', Shortcuts.focusSearch)
|
Mousetrap.bind('s', Shortcuts.focusSearch)
|
||||||
Mousetrap.bind(['ctrl+shift+p', 'command+shift+p'], @toggleMarkdownPreview)
|
Mousetrap.bind(['ctrl+shift+p', 'command+shift+p'], @toggleMarkdownPreview)
|
||||||
|
|
10
app/assets/javascripts/shortcuts_blob.coffee
Normal file
10
app/assets/javascripts/shortcuts_blob.coffee
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
#= require shortcuts
|
||||||
|
|
||||||
|
class @ShortcutsBlob extends Shortcuts
|
||||||
|
constructor: (skipResetBindings) ->
|
||||||
|
super skipResetBindings
|
||||||
|
Mousetrap.bind('y', ShortcutsBlob.copyToClipboard)
|
||||||
|
|
||||||
|
@copyToClipboard: ->
|
||||||
|
clipboardButton = $('.btn-clipboard')
|
||||||
|
clipboardButton.click() if clipboardButton
|
|
@ -3,13 +3,33 @@ expanded = 'page-sidebar-expanded'
|
||||||
|
|
||||||
toggleSidebar = ->
|
toggleSidebar = ->
|
||||||
$('.page-with-sidebar').toggleClass("#{collapsed} #{expanded}")
|
$('.page-with-sidebar').toggleClass("#{collapsed} #{expanded}")
|
||||||
$('header').toggleClass("header-collapsed header-expanded")
|
$('.navbar-fixed-top').toggleClass("header-collapsed header-expanded")
|
||||||
|
|
||||||
|
if $.cookie('pin_nav') is 'true'
|
||||||
|
$('.navbar-fixed-top').toggleClass('header-pinned-nav')
|
||||||
|
$('.page-with-sidebar').toggleClass('page-sidebar-pinned')
|
||||||
|
|
||||||
setTimeout ( ->
|
setTimeout ( ->
|
||||||
niceScrollBars = $('.nicescroll').niceScroll();
|
niceScrollBars = $('.nav-sidebar').niceScroll();
|
||||||
niceScrollBars.updateScrollBar();
|
niceScrollBars.updateScrollBar();
|
||||||
), 300
|
), 300
|
||||||
|
|
||||||
|
$(document)
|
||||||
|
.off 'click', 'body'
|
||||||
|
.on 'click', 'body', (e) ->
|
||||||
|
unless $.cookie('pin_nav') is 'true'
|
||||||
|
$target = $(e.target)
|
||||||
|
$nav = $target.closest('.sidebar-wrapper')
|
||||||
|
pageExpanded = $('.page-with-sidebar').hasClass('page-sidebar-expanded')
|
||||||
|
$toggle = $target.closest('.toggle-nav-collapse, .side-nav-toggle')
|
||||||
|
|
||||||
|
if $nav.length is 0 and pageExpanded and $toggle.length is 0
|
||||||
|
$('.page-with-sidebar')
|
||||||
|
.toggleClass('page-sidebar-collapsed page-sidebar-expanded')
|
||||||
|
|
||||||
|
$('.navbar-fixed-top')
|
||||||
|
.toggleClass('header-collapsed header-expanded')
|
||||||
|
|
||||||
$(document).on("click", '.toggle-nav-collapse, .side-nav-toggle', (e) ->
|
$(document).on("click", '.toggle-nav-collapse, .side-nav-toggle', (e) ->
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
|
|
|
@ -6,12 +6,6 @@ class @Calendar
|
||||||
@daySizeWithSpace = @daySize + (@daySpace * 2)
|
@daySizeWithSpace = @daySize + (@daySpace * 2)
|
||||||
@monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
|
@monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
|
||||||
@months = []
|
@months = []
|
||||||
@highestValue = 0
|
|
||||||
|
|
||||||
# Get the highest value from the timestampes
|
|
||||||
_.each timestamps, (count) =>
|
|
||||||
if count > @highestValue
|
|
||||||
@highestValue = count
|
|
||||||
|
|
||||||
# Loop through the timestamps to create a group of objects
|
# Loop through the timestamps to create a group of objects
|
||||||
# The group of objects will be grouped based on the day of the week they are
|
# The group of objects will be grouped based on the day of the week they are
|
||||||
|
@ -39,8 +33,8 @@ class @Calendar
|
||||||
i++
|
i++
|
||||||
|
|
||||||
# Init color functions
|
# Init color functions
|
||||||
@color = @initColor()
|
|
||||||
@colorKey = @initColorKey()
|
@colorKey = @initColorKey()
|
||||||
|
@color = @initColor()
|
||||||
|
|
||||||
# Init the svg element
|
# Init the svg element
|
||||||
@renderSvg(group)
|
@renderSvg(group)
|
||||||
|
@ -104,7 +98,7 @@ class @Calendar
|
||||||
.attr 'class', 'user-contrib-cell js-tooltip'
|
.attr 'class', 'user-contrib-cell js-tooltip'
|
||||||
.attr 'fill', (stamp) =>
|
.attr 'fill', (stamp) =>
|
||||||
if stamp.count isnt 0
|
if stamp.count isnt 0
|
||||||
@color(stamp.count)
|
@color(Math.min(stamp.count, 40))
|
||||||
else
|
else
|
||||||
'#ededed'
|
'#ededed'
|
||||||
.attr 'data-container', 'body'
|
.attr 'data-container', 'body'
|
||||||
|
@ -164,10 +158,11 @@ class @Calendar
|
||||||
color
|
color
|
||||||
|
|
||||||
initColor: ->
|
initColor: ->
|
||||||
|
colorRange = ['#ededed', @colorKey(0), @colorKey(1), @colorKey(2), @colorKey(3)]
|
||||||
d3.scale
|
d3.scale
|
||||||
.linear()
|
.threshold()
|
||||||
.range(['#acd5f2', '#254e77'])
|
.domain([0, 10, 20, 30])
|
||||||
.domain([0, @highestValue])
|
.range(colorRange)
|
||||||
|
|
||||||
initColorKey: ->
|
initColorKey: ->
|
||||||
d3.scale
|
d3.scale
|
||||||
|
|
|
@ -72,7 +72,7 @@ class @UsersSelect
|
||||||
|
|
||||||
assigneeTemplate = _.template(
|
assigneeTemplate = _.template(
|
||||||
'<% if (username) { %>
|
'<% if (username) { %>
|
||||||
<a class="author_link " href="/u/<%= username %>">
|
<a class="author_link bold" href="/u/<%= username %>">
|
||||||
<% if( avatar ) { %>
|
<% if( avatar ) { %>
|
||||||
<img width="32" class="avatar avatar-inline s32" alt="" src="<%= avatar %>">
|
<img width="32" class="avatar avatar-inline s32" alt="" src="<%= avatar %>">
|
||||||
<% } %>
|
<% } %>
|
||||||
|
@ -82,7 +82,7 @@ class @UsersSelect
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
<% } else { %>
|
<% } else { %>
|
||||||
<span class="assign-yourself">
|
<span class="no-value assign-yourself">
|
||||||
No assignee -
|
No assignee -
|
||||||
<a href="#" class="js-assign-yourself">
|
<a href="#" class="js-assign-yourself">
|
||||||
assign yourself
|
assign yourself
|
||||||
|
|
|
@ -37,3 +37,4 @@
|
||||||
@import "framework/timeline.scss";
|
@import "framework/timeline.scss";
|
||||||
@import "framework/typography.scss";
|
@import "framework/typography.scss";
|
||||||
@import "framework/zen.scss";
|
@import "framework/zen.scss";
|
||||||
|
@import "framework/blank";
|
||||||
|
|
23
app/assets/stylesheets/framework/blank.scss
Normal file
23
app/assets/stylesheets/framework/blank.scss
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
.blank-state {
|
||||||
|
padding-top: 20px;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blank-state-no-icon {
|
||||||
|
padding-top: 40px;
|
||||||
|
padding-bottom: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blank-state-title {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
font-size: 19px;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blank-state-text {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: $gl-padding;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
|
@ -91,6 +91,26 @@
|
||||||
background-color: $white-light;
|
background-color: $white-light;
|
||||||
border-top: none;
|
border-top: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.top-block .container-fluid {
|
||||||
|
background-color: inherit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-header-block {
|
||||||
|
background-color: $white-light;
|
||||||
|
border-bottom: 1px solid $white-dark;
|
||||||
|
padding: 11px 0;
|
||||||
|
margin-bottom: 11px;
|
||||||
|
|
||||||
|
.oneline {
|
||||||
|
line-height: 35px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.no-bottom-space {
|
||||||
|
border-bottom: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.cover-block {
|
.cover-block {
|
||||||
|
|
|
@ -461,10 +461,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-state-active,
|
.ui-datepicker-calendar {
|
||||||
.ui-state-hover {
|
.ui-state-hover,
|
||||||
color: $md-link-color;
|
.ui-state-active {
|
||||||
background-color: $calendar-hover-bg;
|
color: #fff;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-datepicker-prev,
|
.ui-datepicker-prev,
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
*/
|
*/
|
||||||
@mixin gitlab-theme($color-light, $color, $color-darker, $color-dark) {
|
@mixin gitlab-theme($color-light, $color, $color-darker, $color-dark) {
|
||||||
.page-with-sidebar {
|
.page-with-sidebar {
|
||||||
|
.toggle-nav-collapse,
|
||||||
.collapse-nav a {
|
.pin-nav-btn {
|
||||||
color: $color-light;
|
color: $color-light;
|
||||||
background: $color;
|
background: $color;
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,19 @@
|
||||||
* Application Header
|
* Application Header
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@mixin tanuki-logo-colors($path-color) {
|
||||||
|
fill: $path-color;
|
||||||
|
transition: all 0.8s;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&.highlight {
|
||||||
|
fill: lighten($path-color, 25%);
|
||||||
|
transition: all 0.1s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
header {
|
header {
|
||||||
transition-duration: .3s;
|
transition: padding $sidebar-transition-duration;
|
||||||
|
|
||||||
&.navbar-empty {
|
&.navbar-empty {
|
||||||
height: $header-height;
|
height: $header-height;
|
||||||
|
@ -79,14 +90,9 @@ header {
|
||||||
|
|
||||||
&.header-collapsed {
|
&.header-collapsed {
|
||||||
padding: 0 16px;
|
padding: 0 16px;
|
||||||
|
|
||||||
.side-nav-toggle {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.side-nav-toggle {
|
.side-nav-toggle {
|
||||||
display: none;
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: -10px;
|
left: -10px;
|
||||||
margin: 6px 0;
|
margin: 6px 0;
|
||||||
|
@ -108,9 +114,7 @@ header {
|
||||||
.header-content {
|
.header-content {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: $header-height;
|
height: $header-height;
|
||||||
padding-right: 40px;
|
|
||||||
padding-left: 30px;
|
padding-left: 30px;
|
||||||
transition-duration: .3s;
|
|
||||||
|
|
||||||
@media (min-width: $screen-sm-min) {
|
@media (min-width: $screen-sm-min) {
|
||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
|
@ -198,25 +202,24 @@ header {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-collapsed {
|
#tanuki-logo {
|
||||||
margin-left: 0;
|
|
||||||
|
|
||||||
.header-content {
|
#tanuki-left-ear,
|
||||||
|
#tanuki-right-ear,
|
||||||
@media (min-width: $screen-sm-max) {
|
#tanuki-nose {
|
||||||
padding-left: 30px;
|
@include tanuki-logo-colors($tanuki-red);
|
||||||
transition-duration: .3s;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.tanuki-shape {
|
#tanuki-left-eye,
|
||||||
transition: all 0.8s;
|
#tanuki-right-eye {
|
||||||
|
@include tanuki-logo-colors($tanuki-orange);
|
||||||
&:hover, &.highlight {
|
|
||||||
fill: rgb(255, 255, 255);
|
|
||||||
transition: all 0.1s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#tanuki-left-cheek,
|
||||||
|
#tanuki-right-cheek {
|
||||||
|
@include tanuki-logo-colors($tanuki-yellow);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: $screen-xs-max) {
|
@media (max-width: $screen-xs-max) {
|
||||||
|
|
|
@ -159,7 +159,7 @@ ul.content-list {
|
||||||
background-color: $gray-light;
|
background-color: $gray-light;
|
||||||
border: dotted 1px $gray-dark;
|
border: dotted 1px $gray-dark;
|
||||||
margin: 1px 0;
|
margin: 1px 0;
|
||||||
min-height: 30px;
|
min-height: 52px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,5 +97,7 @@
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
word-break: keep-all;
|
word-break: keep-all;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@include bulleted-list;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,3 +110,17 @@
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mixin bulleted-list {
|
||||||
|
> ul {
|
||||||
|
list-style-type: disc;
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style-type: circle;
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style-type: square;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -52,6 +52,19 @@
|
||||||
.git-clone-holder {
|
.git-clone-holder {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Display Star and Fork buttons without counters on mobile.
|
||||||
|
.project-action-buttons {
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
.count-buttons .btn {
|
||||||
|
margin: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.count-buttons .count-with-arrow {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-stats {
|
.project-stats {
|
||||||
|
|
|
@ -18,6 +18,13 @@
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition-duration: .3s;
|
transition-duration: .3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fa {
|
||||||
|
position: relative;
|
||||||
|
top: 3px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: $btn-placeholder-gray;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin scrolling-links() {
|
@mixin scrolling-links() {
|
||||||
|
@ -104,10 +111,6 @@
|
||||||
width: 50%;
|
width: 50%;
|
||||||
line-height: 28px;
|
line-height: 28px;
|
||||||
|
|
||||||
&.wiki-page {
|
|
||||||
padding: 16px 10px 11px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Small devices (phones, tablets, 768px and lower) */
|
/* Small devices (phones, tablets, 768px and lower) */
|
||||||
@media (max-width: $screen-sm-min) {
|
@media (max-width: $screen-sm-min) {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -136,7 +139,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Small devices (phones, tablets, 768px and lower) */
|
/* Small devices (phones, tablets, 768px and lower) */
|
||||||
@media (max-width: $screen-sm-max) {
|
@media (max-width: $screen-xs-max) {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -220,6 +223,7 @@
|
||||||
form {
|
form {
|
||||||
display: block;
|
display: block;
|
||||||
height: auto;
|
height: auto;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
|
||||||
input {
|
input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -242,6 +246,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.adjust {
|
||||||
|
.nav-text, .nav-controls {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.layout-nav {
|
.layout-nav {
|
||||||
|
@ -251,7 +261,7 @@
|
||||||
z-index: 11;
|
z-index: 11;
|
||||||
background: $background-color;
|
background: $background-color;
|
||||||
border-bottom: 1px solid $border-color;
|
border-bottom: 1px solid $border-color;
|
||||||
transition-duration: .3s;
|
transition: padding $sidebar-transition-duration;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
.container-fluid {
|
.container-fluid {
|
||||||
|
@ -313,11 +323,19 @@
|
||||||
.fade-right {
|
.fade-right {
|
||||||
@include fade(left, rgba(250, 250, 250, 0.4), $background-color);
|
@include fade(left, rgba(250, 250, 250, 0.4), $background-color);
|
||||||
right: 0;
|
right: 0;
|
||||||
|
|
||||||
|
.fa {
|
||||||
|
right: -7px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.fade-left {
|
.fade-left {
|
||||||
@include fade(right, rgba(250, 250, 250, 0.4), $background-color);
|
@include fade(right, rgba(250, 250, 250, 0.4), $background-color);
|
||||||
left: 0;
|
left: 0;
|
||||||
|
|
||||||
|
.fa {
|
||||||
|
left: -7px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
li {
|
li {
|
||||||
|
@ -347,6 +365,12 @@
|
||||||
.badge {
|
.badge {
|
||||||
color: $gl-icon-color;
|
color: $gl-icon-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
a, i {
|
||||||
|
color: $black;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,10 @@
|
||||||
margin-top: -2px;
|
margin-top: -2px;
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dropdown-menu-toggle {
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel-body {
|
.panel-body {
|
||||||
|
|
|
@ -165,11 +165,6 @@
|
||||||
background-size: 16px 16px !important;
|
background-size: 16px 16px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Branch/tag selector **/
|
|
||||||
.project-refs-form .select2-container {
|
|
||||||
width: 160px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.select2-results .select2-no-results,
|
.select2-results .select2-no-results,
|
||||||
.select2-results .select2-searching,
|
.select2-results .select2-searching,
|
||||||
.select2-results .select2-ajax-error,
|
.select2-results .select2-ajax-error,
|
||||||
|
|
|
@ -1,26 +1,31 @@
|
||||||
.page-with-sidebar {
|
.page-with-sidebar {
|
||||||
padding-top: $header-height;
|
padding-top: $header-height;
|
||||||
transition-duration: .3s;
|
transition: padding $sidebar-transition-duration;
|
||||||
|
|
||||||
.sidebar-wrapper {
|
.sidebar-wrapper {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
overflow-y: auto;
|
|
||||||
overflow-x: hidden;
|
|
||||||
left: 0;
|
left: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
transition-duration: .3s;
|
overflow: hidden;
|
||||||
|
transition: width $sidebar-transition-duration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-wrapper {
|
.sidebar-wrapper {
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
background: $background-color;
|
background: $background-color;
|
||||||
|
|
||||||
|
.nicescroll-rails-hr {
|
||||||
|
// TODO: Figure out why nicescroll doesn't hide horizontal bar
|
||||||
|
display: none!important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-wrapper {
|
.content-wrapper {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
transition: padding $sidebar-transition-duration;
|
||||||
|
|
||||||
.container-fluid {
|
.container-fluid {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
|
@ -34,50 +39,39 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-wrapper {
|
.sidebar-user {
|
||||||
|
padding: 15px;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: $sidebar_width;
|
||||||
|
overflow: hidden;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 36px;
|
||||||
|
transition: width $sidebar-transition-duration, padding $sidebar-transition-duration;
|
||||||
|
|
||||||
.sidebar-user {
|
@media (min-width: $sidebar-breakpoint) {
|
||||||
padding: 15px 22px;
|
bottom: 50px;
|
||||||
position: fixed;
|
|
||||||
bottom: 0;
|
|
||||||
width: $sidebar_width;
|
|
||||||
overflow: hidden;
|
|
||||||
transition-duration: .3s;
|
|
||||||
|
|
||||||
.username {
|
|
||||||
margin-left: 10px;
|
|
||||||
width: $sidebar_width - 2 * 10px;
|
|
||||||
font-size: 16px;
|
|
||||||
line-height: 34px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.tanuki-shape {
|
|
||||||
transition: all 0.8s;
|
|
||||||
|
|
||||||
&:hover, &.highlight {
|
|
||||||
fill: rgb(255, 255, 255);
|
|
||||||
transition: all 0.1s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.nav-sidebar {
|
.nav-sidebar {
|
||||||
margin-top: 22 + $header-height;
|
position: absolute;
|
||||||
margin-bottom: 116px;
|
top: 50px;
|
||||||
transition-duration: .3s;
|
bottom: 65px;
|
||||||
list-style: none;
|
width: $sidebar_width;
|
||||||
overflow: hidden;
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
|
||||||
|
@media (min-width: $sidebar-breakpoint) {
|
||||||
|
bottom: 115px;
|
||||||
|
}
|
||||||
|
|
||||||
&.navbar-collapse {
|
&.navbar-collapse {
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
li {
|
li {
|
||||||
width: $sidebar_width;
|
|
||||||
|
|
||||||
&.separate-item {
|
&.separate-item {
|
||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
|
@ -90,8 +84,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
width: $sidebar_width;
|
padding: 7px 15px 7px 12px;
|
||||||
padding: 7px 15px 7px 23px;
|
|
||||||
font-size: $gl-font-size;
|
font-size: $gl-font-size;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
display: block;
|
display: block;
|
||||||
|
@ -99,11 +92,9 @@
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
outline: none;
|
outline: none;
|
||||||
|
|
||||||
&:hover {
|
&:hover,
|
||||||
text-decoration: none;
|
&:active,
|
||||||
}
|
&:focus {
|
||||||
|
|
||||||
&:active, &:focus {
|
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,10 +106,6 @@
|
||||||
svg {
|
svg {
|
||||||
margin-right: 13px;
|
margin-right: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.back-link i {
|
|
||||||
transition-duration: .3s;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,37 +116,50 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-subnav {
|
.toggle-nav-collapse {
|
||||||
margin-left: 0;
|
|
||||||
padding-left: 0;
|
|
||||||
|
|
||||||
li {
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.collapse-nav a {
|
|
||||||
width: $sidebar_width;
|
width: $sidebar_width;
|
||||||
position: fixed;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
min-height: 50px;
|
||||||
padding: 5px 0;
|
padding: 5px 0;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
background: transparent;
|
line-height: 30px;
|
||||||
height: 50px;
|
}
|
||||||
text-align: center;
|
|
||||||
line-height: 40px;
|
|
||||||
transition-duration: .3s;
|
|
||||||
outline: none;
|
|
||||||
|
|
||||||
&:hover {
|
.nav-header-btn {
|
||||||
|
padding: 10px 5px;
|
||||||
|
color: inherit;
|
||||||
|
transition-duration: .3s;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
color: $white-light;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-wrapper {
|
.pin-nav-btn {
|
||||||
&.hidden-nav {
|
display: none;
|
||||||
width: 0;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,62 +168,34 @@
|
||||||
|
|
||||||
.sidebar-wrapper {
|
.sidebar-wrapper {
|
||||||
width: 0;
|
width: 0;
|
||||||
|
|
||||||
.nav-sidebar {
|
|
||||||
width: 0;
|
|
||||||
|
|
||||||
li {
|
|
||||||
width: auto;
|
|
||||||
|
|
||||||
a {
|
|
||||||
span {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.collapse-nav a {
|
|
||||||
width: 0;
|
|
||||||
|
|
||||||
i {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-user {
|
|
||||||
width: 0;
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 0;
|
|
||||||
|
|
||||||
.username {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-sidebar-expanded {
|
.page-sidebar-expanded {
|
||||||
|
|
||||||
@media (max-width: $screen-sm-max) {
|
|
||||||
padding-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-wrapper {
|
.sidebar-wrapper {
|
||||||
width: $sidebar_width;
|
width: $sidebar_width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.nav-sidebar {
|
.page-sidebar-pinned {
|
||||||
width: $sidebar_width;
|
.content-wrapper,
|
||||||
|
.layout-nav {
|
||||||
|
@media (min-width: $sidebar-breakpoint) {
|
||||||
|
padding-left: $sidebar_width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
header.header-pinned-nav {
|
||||||
|
@media (min-width: $sidebar-breakpoint) {
|
||||||
|
padding-left: ($sidebar_width + $gl-padding);
|
||||||
|
|
||||||
|
.side-nav-toggle {
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-sidebar li a {
|
.header-content {
|
||||||
width: $sidebar_width;
|
padding-left: 0;
|
||||||
|
|
||||||
&.back-link {
|
|
||||||
i {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ $sidebar_width: 220px;
|
||||||
$gutter_collapsed_width: 62px;
|
$gutter_collapsed_width: 62px;
|
||||||
$gutter_width: 290px;
|
$gutter_width: 290px;
|
||||||
$gutter_inner_width: 258px;
|
$gutter_inner_width: 258px;
|
||||||
|
$sidebar-transition-duration: .15s;
|
||||||
|
$sidebar-breakpoint: 1440px;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* UI elements
|
* UI elements
|
||||||
|
@ -154,6 +156,11 @@ $warning-message-border: #f0e2bb;
|
||||||
/* header */
|
/* header */
|
||||||
$light-grey-header: #faf9f9;
|
$light-grey-header: #faf9f9;
|
||||||
|
|
||||||
|
/* tanuki logo colors */
|
||||||
|
$tanuki-red: #e24329;
|
||||||
|
$tanuki-orange: #fc6d26;
|
||||||
|
$tanuki-yellow: #fca326;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* State colors:
|
* State colors:
|
||||||
*/
|
*/
|
||||||
|
@ -261,5 +268,10 @@ $calendar-hover-bg: #ecf3fe;
|
||||||
$calendar-border-color: rgba(#000, .1);
|
$calendar-border-color: rgba(#000, .1);
|
||||||
$calendar-unselectable-bg: #faf9f9;
|
$calendar-unselectable-bg: #faf9f9;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Personal Access Tokens
|
||||||
|
*/
|
||||||
|
$personal-access-tokens-disabled-label-color: #bbb;
|
||||||
|
|
||||||
$ci-output-bg: #1d1f21;
|
$ci-output-bg: #1d1f21;
|
||||||
$ci-text-color: #c5c8c6;
|
$ci-text-color: #c5c8c6;
|
||||||
|
|
|
@ -38,6 +38,10 @@ table {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
width: 600px;
|
width: 600px;
|
||||||
|
|
||||||
|
& > td {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&#body {
|
&#body {
|
||||||
|
|
|
@ -7,84 +7,119 @@
|
||||||
margin-right: 9px;
|
margin-right: 9px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lists-separator {
|
.commit-header {
|
||||||
margin: 10px 0;
|
padding: 5px 10px;
|
||||||
border-color: #ddd;
|
background-color: $background-color;
|
||||||
}
|
border-top: 1px solid #eee;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
.commits-row {
|
&:first-child {
|
||||||
ul {
|
border-top-width: 0;
|
||||||
margin: 0;
|
|
||||||
|
|
||||||
li.commit {
|
|
||||||
padding: 8px 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.commits-row-date {
|
|
||||||
font-size: 15px;
|
|
||||||
line-height: 20px;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
li.commit {
|
.commit-row-title {
|
||||||
list-style: none;
|
line-height: 1;
|
||||||
|
margin-bottom: 7px;
|
||||||
|
|
||||||
.commit-row-title {
|
.notes_count {
|
||||||
font-size: $list-font-size;
|
float: right;
|
||||||
line-height: 20px;
|
margin-right: 10px;
|
||||||
margin-bottom: 2px;
|
}
|
||||||
|
|
||||||
.btn-clipboard {
|
.str-truncated {
|
||||||
margin-top: -1px;
|
max-width: 70%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.commit-row-message {
|
||||||
|
color: $gl-dark-link-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-expander {
|
||||||
|
display: inline-block;
|
||||||
|
background: $gray-light;
|
||||||
|
color: $gl-placeholder-color;
|
||||||
|
padding: 0 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 1px solid $border-gray-dark;
|
||||||
|
border-radius: $border-radius-default;
|
||||||
|
margin-left: 5px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: darken($gray-light, 10%);
|
||||||
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.notes_count {
|
.commit-actions {
|
||||||
float: right;
|
@media (min-width: $screen-sm-min) {
|
||||||
margin-right: 10px;
|
float: right;
|
||||||
|
margin-left: $gl-padding;
|
||||||
|
margin-top: 2px;
|
||||||
|
font-size: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-transparent {
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
&:not(:first-child) {
|
||||||
|
margin-left: $gl-padding;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.commit_short_id {
|
.commit-short-id {
|
||||||
min-width: 65px;
|
font-family: $monospace_font;
|
||||||
color: $gl-dark-link-color;
|
font-weight: 600;
|
||||||
font-family: $monospace_font;
|
}
|
||||||
|
|
||||||
|
.commit {
|
||||||
|
padding: 10px 0;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
@media (min-width: $screen-sm-min) {
|
||||||
|
padding-left: 20px;
|
||||||
|
|
||||||
|
.commit-info-block {
|
||||||
|
padding-left: 44px;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.str-truncated {
|
&:not(:last-child) {
|
||||||
max-width: 70%;
|
border-bottom: 1px solid #eee;
|
||||||
}
|
}
|
||||||
|
|
||||||
.commit-row-message {
|
a,
|
||||||
color: $gl-dark-link-color;
|
button {
|
||||||
|
color: $gl-dark-link-color;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-expander {
|
.avatar {
|
||||||
background: #eee;
|
position: absolute;
|
||||||
color: #555;
|
top: 10px;
|
||||||
padding: 0 5px;
|
left: 16px;
|
||||||
cursor: pointer;
|
|
||||||
margin-left: 4px;
|
|
||||||
&:hover {
|
|
||||||
background-color: #ddd;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-title {
|
.item-title {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
max-width: 70%;
|
|
||||||
|
@media (min-width: $screen-sm-min) {
|
||||||
|
max-width: 70%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.commit-row-description {
|
.commit-row-description {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
border-left: 1px solid #eee;
|
border-left: 1px solid #eee;
|
||||||
padding: 10px 15px;
|
padding: 10px 15px;
|
||||||
margin: 5px 0 10px 5px;
|
margin: 10px 0;
|
||||||
background: #f9f9f9;
|
background: #f9f9f9;
|
||||||
display: none;
|
display: none;
|
||||||
|
|
||||||
|
@ -93,6 +128,7 @@ li.commit {
|
||||||
background: inherit;
|
background: inherit;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
|
@ -102,7 +138,7 @@ li.commit {
|
||||||
|
|
||||||
.commit-row-info {
|
.commit-row-info {
|
||||||
color: $gl-gray;
|
color: $gl-gray;
|
||||||
line-height: 24px;
|
line-height: 1;
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: $gl-gray;
|
color: $gl-gray;
|
||||||
|
@ -111,10 +147,6 @@ li.commit {
|
||||||
.avatar {
|
.avatar {
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.committed_ago {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.inline-commit {
|
&.inline-commit {
|
||||||
|
|
|
@ -4,6 +4,11 @@
|
||||||
margin-bottom: $gl-padding;
|
margin-bottom: $gl-padding;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
|
|
||||||
|
.commit-short-id {
|
||||||
|
font-family: $regular_font;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
.diff-header {
|
.diff-header {
|
||||||
position: relative;
|
position: relative;
|
||||||
background: $background-color;
|
background: $background-color;
|
||||||
|
|
|
@ -60,14 +60,14 @@
|
||||||
|
|
||||||
.encoding-selector,
|
.encoding-selector,
|
||||||
.license-selector,
|
.license-selector,
|
||||||
.gitignore-selector {
|
.gitignore-selector,
|
||||||
|
.gitlab-ci-yml-selector {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
font-family: $regular_font;
|
font-family: $regular_font;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gitignore-selector {
|
.gitignore-selector, .license-selector, .gitlab-ci-yml-selector {
|
||||||
|
|
||||||
.dropdown {
|
.dropdown {
|
||||||
line-height: 21px;
|
line-height: 21px;
|
||||||
}
|
}
|
||||||
|
@ -77,4 +77,10 @@
|
||||||
width: 220px;
|
width: 220px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.gitlab-ci-yml-selector {
|
||||||
|
.dropdown-menu-toggle {
|
||||||
|
width: 250px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -136,9 +136,10 @@
|
||||||
.event-last-push {
|
.event-last-push {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
.event-last-push-text {
|
.event-last-push-text {
|
||||||
@include str-truncated(100%);
|
@include str-truncated(100%);
|
||||||
padding: 5px 0;
|
padding: 4px 0;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
float: left;
|
float: left;
|
||||||
margin-right: -150px;
|
margin-right: -150px;
|
||||||
|
|
|
@ -57,4 +57,11 @@
|
||||||
|
|
||||||
.documentation {
|
.documentation {
|
||||||
padding: 7px;
|
padding: 7px;
|
||||||
|
|
||||||
|
// Border around images in the help pages.
|
||||||
|
img:not(.emoji) {
|
||||||
|
border: 1px solid $table-border-gray;
|
||||||
|
padding: 5px;
|
||||||
|
margin: 5px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,13 @@
|
||||||
margin-right: 1px;
|
margin-right: 1px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Border around images in issue and MR descriptions.
|
||||||
|
.description img:not(.emoji) {
|
||||||
|
border: 1px solid $table-border-gray;
|
||||||
|
padding: 5px;
|
||||||
|
margin: 5px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.issuable-filter-count {
|
.issuable-filter-count {
|
||||||
|
@ -145,7 +152,6 @@
|
||||||
|
|
||||||
.assign-yourself {
|
.assign-yourself {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
font-weight: normal;
|
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -158,6 +164,10 @@
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.no-value {
|
||||||
|
color: $gl-placeholder-color;
|
||||||
|
}
|
||||||
|
|
||||||
.sidebar-collapsed-icon {
|
.sidebar-collapsed-icon {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
@ -248,11 +258,16 @@
|
||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.issuable-header-btn {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.issuable-header-btn {
|
.issuable-header-btn {
|
||||||
background: $gray-normal;
|
background: $gray-normal;
|
||||||
border: 1px solid $border-gray-normal;
|
border: 1px solid $border-gray-normal;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: $gray-dark;
|
background: $gray-dark;
|
||||||
border: 1px solid $border-gray-dark;
|
border: 1px solid $border-gray-dark;
|
||||||
|
@ -322,7 +337,7 @@
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: #8c8c8c;
|
color: $gl-placeholder-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,11 +50,10 @@
|
||||||
|
|
||||||
.label-row {
|
.label-row {
|
||||||
.label-name {
|
.label-name {
|
||||||
display: block;
|
display: inline-block;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
|
|
||||||
@media (min-width: $screen-sm-min) {
|
@media (min-width: $screen-sm-min) {
|
||||||
display: inline-block;
|
|
||||||
width: 200px;
|
width: 200px;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
@ -63,6 +62,7 @@
|
||||||
.label-description {
|
.label-description {
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
|
margin-left: 50px;
|
||||||
|
|
||||||
@media (min-width: $screen-sm-min) {
|
@media (min-width: $screen-sm-min) {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
@ -115,6 +115,13 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.draggable-handler {
|
||||||
|
display: inline-block;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity .3s;
|
||||||
|
color: $gray-darkest;
|
||||||
|
}
|
||||||
|
|
||||||
.prioritized-labels {
|
.prioritized-labels {
|
||||||
margin-bottom: 30px;
|
margin-bottom: 30px;
|
||||||
|
|
||||||
|
@ -122,6 +129,13 @@
|
||||||
display: none;
|
display: none;
|
||||||
color: $gray-light;
|
color: $gray-light;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
li:hover {
|
||||||
|
.draggable-handler {
|
||||||
|
display: inline-block;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.other-labels {
|
.other-labels {
|
||||||
|
|
|
@ -119,7 +119,12 @@
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: $screen-sm-max) {
|
.btn-grouped {
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: $screen-xs-max) {
|
||||||
h4 {
|
h4 {
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
}
|
}
|
||||||
|
@ -131,10 +136,14 @@
|
||||||
.btn,
|
.btn,
|
||||||
.btn-group,
|
.btn-group,
|
||||||
.accept-action {
|
.accept-action {
|
||||||
width: 100%;
|
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.accept-action {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.accept-control {
|
.accept-control {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
@ -244,6 +253,10 @@
|
||||||
|
|
||||||
.panel-footer {
|
.panel-footer {
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
min-width: auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.commit {
|
.commit {
|
||||||
|
@ -252,9 +265,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatar {
|
.avatar {
|
||||||
width: 20px;
|
margin-left: 0;
|
||||||
height: 20px;
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.commit-row-info {
|
.commit-row-info {
|
||||||
|
@ -282,7 +293,7 @@
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: $screen-sm-min) {
|
@media (min-width: $screen-xs-min) {
|
||||||
float: left;
|
float: left;
|
||||||
width: 50%;
|
width: 50%;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
|
|
@ -219,3 +219,16 @@
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.note-form-actions {
|
||||||
|
@media (max-width: $screen-xs-max) {
|
||||||
|
.btn {
|
||||||
|
float: none;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -84,24 +84,14 @@ ul.notes {
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
@include md-typography;
|
@include md-typography;
|
||||||
|
|
||||||
|
// Reset ul style types since we're nested inside a ul already
|
||||||
|
@include bulleted-list;
|
||||||
|
|
||||||
// On diffs code should wrap nicely and not overflow
|
// On diffs code should wrap nicely and not overflow
|
||||||
code {
|
code {
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset ul style types since we're nested inside a ul already
|
|
||||||
& > ul {
|
|
||||||
list-style-type: disc;
|
|
||||||
|
|
||||||
ul {
|
|
||||||
list-style-type: circle;
|
|
||||||
|
|
||||||
ul {
|
|
||||||
list-style-type: square;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.task-list {
|
ul.task-list {
|
||||||
ul:not(.task-list) {
|
ul:not(.task-list) {
|
||||||
padding-left: 1.3em;
|
padding-left: 1.3em;
|
||||||
|
@ -117,6 +107,13 @@ ul.notes {
|
||||||
code {
|
code {
|
||||||
word-break: keep-all;
|
word-break: keep-all;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Border around images in issue and MR comments.
|
||||||
|
img:not(.emoji) {
|
||||||
|
border: 1px solid $table-border-gray;
|
||||||
|
padding: 5px;
|
||||||
|
margin: 5px 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,6 +136,12 @@ ul.notes {
|
||||||
@media (min-width: $screen-sm-min) {
|
@media (min-width: $screen-sm-min) {
|
||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: $screen-xs-min) {
|
||||||
|
.inline {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.note-emoji-button {
|
.note-emoji-button {
|
||||||
|
@ -258,7 +261,11 @@ ul.notes {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
right: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
|
||||||
|
.note-action-button {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
@media (min-width: $screen-sm-min) {
|
@media (min-width: $screen-sm-min) {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
|
@ -192,6 +192,25 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.personal-access-tokens-never-expires-label {
|
||||||
|
color: $personal-access-tokens-disabled-label-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.datepicker.personal-access-tokens-expires-at .ui-state-disabled span {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.created-personal-access-token-container {
|
||||||
|
#created-personal-access-token {
|
||||||
|
width: 90%;
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-clipboard {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.user-profile {
|
.user-profile {
|
||||||
@media (max-width: $screen-xs-max) {
|
@media (max-width: $screen-xs-max) {
|
||||||
.cover-block {
|
.cover-block {
|
||||||
|
|
|
@ -5,10 +5,12 @@
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-ssh-key-message, .project-limit-message {
|
.no-ssh-key-message, .project-limit-message {
|
||||||
background-color: #f28d35;
|
background-color: #f28d35;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.new_project,
|
.new_project,
|
||||||
.edit-project {
|
.edit-project {
|
||||||
fieldset.features {
|
fieldset.features {
|
||||||
|
@ -18,13 +20,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-name-holder {
|
|
||||||
.help-inline {
|
|
||||||
vertical-align: top;
|
|
||||||
padding: 7px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.project-home-panel {
|
.project-home-panel {
|
||||||
background: $white-light;
|
background: $white-light;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
@ -33,7 +28,7 @@
|
||||||
.container-fluid {
|
.container-fluid {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
@media (min-width: $screen-md-max) {
|
@media (min-width: $screen-lg-min) {
|
||||||
.row {
|
.row {
|
||||||
display: flex;
|
display: flex;
|
||||||
-ms-flex-align: center;
|
-ms-flex-align: center;
|
||||||
|
@ -106,7 +101,8 @@
|
||||||
|
|
||||||
.notifications-btn {
|
.notifications-btn {
|
||||||
|
|
||||||
.fa-bell {
|
.fa-bell,
|
||||||
|
.fa-spinner {
|
||||||
margin-right: 6px;
|
margin-right: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,11 +129,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-group:not(:first-child):not(:last-child) > .btn {
|
|
||||||
border-top-right-radius: 3px;
|
|
||||||
border-bottom-right-radius: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
form {
|
form {
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
|
@ -229,7 +220,7 @@
|
||||||
right: 16px;
|
right: 16px;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
|
|
||||||
@media (max-width: $screen-lg-min) {
|
@media (max-width: $screen-md-max) {
|
||||||
top: 0;
|
top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,7 +229,7 @@
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: 61px;
|
bottom: 61px;
|
||||||
|
|
||||||
@media (max-width: $screen-lg-min) {
|
@media (max-width: $screen-md-max) {
|
||||||
position: relative;
|
position: relative;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
|
@ -376,13 +367,14 @@ a.deploy-project-label {
|
||||||
|
|
||||||
.project-import .btn {
|
.project-import .btn {
|
||||||
float: left;
|
float: left;
|
||||||
|
margin-bottom: 10px;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-stats {
|
.project-stats {
|
||||||
margin-top: $gl-padding;
|
margin-top: $gl-padding;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
padding: 16px 0;
|
padding: 0;
|
||||||
background-color: $white-light;
|
background-color: $white-light;
|
||||||
font-size: 0;
|
font-size: 0;
|
||||||
|
|
||||||
|
@ -391,13 +383,14 @@ a.deploy-project-label {
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav li {
|
.nav li {
|
||||||
display: inline;
|
display: inline-block;
|
||||||
|
margin: 16px 0;
|
||||||
|
margin-right: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav > li > a {
|
.nav > li > a {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
margin-right: 12px;
|
padding: 5px 10px;
|
||||||
padding: 0 10px;
|
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
color: $notes-light-color;
|
color: $notes-light-color;
|
||||||
}
|
}
|
||||||
|
@ -411,12 +404,17 @@ a.deploy-project-label {
|
||||||
font-size: 17px;
|
font-size: 17px;
|
||||||
}
|
}
|
||||||
|
|
||||||
li.missing a {
|
li.missing {
|
||||||
color: #5a6069;
|
border: 1px dashed $border-gray-light;
|
||||||
border: 1px dashed #dce0e5;
|
border-radius: $border-radius-default;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: $notes-light-color;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: #f0f2f5;
|
background-color: $gray-normal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -503,7 +501,8 @@ pre.light-well {
|
||||||
|
|
||||||
.activity-filter-block {
|
.activity-filter-block {
|
||||||
.controls {
|
.controls {
|
||||||
padding-bottom: 10px;
|
padding-bottom: 7px;
|
||||||
|
margin-top: 8px;
|
||||||
border-bottom: 1px solid $border-color;
|
border-bottom: 1px solid $border-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -607,3 +606,26 @@ pre.light-well {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.custom-notifications-form {
|
||||||
|
.is-loading {
|
||||||
|
.custom-notification-event-loading {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-notification-event-loading {
|
||||||
|
display: none;
|
||||||
|
margin-left: 5px;
|
||||||
|
|
||||||
|
&.is-done {
|
||||||
|
color: $gl-text-green;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-refs-form {
|
||||||
|
.dropdown-menu {
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -14,18 +14,28 @@
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#contributors-master {
|
||||||
|
@include make-md-column(12);
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#contributors {
|
#contributors {
|
||||||
.contributors-list {
|
.contributors-list {
|
||||||
margin: 0 0 10px;
|
margin: 0 0 10px;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
float: left;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.person {
|
.person {
|
||||||
&:nth-child(even) {
|
@include make-md-column(6);
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
float: left;
|
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -101,7 +101,7 @@
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
||||||
.commit {
|
.commit {
|
||||||
padding: 0;
|
padding: 0 0 0 55px;
|
||||||
|
|
||||||
.commit-row-title {
|
.commit-row-title {
|
||||||
.commit-row-message {
|
.commit-row-message {
|
||||||
|
@ -129,4 +129,6 @@
|
||||||
.tree-controls {
|
.tree-controls {
|
||||||
float: right;
|
float: right;
|
||||||
margin-top: 11px;
|
margin-top: 11px;
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
class Admin::RunnerProjectsController < Admin::ApplicationController
|
class Admin::RunnerProjectsController < Admin::ApplicationController
|
||||||
before_action :project, only: [:create]
|
before_action :project, only: [:create]
|
||||||
|
|
||||||
def index
|
|
||||||
@runner_projects = project.runner_projects.all
|
|
||||||
@runner_project = project.runner_projects.new
|
|
||||||
end
|
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@runner = Ci::Runner.find(params[:runner_project][:runner_id])
|
@runner = Ci::Runner.find(params[:runner_project][:runner_id])
|
||||||
|
|
||||||
if @runner.assign_to(@project, current_user)
|
return head(403) if @runner.is_shared? || @runner.locked?
|
||||||
|
|
||||||
|
runner_project = @runner.assign_to(@project, current_user)
|
||||||
|
|
||||||
|
if runner_project.persisted?
|
||||||
redirect_to admin_runner_path(@runner)
|
redirect_to admin_runner_path(@runner)
|
||||||
else
|
else
|
||||||
redirect_to admin_runner_path(@runner), alert: 'Failed adding runner to project'
|
redirect_to admin_runner_path(@runner), alert: 'Failed adding runner to project'
|
||||||
|
|
|
@ -8,7 +8,7 @@ class ApplicationController < ActionController::Base
|
||||||
include PageLayoutHelper
|
include PageLayoutHelper
|
||||||
include WorkhorseHelper
|
include WorkhorseHelper
|
||||||
|
|
||||||
before_action :authenticate_user_from_token!
|
before_action :authenticate_user_from_private_token!
|
||||||
before_action :authenticate_user!
|
before_action :authenticate_user!
|
||||||
before_action :validate_user_service_ticket!
|
before_action :validate_user_service_ticket!
|
||||||
before_action :reject_blocked!
|
before_action :reject_blocked!
|
||||||
|
@ -24,7 +24,7 @@ class ApplicationController < ActionController::Base
|
||||||
protect_from_forgery with: :exception
|
protect_from_forgery with: :exception
|
||||||
|
|
||||||
helper_method :abilities, :can?, :current_application_settings
|
helper_method :abilities, :can?, :current_application_settings
|
||||||
helper_method :import_sources_enabled?, :github_import_enabled?, :github_import_configured?, :gitlab_import_enabled?, :gitlab_import_configured?, :bitbucket_import_enabled?, :bitbucket_import_configured?, :gitorious_import_enabled?, :google_code_import_enabled?, :fogbugz_import_enabled?, :git_import_enabled?
|
helper_method :import_sources_enabled?, :github_import_enabled?, :github_import_configured?, :gitlab_import_enabled?, :gitlab_import_configured?, :bitbucket_import_enabled?, :bitbucket_import_configured?, :gitorious_import_enabled?, :google_code_import_enabled?, :fogbugz_import_enabled?, :git_import_enabled?, :gitlab_project_import_enabled?
|
||||||
|
|
||||||
rescue_from Encoding::CompatibilityError do |exception|
|
rescue_from Encoding::CompatibilityError do |exception|
|
||||||
log_exception(exception)
|
log_exception(exception)
|
||||||
|
@ -64,17 +64,10 @@ class ApplicationController < ActionController::Base
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# From https://github.com/plataformatec/devise/wiki/How-To:-Simple-Token-Authentication-Example
|
# This filter handles both private tokens and personal access tokens
|
||||||
# https://gist.github.com/josevalim/fb706b1e933ef01e4fb6
|
def authenticate_user_from_private_token!
|
||||||
def authenticate_user_from_token!
|
token_string = params[:private_token].presence || request.headers['PRIVATE-TOKEN'].presence
|
||||||
user_token = if params[:authenticity_token].presence
|
user = User.find_by_authentication_token(token_string) || User.find_by_personal_access_token(token_string)
|
||||||
params[:authenticity_token].presence
|
|
||||||
elsif params[:private_token].presence
|
|
||||||
params[:private_token].presence
|
|
||||||
elsif request.headers['PRIVATE-TOKEN'].present?
|
|
||||||
request.headers['PRIVATE-TOKEN']
|
|
||||||
end
|
|
||||||
user = user_token && User.find_by_authentication_token(user_token.to_s)
|
|
||||||
|
|
||||||
if user
|
if user
|
||||||
# Notice we are passing store false, so the user is not
|
# Notice we are passing store false, so the user is not
|
||||||
|
@ -326,6 +319,10 @@ class ApplicationController < ActionController::Base
|
||||||
current_application_settings.import_sources.include?('git')
|
current_application_settings.import_sources.include?('git')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def gitlab_project_import_enabled?
|
||||||
|
current_application_settings.import_sources.include?('gitlab_project')
|
||||||
|
end
|
||||||
|
|
||||||
def two_factor_authentication_required?
|
def two_factor_authentication_required?
|
||||||
current_application_settings.require_two_factor_authentication
|
current_application_settings.require_two_factor_authentication
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,44 +1,39 @@
|
||||||
class Dashboard::TodosController < Dashboard::ApplicationController
|
class Dashboard::TodosController < Dashboard::ApplicationController
|
||||||
before_action :find_todos, only: [:index, :destroy, :destroy_all]
|
include TodosHelper
|
||||||
|
|
||||||
|
before_action :find_todos, only: [:index, :destroy_all]
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@todos = @todos.page(params[:page])
|
@todos = @todos.page(params[:page])
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
todo.done
|
TodoService.new.mark_todos_as_done([todo], current_user)
|
||||||
|
|
||||||
todo_notice = 'Todo was successfully marked as done.'
|
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { redirect_to dashboard_todos_path, notice: todo_notice }
|
format.html { redirect_to dashboard_todos_path, notice: 'Todo was successfully marked as done.' }
|
||||||
format.js { head :ok }
|
format.js { head :ok }
|
||||||
format.json do
|
format.json { render json: { count: todos_pending_count, done_count: todos_done_count } }
|
||||||
render json: { count: @todos.size, done_count: current_user.todos.done.count }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy_all
|
def destroy_all
|
||||||
@todos.each(&:done)
|
TodoService.new.mark_todos_as_done(@todos, current_user)
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { redirect_to dashboard_todos_path, notice: 'All todos were marked as done.' }
|
format.html { redirect_to dashboard_todos_path, notice: 'All todos were marked as done.' }
|
||||||
format.js { head :ok }
|
format.js { head :ok }
|
||||||
format.json do
|
format.json { render json: { count: todos_pending_count, done_count: todos_done_count } }
|
||||||
find_todos
|
|
||||||
render json: { count: @todos.size, done_count: current_user.todos.done.count }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def todo
|
def todo
|
||||||
@todo ||= current_user.todos.find(params[:id])
|
@todo ||= find_todos.find(params[:id])
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_todos
|
def find_todos
|
||||||
@todos = TodosFinder.new(current_user, params).execute
|
@todos ||= TodosFinder.new(current_user, params).execute
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
class Groups::NotificationSettingsController < Groups::ApplicationController
|
|
||||||
before_action :authenticate_user!
|
|
||||||
|
|
||||||
def update
|
|
||||||
notification_setting = current_user.notification_settings_for(group)
|
|
||||||
saved = notification_setting.update_attributes(notification_setting_params)
|
|
||||||
|
|
||||||
render json: { saved: saved }
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def notification_setting_params
|
|
||||||
params.require(:notification_setting).permit(:level)
|
|
||||||
end
|
|
||||||
end
|
|
48
app/controllers/import/gitlab_projects_controller.rb
Normal file
48
app/controllers/import/gitlab_projects_controller.rb
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
class Import::GitlabProjectsController < Import::BaseController
|
||||||
|
before_action :verify_gitlab_project_import_enabled
|
||||||
|
|
||||||
|
def new
|
||||||
|
@namespace_id = project_params[:namespace_id]
|
||||||
|
@namespace_name = Namespace.find(project_params[:namespace_id]).name
|
||||||
|
@path = project_params[:path]
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
unless file_is_valid?
|
||||||
|
return redirect_back_or_default(options: { alert: "You need to upload a GitLab project export archive." })
|
||||||
|
end
|
||||||
|
|
||||||
|
@project = Gitlab::ImportExport::ProjectCreator.new(project_params[:namespace_id],
|
||||||
|
current_user,
|
||||||
|
File.expand_path(project_params[:file].path),
|
||||||
|
project_params[:path]).execute
|
||||||
|
|
||||||
|
if @project.saved?
|
||||||
|
redirect_to(
|
||||||
|
project_path(@project),
|
||||||
|
notice: "Project '#{@project.name}' is being imported."
|
||||||
|
)
|
||||||
|
else
|
||||||
|
redirect_to(
|
||||||
|
new_import_gitlab_project_path,
|
||||||
|
alert: "Project could not be imported: #{@project.errors.full_messages.join(', ')}"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def file_is_valid?
|
||||||
|
project_params[:file] && project_params[:file].respond_to?(:read)
|
||||||
|
end
|
||||||
|
|
||||||
|
def verify_gitlab_project_import_enabled
|
||||||
|
render_404 unless gitlab_project_import_enabled?
|
||||||
|
end
|
||||||
|
|
||||||
|
def project_params
|
||||||
|
params.permit(
|
||||||
|
:path, :namespace_id, :file
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
36
app/controllers/notification_settings_controller.rb
Normal file
36
app/controllers/notification_settings_controller.rb
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
class NotificationSettingsController < ApplicationController
|
||||||
|
before_action :authenticate_user!
|
||||||
|
|
||||||
|
def create
|
||||||
|
project = Project.find(params[:project][:id])
|
||||||
|
|
||||||
|
return render_404 unless can?(current_user, :read_project, project)
|
||||||
|
|
||||||
|
@notification_setting = current_user.notification_settings_for(project)
|
||||||
|
@saved = @notification_setting.update_attributes(notification_setting_params)
|
||||||
|
|
||||||
|
render_response
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
@notification_setting = current_user.notification_settings.find(params[:id])
|
||||||
|
@saved = @notification_setting.update_attributes(notification_setting_params)
|
||||||
|
|
||||||
|
render_response
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def render_response
|
||||||
|
render json: {
|
||||||
|
html: view_to_html_string("shared/notifications/_button", notification_setting: @notification_setting),
|
||||||
|
saved: @saved
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def notification_setting_params
|
||||||
|
allowed_fields = NotificationSetting::EMAIL_EVENTS.dup
|
||||||
|
allowed_fields << :level
|
||||||
|
params.require(:notification_setting).permit(allowed_fields)
|
||||||
|
end
|
||||||
|
end
|
|
@ -5,7 +5,7 @@ class Profiles::AccountsController < Profiles::ApplicationController
|
||||||
|
|
||||||
def unlink
|
def unlink
|
||||||
provider = params[:provider]
|
provider = params[:provider]
|
||||||
current_user.identities.find_by(provider: provider).destroy
|
current_user.identities.find_by(provider: provider).destroy unless provider.to_s == 'saml'
|
||||||
redirect_to profile_account_path
|
redirect_to profile_account_path
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
class Profiles::NotificationsController < Profiles::ApplicationController
|
class Profiles::NotificationsController < Profiles::ApplicationController
|
||||||
def show
|
def show
|
||||||
@user = current_user
|
@user = current_user
|
||||||
@group_notifications = current_user.notification_settings.for_groups
|
@group_notifications = current_user.notification_settings.for_groups.order(:id)
|
||||||
@project_notifications = current_user.notification_settings.for_projects
|
@project_notifications = current_user.notification_settings.for_projects.order(:id)
|
||||||
@global_notification_setting = current_user.global_notification_setting
|
@global_notification_setting = current_user.global_notification_setting
|
||||||
end
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
if current_user.update_attributes(user_params) && update_notification_settings
|
if current_user.update_attributes(user_params)
|
||||||
flash[:notice] = "Notification settings saved"
|
flash[:notice] = "Notification settings saved"
|
||||||
else
|
else
|
||||||
flash[:alert] = "Failed to save new settings"
|
flash[:alert] = "Failed to save new settings"
|
||||||
|
@ -19,16 +19,4 @@ class Profiles::NotificationsController < Profiles::ApplicationController
|
||||||
def user_params
|
def user_params
|
||||||
params.require(:user).permit(:notification_email)
|
params.require(:user).permit(:notification_email)
|
||||||
end
|
end
|
||||||
|
|
||||||
def global_notification_setting_params
|
|
||||||
params.require(:global_notification_setting).permit(:level)
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def update_notification_settings
|
|
||||||
return true unless global_notification_setting_params
|
|
||||||
|
|
||||||
current_user.global_notification_setting.update_attributes(global_notification_setting_params)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
class Profiles::PersonalAccessTokensController < Profiles::ApplicationController
|
||||||
|
before_action :load_personal_access_tokens, only: :index
|
||||||
|
|
||||||
|
def index
|
||||||
|
@personal_access_token = current_user.personal_access_tokens.build
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
@personal_access_token = current_user.personal_access_tokens.generate(personal_access_token_params)
|
||||||
|
|
||||||
|
if @personal_access_token.save
|
||||||
|
flash[:personal_access_token] = @personal_access_token.token
|
||||||
|
redirect_to profile_personal_access_tokens_path, notice: "Your new personal access token has been created."
|
||||||
|
else
|
||||||
|
load_personal_access_tokens
|
||||||
|
render :index
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def revoke
|
||||||
|
@personal_access_token = current_user.personal_access_tokens.find(params[:id])
|
||||||
|
|
||||||
|
if @personal_access_token.revoke!
|
||||||
|
flash[:notice] = "Revoked personal access token #{@personal_access_token.name}!"
|
||||||
|
else
|
||||||
|
flash[:alert] = "Could not revoke personal access token #{@personal_access_token.name}."
|
||||||
|
end
|
||||||
|
|
||||||
|
redirect_to profile_personal_access_tokens_path
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def personal_access_token_params
|
||||||
|
params.require(:personal_access_token).permit(:name, :expires_at)
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_personal_access_tokens
|
||||||
|
@active_personal_access_tokens = current_user.personal_access_tokens.active.order(:expires_at)
|
||||||
|
@inactive_personal_access_tokens = current_user.personal_access_tokens.inactive
|
||||||
|
end
|
||||||
|
end
|
|
@ -74,7 +74,7 @@ class Projects::ApplicationController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def require_branch_head
|
def require_branch_head
|
||||||
unless @repository.branch_names.include?(@ref)
|
unless @repository.branch_exists?(@ref)
|
||||||
redirect_to(
|
redirect_to(
|
||||||
namespace_project_tree_path(@project.namespace, @project, @ref),
|
namespace_project_tree_path(@project.namespace, @project, @ref),
|
||||||
notice: "This action is not allowed unless you are on a branch"
|
notice: "This action is not allowed unless you are on a branch"
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
class Projects::NotificationSettingsController < Projects::ApplicationController
|
|
||||||
before_action :authenticate_user!
|
|
||||||
|
|
||||||
def update
|
|
||||||
notification_setting = current_user.notification_settings_for(project)
|
|
||||||
saved = notification_setting.update_attributes(notification_setting_params)
|
|
||||||
|
|
||||||
render json: { saved: saved }
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def notification_setting_params
|
|
||||||
params.require(:notification_setting).permit(:level)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -54,6 +54,6 @@ class Projects::PipelinesController < Projects::ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def commit
|
def commit
|
||||||
@commit ||= @pipeline.commit_data
|
@commit ||= @pipeline.commit
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,11 +6,13 @@ class Projects::RunnerProjectsController < Projects::ApplicationController
|
||||||
def create
|
def create
|
||||||
@runner = Ci::Runner.find(params[:runner_project][:runner_id])
|
@runner = Ci::Runner.find(params[:runner_project][:runner_id])
|
||||||
|
|
||||||
|
return head(403) if @runner.is_shared? || @runner.locked?
|
||||||
return head(403) unless current_user.ci_authorized_runners.include?(@runner)
|
return head(403) unless current_user.ci_authorized_runners.include?(@runner)
|
||||||
|
|
||||||
path = runners_path(project)
|
path = runners_path(project)
|
||||||
|
runner_project = @runner.assign_to(project, current_user)
|
||||||
|
|
||||||
if @runner.assign_to(project, current_user)
|
if runner_project.persisted?
|
||||||
redirect_to path
|
redirect_to path
|
||||||
else
|
else
|
||||||
redirect_to path, alert: 'Failed adding runner to project'
|
redirect_to path, alert: 'Failed adding runner to project'
|
||||||
|
|
|
@ -5,10 +5,9 @@ class Projects::RunnersController < Projects::ApplicationController
|
||||||
layout 'project_settings'
|
layout 'project_settings'
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@runners = project.runners.ordered
|
@project_runners = project.runners.ordered
|
||||||
@specific_runners = current_user.ci_authorized_runners.
|
@assignable_runners = current_user.ci_authorized_runners.
|
||||||
where.not(id: project.runners).
|
assignable_for(project).ordered.page(params[:page]).per(20)
|
||||||
ordered.page(params[:page]).per(20)
|
|
||||||
@shared_runners = Ci::Runner.shared.active
|
@shared_runners = Ci::Runner.shared.active
|
||||||
@shared_runners_count = @shared_runners.count(:all)
|
@shared_runners_count = @shared_runners.count(:all)
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,8 +6,10 @@ class Projects::TagsController < Projects::ApplicationController
|
||||||
before_action :authorize_admin_project!, only: [:destroy]
|
before_action :authorize_admin_project!, only: [:destroy]
|
||||||
|
|
||||||
def index
|
def index
|
||||||
sorted = VersionSorter.rsort(@repository.tag_names)
|
@sort = params[:sort] || 'name'
|
||||||
@tags = Kaminari.paginate_array(sorted).page(params[:page])
|
@tags = @repository.tags_sorted_by(@sort)
|
||||||
|
@tags = Kaminari.paginate_array(@tags).page(params[:page])
|
||||||
|
|
||||||
@releases = project.releases.where(tag: @tags)
|
@releases = project.releases.where(tag: @tags)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,12 @@
|
||||||
class Projects::TodosController < Projects::ApplicationController
|
class Projects::TodosController < Projects::ApplicationController
|
||||||
|
before_action :authenticate_user!, only: [:create]
|
||||||
|
|
||||||
def create
|
def create
|
||||||
todos = TodoService.new.mark_todo(issuable, current_user)
|
todo = TodoService.new.mark_todo(issuable, current_user)
|
||||||
|
|
||||||
render json: {
|
render json: {
|
||||||
todo: todos,
|
count: current_user.todos_pending_count,
|
||||||
count: current_user.todos.pending.count,
|
delete_path: dashboard_todo_path(todo)
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def update
|
|
||||||
current_user.todos.find_by_id(params[:id]).update(state: :done)
|
|
||||||
|
|
||||||
render json: {
|
|
||||||
count: current_user.todos.pending.count,
|
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -22,7 +16,13 @@ class Projects::TodosController < Projects::ApplicationController
|
||||||
@issuable ||= begin
|
@issuable ||= begin
|
||||||
case params[:issuable_type]
|
case params[:issuable_type]
|
||||||
when "issue"
|
when "issue"
|
||||||
@project.issues.find(params[:issuable_id])
|
issue = @project.issues.find(params[:issuable_id])
|
||||||
|
|
||||||
|
if can?(current_user, :read_issue, issue)
|
||||||
|
issue
|
||||||
|
else
|
||||||
|
render_404
|
||||||
|
end
|
||||||
when "merge_request"
|
when "merge_request"
|
||||||
@project.merge_requests.find(params[:issuable_id])
|
@project.merge_requests.find(params[:issuable_id])
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
class ProjectsController < Projects::ApplicationController
|
class ProjectsController < Projects::ApplicationController
|
||||||
include ExtractsPath
|
include ExtractsPath
|
||||||
|
|
||||||
before_action :authenticate_user!, except: [:show, :activity]
|
before_action :authenticate_user!, except: [:show, :activity, :refs]
|
||||||
before_action :project, except: [:new, :create]
|
before_action :project, except: [:new, :create]
|
||||||
before_action :repository, except: [:new, :create]
|
before_action :repository, except: [:new, :create]
|
||||||
before_action :assign_ref_vars, :tree, only: [:show], if: :repo_exists?
|
before_action :assign_ref_vars, :tree, only: [:show], if: :repo_exists?
|
||||||
|
|
||||||
# Authorize
|
# Authorize
|
||||||
before_action :authorize_admin_project!, only: [:edit, :update, :housekeeping]
|
before_action :authorize_admin_project!, only: [:edit, :update, :housekeeping, :download_export, :export, :remove_export, :generate_new_export]
|
||||||
before_action :event_filter, only: [:show, :activity]
|
before_action :event_filter, only: [:show, :activity]
|
||||||
|
|
||||||
layout :determine_layout
|
layout :determine_layout
|
||||||
|
@ -143,6 +143,7 @@ class ProjectsController < Projects::ApplicationController
|
||||||
issues: autocomplete.issues,
|
issues: autocomplete.issues,
|
||||||
milestones: autocomplete.milestones,
|
milestones: autocomplete.milestones,
|
||||||
mergerequests: autocomplete.merge_requests,
|
mergerequests: autocomplete.merge_requests,
|
||||||
|
labels: autocomplete.labels,
|
||||||
members: participants
|
members: participants
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,6 +186,48 @@ class ProjectsController < Projects::ApplicationController
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def export
|
||||||
|
@project.add_export_job(current_user: current_user)
|
||||||
|
|
||||||
|
redirect_to(
|
||||||
|
edit_project_path(@project),
|
||||||
|
notice: "Project export started. A download link will be sent by email."
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def download_export
|
||||||
|
export_project_path = @project.export_project_path
|
||||||
|
|
||||||
|
if export_project_path
|
||||||
|
send_file export_project_path, disposition: 'attachment'
|
||||||
|
else
|
||||||
|
redirect_to(
|
||||||
|
edit_project_path(@project),
|
||||||
|
alert: "Project export link has expired. Please generate a new export from your project settings."
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove_export
|
||||||
|
if @project.remove_exports
|
||||||
|
flash[:notice] = "Project export has been deleted."
|
||||||
|
else
|
||||||
|
flash[:alert] = "Project export could not be deleted."
|
||||||
|
end
|
||||||
|
redirect_to(edit_project_path(@project))
|
||||||
|
end
|
||||||
|
|
||||||
|
def generate_new_export
|
||||||
|
if @project.remove_exports
|
||||||
|
export
|
||||||
|
else
|
||||||
|
redirect_to(
|
||||||
|
edit_project_path(@project),
|
||||||
|
alert: "Project export could not be deleted."
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def toggle_star
|
def toggle_star
|
||||||
current_user.toggle_star(@project)
|
current_user.toggle_star(@project)
|
||||||
@project.reload
|
@project.reload
|
||||||
|
@ -208,6 +251,24 @@ class ProjectsController < Projects::ApplicationController
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def refs
|
||||||
|
options = {
|
||||||
|
'Branches' => @repository.branch_names,
|
||||||
|
}
|
||||||
|
|
||||||
|
unless @repository.tag_count.zero?
|
||||||
|
options['Tags'] = VersionSorter.rsort(@repository.tag_names)
|
||||||
|
end
|
||||||
|
|
||||||
|
# If reference is commit id - we should add it to branch/tag selectbox
|
||||||
|
ref = Addressable::URI.unescape(params[:ref])
|
||||||
|
if ref && options.flatten(2).exclude?(ref) && ref =~ /\A[0-9a-zA-Z]{6,52}\z/
|
||||||
|
options['Commits'] = [ref]
|
||||||
|
end
|
||||||
|
|
||||||
|
render json: options.to_json
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def determine_layout
|
def determine_layout
|
||||||
|
@ -242,8 +303,14 @@ class ProjectsController < Projects::ApplicationController
|
||||||
project.repository_exists? && !project.empty_repo?
|
project.repository_exists? && !project.empty_repo?
|
||||||
end
|
end
|
||||||
|
|
||||||
# Override get_id from ExtractsPath, which returns the branch and file path
|
# Override extract_ref from ExtractsPath, which returns the branch and file path
|
||||||
# for the blob/tree, which in this case is just the root of the default branch.
|
# for the blob/tree, which in this case is just the root of the default branch.
|
||||||
|
# This way we avoid to access the repository.ref_names.
|
||||||
|
def extract_ref(_id)
|
||||||
|
[get_id, '']
|
||||||
|
end
|
||||||
|
|
||||||
|
# Override get_id from ExtractsPath in this case is just the root of the default branch.
|
||||||
def get_id
|
def get_id
|
||||||
project.repository.root_ref
|
project.repository.root_ref
|
||||||
end
|
end
|
||||||
|
|
|
@ -123,7 +123,7 @@ class TodosFinder
|
||||||
end
|
end
|
||||||
|
|
||||||
def by_state(items)
|
def by_state(items)
|
||||||
case params[:state]
|
case params[:state].to_s
|
||||||
when 'done'
|
when 'done'
|
||||||
items.done
|
items.done
|
||||||
else
|
else
|
||||||
|
|
|
@ -101,22 +101,6 @@ module ApplicationHelper
|
||||||
'Never'
|
'Never'
|
||||||
end
|
end
|
||||||
|
|
||||||
def grouped_options_refs
|
|
||||||
repository = @project.repository
|
|
||||||
|
|
||||||
options = [
|
|
||||||
['Branches', repository.branch_names],
|
|
||||||
['Tags', VersionSorter.rsort(repository.tag_names)]
|
|
||||||
]
|
|
||||||
|
|
||||||
# If reference is commit id - we should add it to branch/tag selectbox
|
|
||||||
if @ref && !options.flatten.include?(@ref) && @ref =~ /\A[0-9a-zA-Z]{6,52}\z/
|
|
||||||
options << ['Commit', [@ref]]
|
|
||||||
end
|
|
||||||
|
|
||||||
grouped_options_for_select(options, @ref || @project.default_branch)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Define whenever show last push event
|
# Define whenever show last push event
|
||||||
# with suggestion to create MR
|
# with suggestion to create MR
|
||||||
def show_last_push_widget?(event)
|
def show_last_push_widget?(event)
|
||||||
|
@ -132,7 +116,7 @@ module ApplicationHelper
|
||||||
return false if project.merge_requests.where(source_branch: event.branch_name).opened.any?
|
return false if project.merge_requests.where(source_branch: event.branch_name).opened.any?
|
||||||
|
|
||||||
# Skip if user removed branch right after that
|
# Skip if user removed branch right after that
|
||||||
return false unless project.repository.branch_names.include?(event.branch_name)
|
return false unless project.repository.branch_exists?(event.branch_name)
|
||||||
|
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
|
@ -180,18 +180,22 @@ module BlobHelper
|
||||||
licenses = Licensee::License.all
|
licenses = Licensee::License.all
|
||||||
|
|
||||||
@licenses_for_select = {
|
@licenses_for_select = {
|
||||||
Popular: licenses.select(&:featured).map { |license| [license.name, license.key] },
|
Popular: licenses.select(&:featured).map { |license| { name: license.name, id: license.key } },
|
||||||
Other: licenses.reject(&:featured).map { |license| [license.name, license.key] }
|
Other: licenses.reject(&:featured).map { |license| { name: license.name, id: license.key } }
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def gitignore_names
|
def gitignore_names
|
||||||
return @gitignore_names if defined?(@gitignore_names)
|
@gitignore_names ||=
|
||||||
|
Gitlab::Template::Gitignore.categories.keys.map do |k|
|
||||||
|
[k, Gitlab::Template::Gitignore.by_category(k).map { |t| { name: t.name } }]
|
||||||
|
end.to_h
|
||||||
|
end
|
||||||
|
|
||||||
@gitignore_names = {
|
def gitlab_ci_ymls
|
||||||
Global: Gitlab::Gitignore.global.map { |gitignore| { name: gitignore.name } },
|
@gitlab_ci_ymls ||=
|
||||||
# Note that the key here doesn't cover it really
|
Gitlab::Template::GitlabCiYml.categories.keys.map do |k|
|
||||||
Languages: Gitlab::Gitignore.languages_frameworks.map{ |gitignore| { name: gitignore.name } }
|
[k, Gitlab::Template::GitlabCiYml.by_category(k).map { |t| { name: t.name } }]
|
||||||
}
|
end.to_h
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,7 +10,7 @@ module BranchesHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_push_branch?(project, branch_name)
|
def can_push_branch?(project, branch_name)
|
||||||
return false unless project.repository.branch_names.include?(branch_name)
|
return false unless project.repository.branch_exists?(branch_name)
|
||||||
|
|
||||||
::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(branch_name)
|
::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(branch_name)
|
||||||
end
|
end
|
||||||
|
|
|
@ -17,7 +17,25 @@ module ButtonHelper
|
||||||
def clipboard_button(data = {})
|
def clipboard_button(data = {})
|
||||||
content_tag :button,
|
content_tag :button,
|
||||||
icon('clipboard'),
|
icon('clipboard'),
|
||||||
class: 'btn btn-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,
|
data: data,
|
||||||
type: :button
|
type: :button
|
||||||
end
|
end
|
||||||
|
|
|
@ -38,10 +38,10 @@ module CiStatusHelper
|
||||||
icon(icon_name + ' fw')
|
icon(icon_name + ' fw')
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_commit_status(commit, tooltip_placement: 'auto left')
|
def render_commit_status(commit, tooltip_placement: 'auto left', cssclass: '')
|
||||||
project = commit.project
|
project = commit.project
|
||||||
path = builds_namespace_project_commit_path(project.namespace, project, commit)
|
path = builds_namespace_project_commit_path(project.namespace, project, commit)
|
||||||
render_status_with_link('commit', commit.status, path, tooltip_placement)
|
render_status_with_link('commit', commit.status, path, tooltip_placement, cssclass: cssclass)
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_pipeline_status(pipeline, tooltip_placement: 'auto left')
|
def render_pipeline_status(pipeline, tooltip_placement: 'auto left')
|
||||||
|
@ -57,10 +57,10 @@ module CiStatusHelper
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def render_status_with_link(type, status, path, tooltip_placement)
|
def render_status_with_link(type, status, path, tooltip_placement, cssclass: '')
|
||||||
link_to ci_icon_for_status(status),
|
link_to ci_icon_for_status(status),
|
||||||
path,
|
path,
|
||||||
class: "ci-status-link ci-status-icon-#{status.dasherize}",
|
class: "ci-status-link ci-status-icon-#{status.dasherize} #{cssclass}",
|
||||||
title: "#{type.titleize}: #{ci_label_for_status(status)}",
|
title: "#{type.titleize}: #{ci_label_for_status(status)}",
|
||||||
data: { toggle: 'tooltip', placement: tooltip_placement }
|
data: { toggle: 'tooltip', placement: tooltip_placement }
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,6 +16,16 @@ module CommitsHelper
|
||||||
commit_person_link(commit, options.merge(source: :committer))
|
commit_person_link(commit, options.merge(source: :committer))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def commit_author_avatar(commit, options = {})
|
||||||
|
options = options.merge(source: :author)
|
||||||
|
user = commit.send(options[:source])
|
||||||
|
|
||||||
|
source_email = clean(commit.send "#{options[:source]}_email".to_sym)
|
||||||
|
person_email = user.try(:email) || source_email
|
||||||
|
|
||||||
|
image_tag(avatar_icon(person_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]} hidden-xs", width: options[:size], alt: "")
|
||||||
|
end
|
||||||
|
|
||||||
def image_diff_class(diff)
|
def image_diff_class(diff)
|
||||||
if diff.deleted_file
|
if diff.deleted_file
|
||||||
"deleted"
|
"deleted"
|
||||||
|
@ -102,24 +112,24 @@ module CommitsHelper
|
||||||
if current_controller?(:projects, :commits)
|
if current_controller?(:projects, :commits)
|
||||||
if @repo.blob_at(commit.id, @path)
|
if @repo.blob_at(commit.id, @path)
|
||||||
return link_to(
|
return link_to(
|
||||||
"Browse File »",
|
"Browse File",
|
||||||
namespace_project_blob_path(project.namespace, project,
|
namespace_project_blob_path(project.namespace, project,
|
||||||
tree_join(commit.id, @path)),
|
tree_join(commit.id, @path)),
|
||||||
class: "pull-right"
|
class: "btn btn-default"
|
||||||
)
|
)
|
||||||
elsif @path.present?
|
elsif @path.present?
|
||||||
return link_to(
|
return link_to(
|
||||||
"Browse Directory »",
|
"Browse Directory",
|
||||||
namespace_project_tree_path(project.namespace, project,
|
namespace_project_tree_path(project.namespace, project,
|
||||||
tree_join(commit.id, @path)),
|
tree_join(commit.id, @path)),
|
||||||
class: "pull-right"
|
class: "btn btn-default"
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
link_to(
|
link_to(
|
||||||
"Browse Files",
|
"Browse Files",
|
||||||
namespace_project_tree_path(project.namespace, project, commit),
|
namespace_project_tree_path(project.namespace, project, commit),
|
||||||
class: "pull-right"
|
class: "btn btn-default"
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -187,12 +197,10 @@ module CommitsHelper
|
||||||
source_email = clean(commit.send "#{options[:source]}_email".to_sym)
|
source_email = clean(commit.send "#{options[:source]}_email".to_sym)
|
||||||
|
|
||||||
person_name = user.try(:name) || source_name
|
person_name = user.try(:name) || source_name
|
||||||
person_email = user.try(:email) || source_email
|
|
||||||
|
|
||||||
text =
|
text =
|
||||||
if options[:avatar]
|
if options[:avatar]
|
||||||
avatar = image_tag(avatar_icon(person_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size], alt: "")
|
%Q{<span class="commit-#{options[:source]}-name">#{person_name}</span>}
|
||||||
%Q{#{avatar} <span class="commit-#{options[:source]}-name">#{person_name}</span>}
|
|
||||||
else
|
else
|
||||||
person_name
|
person_name
|
||||||
end
|
end
|
||||||
|
|
|
@ -135,6 +135,11 @@ module DiffHelper
|
||||||
toggle_whitespace_link(url, options)
|
toggle_whitespace_link(url, options)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def diff_compare_whitespace_link(project, from, to, options)
|
||||||
|
url = namespace_project_compare_path(project.namespace, project, from, to, params_with_whitespace)
|
||||||
|
toggle_whitespace_link(url, options)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def hide_whitespace?
|
def hide_whitespace?
|
||||||
|
|
|
@ -50,8 +50,6 @@ module GitlabMarkdownHelper
|
||||||
|
|
||||||
context[:project] ||= @project
|
context[:project] ||= @project
|
||||||
|
|
||||||
text = Banzai.pre_process(text, context)
|
|
||||||
|
|
||||||
html = Banzai.render(text, context)
|
html = Banzai.render(text, context)
|
||||||
|
|
||||||
context.merge!(
|
context.merge!(
|
||||||
|
|
|
@ -67,9 +67,9 @@ module IssuablesHelper
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def has_todo(issuable)
|
def issuable_todo(issuable)
|
||||||
unless current_user.nil?
|
if current_user
|
||||||
current_user.todos.find_by(target_id: issuable.id, state: :pending)
|
current_user.todos.find_by(target: issuable, state: :pending)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -6,10 +6,10 @@ module MembersHelper
|
||||||
"#{action}_#{member.type.underscore}".to_sym
|
"#{action}_#{member.type.underscore}".to_sym
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_see_member_roles?(source:, user: nil)
|
def default_show_roles(member)
|
||||||
return false unless user
|
can?(current_user, action_member_permission(:update, member), member) ||
|
||||||
|
can?(current_user, action_member_permission(:destroy, member), member) ||
|
||||||
user.is_admin? || source.members.exists?(user_id: user.id)
|
can?(current_user, action_member_permission(:admin, member), member.source)
|
||||||
end
|
end
|
||||||
|
|
||||||
def remove_member_message(member, user: nil)
|
def remove_member_message(member, user: nil)
|
||||||
|
|
|
@ -12,10 +12,10 @@ module NavHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
def page_sidebar_class
|
def page_sidebar_class
|
||||||
if nav_menu_collapsed?
|
if pinned_nav?
|
||||||
"page-sidebar-collapsed"
|
"page-sidebar-expanded page-sidebar-pinned"
|
||||||
else
|
else
|
||||||
"page-sidebar-expanded"
|
"page-sidebar-collapsed"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -36,7 +36,15 @@ module NavHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
def nav_header_class
|
def nav_header_class
|
||||||
class_name = " with-horizontal-nav" if defined?(nav) && nav
|
class_name = ''
|
||||||
|
class_name << " with-horizontal-nav" if defined?(nav) && nav
|
||||||
|
|
||||||
|
if pinned_nav?
|
||||||
|
class_name << " header-expanded header-pinned-nav"
|
||||||
|
else
|
||||||
|
class_name << " header-collapsed"
|
||||||
|
end
|
||||||
|
|
||||||
class_name
|
class_name
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -47,4 +55,8 @@ module NavHelper
|
||||||
def nav_control_class
|
def nav_control_class
|
||||||
"nav-control" if current_user
|
"nav-control" if current_user
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def pinned_nav?
|
||||||
|
cookies[:pin_nav] == 'true'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -34,7 +34,7 @@ module NotificationsHelper
|
||||||
def notification_description(level)
|
def notification_description(level)
|
||||||
case level.to_sym
|
case level.to_sym
|
||||||
when :participating
|
when :participating
|
||||||
'You will only receive notifications from related resources'
|
'You will only receive notifications for threads you have participated in'
|
||||||
when :mention
|
when :mention
|
||||||
'You will receive notifications only for comments in which you were @mentioned'
|
'You will receive notifications only for comments in which you were @mentioned'
|
||||||
when :watch
|
when :watch
|
||||||
|
@ -43,6 +43,8 @@ module NotificationsHelper
|
||||||
'You will not get any notifications via email'
|
'You will not get any notifications via email'
|
||||||
when :global
|
when :global
|
||||||
'Use your global notification setting'
|
'Use your global notification setting'
|
||||||
|
when :custom
|
||||||
|
'You will only receive notifications for the events you choose'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -62,22 +64,14 @@ module NotificationsHelper
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def notification_level_radio_buttons
|
# Identifier to trigger individually dropdowns and custom settings modals in the same view
|
||||||
html = ""
|
def notifications_menu_identifier(type, notification_setting)
|
||||||
|
"#{type}-#{notification_setting.user_id}-#{notification_setting.source_id}-#{notification_setting.source_type}"
|
||||||
|
end
|
||||||
|
|
||||||
NotificationSetting.levels.each_key do |level|
|
# Create hidden field to send notification setting source to controller
|
||||||
level = level.to_sym
|
def hidden_setting_source_input(notification_setting)
|
||||||
next if level == :global
|
return unless notification_setting.source_type
|
||||||
|
hidden_field_tag "#{notification_setting.source_type.downcase}[id]", notification_setting.source_id
|
||||||
html << content_tag(:div, class: "radio") do
|
|
||||||
content_tag(:label, { value: level }) do
|
|
||||||
radio_button_tag(:"global_notification_setting[level]", level, @global_notification_setting.level.to_sym == level) +
|
|
||||||
content_tag(:div, level.to_s.capitalize, class: "level-title") +
|
|
||||||
content_tag(:p, notification_description(level))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
html.html_safe
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -41,7 +41,7 @@ module ProjectsHelper
|
||||||
author_html = author_html.html_safe
|
author_html = author_html.html_safe
|
||||||
|
|
||||||
if opts[:name]
|
if opts[:name]
|
||||||
link_to(author_html, user_path(author), class: "author_link #{"#{opts[:mobile_classes]}" if opts[:mobile_classes]}").html_safe
|
link_to(author_html, user_path(author), class: "author_link #{"#{opts[:extra_class]}" if opts[:extra_class]} #{"#{opts[:mobile_classes]}" if opts[:mobile_classes]}").html_safe
|
||||||
else
|
else
|
||||||
title = opts[:title].sub(":name", sanitize(author.name))
|
title = opts[:title].sub(":name", sanitize(author.name))
|
||||||
link_to(author_html, user_path(author), class: "author_link has-tooltip", title: title, data: { container: 'body' } ).html_safe
|
link_to(author_html, user_path(author), class: "author_link has-tooltip", title: title, data: { container: 'body' } ).html_safe
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
module TodosHelper
|
module TodosHelper
|
||||||
def todos_pending_count
|
def todos_pending_count
|
||||||
current_user.todos.pending.count
|
TodosFinder.new(current_user, state: :pending).execute.count
|
||||||
end
|
end
|
||||||
|
|
||||||
def todos_done_count
|
def todos_done_count
|
||||||
current_user.todos.done.count
|
TodosFinder.new(current_user, state: :done).execute.count
|
||||||
end
|
end
|
||||||
|
|
||||||
def todo_action_name(todo)
|
def todo_action_name(todo)
|
||||||
|
@ -12,7 +12,7 @@ module TodosHelper
|
||||||
when Todo::ASSIGNED then 'assigned you'
|
when Todo::ASSIGNED then 'assigned you'
|
||||||
when Todo::MENTIONED then 'mentioned you on'
|
when Todo::MENTIONED then 'mentioned you on'
|
||||||
when Todo::BUILD_FAILED then 'The build failed for your'
|
when Todo::BUILD_FAILED then 'The build failed for your'
|
||||||
when Todo::MARKED then 'marked this as a Todo for'
|
when Todo::MARKED then 'added a todo for'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue