Merge branch 'master' into awardables

This commit is contained in:
Zeger-Jan van de Weg 2016-05-11 08:47:04 +02:00
commit 7e6dcf9cd0
810 changed files with 13061 additions and 6377 deletions

View File

@ -115,6 +115,11 @@ bundler:audit:
script: script:
- "bundle exec bundle-audit check --update --ignore OSVDB-115941" - "bundle exec bundle-audit check --update --ignore OSVDB-115941"
db-migrate-reset:
stage: test
script:
- RAILS_ENV=test bundle exec rake db:migrate:reset
# Ruby 2.2 jobs # Ruby 2.2 jobs
spec:feature:ruby22: spec:feature:ruby22:

View File

@ -728,7 +728,7 @@ Metrics/ParameterLists:
# A complexity metric geared towards measuring complexity for a human reader. # A complexity metric geared towards measuring complexity for a human reader.
Metrics/PerceivedComplexity: Metrics/PerceivedComplexity:
Enabled: true Enabled: true
Max: 17 Max: 18
#################### Lint ################################ #################### Lint ################################
@ -953,10 +953,9 @@ Performance/DoubleStartEndWith:
Performance/EndWith: Performance/EndWith:
Enabled: false Enabled: false
# TODO: Enable LstripRstrip Cop.
# Use `strip` instead of `lstrip.rstrip`. # Use `strip` instead of `lstrip.rstrip`.
Performance/LstripRstrip: Performance/LstripRstrip:
Enabled: false Enabled: true
# TODO: Enable RangeInclude Cop. # TODO: Enable RangeInclude Cop.
# Use `Range#cover?` instead of `Range#include?`. # Use `Range#cover?` instead of `Range#include?`.

View File

@ -65,7 +65,7 @@ linters:
# Reports when you have an empty rule set. # Reports when you have an empty rule set.
EmptyRule: EmptyRule:
enabled: false enabled: true
# Reports when you have an @extend directive. # Reports when you have an @extend directive.
ExtendDirective: ExtendDirective:
@ -244,11 +244,11 @@ linters:
# URLs should be valid and not contain protocols or domain names. # URLs should be valid and not contain protocols or domain names.
UrlFormat: UrlFormat:
enabled: false enabled: true
# URLs should always be enclosed within quotes. # URLs should always be enclosed within quotes.
UrlQuotes: UrlQuotes:
enabled: false enabled: true
# Properties, like color and font, are easier to read and maintain # Properties, like color and font, are easier to read and maintain
# when defined using variables rather than literals. # when defined using variables rather than literals.

181
CHANGELOG
View File

@ -1,26 +1,105 @@
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.7.0 (unreleased) v 8.8.0 (unreleased)
- Assign labels and milestone to target project when moving issue. !3934 (Long Nguyen)
- Use a case-insensitive comparison in sanitizing URI schemes
- Project#open_branches has been cleaned up and no longer loads entire records into memory.
- Escape HTML in commit titles in system note messages
- Improve multiple branch push performance by memoizing permission checking
- Log to application.log when an admin starts and stops impersonating a user
- Updated gitlab_git to 10.1.0
- GitAccess#protected_tag? no longer loads all tags just to check if a single one exists
- Reduce delay in destroying a project from 1-minute to immediately
- Make build status canceled if any of the jobs was canceled and none failed
- Upgrade Sidekiq to 4.1.2
- Sanitize repo paths in new project error message
- Bump mail_room to 0.7.0 to fix stuck IDLE connections
- Remove future dates from contribution calendar graph.
- Support e-mail notifications for comments on project snippets
- Use ActionDispatch Remote IP for Akismet checking
- Fix error when visiting commit builds page before build was updated
- Add 'l' shortcut to open Label dropdown on issuables and 'i' to create new issue on a project
- Update SVG sanitizer to conform to SVG 1.1
- Updated search UI
- Display informative message when new milestone is created
- Sanitize milestones and labels titles
- Support multi-line tag messages. !3833 (Calin Seciu)
- Allow "NEWS" and "CHANGES" as alternative names for CHANGELOG. !3768 (Connor Shea)
- Added button to toggle whitespaces changes on diff view
- Backport GitHub Enterprise import support from EE
- Create tags using Rugged for performance reasons. !3745
- API: Expose Issue#user_notes_count. !3126 (Anton Popov)
- Files over 5MB can only be viewed in their raw form, files over 1MB without highlighting !3718
- Add support for supressing text diffs using .gitattributes on the default branch (Matt Oakes)
- Added multiple colors for labels in dropdowns when dups happen.
- Improve description for the Two-factor Authentication sign-in screen. (Connor Shea)
- API support for the 'since' and 'until' operators on commit requests (Paco Guzman)
- Fix Gravatar hint in user profile when Gravatar is disabled. !3988 (Artem Sidorenko)
- Expire repository exists? and has_visible_content? caches after a push if necessary
- Fix unintentional filtering bug in issues sorted by milestone due (Takuya Noguchi)
- Fix adding a todo for private group members (Ahmad Sherif)
v 8.7.4
- Fix always showing build notification message when switching between merge requests
- Links for Redmine issue references are generated correctly again (Benedikt Huss)
v 8.7.3
- Emails, Gitlab::Email::Message, Gitlab::Diff, and Premailer::Adapter::Nokogiri are now instrumented
- Merge request widget displays TeamCity build state and code coverage correctly again.
- Fix the line code when importing PR review comments from GitHub. !4010
- Wikis are now initialized on legacy projects when checking repositories
v 8.7.2
- The "New Branch" button is now loaded asynchronously
- Fix error 500 when trying to create a wiki page
- Updated spacing between notification label and button
- Label titles in filters are now escaped properly
v 8.7.1
- Throttle the update of `project.last_activity_at` to 1 minute. !3848
- Fix .gitlab-ci.yml parsing issue when hidde job is a template without script definition. !3849
- Fix license detection to detect all license files, not only known licenses. !3878
- Use the `can?` helper instead of `current_user.can?`. !3882
- Prevent users from deleting Webhooks via API they do not own
- Fix Error 500 due to stale cache when projects are renamed or transferred
- Update width of search box to fix Safari bug. !3900 (Jedidiah)
- Use the `can?` helper instead of `current_user.can?`
v 8.7.0
- Gitlab::GitAccess and Gitlab::GitAccessWiki are now instrumented
- Fix vulnerability that made it possible to gain access to private labels and milestones
- The number of InfluxDB points stored per UDP packet can now be configured
- Fix error when cross-project label reference used with non-existent project
- Transactions for /internal/allowed now have an "action" tag set
- Method instrumentation now uses Module#prepend instead of aliasing methods
- Repository.clean_old_archives is now instrumented
- Add support for environment variables on a job level in CI configuration file
- SQL query counts are now tracked per transaction
- The Projects::HousekeepingService class has extra instrumentation - The Projects::HousekeepingService class has extra instrumentation
- All service classes (those residing in app/services) are now instrumented - All service classes (those residing in app/services) are now instrumented
- Developers can now add custom tags to transactions - Developers can now add custom tags to transactions
- Loading of an issue's referenced merge requests and related branches is now done asynchronously - Loading of an issue's referenced merge requests and related branches is now done asynchronously
- Enable gzip for assets, makes the page size significantly smaller. !3544 / !3632 (Connor Shea) - Enable gzip for assets, makes the page size significantly smaller. !3544 / !3632 (Connor Shea)
- Add support to cherry-pick any commit into any branch in the web interface (Minqi Pan)
- Project switcher uses new dropdown styling - Project switcher uses new dropdown styling
- Load award emoji images separately unless opening the full picker. Saves several hundred KBs of data for most pages. (Connor Shea) - Load award emoji images separately unless opening the full picker. Saves several hundred KBs of data for most pages. (Connor Shea)
- Do not include award_emojis in issue and merge_request comment_count !3610 (Lucas Charles) - Do not include award_emojis in issue and merge_request comment_count !3610 (Lucas Charles)
- Restrict user profiles when public visibility level is restricted.
- Add ability set due date to issues, sort and filter issues by due date (Mehmet Beydogan)
- All images in discussions and wikis now link to their source files !3464 (Connor Shea). - All images in discussions and wikis now link to their source files !3464 (Connor Shea).
- Return status code 303 after a branch DELETE operation to avoid project deletion (Stan Hu) - Return status code 303 after a branch DELETE operation to avoid project deletion (Stan Hu)
- Add setting for customizing the list of trusted proxies !3524 - Add setting for customizing the list of trusted proxies !3524
- Allow projects to be transfered to a lower visibility level group - Allow projects to be transfered to a lower visibility level group
- Fix `signed_in_ip` being set to 127.0.0.1 when using a reverse proxy !3524 - Fix `signed_in_ip` being set to 127.0.0.1 when using a reverse proxy !3524
- Improved Markdown rendering performance !3389 - Improved Markdown rendering performance !3389
- Make shared runners text in box configurable
- Don't attempt to look up an avatar in repo if repo directory does not exist (Stan Hu) - Don't attempt to look up an avatar in repo if repo directory does not exist (Stan Hu)
- API: Ability to subscribe and unsubscribe from issues and merge requests (Robert Schilling) - API: Ability to subscribe and unsubscribe from issues and merge requests (Robert Schilling)
- Expose project badges in project settings - Expose project badges in project settings
- Make /profile/keys/new redirect to /profile/keys for back-compat. !3717 - Make /profile/keys/new redirect to /profile/keys for back-compat. !3717
- Preserve time notes/comments have been updated at when moving issue - Preserve time notes/comments have been updated at when moving issue
- Make HTTP(s) label consistent on clone bar (Stan Hu) - Make HTTP(s) label consistent on clone bar (Stan Hu)
- Add support for `after_script`, requires Runner 1.2 (Kamil Trzciński)
- Expose label description in API (Mariusz Jachimowicz) - Expose label description in API (Mariusz Jachimowicz)
- API: Ability to update a group (Robert Schilling) - API: Ability to update a group (Robert Schilling)
- API: Ability to move issues (Robert Schilling) - API: Ability to move issues (Robert Schilling)
@ -28,6 +107,8 @@ v 8.7.0 (unreleased)
- Fix a bug whith trailing slash in teamcity_url (Charles May) - Fix a bug whith trailing slash in teamcity_url (Charles May)
- Allow back dating on issues when created or updated through the API - Allow back dating on issues when created or updated through the API
- Allow back dating on issue notes when created through the API - Allow back dating on issue notes when created through the API
- Propose license template when creating a new LICENSE file
- API: Expose /licenses and /licenses/:key
- Fix avatar stretching by providing a cropping feature - Fix avatar stretching by providing a cropping feature
- API: Expose `subscribed` for issues and merge requests (Robert Schilling) - API: Expose `subscribed` for issues and merge requests (Robert Schilling)
- Allow SAML to handle external users based on user's information !3530 - Allow SAML to handle external users based on user's information !3530
@ -35,8 +116,9 @@ v 8.7.0 (unreleased)
- Add endpoints to archive or unarchive a project !3372 - Add endpoints to archive or unarchive a project !3372
- Fix a bug whith trailing slash in bamboo_url - Fix a bug whith trailing slash in bamboo_url
- Add links to CI setup documentation from project settings and builds pages - Add links to CI setup documentation from project settings and builds pages
- Display project members page to all members
- Handle nil descriptions in Slack issue messages (Stan Hu) - Handle nil descriptions in Slack issue messages (Stan Hu)
- Add automated repository integrity checks - Add automated repository integrity checks (OFF by default)
- API: Expose open_issues_count, closed_issues_count, open_merge_requests_count for labels (Robert Schilling) - API: Expose open_issues_count, closed_issues_count, open_merge_requests_count for labels (Robert Schilling)
- API: Ability to star and unstar a project (Robert Schilling) - API: Ability to star and unstar a project (Robert Schilling)
- Add default scope to projects to exclude projects pending deletion - Add default scope to projects to exclude projects pending deletion
@ -45,6 +127,7 @@ v 8.7.0 (unreleased)
- Use rugged to change HEAD in Project#change_head (P.S.V.R) - Use rugged to change HEAD in Project#change_head (P.S.V.R)
- API: Ability to filter milestones by state `active` and `closed` (Robert Schilling) - API: Ability to filter milestones by state `active` and `closed` (Robert Schilling)
- API: Fix milestone filtering by `iid` (Robert Schilling) - API: Fix milestone filtering by `iid` (Robert Schilling)
- Make before_script and after_script overridable on per-job (Kamil Trzciński)
- API: Delete notes of issues, snippets, and merge requests (Robert Schilling) - API: Delete notes of issues, snippets, and merge requests (Robert Schilling)
- Implement 'Groups View' as an option for dashboard preferences !3379 (Elias W.) - Implement 'Groups View' as an option for dashboard preferences !3379 (Elias W.)
- Better errors handling when creating milestones inside groups - Better errors handling when creating milestones inside groups
@ -52,34 +135,77 @@ v 8.7.0 (unreleased)
- Hide `Create a group` help block when creating a new project in a group - Hide `Create a group` help block when creating a new project in a group
- Implement 'TODOs View' as an option for dashboard preferences !3379 (Elias W.) - Implement 'TODOs View' as an option for dashboard preferences !3379 (Elias W.)
- Allow issues and merge requests to be assigned to the author !2765 - Allow issues and merge requests to be assigned to the author !2765
- Make Ci::Commit to group only similar builds and make it stateful (ref, tag)
- Gracefully handle notes on deleted commits in merge requests (Stan Hu) - Gracefully handle notes on deleted commits in merge requests (Stan Hu)
- Decouple membership and notifications - Decouple membership and notifications
- Fix creation of merge requests for orphaned branches (Stan Hu) - Fix creation of merge requests for orphaned branches (Stan Hu)
- API: Ability to retrieve a single tag (Robert Schilling) - API: Ability to retrieve a single tag (Robert Schilling)
- While signing up, don't persist the user password across form redisplays
- Fall back to `In-Reply-To` and `References` headers when sub-addressing is not available (David Padilla) - Fall back to `In-Reply-To` and `References` headers when sub-addressing is not available (David Padilla)
- Remove "Congratulations!" tweet button on newly-created project. (Connor Shea) - Remove "Congratulations!" tweet button on newly-created project. (Connor Shea)
- Fix admin/projects when using visibility levels on search (PotHix) - Fix admin/projects when using visibility levels on search (PotHix)
- Build status notifications - Build status notifications
- Update email confirmation interface
- API: Expose user location (Robert Schilling) - API: Expose user location (Robert Schilling)
- API: Do not leak group existence via return code (Robert Schilling) - API: Do not leak group existence via return code (Robert Schilling)
- ClosingIssueExtractor regex now also works with colons. e.g. "Fixes: #1234" !3591 - ClosingIssueExtractor regex now also works with colons. e.g. "Fixes: #1234" !3591
- Update number of Todos in the sidebar when it's marked as "Done". !3600 - Update number of Todos in the sidebar when it's marked as "Done". !3600
- Sanitize branch names created for confidential issues
- API: Expose 'updated_at' for issue, snippet, and merge request notes (Robert Schilling) - API: Expose 'updated_at' for issue, snippet, and merge request notes (Robert Schilling)
- API: User can leave a project through the API when not master or owner. !3613 - API: User can leave a project through the API when not master or owner. !3613
- Fix repository cache invalidation issue when project is recreated with an empty repo (Stan Hu) - Fix repository cache invalidation issue when project is recreated with an empty repo (Stan Hu)
- Fix: Allow empty recipients list for builds emails service when pushed is added (Frank Groeneveld) - Fix: Allow empty recipients list for builds emails service when pushed is added (Frank Groeneveld)
- Improved markdown forms - Improved markdown forms
- Diff design updates (colors, button styles, etc)
- Copying and pasting a diff no longer pastes the line numbers or +/-
- Add null check to formData when updating profile content to fix Firefox bug
- Disable spellcheck and autocorrect for username field in admin page
- Delete tags using Rugged for performance reasons (Robert Schilling) - Delete tags using Rugged for performance reasons (Robert Schilling)
- Add Slack notifications when Wiki is edited (Sebastian Klier)
- Diffs load at the correct point when linking from from number - Diffs load at the correct point when linking from from number
- Selected diff rows highlight - Selected diff rows highlight
- Fix emoji categories in the emoji picker - Fix emoji categories in the emoji picker
- API: Properly display annotated tags for GET /projects/:id/repository/tags (Robert Schilling)
- Add encrypted credentials for imported projects and migrate old ones - Add encrypted credentials for imported projects and migrate old ones
- Properly format all merge request references with ! rather than # !3740 (Ben Bodenmiller)
- Author and participants are displayed first on users autocompletion - Author and participants are displayed first on users autocompletion
- Show number sign on external issue reference text (Florent Baldino)
- Updated print style for issues
- Use GitHub Issue/PR number as iid to keep references
- Import GitHub labels
- Add option to filter by "Owned projects" on dashboard page
- Import GitHub milestones
- Execute system web hooks on push to the project
- Allow enable/disable push events for system hooks
- Fix GitHub project's link in the import page when provider has a custom URL
- Add RAW build trace output and button on build page
- Add incremental build trace update into CI API
v 8.6.8
- Prevent privilege escalation via "impersonate" feature
- Prevent privilege escalation via notes API
- Prevent privilege escalation via project webhook API
- Prevent XSS via Git branch and tag names
- Prevent XSS via custom issue tracker URL
- Prevent XSS via `window.opener`
- Prevent XSS via label drop-down
- Prevent information disclosure via milestone API
- Prevent information disclosure via snippet API
- Prevent information disclosure via project labels
- Prevent information disclosure via new merge request page
v 8.6.7
- Fix persistent XSS vulnerability in `commit_person_link` helper
- Fix persistent XSS vulnerability in Label and Milestone dropdowns
- Fix vulnerability that made it possible to enumerate private projects belonging to group
v 8.6.6 v 8.6.6
- Expire the exists cache before deletion to ensure project dir actually exists (Stan Hu). !3413 - Expire the exists cache before deletion to ensure project dir actually exists (Stan Hu). !3413
- Fix error on language detection when repository has no HEAD (e.g., master branch) (Jeroen Bobbeldijk). !3654 - Fix error on language detection when repository has no HEAD (e.g., master branch) (Jeroen Bobbeldijk). !3654
- Fix revoking of authorized OAuth applications (Connor Shea). !3690 - Fix revoking of authorized OAuth applications (Connor Shea). !3690
- Fix error on language detection when repository has no HEAD (e.g., master branch). !3654 (Jeroen Bobbeldijk)
- Issuable header is consistent between issues and merge requests
- Improved spacing in issuable header on mobile
v 8.6.5 v 8.6.5
- Fix importing from GitHub Enterprise. !3529 - Fix importing from GitHub Enterprise. !3529
@ -209,6 +335,20 @@ 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.12
- Prevent privilege escalation via "impersonate" feature
- Prevent privilege escalation via notes API
- Prevent privilege escalation via project webhook API
- Prevent XSS via Git branch and tag names
- Prevent XSS via custom issue tracker URL
- Prevent XSS via `window.opener`
- Prevent information disclosure via snippet API
- Prevent information disclosure via project labels
- Prevent information disclosure via new merge request page
v 8.5.11
- Fix persistent XSS vulnerability in `commit_person_link` helper
v 8.5.10 v 8.5.10
- Fix a 2FA authentication spoofing vulnerability. - Fix a 2FA authentication spoofing vulnerability.
@ -356,6 +496,20 @@ 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.10
- Prevent privilege escalation via "impersonate" feature
- Prevent privilege escalation via notes API
- Prevent privilege escalation via project webhook API
- Prevent XSS via Git branch and tag names
- Prevent XSS via custom issue tracker URL
- Prevent XSS via `window.opener`
- Prevent information disclosure via snippet API
- Prevent information disclosure via project labels
- Prevent information disclosure via new merge request page
v 8.4.9
- Fix persistent XSS vulnerability in `commit_person_link` helper
v 8.4.8 v 8.4.8
- Fix a 2FA authentication spoofing vulnerability. - Fix a 2FA authentication spoofing vulnerability.
@ -478,6 +632,18 @@ 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.9
- Prevent privilege escalation via "impersonate" feature
- Prevent privilege escalation via notes API
- Prevent privilege escalation via project webhook API
- Prevent XSS via custom issue tracker URL
- Prevent XSS via `window.opener`
- Prevent information disclosure via project labels
- Prevent information disclosure via new merge request page
v 8.3.8
- Fix persistent XSS vulnerability in `commit_person_link` helper
v 8.3.7 v 8.3.7
- Fix a 2FA authentication spoofing vulnerability. - Fix a 2FA authentication spoofing vulnerability.
@ -584,6 +750,17 @@ 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.5
- Prevent privilege escalation via "impersonate" feature
- Prevent privilege escalation via notes API
- Prevent privilege escalation via project webhook API
- Prevent XSS via `window.opener`
- Prevent information disclosure via project labels
- Prevent information disclosure via new merge request page
v 8.2.4
- Bump Git version requirement to 2.7.4
v 8.2.3 v 8.2.3
- Fix application settings cache not expiring after changes (Stan Hu) - Fix application settings cache not expiring after changes (Stan Hu)
- Fix Error 500s when creating global milestones with Unicode characters (Stan Hu) - Fix Error 500s when creating global milestones with Unicode characters (Stan Hu)

View File

@ -38,7 +38,7 @@ source edition, and GitLab Enterprise Edition (EE) which is our commercial
edition. Throughout this guide you will see references to CE and EE for edition. Throughout this guide you will see references to CE and EE for
abbreviation. abbreviation.
If you have read this guide and want to know how the GitLab [core team][core-team] If you have read this guide and want to know how the GitLab [core team]
operates please see [the GitLab contributing process](PROCESS.md). operates please see [the GitLab contributing process](PROCESS.md).
## Contributor license agreement ## Contributor license agreement
@ -135,12 +135,23 @@ For feature proposals for EE, open an issue on the
In order to help track the feature proposals, we have created a In order to help track the feature proposals, we have created a
[`feature proposal`][fpl] label. For the time being, users that are not members [`feature proposal`][fpl] label. For the time being, users that are not members
of the project cannot add labels. You can instead ask one of the [core team][core-team] of the project cannot add labels. You can instead ask one of the [core team]
members to add the label `feature proposal` to the issue. members to add the label `feature proposal` to the issue or add the following
code snippet right after your description in a new line: `~"feature proposal"`.
Please keep feature proposals as small and simple as possible, complex ones Please keep feature proposals as small and simple as possible, complex ones
might be edited to make them small and simple. might be edited to make them small and simple.
You are encouraged to use the template below for feature proposals.
```
## Description including problem, use cases, benefits, and/or goals
## Proposal
## Links / references
```
For changes in the interface, it can be helpful to create a mockup first. For changes in the interface, it can be helpful to create a mockup first.
If you want to create something yourself, consider opening an issue first to If you want to create something yourself, consider opening an issue first to
discuss whether it is interesting to include this in GitLab. discuss whether it is interesting to include this in GitLab.
@ -323,6 +334,7 @@ request is as follows:
[shell command guidelines](doc/development/shell_commands.md) [shell command guidelines](doc/development/shell_commands.md)
1. If your code creates new files on disk please read the 1. If your code creates new files on disk please read the
[shared files guidelines](doc/development/shared_files.md). [shared files guidelines](doc/development/shared_files.md).
1. When writing commit messages please follow [these](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) [guidelines](http://chris.beams.io/posts/git-commit/).
The **official merge window** is in the beginning of the month from the 1st to The **official merge window** is in the beginning of the month from the 1st to
the 7th day of the month. This is the best time to submit an MR and get the 7th day of the month. This is the best time to submit an MR and get
@ -343,12 +355,11 @@ is it will be merged (quickly). After that you can send more MRs to enhance it.
For examples of feedback on merge requests please look at already For examples of feedback on merge requests please look at already
[closed merge requests][closed-merge-requests]. If you would like quick feedback [closed merge requests][closed-merge-requests]. If you would like quick feedback
on your merge request feel free to mention one of the Merge Marshalls in the on your merge request feel free to mention one of the Merge Marshalls in the
[core team][core-team] or one of the [core team] or one of the [Merge request coaches](https://about.gitlab.com/team/).
[Merge request coaches](https://about.gitlab.com/team/).
Please ensure that your merge request meets the contribution acceptance criteria. Please ensure that your merge request meets the contribution acceptance criteria.
When having your code reviewed and when reviewing merge requests please take the When having your code reviewed and when reviewing merge requests please take the
[Thoughtbot code review guide] into account. [code review guidelines](doc/development/code_review.md) into account.
### Merge request description format ### Merge request description format
@ -496,7 +507,7 @@ reported by emailing `contact@gitlab.com`.
This Code of Conduct is adapted from the [Contributor Covenant][contributor-covenant], version 1.1.0, This Code of Conduct is adapted from the [Contributor Covenant][contributor-covenant], version 1.1.0,
available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/). available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/).
[core-team]: https://about.gitlab.com/core-team/ [core team]: https://about.gitlab.com/core-team/
[getting-help]: https://about.gitlab.com/getting-help/ [getting-help]: https://about.gitlab.com/getting-help/
[codetriage]: http://www.codetriage.com/gitlabhq/gitlabhq [codetriage]: http://www.codetriage.com/gitlabhq/gitlabhq
[up-for-grabs]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=up-for-grabs [up-for-grabs]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=up-for-grabs
@ -522,4 +533,3 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor
[gitlab-design]: https://gitlab.com/gitlab-org/gitlab-design [gitlab-design]: https://gitlab.com/gitlab-org/gitlab-design
[free Antetype viewer (Mac OSX only)]: https://itunes.apple.com/us/app/antetype-viewer/id824152298?mt=12 [free Antetype viewer (Mac OSX only)]: https://itunes.apple.com/us/app/antetype-viewer/id824152298?mt=12
[`gitlab1.atype` file]: https://gitlab.com/gitlab-org/gitlab-design/tree/master/gitlab1.atype/ [`gitlab1.atype` file]: https://gitlab.com/gitlab-org/gitlab-design/tree/master/gitlab1.atype/
[Thoughtbot code review guide]: https://github.com/thoughtbot/guides/tree/master/code-review

18
Gemfile
View File

@ -19,8 +19,8 @@ gem "pg", '~> 0.18.2', group: :postgres
# Authentication libraries # Authentication libraries
gem 'devise', '~> 3.5.4' gem 'devise', '~> 3.5.4'
gem 'doorkeeper', '~> 3.1'
gem 'devise-async', '~> 0.9.0' gem 'devise-async', '~> 0.9.0'
gem 'doorkeeper', '~> 2.2.0'
gem 'omniauth', '~> 1.3.1' gem 'omniauth', '~> 1.3.1'
gem 'omniauth-auth0', '~> 1.4.1' gem 'omniauth-auth0', '~> 1.4.1'
gem 'omniauth-azure-oauth2', '~> 0.0.6' gem 'omniauth-azure-oauth2', '~> 0.0.6'
@ -178,7 +178,7 @@ gem 'ruby-fogbugz', '~> 0.2.1'
gem 'd3_rails', '~> 3.5.0' gem 'd3_rails', '~> 3.5.0'
#cal-heatmap #cal-heatmap
gem 'cal-heatmap-rails', '~> 3.5.0' gem 'cal-heatmap-rails', '~> 3.6.0'
# underscore-rails # underscore-rails
gem "underscore-rails", "~> 1.8.0" gem "underscore-rails", "~> 1.8.0"
@ -190,6 +190,9 @@ gem 'babosa', '~> 1.0.2'
# Sanitizes SVG input # Sanitizes SVG input
gem "loofah", "~> 2.0.3" gem "loofah", "~> 2.0.3"
# Working with license
gem 'licensee', '~> 8.0.0'
# Protect against bruteforcing # Protect against bruteforcing
gem "rack-attack", '~> 4.3.1' gem "rack-attack", '~> 4.3.1'
@ -214,7 +217,7 @@ gem 'font-awesome-rails', '~> 4.2'
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.0.0' gem 'jquery-rails', '~> 4.1.0'
gem 'jquery-scrollto-rails', '~> 1.4.3' gem 'jquery-scrollto-rails', '~> 1.4.3'
gem 'jquery-ui-rails', '~> 5.0.0' gem 'jquery-ui-rails', '~> 5.0.0'
gem 'raphael-rails', '~> 2.1.2' gem 'raphael-rails', '~> 2.1.2'
@ -239,8 +242,7 @@ group :development do
gem "foreman" gem "foreman"
gem 'brakeman', '~> 3.2.0', require: false gem 'brakeman', '~> 3.2.0', require: false
gem "annotate", "~> 2.7.0" gem 'letter_opener_web', '~> 1.3.0'
gem "letter_opener", '~> 1.1.2'
gem 'quiet_assets', '~> 1.0.2' gem 'quiet_assets', '~> 1.0.2'
gem 'rerun', '~> 0.11.0' gem 'rerun', '~> 0.11.0'
gem 'bullet', require: false gem 'bullet', require: false
@ -267,7 +269,7 @@ group :development, :test do
gem 'database_cleaner', '~> 1.4.0' gem 'database_cleaner', '~> 1.4.0'
gem 'factory_girl_rails', '~> 4.6.0' gem 'factory_girl_rails', '~> 4.6.0'
gem 'rspec-rails', '~> 3.3.0' gem 'rspec-rails', '~> 3.4.0'
gem 'rspec-retry' gem 'rspec-retry'
gem 'spinach-rails', '~> 0.2.1' gem 'spinach-rails', '~> 0.2.1'
gem 'spinach-rerun-reporter', '~> 0.0.2' gem 'spinach-rerun-reporter', '~> 0.0.2'
@ -315,9 +317,9 @@ end
gem "newrelic_rpm", '~> 3.14' gem "newrelic_rpm", '~> 3.14'
gem 'octokit', '~> 3.8.0' gem 'octokit', '~> 4.3.0'
gem "mail_room", "~> 0.6.1" gem "mail_room", "~> 0.7"
gem 'email_reply_parser', '~> 0.5.8' gem 'email_reply_parser', '~> 0.5.8'

View File

@ -51,9 +51,6 @@ GEM
activerecord (>= 3.0) activerecord (>= 3.0)
akismet (2.0.0) akismet (2.0.0)
allocations (1.0.4) allocations (1.0.4)
annotate (2.7.0)
activerecord (>= 3.2, < 6.0)
rake (~> 10.4)
arel (6.0.3) arel (6.0.3)
asana (0.4.0) asana (0.4.0)
faraday (~> 0.9) faraday (~> 0.9)
@ -103,7 +100,7 @@ GEM
bundler (~> 1.2) bundler (~> 1.2)
thor (~> 0.18) thor (~> 0.18)
byebug (8.2.1) byebug (8.2.1)
cal-heatmap-rails (3.5.1) cal-heatmap-rails (3.6.0)
capybara (2.6.2) capybara (2.6.2)
addressable addressable
mime-types (>= 1.16) mime-types (>= 1.16)
@ -134,7 +131,7 @@ GEM
execjs execjs
coffee-script-source (1.10.0) coffee-script-source (1.10.0)
colorize (0.7.7) colorize (0.7.7)
concurrent-ruby (1.0.0) concurrent-ruby (1.0.2)
connection_pool (2.2.0) connection_pool (2.2.0)
coveralls (0.8.13) coveralls (0.8.13)
json (~> 1.8) json (~> 1.8)
@ -175,7 +172,7 @@ GEM
diff-lcs (1.2.5) diff-lcs (1.2.5)
diffy (3.0.7) diffy (3.0.7)
docile (1.1.5) docile (1.1.5)
doorkeeper (2.2.2) doorkeeper (3.1.0)
railties (>= 3.2) railties (>= 3.2)
dropzonejs-rails (0.7.2) dropzonejs-rails (0.7.2)
rails (> 3.1) rails (> 3.1)
@ -186,7 +183,7 @@ GEM
encryptor (1.3.0) encryptor (1.3.0)
equalizer (0.0.11) equalizer (0.0.11)
erubis (2.7.0) erubis (2.7.0)
escape_utils (1.1.0) escape_utils (1.1.1)
eventmachine (1.0.8) eventmachine (1.0.8)
excon (0.45.4) excon (0.45.4)
execjs (2.6.0) execjs (2.6.0)
@ -336,7 +333,7 @@ GEM
json json
get_process_mem (0.2.0) get_process_mem (0.2.0)
gherkin-ruby (0.3.2) gherkin-ruby (0.3.2)
github-linguist (4.7.5) github-linguist (4.7.6)
charlock_holmes (~> 0.7.3) charlock_holmes (~> 0.7.3)
escape_utils (~> 1.1.0) escape_utils (~> 1.1.0)
mime-types (>= 1.19) mime-types (>= 1.19)
@ -346,14 +343,14 @@ GEM
flowdock (~> 0.7) flowdock (~> 0.7)
gitlab-grit (>= 2.4.1) gitlab-grit (>= 2.4.1)
multi_json multi_json
gitlab-grit (2.7.3) gitlab-grit (2.8.1)
charlock_holmes (~> 0.6) charlock_holmes (~> 0.6)
diff-lcs (~> 1.1) diff-lcs (~> 1.1)
mime-types (~> 1.15) mime-types (>= 1.16, < 3)
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.0.0) gitlab_git (10.1.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)
@ -431,8 +428,8 @@ GEM
json json
ipaddress (0.8.2) ipaddress (0.8.2)
jquery-atwho-rails (1.3.2) jquery-atwho-rails (1.3.2)
jquery-rails (4.0.5) jquery-rails (4.1.1)
rails-dom-testing (~> 1.0) rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0) railties (>= 4.2.0)
thor (>= 0.14, < 2.0) thor (>= 0.14, < 2.0)
jquery-scrollto-rails (1.4.3) jquery-scrollto-rails (1.4.3)
@ -450,8 +447,14 @@ GEM
kgio (2.10.0) kgio (2.10.0)
launchy (2.4.3) launchy (2.4.3)
addressable (~> 2.3) addressable (~> 2.3)
letter_opener (1.1.2) letter_opener (1.4.1)
launchy (~> 2.2) launchy (~> 2.2)
letter_opener_web (1.3.0)
actionmailer (>= 3.2)
letter_opener (~> 1.0)
railties (>= 3.2)
licensee (8.0.0)
rugged (>= 0.24b)
listen (3.0.5) listen (3.0.5)
rb-fsevent (>= 0.9.3) rb-fsevent (>= 0.9.3)
rb-inotify (>= 0.9) rb-inotify (>= 0.9)
@ -461,9 +464,9 @@ 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.6.1) mail_room (0.7.0)
method_source (0.8.2) method_source (0.8.2)
mime-types (1.25.1) mime-types (2.99.1)
mimemagic (0.3.0) mimemagic (0.3.0)
mini_portile2 (2.0.0) mini_portile2 (2.0.0)
minitest (5.7.0) minitest (5.7.0)
@ -485,8 +488,8 @@ GEM
multi_json (~> 1.3) multi_json (~> 1.3)
multi_xml (~> 0.5) multi_xml (~> 0.5)
rack (~> 1.2) rack (~> 1.2)
octokit (3.8.0) octokit (4.3.0)
sawyer (~> 0.6.0, >= 0.5.3) sawyer (~> 0.7.0, >= 0.5.3)
omniauth (1.3.1) omniauth (1.3.1)
hashie (>= 1.2, < 4) hashie (>= 1.2, < 4)
rack (>= 1.0, < 3) rack (>= 1.0, < 3)
@ -627,7 +630,7 @@ GEM
recaptcha (1.0.2) recaptcha (1.0.2)
json json
redcarpet (3.3.3) redcarpet (3.3.3)
redis (3.2.2) redis (3.3.0)
redis-actionpack (4.0.1) redis-actionpack (4.0.1)
actionpack (~> 4) actionpack (~> 4)
redis-rack (~> 1.5.0) redis-rack (~> 1.5.0)
@ -658,29 +661,29 @@ GEM
chunky_png chunky_png
rqrcode-rails3 (0.1.7) rqrcode-rails3 (0.1.7)
rqrcode (>= 0.4.2) rqrcode (>= 0.4.2)
rspec (3.3.0) rspec (3.4.0)
rspec-core (~> 3.3.0) rspec-core (~> 3.4.0)
rspec-expectations (~> 3.3.0) rspec-expectations (~> 3.4.0)
rspec-mocks (~> 3.3.0) rspec-mocks (~> 3.4.0)
rspec-core (3.3.2) rspec-core (3.4.4)
rspec-support (~> 3.3.0) rspec-support (~> 3.4.0)
rspec-expectations (3.3.1) rspec-expectations (3.4.0)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.3.0) rspec-support (~> 3.4.0)
rspec-mocks (3.3.2) rspec-mocks (3.4.1)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.3.0) rspec-support (~> 3.4.0)
rspec-rails (3.3.3) rspec-rails (3.4.2)
actionpack (>= 3.0, < 4.3) actionpack (>= 3.0, < 4.3)
activesupport (>= 3.0, < 4.3) activesupport (>= 3.0, < 4.3)
railties (>= 3.0, < 4.3) railties (>= 3.0, < 4.3)
rspec-core (~> 3.3.0) rspec-core (~> 3.4.0)
rspec-expectations (~> 3.3.0) rspec-expectations (~> 3.4.0)
rspec-mocks (~> 3.3.0) rspec-mocks (~> 3.4.0)
rspec-support (~> 3.3.0) rspec-support (~> 3.4.0)
rspec-retry (0.4.5) rspec-retry (0.4.5)
rspec-core rspec-core
rspec-support (3.3.0) rspec-support (3.4.1)
rubocop (0.38.0) rubocop (0.38.0)
parser (>= 2.3.0.6, < 3.0) parser (>= 2.3.0.6, < 3.0)
powerpack (~> 0.1) powerpack (~> 0.1)
@ -712,8 +715,8 @@ GEM
sprockets (>= 2.8, < 4.0) sprockets (>= 2.8, < 4.0)
sprockets-rails (>= 2.0, < 4.0) sprockets-rails (>= 2.0, < 4.0)
tilt (>= 1.1, < 3) tilt (>= 1.1, < 3)
sawyer (0.6.0) sawyer (0.7.0)
addressable (~> 2.3.5) addressable (>= 2.3.5, < 2.5)
faraday (~> 0.8, < 0.10) faraday (~> 0.8, < 0.10)
scss_lint (0.47.1) scss_lint (0.47.1)
rake (>= 0.9, < 11) rake (>= 0.9, < 11)
@ -734,10 +737,9 @@ GEM
rack rack
shoulda-matchers (2.8.0) shoulda-matchers (2.8.0)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
sidekiq (4.0.1) sidekiq (4.1.2)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
connection_pool (~> 2.2, >= 2.2.0) connection_pool (~> 2.2, >= 2.2.0)
json (~> 1.0)
redis (~> 3.2, >= 3.2.1) redis (~> 3.2, >= 3.2.1)
sidekiq-cron (0.4.0) sidekiq-cron (0.4.0)
redis-namespace (>= 1.5.2) redis-namespace (>= 1.5.2)
@ -888,7 +890,6 @@ DEPENDENCIES
after_commit_queue after_commit_queue
akismet (~> 2.0) akismet (~> 2.0)
allocations (~> 1.0) allocations (~> 1.0)
annotate (~> 2.7.0)
asana (~> 0.4.0) asana (~> 0.4.0)
asciidoctor (~> 1.5.2) asciidoctor (~> 1.5.2)
attr_encrypted (~> 1.3.4) attr_encrypted (~> 1.3.4)
@ -903,7 +904,7 @@ DEPENDENCIES
bullet bullet
bundler-audit bundler-audit
byebug byebug
cal-heatmap-rails (~> 3.5.0) cal-heatmap-rails (~> 3.6.0)
capybara (~> 2.6.2) capybara (~> 2.6.2)
capybara-screenshot (~> 1.0.0) capybara-screenshot (~> 1.0.0)
carrierwave (~> 0.10.0) carrierwave (~> 0.10.0)
@ -920,7 +921,7 @@ DEPENDENCIES
devise-async (~> 0.9.0) devise-async (~> 0.9.0)
devise-two-factor (~> 2.0.0) devise-two-factor (~> 2.0.0)
diffy (~> 3.0.3) diffy (~> 3.0.3)
doorkeeper (~> 2.2.0) doorkeeper (~> 3.1)
dropzonejs-rails (~> 0.7.1) dropzonejs-rails (~> 0.7.1)
email_reply_parser (~> 0.5.8) email_reply_parser (~> 0.5.8)
email_spec (~> 1.6.0) email_spec (~> 1.6.0)
@ -951,14 +952,15 @@ DEPENDENCIES
httparty (~> 0.13.3) httparty (~> 0.13.3)
influxdb (~> 0.2) influxdb (~> 0.2)
jquery-atwho-rails (~> 1.3.2) jquery-atwho-rails (~> 1.3.2)
jquery-rails (~> 4.0.0) jquery-rails (~> 4.1.0)
jquery-scrollto-rails (~> 1.4.3) jquery-scrollto-rails (~> 1.4.3)
jquery-turbolinks (~> 2.1.0) jquery-turbolinks (~> 2.1.0)
jquery-ui-rails (~> 5.0.0) jquery-ui-rails (~> 5.0.0)
kaminari (~> 0.16.3) kaminari (~> 0.16.3)
letter_opener (~> 1.1.2) letter_opener_web (~> 1.3.0)
licensee (~> 8.0.0)
loofah (~> 2.0.3) loofah (~> 2.0.3)
mail_room (~> 0.6.1) mail_room (~> 0.7)
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)
@ -968,7 +970,7 @@ DEPENDENCIES
newrelic_rpm (~> 3.14) newrelic_rpm (~> 3.14)
nokogiri (~> 1.6.7, >= 1.6.7.2) nokogiri (~> 1.6.7, >= 1.6.7.2)
oauth2 (~> 1.0.0) oauth2 (~> 1.0.0)
octokit (~> 3.8.0) octokit (~> 4.3.0)
omniauth (~> 1.3.1) omniauth (~> 1.3.1)
omniauth-auth0 (~> 1.4.1) omniauth-auth0 (~> 1.4.1)
omniauth-azure-oauth2 (~> 0.0.6) omniauth-azure-oauth2 (~> 0.0.6)
@ -1008,7 +1010,7 @@ DEPENDENCIES
responders (~> 2.0) responders (~> 2.0)
rouge (~> 1.10.1) rouge (~> 1.10.1)
rqrcode-rails3 (~> 0.1.7) rqrcode-rails3 (~> 0.1.7)
rspec-rails (~> 3.3.0) rspec-rails (~> 3.4.0)
rspec-retry rspec-retry
rubocop (~> 0.38.0) rubocop (~> 0.38.0)
ruby-fogbugz (~> 0.2.1) ruby-fogbugz (~> 0.2.1)
@ -1055,4 +1057,4 @@ DEPENDENCIES
wikicloth (= 0.8.1) wikicloth (= 0.8.1)
BUNDLED WITH BUNDLED WITH
1.11.2 1.12.3

View File

@ -59,7 +59,7 @@ core team members will mention this person.
Workflow labels are purposely not very detailed since that would be hard to keep Workflow labels are purposely not very detailed since that would be hard to keep
updated as you would need to re-evaluate them after every comment. We optionally updated as you would need to re-evaluate them after every comment. We optionally
use functional labels on demand when want to group related issues to get an use functional labels on demand when we want to group related issues to get an
overview (for example all issues related to RVM, to tackle them in one go) and overview (for example all issues related to RVM, to tackle them in one go) and
to add details to the issue. to add details to the issue.
@ -73,6 +73,7 @@ in support or comment for further detail. Do not use `feature request`.
- ~bug is an issue reporting undesirable or incorrect behavior. - ~bug is an issue reporting undesirable or incorrect behavior.
- ~customer is an issue reported by enterprise subscribers. This label should - ~customer is an issue reported by enterprise subscribers. This label should
be accompanied by *bug* or *feature proposal* labels. be accompanied by *bug* or *feature proposal* labels.
Example workflow: when a UX designer provided a design but it needs frontend work they remove the UX label and add the frontend label. Example workflow: when a UX designer provided a design but it needs frontend work they remove the UX label and add the frontend label.
## Functional labels ## Functional labels
@ -105,6 +106,25 @@ sensitive as to how you word things. Use Emoji to express your feelings (heart,
star, smile, etc.). Some good tips about giving feedback to merge requests is in star, smile, etc.). Some good tips about giving feedback to merge requests is in
the [Thoughtbot code review guide]. the [Thoughtbot code review guide].
## Feature Freeze
5 working days before the 22nd the stable branches for the upcoming release will
be frozen for major changes. Merge requests may still be merged into master
during this period. By freezing the stable branches prior to a release there's
no need to worry about last minute merge requests potentially breaking a lot of
things.
What is considered to be a major change is determined on a case by case basis as
this definition depends very much on the context of changes. For example, a 5
line change might have a big impact on the entire application. Ultimately the
decision will be made by those reviewing a merge request and the release
manager.
During the feature freeze all merge requests that are meant to go into the next
release should have the correct milestone assigned _and_ have the label
~"Pick into Stable" set. Merge requests without a milestone and this label will
not be merged into any stable branches.
## Copy & paste responses ## Copy & paste responses
### Improperly formatted issue ### Improperly formatted issue

View File

@ -1,6 +1,6 @@
# GitLab # GitLab
[![build status](https://ci.gitlab.com/projects/1/status.svg?ref=master)](https://ci.gitlab.com/projects/1?ref=master) [![build status](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/build.svg)](https://gitlab.com/gitlab-org/gitlab-ce/commits/master)
[![Build Status](https://semaphoreci.com/api/v1/projects/2f1a5809-418b-4cc2-a1f4-819607579fe7/400484/shields_badge.svg)](https://semaphoreci.com/gitlabhq/gitlabhq) [![Build Status](https://semaphoreci.com/api/v1/projects/2f1a5809-418b-4cc2-a1f4-819607579fe7/400484/shields_badge.svg)](https://semaphoreci.com/gitlabhq/gitlabhq)
[![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.svg)](https://codeclimate.com/github/gitlabhq/gitlabhq) [![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.svg)](https://codeclimate.com/github/gitlabhq/gitlabhq)
[![Coverage Status](https://coveralls.io/repos/gitlabhq/gitlabhq/badge.svg?branch=master)](https://coveralls.io/r/gitlabhq/gitlabhq?branch=master) [![Coverage Status](https://coveralls.io/repos/gitlabhq/gitlabhq/badge.svg?branch=master)](https://coveralls.io/r/gitlabhq/gitlabhq?branch=master)
@ -20,6 +20,10 @@ To see how GitLab looks please see the [features page on our website](https://ab
- Completely free and open source (MIT Expat license) - Completely free and open source (MIT Expat license)
- Powered by [Ruby on Rails](https://github.com/rails/rails) - Powered by [Ruby on Rails](https://github.com/rails/rails)
## Hiring
We're hiring developers, support people, and production engineers all the time, please see our [jobs page](https://about.gitlab.com/jobs/).
## Editions ## Editions
There are two editions of GitLab: There are two editions of GitLab:
@ -31,11 +35,11 @@ There are two editions of GitLab:
On [about.gitlab.com](https://about.gitlab.com/) you can find more information about: On [about.gitlab.com](https://about.gitlab.com/) you can find more information about:
- [Subscriptions](https://about.gitlab.com/subscription/) - [Subscriptions](https://about.gitlab.com/pricing/)
- [Consultancy](https://about.gitlab.com/consultancy/) - [Consultancy](https://about.gitlab.com/consultancy/)
- [Community](https://about.gitlab.com/community/) - [Community](https://about.gitlab.com/community/)
- [Hosted GitLab.com](https://about.gitlab.com/gitlab-com/) use GitLab as a free service - [Hosted GitLab.com](https://about.gitlab.com/gitlab-com/) use GitLab as a free service
- [GitLab Enterprise Edition](https://about.gitlab.com/gitlab-ee/) with additional features aimed at larger organizations. - [GitLab Enterprise Edition](https://about.gitlab.com/features/#enterprise) with additional features aimed at larger organizations.
- [GitLab CI](https://about.gitlab.com/gitlab-ci/) a continuous integration (CI) server that is easy to integrate with GitLab. - [GitLab CI](https://about.gitlab.com/gitlab-ci/) a continuous integration (CI) server that is easy to integrate with GitLab.
## Requirements ## Requirements
@ -80,7 +84,7 @@ There are a lot of [third-party applications integrating with GitLab](https://ab
## GitLab release cycle ## GitLab release cycle
For more information about the release process see the [release documentation](http://doc.gitlab.com/ce/release/). For more information about the release process see the [release documentation](https://gitlab.com/gitlab-org/release-tools/blob/master/README.md).
## Upgrading ## Upgrading

View File

@ -1 +1 @@
8.7.0-pre 8.8.0-pre

View File

@ -5,6 +5,7 @@
group_projects_path: "/api/:version/groups/:id/projects.json" group_projects_path: "/api/:version/groups/:id/projects.json"
projects_path: "/api/:version/projects.json" projects_path: "/api/:version/projects.json"
labels_path: "/api/:version/projects/:id/labels" labels_path: "/api/:version/projects/:id/labels"
license_path: "/api/:version/licenses/:key"
group: (group_id, callback) -> group: (group_id, callback) ->
url = Api.buildUrl(Api.group_path) url = Api.buildUrl(Api.group_path)
@ -92,6 +93,16 @@
).done (projects) -> ).done (projects) ->
callback(projects) callback(projects)
# Return text for a specific license
licenseText: (key, data, callback) ->
url = Api.buildUrl(Api.license_path).replace(':key', key)
$.ajax(
url: url
data: data
).done (license) ->
callback(license)
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)

View File

@ -174,7 +174,7 @@ $ ->
$('.trigger-submit').on 'change', -> $('.trigger-submit').on 'change', ->
$(@).parents('form').submit() $(@).parents('form').submit()
gl.utils.localTimeAgo($('abbr.timeago, .js-timeago'), false) gl.utils.localTimeAgo($('abbr.timeago, .js-timeago'), true)
# Flash # Flash
if (flash = $(".flash-container")).length > 0 if (flash = $(".flash-container")).length > 0
@ -204,6 +204,7 @@ $ ->
$('.header-content .title').toggle() $('.header-content .title').toggle()
$('.header-content .navbar-collapse').toggle() $('.header-content .navbar-collapse').toggle()
$('.navbar-toggle').toggleClass('active') $('.navbar-toggle').toggleClass('active')
$('.navbar-toggle i').toggleClass("fa-angle-right fa-angle-left")
# Show/hide comments on diff # Show/hide comments on diff
$("body").on "click", ".js-toggle-diff-comments", (e) -> $("body").on "click", ".js-toggle-diff-comments", (e) ->

View File

@ -105,7 +105,7 @@ class @AwardsHandler
@postEmoji awardUrl, emoji, => @postEmoji awardUrl, emoji, =>
@addAwardToEmojiBar(emoji) @addAwardToEmojiBar(emoji)
$(".emoji-menu").removeClass "is-visible" $('.emoji-menu').removeClass 'is-visible'
addAwardToEmojiBar: (emoji) -> addAwardToEmojiBar: (emoji) ->
@addEmojiToFrequentlyUsedList(emoji) @addEmojiToFrequentlyUsedList(emoji)
@ -168,7 +168,7 @@ class @AwardsHandler
@resetTooltip(award_block) @resetTooltip(award_block)
resetTooltip: (award) -> resetTooltip: (award) ->
award.tooltip("destroy") award.tooltip('destroy')
# "destroy" call is asynchronous and there is no appropriate callback on it, this is why we need to set timeout. # "destroy" call is asynchronous and there is no appropriate callback on it, this is why we need to set timeout.
setTimeout (-> setTimeout (->
@ -194,13 +194,13 @@ class @AwardsHandler
$currentBlock.removeClass 'hidden' $currentBlock.removeClass 'hidden'
resolveNameToCssClass: (emoji) -> resolveNameToCssClass: (emoji) ->
emoji_icon = $(".emoji-menu-content [data-emoji='#{emoji}']") emojiIcon = $(".emoji-menu-content [data-emoji='#{emoji}']")
if emoji_icon.length > 0 if emojiIcon.length > 0
unicodeName = emoji_icon.data("unicode-name") unicodeName = emojiIcon.data('unicode-name')
else else
# Find by alias # Find by alias
unicodeName = $(".emoji-menu-content [data-aliases*=':#{emoji}:']").data("unicode-name") unicodeName = $(".emoji-menu-content [data-aliases*=':#{emoji}:']").data('unicode-name')
"emoji-#{unicodeName}" "emoji-#{unicodeName}"
@ -217,45 +217,42 @@ class @AwardsHandler
scrollTop: $('.awards').offset().top - 80 scrollTop: $('.awards').offset().top - 80
}, 200) }, 200)
normilizeEmojiName: (emoji) ->
@aliases[emoji] || emoji
addEmojiToFrequentlyUsedList: (emoji) -> addEmojiToFrequentlyUsedList: (emoji) ->
frequently_used_emojis = @getFrequentlyUsedEmojis() frequentlyUsedEmojis = @getFrequentlyUsedEmojis()
frequently_used_emojis.push(emoji) frequentlyUsedEmojis.push(emoji)
$.cookie('frequently_used_emojis', frequently_used_emojis.join(","), { expires: 365 }) $.cookie('frequently_used_emojis', frequentlyUsedEmojis.join(','), { expires: 365 })
getFrequentlyUsedEmojis: -> getFrequentlyUsedEmojis: ->
frequently_used_emojis = ($.cookie('frequently_used_emojis') || "").split(",") frequentlyUsedEmojis = ($.cookie('frequently_used_emojis') || '').split(',')
_.compact(_.uniq(frequently_used_emojis)) _.compact(_.uniq(frequentlyUsedEmojis))
renderFrequentlyUsedBlock: -> renderFrequentlyUsedBlock: ->
if $.cookie('frequently_used_emojis') if $.cookie('frequently_used_emojis')
frequently_used_emojis = @getFrequentlyUsedEmojis() frequentlyUsedEmojis = @getFrequentlyUsedEmojis()
ul = $("<ul class='clearfix emoji-menu-list'>") ul = $("<ul class='clearfix emoji-menu-list'>")
for emoji in frequently_used_emojis for emoji in frequently_used_emojis
$(".emoji-menu-content [data-emoji='#{emoji}']").closest("li").clone().appendTo(ul) $(".emoji-menu-content [data-emoji='#{emoji}']").closest("li").clone().appendTo(ul)
$("input.emoji-search").after(ul).after($("<h5>").text("Frequently used")) $('input.emoji-search').after(ul).after($('<h5>').text('Frequently used'))
setupSearch: -> setupSearch: ->
$("input.emoji-search").on 'keyup', (ev) => $('input.emoji-search').keyup (ev) =>
term = $(ev.target).val() term = $(ev.target).val()
# Clean previous search results # Clean previous search results
$("ul.emoji-menu-search, h5.emoji-search").remove() $('ul.emoji-menu-search, h5.emoji-search').remove()
if term if term
# Generate a search result block # Generate a search result block
h5 = $("<h5>").text("Search results").addClass("emoji-search") h5 = $('<h5>').text('Search results').addClass('emoji-search')
found_emojis = @searchEmojis(term).show() foundEmojis = @searchEmojis(term).show()
ul = $("<ul>").addClass("emoji-menu-list emoji-menu-search").append(found_emojis) ul = $('<ul>').addClass('emoji-menu-list emoji-menu-search').append(foundEmojis)
$(".emoji-menu-content ul, .emoji-menu-content h5").hide() $('.emoji-menu-content ul, .emoji-menu-content h5').hide()
$(".emoji-menu-content").append(h5).append(ul) $('.emoji-menu-content').append(h5).append(ul)
else else
$(".emoji-menu-content").children().show() $('.emoji-menu-content').children().show()
searchEmojis: (term)-> searchEmojis: (term)->
$(".emoji-menu-content [data-emoji*='#{term}']").closest("li").clone() $(".emoji-menu-content [data-emoji*='#{term}']").closest("li").clone()

View File

@ -0,0 +1,30 @@
class @BlobLicenseSelector
licenseRegex: /^(.+\/)?(licen[sc]e|copying)($|\.)/i
constructor: (editor) ->
@$licenseSelector = $('.js-license-selector')
$fileNameInput = $('#file_name')
initialFileNameValue = if $fileNameInput.length
$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()

View File

@ -1,44 +1,39 @@
class @EditBlob class @EditBlob
constructor: (assets_path, mode)-> constructor: (assets_path, ace_mode = null) ->
ace.config.set "modePath", assets_path + '/ace' ace.config.set "modePath", "#{assets_path}/ace"
ace.config.loadModule "ace/ext/searchbox" ace.config.loadModule "ace/ext/searchbox"
if mode @editor = ace.edit("editor")
ace_mode = mode @editor.focus()
editor = ace.edit("editor") @editor.getSession().setMode "ace/mode/#{ace_mode}" if ace_mode
editor.focus()
@editor = editor
if ace_mode
editor.getSession().setMode "ace/mode/" + ace_mode
# Before a form submission, move the content from the Ace editor into the # Before a form submission, move the content from the Ace editor into the
# submitted textarea # submitted textarea
$('form').submit -> $('form').submit =>
$("#file-content").val(editor.getValue()) $("#file-content").val(@editor.getValue())
editModePanes = $(".js-edit-mode-pane") @initModePanesAndLinks()
editModeLinks = $(".js-edit-mode a") new BlobLicenseSelector(@editor)
editModeLinks.click (event) ->
event.preventDefault()
currentLink = $(this)
paneId = currentLink.attr("href")
currentPane = editModePanes.filter(paneId)
editModeLinks.parent().removeClass "active hover"
currentLink.parent().addClass "active hover"
editModePanes.hide()
if paneId is "#preview"
currentPane.fadeIn 200
$.post currentLink.data("preview-url"),
content: editor.getValue()
, (response) ->
currentPane.empty().append response
currentPane.syntaxHighlight()
return
else initModePanesAndLinks: ->
currentPane.fadeIn 200 @$editModePanes = $(".js-edit-mode-pane")
editor.focus() @$editModeLinks = $(".js-edit-mode a")
return @$editModeLinks.click @editModeLinkClickHandler
editor: -> editModeLinkClickHandler: (event) =>
return @editor event.preventDefault()
currentLink = $(event.target)
paneId = currentLink.attr("href")
currentPane = @$editModePanes.filter(paneId)
@$editModeLinks.parent().removeClass "active hover"
currentLink.parent().addClass "active hover"
@$editModePanes.hide()
currentPane.fadeIn 200
if paneId is "#preview"
$.post currentLink.data("preview-url"),
content: @editor.getValue()
, (response) ->
currentPane.empty().append response
currentPane.syntaxHighlight()
else
@editor.focus()

View File

@ -1,20 +0,0 @@
class @NewBlob
constructor: (assets_path, mode)->
ace.config.set "modePath", assets_path + '/ace'
ace.config.loadModule "ace/ext/searchbox"
if mode
ace_mode = mode
editor = ace.edit("editor")
editor.focus()
@editor = editor
if ace_mode
editor.getSession().setMode "ace/mode/" + ace_mode
# Before a form submission, move the content from the Ace editor into the
# submitted textarea
$('form').submit ->
$("#file-content").val(editor.getValue())
editor: ->
return @editor

View File

@ -1,7 +1,7 @@
class @CommitsList class @CommitsList
@timer = null @timer = null
@init: (ref, limit) -> @init: (limit) ->
$("body").on "click", ".day-commits-table li.commit", (event) -> $("body").on "click", ".day-commits-table li.commit", (event) ->
if event.target.nodeName != "A" if event.target.nodeName != "A"
location.href = $(this).attr("url") location.href = $(this).attr("url")

View File

@ -17,6 +17,7 @@ class Dispatcher
switch page switch page
when 'projects:issues:index' when 'projects:issues:index'
Issues.init() Issues.init()
Issuable.init()
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
when 'projects:issues:show' when 'projects:issues:show'
new Issue() new Issue()
@ -59,7 +60,7 @@ class Dispatcher
new ZenMode() new ZenMode()
when 'projects:merge_requests:index' when 'projects:merge_requests:index'
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
MergeRequests.init() Issuable.init()
when 'dashboard:activity' when 'dashboard:activity'
new Activities() new Activities()
when 'dashboard:projects:starred' when 'dashboard:projects:starred'
@ -109,6 +110,8 @@ class Dispatcher
new BuildArtifacts() new BuildArtifacts()
when 'projects:group_links:index' when 'projects:group_links:index'
new GroupsSelect() new GroupsSelect()
when 'search:show'
new Search()
switch path.first() switch path.first()
when 'admin' when 'admin'

View File

@ -61,6 +61,7 @@ class @DropzoneInput
return return
drop: -> drop: ->
$mdArea.removeClass 'is-dropzone-hover'
form.find(".div-dropzone-hover").css "opacity", 0 form.find(".div-dropzone-hover").css "opacity", 0
form_textarea.focus() form_textarea.focus()
return return

View File

@ -0,0 +1,64 @@
class @DueDateSelect
constructor: ->
$loading = $('.js-issuable-update .due_date')
.find('.block-loading')
.hide()
$('.js-due-date-select').each (i, dropdown) ->
$dropdown = $(dropdown)
$dropdownParent = $dropdown.closest('.dropdown')
$datePicker = $dropdownParent.find('.js-due-date-calendar')
$block = $dropdown.closest('.block')
$selectbox = $dropdown.closest('.selectbox')
$value = $block.find('.value')
$sidebarValue = $('.js-due-date-sidebar-value', $block)
fieldName = $dropdown.data('field-name')
abilityName = $dropdown.data('ability-name')
issueUpdateURL = $dropdown.data('issue-update')
$dropdown.glDropdown(
hidden: ->
$selectbox.hide()
$value.removeAttr('style')
)
addDueDate = ->
# Create the post date
value = $("input[name='#{fieldName}']").val()
date = new Date value.replace(new RegExp('-', 'g'), ',')
mediumDate = $.datepicker.formatDate 'M d, yy', date
data = {}
data[abilityName] = {}
data[abilityName].due_date = value
$.ajax(
type: 'PUT'
url: issueUpdateURL
data: data
beforeSend: ->
$loading.fadeIn()
$dropdown.trigger('loading.gl.dropdown')
$selectbox.hide()
$value.removeAttr('style')
$value.html(mediumDate)
$sidebarValue.html(mediumDate)
).done (data) ->
$dropdown.trigger('loaded.gl.dropdown')
$dropdown.dropdown('toggle')
$loading.fadeOut()
$datePicker.datepicker(
dateFormat: 'yy-mm-dd',
defaultDate: $("input[name='#{fieldName}']").val()
altField: "input[name='#{fieldName}']"
onSelect: ->
addDueDate()
)
$(document)
.off 'click', '.ui-datepicker-header a'
.on 'click', '.ui-datepicker-header a', (e) ->
e.stopImmediatePropagation()

View File

@ -32,10 +32,8 @@ class GitLabDropdownFilter
else if @input.val() is "" and $inputContainer.hasClass HAS_VALUE_CLASS else if @input.val() is "" and $inputContainer.hasClass HAS_VALUE_CLASS
$inputContainer.removeClass HAS_VALUE_CLASS $inputContainer.removeClass HAS_VALUE_CLASS
if keyCode is 13 and @input.val() isnt "" if keyCode is 13
if @options.enterCallback return false
@options.enterCallback()
return
clearTimeout timeout clearTimeout timeout
timeout = setTimeout => timeout = setTimeout =>
@ -132,7 +130,6 @@ class GitLabDropdown
@filterInput = @getElement(FILTER_INPUT) @filterInput = @getElement(FILTER_INPUT)
@highlight = false @highlight = false
@filterInputBlur = true @filterInputBlur = true
@enterCallback = true
} = @options } = @options
self = @ self = @
@ -157,6 +154,9 @@ class GitLabDropdown
@fullData = data @fullData = data
@parseData @fullData @parseData @fullData
if @options.filterable
@filterInput.trigger 'keyup'
} }
# Init filterable # Init filterable
@ -178,15 +178,15 @@ class GitLabDropdown
callback: (data) => callback: (data) =>
currentIndex = -1 currentIndex = -1
@parseData data @parseData data
enterCallback: =>
if @enterCallback
@selectRowAtIndex 0
# Event listeners # Event listeners
@dropdown.on "shown.bs.dropdown", @opened @dropdown.on "shown.bs.dropdown", @opened
@dropdown.on "hidden.bs.dropdown", @hidden @dropdown.on "hidden.bs.dropdown", @hidden
@dropdown.on "click", ".dropdown-menu, .dropdown-menu-close", @shouldPropagate @dropdown.on "click", ".dropdown-menu, .dropdown-menu-close", @shouldPropagate
@dropdown.on 'keyup', (e) =>
if e.which is 27 # Escape key
$('.dropdown-menu-close', @dropdown).trigger 'click'
if @dropdown.find(".dropdown-toggle-page").length if @dropdown.find(".dropdown-toggle-page").length
@dropdown.find(".dropdown-toggle-page, .dropdown-menu-back").on "click", (e) => @dropdown.find(".dropdown-toggle-page, .dropdown-menu-back").on "click", (e) =>
@ -224,6 +224,9 @@ class GitLabDropdown
menu.toggleClass PAGE_TWO_CLASS menu.toggleClass PAGE_TWO_CLASS
# Focus first visible input on active page
@dropdown.find('[class^="dropdown-page-"]:visible :text:visible:first').focus()
parseData: (data) -> parseData: (data) ->
@renderedData = data @renderedData = data
@ -243,7 +246,8 @@ class GitLabDropdown
shouldPropagate: (e) => shouldPropagate: (e) =>
if @options.multiSelect if @options.multiSelect
$target = $(e.target) $target = $(e.target)
if not $target.hasClass('dropdown-menu-close') and not $target.hasClass('dropdown-menu-close-icon')
if not $target.hasClass('dropdown-menu-close') and not $target.hasClass('dropdown-menu-close-icon') and not $target.data('is-link')
e.stopPropagation() e.stopPropagation()
return false return false
else else
@ -378,7 +382,6 @@ class GitLabDropdown
selectedObject = @renderedData[selectedIndex] selectedObject = @renderedData[selectedIndex]
value = if @options.id then @options.id(selectedObject, el) else selectedObject.id value = if @options.id then @options.id(selectedObject, el) else selectedObject.id
field = @dropdown.parent().find("input[name='#{fieldName}'][value='#{value}']") field = @dropdown.parent().find("input[name='#{fieldName}'][value='#{value}']")
if el.hasClass(ACTIVE_CLASS) if el.hasClass(ACTIVE_CLASS)
el.removeClass(ACTIVE_CLASS) el.removeClass(ACTIVE_CLASS)
field.remove() field.remove()
@ -389,13 +392,13 @@ class GitLabDropdown
else else
selectedObject selectedObject
else else
if !value? if not @options.multiSelect or el.hasClass('dropdown-clear-active')
field.remove()
if not @options.multiSelect
@dropdown.find(".#{ACTIVE_CLASS}").removeClass ACTIVE_CLASS @dropdown.find(".#{ACTIVE_CLASS}").removeClass ACTIVE_CLASS
@dropdown.parent().find("input[name='#{fieldName}']").remove() @dropdown.parent().find("input[name='#{fieldName}']").remove()
if !value?
field.remove()
# Toggle active class for the tick mark # Toggle active class for the tick mark
el.addClass ACTIVE_CLASS el.addClass ACTIVE_CLASS

View File

@ -4,18 +4,33 @@ class @ImporterStatus
this.setAutoUpdate() this.setAutoUpdate()
initStatusPage: -> initStatusPage: ->
$(".js-add-to-import").click (event) => $('.js-add-to-import')
new_namespace = null .off 'click'
tr = $(event.currentTarget).closest("tr") .on 'click', (e) =>
id = tr.attr("id").replace("repo_", "") new_namespace = null
if tr.find(".import-target input").length > 0 $btn = $(e.currentTarget)
new_namespace = tr.find(".import-target input").prop("value") $tr = $btn.closest('tr')
tr.find(".import-target").empty().append(new_namespace + "/" + tr.find(".import-target").data("project_name")) id = $tr.attr('id').replace('repo_', '')
$.post @import_url, {repo_id: id, new_namespace: new_namespace}, dataType: 'script' if $tr.find('.import-target input').length > 0
new_namespace = $tr.find('.import-target input').prop('value')
$tr.find('.import-target').empty().append("#{new_namespace} / #{$tr.find('.import-target').data('project_name')}")
$(".js-import-all").click (event) => $btn
$(".js-add-to-import").each -> .disable()
$(this).click() .addClass 'is-loading'
$.post @import_url, {repo_id: id, new_namespace: new_namespace}, dataType: 'script'
$('.js-import-all')
.off 'click'
.on 'click', (e) ->
$btn = $(@)
$btn
.disable()
.addClass 'is-loading'
$('.js-add-to-import').each ->
$(this).trigger('click')
setAutoUpdate: -> setAutoUpdate: ->
setInterval (=> setInterval (=>

View File

@ -0,0 +1,84 @@
@Issuable =
init: ->
Issuable.initTemplates()
Issuable.initSearch()
initTemplates: ->
Issuable.labelRow = _.template(
'<% _.each(labels, function(label){ %>
<span class="label-row">
<a href="#"><span class="label color-label has-tooltip" style="background-color: <%= label.color %>; color: <%= label.text_color %>" title="<%= _.escape(label.description) %>" data-container="body"><%= _.escape(label.title) %></span></a>
</span>
<% }); %>'
)
initSearch: ->
@timer = null
$('#issue_search')
.off 'keyup'
.on 'keyup', ->
clearTimeout(@timer)
@timer = setTimeout( ->
Issuable.filterResults $('#issue_search_form')
, 500)
toggleLabelFilters: ->
$filteredLabels = $('.filtered-labels')
if $filteredLabels.find('.label-row').length > 0
$filteredLabels.removeClass('hidden')
else
$filteredLabels.addClass('hidden')
filterResults: (form) =>
formData = form.serialize()
$('.issues-holder, .merge-requests-holder').css('opacity', '0.5')
formAction = form.attr('action')
issuesUrl = formAction
issuesUrl += ("#{if formAction.indexOf('?') < 0 then '?' else '&'}")
issuesUrl += formData
$.ajax
type: 'GET'
url: formAction
data: formData
complete: ->
$('.issues-holder, .merge-requests-holder').css('opacity', '1.0')
success: (data) ->
$('.issues-holder, .merge-requests-holder').html(data.html)
# Change url so if user reload a page - search results are saved
history.replaceState {page: issuesUrl}, document.title, issuesUrl
Issuable.reload()
Issuable.updateStateFilters()
$filteredLabels = $('.filtered-labels')
if typeof Issuable.labelRow is 'function'
$filteredLabels.html(Issuable.labelRow(data))
Issuable.toggleLabelFilters()
dataType: "json"
reload: ->
if Issues.created
Issues.initChecks()
$('#filter_issue_search').val($('#issue_search').val())
updateStateFilters: ->
stateFilters = $('.issues-state-filters')
newParams = {}
paramKeys = ['author_id', 'milestone_title', 'assignee_id', 'issue_search']
for paramKey in paramKeys
newParams[paramKey] = gl.utils.getParameterValues(paramKey)[0] or ''
if stateFilters.length
stateFilters.find('a').each ->
initialUrl = gl.utils.removeParamQueryString($(this).attr('href'), 'label_name[]')
labelNameValues = gl.utils.getParameterValues('label_name[]')
if labelNameValues
labelNameQueryString = ("label_name[]=#{value}" for value in labelNameValues).join('&')
newUrl = "#{gl.utils.mergeUrlParams(newParams, initialUrl)}&#{labelNameQueryString}"
else
newUrl = gl.utils.mergeUrlParams(newParams, initialUrl)
$(this).attr 'href', newUrl

View File

@ -9,21 +9,29 @@ class @IssuableContext
$(".issuable-sidebar .inline-update").on "change", ".js-assignee", -> $(".issuable-sidebar .inline-update").on "change", ".js-assignee", ->
$(this).submit() $(this).submit()
$(document).off("click", ".edit-link").on "click",".edit-link", (e) -> $(document)
$block = $(@).parents('.block') .off 'click', '.issuable-sidebar .dropdown-content a'
$selectbox = $block.find('.selectbox') .on 'click', '.issuable-sidebar .dropdown-content a', (e) ->
if $selectbox.is(':visible') e.preventDefault()
$selectbox.hide()
$block.find('.value').show()
else
$selectbox.show()
$block.find('.value').hide()
if $selectbox.is(':visible') $(document)
setTimeout (-> .off 'click', '.edit-link'
$block.find('.dropdown-menu-toggle').trigger 'click' .on 'click', '.edit-link', (e) ->
), 0 e.preventDefault()
$block = $(@).parents('.block')
$selectbox = $block.find('.selectbox')
if $selectbox.is(':visible')
$selectbox.hide()
$block.find('.value').show()
else
$selectbox.show()
$block.find('.value').hide()
if $selectbox.is(':visible')
setTimeout ->
$block.find('.dropdown-menu-toggle').trigger 'click'
, 0
$(".right-sidebar").niceScroll() $(".right-sidebar").niceScroll()

View File

@ -12,6 +12,7 @@ class @Issue
@initMergeRequests() @initMergeRequests()
@initRelatedBranches() @initRelatedBranches()
@initCanCreateBranch()
initTaskList: -> initTaskList: ->
$('.detail-page-description .js-task-list-container').taskList('enable') $('.detail-page-description .js-task-list-container').taskList('enable')
@ -92,3 +93,25 @@ class @Issue
.success (data) -> .success (data) ->
if 'html' of data if 'html' of data
$container.html(data.html) $container.html(data.html)
initCanCreateBranch: ->
$container = $('div#new-branch')
# If the user doesn't have the required permissions the container isn't
# rendered at all.
return unless $container
$.getJSON($container.data('path'))
.error ->
$container.find('.checking').hide()
$container.find('.unavailable').show()
new Flash('Failed to check if a new branch can be created.', 'alert')
.success (data) ->
if data.can_create_branch
$container.find('.checking').hide()
$container.find('.available').show()
$container.find('a').attr('disabled', false)
else
$container.find('.checking').hide()
$container.find('.unavailable').show()

View File

@ -1,6 +1,6 @@
@Issues = @Issues =
init: -> init: ->
Issues.initSearch() Issues.created = true
Issues.initChecks() Issues.initChecks()
$("body").on "ajax:success", ".close_issue, .reopen_issue", -> $("body").on "ajax:success", ".close_issue, .reopen_issue", ->
@ -15,10 +15,6 @@
else else
$(this).html totalIssues - 1 $(this).html totalIssues - 1
reload: ->
Issues.initChecks()
$('#filter_issue_search').val($('#issue_search').val())
initChecks: -> initChecks: ->
$(".check_all_issues").click -> $(".check_all_issues").click ->
$(".selected_issue").prop("checked", @checked) $(".selected_issue").prop("checked", @checked)
@ -26,51 +22,6 @@
$(".selected_issue").bind "change", Issues.checkChanged $(".selected_issue").bind "change", Issues.checkChanged
# Update state filters if present in page
updateStateFilters: ->
stateFilters = $('.issues-state-filters')
newParams = {}
paramKeys = ['author_id', 'label_name', 'milestone_title', 'assignee_id', 'issue_search']
for paramKey in paramKeys
newParams[paramKey] = gl.utils.getUrlParameter(paramKey) or ''
if stateFilters.length
stateFilters.find('a').each ->
initialUrl = $(this).attr 'href'
$(this).attr 'href', gl.utils.mergeUrlParams(newParams, initialUrl)
# Make sure we trigger ajax request only after user stop typing
initSearch: ->
@timer = null
$("#issue_search").keyup ->
clearTimeout(@timer)
@timer = setTimeout( ->
Issues.filterResults $("#issue_search_form")
, 500)
filterResults: (form) =>
$('.issues-holder, .merge-requests-holder').css("opacity", '0.5')
formAction = form.attr('action')
formData = form.serialize()
issuesUrl = formAction
issuesUrl += ("#{if formAction.indexOf("?") < 0 then '?' else '&'}")
issuesUrl += formData
$.ajax
type: "GET"
url: formAction
data: formData
complete: ->
$('.issues-holder, .merge-requests-holder').css("opacity", '1.0')
success: (data) ->
$('.issues-holder, .merge-requests-holder').html(data.html)
# Change url so if user reload a page - search results are saved
history.replaceState {page: issuesUrl}, document.title, issuesUrl
Issues.reload()
Issues.updateStateFilters()
dataType: "json"
checkChanged: -> checkChanged: ->
checked_issues = $(".selected_issue:checked") checked_issues = $(".selected_issue:checked")
if checked_issues.length > 0 if checked_issues.length > 0

View File

@ -6,7 +6,7 @@ class @LabelsSelect
labelUrl = $dropdown.data('labels') labelUrl = $dropdown.data('labels')
issueUpdateURL = $dropdown.data('issueUpdate') issueUpdateURL = $dropdown.data('issueUpdate')
selectedLabel = $dropdown.data('selected') selectedLabel = $dropdown.data('selected')
if selectedLabel? if selectedLabel? and not $dropdown.hasClass 'js-multiselect'
selectedLabel = selectedLabel.split(',') selectedLabel = selectedLabel.split(',')
newLabelField = $('#new_label_name') newLabelField = $('#new_label_name')
newColorField = $('#new_label_color') newColorField = $('#new_label_color')
@ -16,33 +16,32 @@ class @LabelsSelect
abilityName = $dropdown.data('ability-name') abilityName = $dropdown.data('ability-name')
$selectbox = $dropdown.closest('.selectbox') $selectbox = $dropdown.closest('.selectbox')
$block = $selectbox.closest('.block') $block = $selectbox.closest('.block')
$form = $dropdown.closest('form')
$sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon span') $sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon span')
$value = $block.find('.value') $value = $block.find('.value')
$newLabelError = $('.js-label-error')
$colorPreview = $('.js-dropdown-label-color-preview')
$newLabelCreateButton = $('.js-new-label-btn')
$newLabelError.hide()
$loading = $block.find('.block-loading').fadeOut() $loading = $block.find('.block-loading').fadeOut()
if newLabelField.length
$newLabelCreateButton = $('.js-new-label-btn')
$colorPreview = $('.js-dropdown-label-color-preview')
$newLabelError = $dropdown.parent().find('.js-label-error')
$newLabelError.hide()
# Suggested colors in the dropdown to chose from pre-chosen colors
$('.suggest-colors-dropdown a').on 'click', (e) ->
issueURLSplit = issueUpdateURL.split('/') if issueUpdateURL? issueURLSplit = issueUpdateURL.split('/') if issueUpdateURL?
if issueUpdateURL if issueUpdateURL
labelHTMLTemplate = _.template( labelHTMLTemplate = _.template(
'<% _.each(labels, function(label){ %> '<% _.each(labels, function(label){ %>
<a href="<%= ["",issueURLSplit[1], issueURLSplit[2],""].join("/") %>issues?label_name=<%= label.title %>"> <a href="<%= ["",issueURLSplit[1], issueURLSplit[2],""].join("/") %>issues?label_name[]=<%= _.escape(label.title) %>">
<span class="label has-tooltip color-label" title="<%= label.description %>" style="background-color: <%= label.color %>;"> <span class="label has-tooltip color-label" title="<%= _.escape(label.description) %>" style="background-color: <%= label.color %>; color: <%= label.text_color %>;">
<%= label.title %> <%= _.escape(label.title) %>
</span> </span>
</a> </a>
<% }); %>' <% }); %>'
); )
labelNoneHTMLTemplate = _.template('<div class="light">None</div>') labelNoneHTMLTemplate = _.template('<div class="light">None</div>')
if newLabelField.length and $dropdown.hasClass 'js-extra-options' if newLabelField.length
# Suggested colors in the dropdown to chose from pre-chosen colors
$('.suggest-colors-dropdown a').on "click", (e) -> $('.suggest-colors-dropdown a').on "click", (e) ->
e.preventDefault() e.preventDefault()
e.stopPropagation() e.stopPropagation()
@ -81,26 +80,25 @@ class @LabelsSelect
enableLabelCreateButton = -> enableLabelCreateButton = ->
if newLabelField.val() isnt '' and newColorField.val() isnt '' if newLabelField.val() isnt '' and newColorField.val() isnt ''
$newLabelError.hide() $newLabelError.hide()
$('.js-new-label-btn').disable()
# Create new label with API
Api.newLabel projectId, {
name: newLabelField.val()
color: newColorField.val()
}, (label) ->
$('.js-new-label-btn').enable()
if label.message?
$newLabelError
.text label.message
.show()
else
$('.dropdown-menu-back', $dropdown.parent()).trigger 'click'
$newLabelCreateButton.enable() $newLabelCreateButton.enable()
else else
$newLabelCreateButton.disable() $newLabelCreateButton.disable()
saveLabel = ->
# Create new label with API
Api.newLabel projectId, {
name: newLabelField.val()
color: newColorField.val()
}, (label) ->
$newLabelCreateButton.enable()
if label.message?
$newLabelError
.text label.message
.show()
else
$('.dropdown-menu-back', $dropdown.parent()).trigger 'click'
newLabelField.on 'keyup change', enableLabelCreateButton newLabelField.on 'keyup change', enableLabelCreateButton
newColorField.on 'keyup change', enableLabelCreateButton newColorField.on 'keyup change', enableLabelCreateButton
@ -111,24 +109,7 @@ class @LabelsSelect
.on 'click', (e) -> .on 'click', (e) ->
e.preventDefault() e.preventDefault()
e.stopPropagation() e.stopPropagation()
saveLabel()
if newLabelField.val() isnt '' and newColorField.val() isnt ''
$newLabelError.hide()
$('.js-new-label-btn').disable()
# Create new label with API
Api.newLabel projectId, {
name: newLabelField.val()
color: newColorField.val()
}, (label) ->
$('.js-new-label-btn').enable()
if label.message?
$newLabelError
.text label.message
.show()
else
$('.dropdown-menu-back', $dropdown.parent()).trigger 'click'
saveLabelData = -> saveLabelData = ->
selected = $dropdown selected = $dropdown
@ -171,7 +152,7 @@ class @LabelsSelect
.find('a') .find('a')
.each((i) -> .each((i) ->
setTimeout(=> setTimeout(=>
glAnimate($(@), 'pulse') gl.animate.animate($(@), 'pulse')
,200 * i ,200 * i
) )
) )
@ -182,6 +163,21 @@ class @LabelsSelect
$.ajax( $.ajax(
url: labelUrl url: labelUrl
).done (data) -> ).done (data) ->
data = _.chain data
.groupBy (label) ->
label.title
.map (label) ->
color = _.map label, (dup) ->
dup.color
return {
id: label[0].id
title: label[0].title
color: color
duplicate: color.length > 1
}
.value()
if $dropdown.hasClass 'js-extra-options' if $dropdown.hasClass 'js-extra-options'
if showNo if showNo
data.unshift( data.unshift(
@ -197,21 +193,47 @@ class @LabelsSelect
if data.length > 2 if data.length > 2
data.splice 2, 0, 'divider' data.splice 2, 0, 'divider'
callback data callback data
renderRow: (label) -> renderRow: (label) ->
selectedClass = '' removesAll = label.id is 0 or not label.id?
if $selectbox.find("input[type='hidden']\
[name='#{$dropdown.data('field-name')}']\
[value='#{label.id}']").length
selectedClass = 'is-active'
color = if label.color? then "<span class='dropdown-label-box' style='background-color: #{label.color}'></span>" else "" selectedClass = []
if $form.find("input[type='hidden']\
[name='#{$dropdown.data('fieldName')}']\
[value='#{this.id(label)}']").length
selectedClass.push 'is-active'
if $dropdown.hasClass('js-multiselect') and removesAll
selectedClass.push 'dropdown-clear-active'
if label.duplicate
spacing = 100 / label.color.length
# Reduce the colors to 4
label.color = label.color.filter (color, i) ->
i < 4
color = _.map(label.color, (color, i) ->
percentFirst = Math.floor(spacing * i)
percentSecond = Math.floor(spacing * (i + 1))
"#{color} #{percentFirst}%,#{color} #{percentSecond}% "
).join(',')
color = "linear-gradient(#{color})"
else
if label.color?
color = label.color[0]
if color
colorEl = "<span class='dropdown-label-box' style='background: #{color}'></span>"
else
colorEl = ''
"<li> "<li>
<a href='#' class='#{selectedClass}'> <a href='#' class='#{selectedClass.join(' ')}'>
#{color} #{colorEl}
#{label.title} #{_.escape(label.title)}
</a> </a>
</li>" </li>"
filterable: true filterable: true
@ -219,37 +241,56 @@ class @LabelsSelect
fields: ['title'] fields: ['title']
selectable: true selectable: true
toggleLabel: (selected) -> toggleLabel: (selected, el) ->
selected_labels = $('.js-label-select').siblings('.dropdown-menu-labels').find('.is-active')
if selected and selected.title? if selected and selected.title?
selected.title if selected_labels.length > 1
"#{selected.title} +#{selected_labels.length - 1} more"
else
selected.title
else if not selected and selected_labels.length isnt 0
if selected_labels.length > 1
"#{$(selected_labels[0]).text()} +#{selected_labels.length - 1} more"
else if selected_labels.length is 1
$(selected_labels).text()
else else
defaultLabel defaultLabel
fieldName: $dropdown.data('field-name') fieldName: $dropdown.data('field-name')
id: (label) -> id: (label) ->
if label.isAny? if $dropdown.hasClass("js-filter-submit") and not label.isAny?
'' _.escape label.title
else if $dropdown.hasClass "js-filter-submit"
label.title
else else
label.id label.id
hidden: -> hidden: ->
page = $('body').data 'page'
isIssueIndex = page is 'projects:issues:index'
isMRIndex = page is 'projects:merge_requests:index'
$selectbox.hide() $selectbox.hide()
# display:block overrides the hide-collapse rule # display:block overrides the hide-collapse rule
$value.removeAttr('style') $value.removeAttr('style')
if $dropdown.hasClass 'js-multiselect' if $dropdown.hasClass 'js-multiselect'
saveLabelData() if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex)
selectedLabels = $dropdown
.closest('form')
.find("input:hidden[name='#{$dropdown.data('fieldName')}']")
Issuable.filterResults $dropdown.closest('form')
else if $dropdown.hasClass('js-filter-submit')
$dropdown.closest('form').submit()
else
saveLabelData()
multiSelect: $dropdown.hasClass 'js-multiselect' multiSelect: $dropdown.hasClass 'js-multiselect'
clicked: (label) -> clicked: (label) ->
page = $('body').data 'page' page = $('body').data 'page'
isIssueIndex = page is 'projects:issues:index' isIssueIndex = page is 'projects:issues:index'
isMRIndex = page is page is 'projects:merge_requests:index' isMRIndex = page is 'projects:merge_requests:index'
if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex) if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex)
selectedLabel = label.title if not $dropdown.hasClass 'js-multiselect'
selectedLabel = label.title
Issues.filterResults $dropdown.closest('form') Issuable.filterResults $dropdown.closest('form')
else if $dropdown.hasClass 'js-filter-submit' else if $dropdown.hasClass 'js-filter-submit'
$dropdown.closest('form').submit() $dropdown.closest('form').submit()
else else

View File

@ -1,13 +1,39 @@
((w) -> ((w) ->
if not w.gl? then w.gl = {}
if not gl.animate? then gl.animate = {}
w.glAnimate = ($el, animation, done) -> gl.animate.animate = ($el, animation, options, done) ->
if options?.cssStart?
$el.css(options.cssStart)
$el $el
.removeClass() .removeClass(animation + ' animated')
.addClass(animation + ' animated') .addClass(animation + ' animated')
.one 'webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', -> .one 'webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', ->
$(this).removeClass() $(this).removeClass(animation + ' animated')
if done?
done()
if options?.cssEnd?
$el.css(options.cssEnd)
return return
return return
return
gl.animate.animateEach = ($els, animation, time, options, done) ->
dfd = $.Deferred()
if not $els.length
dfd.resolve()
$els.each((i) ->
setTimeout(=>
$this = $(@)
gl.animate.animate($this, animation, options, =>
if i is $els.length - 1
dfd.resolve()
if done?
done()
)
,time * i
)
return
)
return dfd.promise()
return
) window ) window

View File

@ -3,16 +3,20 @@
w.gl ?= {} w.gl ?= {}
w.gl.utils ?= {} w.gl.utils ?= {}
w.gl.utils.getUrlParameter = (sParam) -> # Returns an array containing the value(s) of the
# of the key passed as an argument
w.gl.utils.getParameterValues = (sParam) ->
sPageURL = decodeURIComponent(window.location.search.substring(1)) sPageURL = decodeURIComponent(window.location.search.substring(1))
sURLVariables = sPageURL.split('&') sURLVariables = sPageURL.split('&')
sParameterName = undefined sParameterName = undefined
values = []
i = 0 i = 0
while i < sURLVariables.length while i < sURLVariables.length
sParameterName = sURLVariables[i].split('=') sParameterName = sURLVariables[i].split('=')
if sParameterName[0] is sParam if sParameterName[0] is sParam
return if sParameterName[1] is undefined then true else sParameterName[1] values.push(sParameterName[1])
i++ i++
values
# # # #
# @param {Object} params - url keys and value to merge # @param {Object} params - url keys and value to merge
@ -28,4 +32,12 @@
newUrl = "#{newUrl}#{(if newUrl.indexOf('?') > 0 then '&' else '?')}#{paramName}=#{paramValue}" newUrl = "#{newUrl}#{(if newUrl.indexOf('?') > 0 then '&' else '?')}#{paramName}=#{paramValue}"
newUrl newUrl
# removes parameter query string from url. returns the modified url
w.gl.utils.removeParamQueryString = (url, param) ->
url = decodeURIComponent(url)
urlVariables = url.split('&')
(
variables for variables in urlVariables when variables.indexOf(param) is -1
).join('&')
) window ) window

View File

@ -87,8 +87,8 @@ class @MergeRequestTabs
if window.location.hash if window.location.hash
navBarHeight = $('.navbar-gitlab').outerHeight() navBarHeight = $('.navbar-gitlab').outerHeight()
$el = $("#{container} #{window.location.hash}") $el = $("#{container} #{window.location.hash}:not(.match)")
$.scrollTo("#{container} #{window.location.hash}", offset: -navBarHeight) if $el.length $.scrollTo("#{container} #{window.location.hash}:not(.match)", offset: -navBarHeight) if $el.length
# Activate a tab based on the current action # Activate a tab based on the current action
activateTab: (action) -> activateTab: (action) ->
@ -176,16 +176,17 @@ class @MergeRequestTabs
if locationHash isnt '' if locationHash isnt ''
hashClassString = ".#{locationHash.replace('#', '')}" hashClassString = ".#{locationHash.replace('#', '')}"
$diffLine = $(locationHash) $diffLine = $("#{locationHash}:not(.match)", $('#diffs'))
if $diffLine.is ':not(tr)' if not $diffLine.is 'tr'
$diffLine = $("td#{locationHash}, td#{hashClassString}") $diffLine = $('#diffs').find("td#{locationHash}, td#{hashClassString}")
else else
$diffLine = $('td', $diffLine) $diffLine = $diffLine.find('td')
$diffLine.addClass 'hll' if $diffLine.length
diffLineTop = $diffLine.offset().top $diffLine.addClass 'hll'
navBarHeight = $('.navbar-gitlab').outerHeight() diffLineTop = $diffLine.offset().top
navBarHeight = $('.navbar-gitlab').outerHeight()
loadBuilds: (source) -> loadBuilds: (source) ->
return if @buildsLoaded return if @buildsLoaded

View File

@ -9,11 +9,12 @@ class @MergeRequestWidget
constructor: (@opts) -> constructor: (@opts) ->
$('#modal_merge_info').modal(show: false) $('#modal_merge_info').modal(show: false)
@firstCICheck = true @firstCICheck = true
@readyForCICheck = true @readyForCICheck = false
clearInterval @fetchBuildStatusInterval clearInterval @fetchBuildStatusInterval
@clearEventListeners() @clearEventListeners()
@addEventListeners() @addEventListeners()
@getCIStatus(false)
@pollCIStatus() @pollCIStatus()
notifyPermissions() notifyPermissions()
@ -68,20 +69,18 @@ class @MergeRequestWidget
$.getJSON @opts.ci_status_url, (data) => $.getJSON @opts.ci_status_url, (data) =>
@readyForCICheck = true @readyForCICheck = true
if @firstCICheck if data.status is ''
@firstCICheck = false
@opts.ci_status = data.status
if @opts.ci_status is ''
@opts.ci_status = data.status
return return
if data.status isnt @opts.ci_status and data.status? if @firstCICheck || data.status isnt @opts.ci_status and data.status?
@opts.ci_status = data.status
@showCIStatus data.status @showCIStatus data.status
if data.coverage if data.coverage
@showCICoverage data.coverage @showCICoverage data.coverage
if showNotification # The first check should only update the UI, a notification
# should only be displayed on status changes
if showNotification and not @firstCICheck
status = @ciLabelForStatus(data.status) status = @ciLabelForStatus(data.status)
if status is "preparing" if status is "preparing"
@ -104,8 +103,7 @@ class @MergeRequestWidget
@close() @close()
Turbolinks.visit _this.opts.builds_path Turbolinks.visit _this.opts.builds_path
) )
@firstCICheck = false
@opts.ci_status = data.status
showCIStatus: (state) -> showCIStatus: (state) ->
$('.ci_widget').hide() $('.ci_widget').hide()

View File

@ -1,35 +0,0 @@
#
# * Filter merge requests
#
@MergeRequests =
init: ->
MergeRequests.initSearch()
# Make sure we trigger ajax request only after user stop typing
initSearch: ->
@timer = null
$("#issue_search").keyup ->
clearTimeout(@timer)
@timer = setTimeout(MergeRequests.filterResults, 500)
filterResults: =>
form = $("#issue_search_form")
search = $("#issue_search").val()
$('.merge-requests-holder').css("opacity", '0.5')
issues_url = form.attr('action') + '?' + form.serialize()
$.ajax
type: "GET"
url: form.attr('action')
data: form.serialize()
complete: ->
$('.merge-requests-holder').css("opacity", '1.0')
success: (data) ->
$('.merge-requests-holder').html(data.html)
# Change url so if user reload a page - search results are saved
history.replaceState {page: issues_url}, document.title, issues_url
MergeRequests.reload()
dataType: "json"
reload: ->
$('#filter_issue_search').val($('#issue_search').val())

View File

@ -24,7 +24,7 @@ class @MilestoneSelect
if issueUpdateURL if issueUpdateURL
milestoneLinkTemplate = _.template( milestoneLinkTemplate = _.template(
'<a href="/<%= namespace %>/<%= path %>/milestones/<%= iid %>"><%= title %></a>' '<a href="/<%= namespace %>/<%= path %>/milestones/<%= iid %>"><%= _.escape(title) %></a>'
) )
milestoneLinkNoneTemplate = '<div class="light">None</div>' milestoneLinkNoneTemplate = '<div class="light">None</div>'
@ -71,7 +71,7 @@ class @MilestoneSelect
defaultLabel defaultLabel
fieldName: $dropdown.data('field-name') fieldName: $dropdown.data('field-name')
text: (milestone) -> text: (milestone) ->
milestone.title _.escape(milestone.title)
id: (milestone) -> id: (milestone) ->
if !useId if !useId
milestone.name milestone.name
@ -97,7 +97,7 @@ class @MilestoneSelect
selectedMilestone = selected.name selectedMilestone = selected.name
else else
selectedMilestone = '' selectedMilestone = ''
Issues.filterResults $dropdown.closest('form') Issuable.filterResults $dropdown.closest('form')
else if $dropdown.hasClass('js-filter-submit') else if $dropdown.hasClass('js-filter-submit')
$dropdown.closest('form').submit() $dropdown.closest('form').submit()
else else

View File

@ -75,6 +75,9 @@ class @Notes
# when issue status changes, we need to refresh data # when issue status changes, we need to refresh data
$(document).on "issuable:change", @refresh $(document).on "issuable:change", @refresh
# when a key is clicked on the notes
$(document).on "keydown", ".js-note-text", @keydownNoteText
cleanBinding: -> cleanBinding: ->
$(document).off "ajax:success", ".js-main-target-form" $(document).off "ajax:success", ".js-main-target-form"
$(document).off "ajax:success", ".js-discussion-note-form" $(document).off "ajax:success", ".js-discussion-note-form"
@ -92,10 +95,19 @@ class @Notes
$(document).off "click", ".js-note-target-reopen" $(document).off "click", ".js-note-target-reopen"
$(document).off "click", ".js-note-target-close" $(document).off "click", ".js-note-target-close"
$(document).off "click", ".js-note-discard" $(document).off "click", ".js-note-discard"
$(document).off "keydown", ".js-note-text"
$('.note .js-task-list-container').taskList('disable') $('.note .js-task-list-container').taskList('disable')
$(document).off 'tasklist:changed', '.note .js-task-list-container' $(document).off 'tasklist:changed', '.note .js-task-list-container'
keydownNoteText: (e) ->
$this = $(this)
if $this.val() is '' and e.which is 38 #aka the up key
myLastNote = $("li.note[data-author-id='#{gon.current_user_id}'][data-editable]:last")
if myLastNote.length
myLastNoteEditBtn = myLastNote.find('.js-note-edit')
myLastNoteEditBtn.trigger('click', [true, myLastNote])
initRefresh: -> initRefresh: ->
clearInterval(Notes.interval) clearInterval(Notes.interval)
Notes.interval = setInterval => Notes.interval = setInterval =>
@ -343,7 +355,7 @@ class @Notes
Adds a hidden div with the original content of the note to fill the edit note form with Adds a hidden div with the original content of the note to fill the edit note form with
if the user cancels if the user cancels
### ###
showEditForm: (e) -> showEditForm: (e, scrollTo, myLastNote) ->
e.preventDefault() e.preventDefault()
note = $(this).closest(".note") note = $(this).closest(".note")
note.addClass "is-editting" note.addClass "is-editting"
@ -354,9 +366,27 @@ class @Notes
# Show the attachment delete link # Show the attachment delete link
note.find(".js-note-attachment-delete").show() note.find(".js-note-attachment-delete").show()
new GLForm form done = ($noteText) ->
# Neat little trick to put the cursor at the end
noteTextVal = $noteText.val()
$noteText.val('').val(noteTextVal);
form.find(".js-note-text").focus() new GLForm form
if scrollTo? and myLastNote?
# scroll to the bottom
# so the open of the last element doesn't make a jump
$('html, body').scrollTop($(document).height());
$('html, body').animate({
scrollTop: myLastNote.offset().top - 150
}, 500, ->
$noteText = form.find(".js-note-text")
$noteText.focus()
done($noteText)
);
else
$noteText = form.find('.js-note-text')
$noteText.focus()
done($noteText)
### ###
Called in response to clicking the edit note link Called in response to clicking the edit note link

View File

@ -45,9 +45,10 @@ class @Profile
saveForm: -> saveForm: ->
self = @ self = @
formData = new FormData(@form[0]) formData = new FormData(@form[0])
formData.append('user[avatar]', @avatarGlCrop.getBlob(), 'avatar.png')
avatarBlob = @avatarGlCrop.getBlob()
formData.append('user[avatar]', avatarBlob, 'avatar.png') if avatarBlob?
$.ajax $.ajax
url: @form.attr('action') url: @form.attr('action')

View File

@ -1,10 +1,12 @@
class @Sidebar class @Sidebar
constructor: (currentUser) -> constructor: (currentUser) ->
@sidebar = $('aside')
@addEventListeners() @addEventListeners()
addEventListeners: -> addEventListeners: ->
$('aside').on('click', '.sidebar-collapsed-icon', @sidebarCollapseClicked) @sidebar.on('click', '.sidebar-collapsed-icon', @, @sidebarCollapseClicked)
$('.dropdown').on('hidden.gl.dropdown', @sidebarDropdownHidden) $('.dropdown').on('hidden.gl.dropdown', @, @onSidebarDropdownHidden)
$('.dropdown').on('loading.gl.dropdown', @sidebarDropdownLoading) $('.dropdown').on('loading.gl.dropdown', @sidebarDropdownLoading)
$('.dropdown').on('loaded.gl.dropdown', @sidebarDropdownLoaded) $('.dropdown').on('loaded.gl.dropdown', @sidebarDropdownLoaded)
@ -30,26 +32,56 @@ class @Sidebar
else else
i.show() i.show()
sidebarCollapseClicked: (e) -> sidebarCollapseClicked: (e) ->
sidebar = e.data
e.preventDefault() e.preventDefault()
$block = $(@).closest('.block') $block = $(@).closest('.block')
sidebar.openDropdown($block);
$('aside') openDropdown: (blockOrName) ->
.find('.gutter-toggle') $block = if _.isString(blockOrName) then @getBlock(blockOrName) else blockOrName
.trigger('click')
$editLink = $block.find('.edit-link')
if $editLink.length $block.find('.edit-link').trigger('click')
$editLink.trigger('click')
$block.addClass('collapse-after-update')
$('.page-with-sidebar').addClass('with-overlay')
sidebarDropdownHidden: (e) -> if not @isOpen()
@setCollapseAfterUpdate($block)
@toggleSidebar('open')
setCollapseAfterUpdate: ($block) ->
$block.addClass('collapse-after-update')
$('.page-with-sidebar').addClass('with-overlay')
onSidebarDropdownHidden: (e) ->
sidebar = e.data
e.preventDefault()
$block = $(@).closest('.block') $block = $(@).closest('.block')
sidebar.sidebarDropdownHidden($block)
sidebarDropdownHidden: ($block) ->
if $block.hasClass('collapse-after-update') if $block.hasClass('collapse-after-update')
$block.removeClass('collapse-after-update') $block.removeClass('collapse-after-update')
$('.page-with-sidebar').removeClass('with-overlay') $('.page-with-sidebar').removeClass('with-overlay')
$('aside') @toggleSidebar('hide')
.find('.gutter-toggle')
.trigger('click') triggerOpenSidebar: ->
@sidebar
.find('.js-sidebar-toggle')
.trigger('click')
toggleSidebar: (action = 'toggle') ->
if action is 'toggle'
@triggerOpenSidebar()
if action is 'open'
@triggerOpenSidebar() if not @isOpen()
if action is 'hide'
@triggerOpenSidebar() is @isOpen()
isOpen: ->
@sidebar.is('.right-sidebar-expanded')
getBlock: (name) ->
@sidebar.find(".block.#{name}")

View File

@ -0,0 +1,75 @@
class @Search
constructor: ->
$groupDropdown = $('.js-search-group-dropdown')
$projectDropdown = $('.js-search-project-dropdown')
@eventListeners()
$groupDropdown.glDropdown(
selectable: true
filterable: true
fieldName: 'group_id'
data: (term, callback) ->
Api.groups term, null, (data) ->
data.unshift(
name: 'Any'
)
data.splice 1, 0, 'divider'
callback(data)
id: (obj) ->
obj.id
text: (obj) ->
obj.name
toggleLabel: (obj) ->
"#{$groupDropdown.data('default-label')} #{obj.name}"
clicked: =>
@submitSearch()
)
$projectDropdown.glDropdown(
selectable: true
filterable: true
fieldName: 'project_id'
data: (term, callback) ->
Api.projects term, 'id', (data) ->
data.unshift(
name_with_namespace: 'Any'
)
data.splice 1, 0, 'divider'
callback(data)
id: (obj) ->
obj.id
text: (obj) ->
obj.name_with_namespace
toggleLabel: (obj) ->
"#{$projectDropdown.data('default-label')} #{obj.name_with_namespace}"
clicked: =>
@submitSearch()
)
eventListeners: ->
$(document)
.off 'keyup', '.js-search-input'
.on 'keyup', '.js-search-input', @searchKeyUp
$(document)
.off 'click', '.js-search-clear'
.on 'click', '.js-search-clear', @clearSearchField
submitSearch: ->
$('.js-search-form').submit()
searchKeyUp: ->
$input = $(@)
if $input.val() is ''
$('.js-search-clear').addClass 'hidden'
else
$('.js-search-clear').removeClass 'hidden'
clearSearchField: ->
$('.js-search-input')
.val ''
.trigger 'keyup'
.focus()

View File

@ -2,34 +2,35 @@ class @Shortcuts
constructor: -> constructor: ->
@enabledHelp = [] @enabledHelp = []
Mousetrap.reset() Mousetrap.reset()
Mousetrap.bind('?', @selectiveHelp) 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)
Mousetrap.bind('t', -> Turbolinks.visit(findFileURL)) if findFileURL? Mousetrap.bind('t', -> Turbolinks.visit(findFileURL)) if findFileURL?
selectiveHelp: (e) => onToggleHelp: (e) =>
Shortcuts.showHelp(e, @enabledHelp) e.preventDefault()
@toggleHelp(@enabledHelp)
toggleMarkdownPreview: (e) => toggleMarkdownPreview: (e) =>
$(document).triggerHandler('markdown-preview:toggle', [e]) $(document).triggerHandler('markdown-preview:toggle', [e])
@showHelp: (e, location) -> toggleHelp: (location) ->
if $('#modal-shortcuts').length > 0 $modal = $('#modal-shortcuts')
$('#modal-shortcuts').modal('show')
else if $modal.length
url = '/help/shortcuts' $modal.modal('toggle')
url = gon.relative_url_root + url if gon.relative_url_root? return
$.ajax(
url: url, $.ajax(
dataType: 'script', url: gon.shortcuts_path,
success: (e) -> dataType: 'script',
if location and location.length > 0 success: (e) ->
$(l).show() for l in location if location and location.length > 0
else $(l).show() for l in location
$('.hidden-shortcut').show() else
$('.js-more-help-button').remove() $('.hidden-shortcut').show()
) $('.js-more-help-button').remove()
e.preventDefault() )
@focusSearch: (e) -> @focusSearch: (e) ->
$('#search').focus() $('#search').focus()

View File

@ -4,18 +4,8 @@
class @ShortcutsIssuable extends ShortcutsNavigation class @ShortcutsIssuable extends ShortcutsNavigation
constructor: (isMergeRequest) -> constructor: (isMergeRequest) ->
super() super()
Mousetrap.bind('a', -> Mousetrap.bind('a', @openSidebarDropdown.bind(@, 'assignee'))
$('.block.assignee .edit-link').trigger('click') Mousetrap.bind('m', @openSidebarDropdown.bind(@, 'milestone'))
return false
)
Mousetrap.bind('m', ->
$('.block.milestone .edit-link').trigger('click')
return false
)
Mousetrap.bind('r', =>
@replyWithSelectedText()
return false
)
Mousetrap.bind('j', => Mousetrap.bind('j', =>
@prevIssue() @prevIssue()
return false return false
@ -28,7 +18,7 @@ class @ShortcutsIssuable extends ShortcutsNavigation
@editIssue() @editIssue()
return false return false
) )
Mousetrap.bind('l', @openSidebarDropdown.bind(@, 'labels'))
if isMergeRequest if isMergeRequest
@enabledHelp.push('.hidden-shortcut.merge_requests') @enabledHelp.push('.hidden-shortcut.merge_requests')
@ -71,3 +61,7 @@ class @ShortcutsIssuable extends ShortcutsNavigation
editIssue: -> editIssue: ->
$editBtn = $('.issuable-edit') $editBtn = $('.issuable-edit')
Turbolinks.visit($editBtn.attr('href')) Turbolinks.visit($editBtn.attr('href'))
openSidebarDropdown: (name) ->
sidebar.openDropdown(name)
return false

View File

@ -14,6 +14,7 @@ class @ShortcutsNavigation extends Shortcuts
Mousetrap.bind('g m', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-merge_requests')) Mousetrap.bind('g m', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-merge_requests'))
Mousetrap.bind('g w', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-wiki')) Mousetrap.bind('g w', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-wiki'))
Mousetrap.bind('g s', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-snippets')) Mousetrap.bind('g s', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-snippets'))
Mousetrap.bind('i', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-new-issue'))
@enabledHelp.push('.hidden-shortcut.project') @enabledHelp.push('.hidden-shortcut.project')
@findAndFollowLink: (selector) -> @findAndFollowLink: (selector) ->

View File

@ -12,7 +12,7 @@ toggleSidebar = ->
niceScrollBars.updateScrollBar(); niceScrollBars.updateScrollBar();
), 300 ), 300
$(document).on("click", '.toggle-nav-collapse', (e) -> $(document).on("click", '.toggle-nav-collapse, .side-nav-toggle', (e) ->
e.preventDefault() e.preventDefault()
toggleSidebar() toggleSidebar()

View File

@ -1,5 +1,11 @@
class @Todos class @Todos
constructor: (@name) -> constructor: (opts = {}) ->
{
@el = $('.js-todos-options')
} = opts
@perPage = @el.data('perPage')
@clearListeners() @clearListeners()
@initBtnListeners() @initBtnListeners()
@ -26,6 +32,7 @@ class @Todos
dataType: 'json' dataType: 'json'
data: '_method': 'delete' data: '_method': 'delete'
success: (data) => success: (data) =>
@redirectIfNeeded data.count
@clearDone $this.closest('li') @clearDone $this.closest('li')
@updateBadges data @updateBadges data
@ -57,11 +64,46 @@ class @Todos
$('.todos-pending .badge, .todos-pending-count').text data.count $('.todos-pending .badge, .todos-pending-count').text data.count
$('.todos-done .badge').text data.done_count $('.todos-done .badge').text data.done_count
getTotalPages: ->
@el.data('totalPages')
getCurrentPage: ->
@el.data('currentPage')
getTodosPerPage: ->
@el.data('perPage')
redirectIfNeeded: (total) ->
currPages = @getTotalPages()
currPage = @getCurrentPage()
# Refresh if no remaining Todos
if not total
location.reload()
return
# Do nothing if no pagination
return if not currPages
newPages = Math.ceil(total / @getTodosPerPage())
url = location.href # Includes query strings
# If new total of pages is different than we have now
if newPages isnt currPages
# Redirect to previous page if there's one available
if currPages > 1 and currPage is currPages
pageParams =
page: currPages - 1
url = gl.utils.mergeUrlParams(pageParams, url)
Turbolinks.visit(url)
goToTodoUrl: (e)-> goToTodoUrl: (e)->
todoLink = $(this).data('url') todoLink = $(this).data('url')
return unless todoLink return unless todoLink
if e.metaKey # Allow Meta-Click or Mouse3-click to open in a new tab
if e.metaKey or e.which is 2
e.preventDefault() e.preventDefault()
window.open(todoLink,'_blank') window.open(todoLink,'_blank')
else else

View File

@ -92,7 +92,7 @@ class @UserTabs
@setCurrentAction(action) @setCurrentAction(action)
activateTab: (action) -> activateTab: (action) ->
@parentEl.find(".nav-links .#{action}-tab a").tab('show') @parentEl.find(".nav-links .js-#{action}-tab a").tab('show')
setTab: (source, action) -> setTab: (source, action) ->
return if @loaded[action] is true return if @loaded[action] is true

View File

@ -158,7 +158,7 @@ class @UsersSelect
if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex) if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex)
selectedId = user.id selectedId = user.id
Issues.filterResults $dropdown.closest('form') Issuable.filterResults $dropdown.closest('form')
else if $dropdown.hasClass 'js-filter-submit' else if $dropdown.hasClass 'js-filter-submit'
$dropdown.closest('form').submit() $dropdown.closest('form').submit()
else else

View File

@ -25,6 +25,7 @@
@import "framework/lists.scss"; @import "framework/lists.scss";
@import "framework/markdown_area.scss"; @import "framework/markdown_area.scss";
@import "framework/mobile.scss"; @import "framework/mobile.scss";
@import "framework/modal.scss";
@import "framework/nav.scss"; @import "framework/nav.scss";
@import "framework/pagination.scss"; @import "framework/pagination.scss";
@import "framework/progress.scss"; @import "framework/progress.scss";

View File

@ -28,6 +28,7 @@
&.s46 { width: 46px; height: 46px; margin-right: 15px; } &.s46 { width: 46px; height: 46px; margin-right: 15px; }
&.s48 { width: 48px; height: 48px; margin-right: 10px; } &.s48 { width: 48px; height: 48px; margin-right: 10px; }
&.s60 { width: 60px; height: 60px; margin-right: 12px; } &.s60 { width: 60px; height: 60px; margin-right: 12px; }
&.s70 { width: 70px; height: 70px; margin-right: 14px; }
&.s90 { width: 90px; height: 90px; margin-right: 15px; } &.s90 { width: 90px; height: 90px; margin-right: 15px; }
&.s110 { width: 110px; height: 110px; margin-right: 15px; } &.s110 { width: 110px; height: 110px; margin-right: 15px; }
&.s140 { width: 140px; height: 140px; margin-right: 20px; } &.s140 { width: 140px; height: 140px; margin-right: 20px; }

View File

@ -1,5 +1,5 @@
.light-well { .light-well {
background-color: #f8fafc; background-color: $background-color;
padding: 15px; padding: 15px;
} }
@ -18,7 +18,7 @@
line-height: 36px; line-height: 36px;
} }
.gray-content-block { .row-content-block {
margin-top: 0; margin-top: 0;
margin-bottom: -$gl-padding; margin-bottom: -$gl-padding;
background-color: $background-color; background-color: $background-color;
@ -81,6 +81,11 @@
margin-left: 10px; margin-left: 10px;
} }
} }
&.build-content {
background-color: $white-light;
border-top: none;
}
} }
.cover-block { .cover-block {
@ -113,7 +118,7 @@
line-height: 1.1; line-height: 1.1;
h1 { h1 {
color: #313236; color: $gl-gray-dark;
margin-bottom: 6px; margin-bottom: 6px;
font-size: 23px; font-size: 23px;
} }
@ -150,6 +155,41 @@
right: auto; right: auto;
} }
} }
&.groups-cover-block {
background: $white-light;
border-bottom: 1px solid $border-color;
text-align: left;
padding: 24px 0;
.group-info {
.cover-title {
margin-top: 9px;
}
p {
margin-bottom: 0;
}
}
@media (max-width: $screen-xs-max) {
text-align: center;
.avatar {
float: none;
}
}
}
.group-info {
h1 {
display: inline;
font-weight: normal;
font-size: 24px;
color: $gl-title-color;
}
}
} }
.block-connector { .block-connector {

View File

@ -59,7 +59,7 @@
} }
@mixin btn-gray { @mixin btn-gray {
@include btn-color($gray-light, $border-gray-light, $gray-normal, $border-gray-light, $gray-dark, $border-gray-dark, #313236); @include btn-color($gray-light, $border-gray-light, $gray-normal, $border-gray-light, $gray-dark, $border-gray-dark, $gl-gray-dark);
} }
@mixin btn-white { @mixin btn-white {
@ -139,11 +139,19 @@
pointer-events: auto !important; pointer-events: auto !important;
} }
&[disabled] {
pointer-events: none !important;
}
.caret { .caret {
margin-left: 5px; margin-left: 5px;
} }
} }
.btn-lg {
padding: 12px 20px;
}
.btn-transparent { .btn-transparent {
color: $btn-transparent-color; color: $btn-transparent-color;
background-color: transparent; background-color: transparent;
@ -243,3 +251,10 @@
.btn-file-option { .btn-file-option {
background: linear-gradient(180deg, $white-light 25%, $gray-light 100%); background: linear-gradient(180deg, $white-light 25%, $gray-light 100%);
} }
.btn-build {
margin-left: 10px;
i {
color: $gl-icon-color;
}
}

View File

@ -54,6 +54,10 @@
fill: #254e77 !important; fill: #254e77 !important;
} }
.future {
visibility: hidden;
}
.domain-background { .domain-background {
fill: none; fill: none;
shape-rendering: crispedges; shape-rendering: crispedges;

View File

@ -11,6 +11,7 @@
.prepend-top-10 { margin-top: 10px } .prepend-top-10 { margin-top: 10px }
.prepend-top-default { margin-top: $gl-padding !important; } .prepend-top-default { margin-top: $gl-padding !important; }
.prepend-top-20 { margin-top: 20px } .prepend-top-20 { margin-top: 20px }
.prepend-left-5 { margin-left: 5px }
.prepend-left-10 { margin-left: 10px } .prepend-left-10 { margin-left: 10px }
.prepend-left-default { margin-left: $gl-padding; } .prepend-left-default { margin-left: $gl-padding; }
.prepend-left-20 { margin-left: 20px } .prepend-left-20 { margin-left: 20px }

View File

@ -42,7 +42,7 @@
font-size: 15px; font-size: 15px;
text-align: left; text-align: left;
border: 1px solid $dropdown-toggle-border-color; border: 1px solid $dropdown-toggle-border-color;
border-radius: $dropdown-border-radius; border-radius: $border-radius-base;
outline: 0; outline: 0;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
@ -80,7 +80,7 @@
padding: 10px 0; padding: 10px 0;
background-color: $dropdown-bg; background-color: $dropdown-bg;
border: 1px solid $dropdown-border-color; border: 1px solid $dropdown-border-color;
border-radius: $dropdown-border-radius; border-radius: $border-radius-base;
box-shadow: 0 2px 4px $dropdown-shadow-color; box-shadow: 0 2px 4px $dropdown-shadow-color;
&.is-loading { &.is-loading {
@ -248,7 +248,7 @@
.dropdown-title { .dropdown-title {
position: relative; position: relative;
padding: 0 25px 15px; padding: 0 25px 10px;
margin: 0 10px 10px; margin: 0 10px 10px;
font-weight: 600; font-weight: 600;
line-height: 1; line-height: 1;
@ -278,7 +278,7 @@
right: 5px; right: 5px;
width: 20px; width: 20px;
height: 20px; height: 20px;
top: -1px; top: -3px;
} }
.dropdown-menu-back { .dropdown-menu-back {
@ -320,7 +320,7 @@
} }
} }
.dropdown-input-field { .dropdown-input-field, .default-dropdown-input {
width: 100%; width: 100%;
padding: 0 7px; padding: 0 7px;
color: $dropdown-input-color; color: $dropdown-input-color;
@ -358,6 +358,13 @@
border-top: 1px solid $dropdown-divider-color; border-top: 1px solid $dropdown-divider-color;
} }
.dropdown-due-date-footer {
padding-top: 0;
margin-left: 10px;
margin-right: 10px;
border-top: 0;
}
.dropdown-footer-list { .dropdown-footer-list {
font-size: 14px; font-size: 14px;
@ -395,3 +402,122 @@
height: 15px; height: 15px;
border-radius: $border-radius-base; border-radius: $border-radius-base;
} }
.dropdown-menu-due-date {
.dropdown-content {
max-height: 230px;
}
.ui-widget {
table {
margin: 0;
}
&.ui-datepicker-inline {
padding: 0 10px;
border: 0;
width: 100%;
}
.ui-datepicker-header {
padding: 0 8px 10px;
border: 0;
.ui-icon {
background: none;
font-size: 20px;
text-indent: 0;
&:before {
display: block;
position: relative;
top: -2px;
color: $dropdown-title-btn-color;
font: normal normal normal 14px/1 FontAwesome;
font-size: inherit;
text-rendering: auto;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
}
}
.ui-state-active,
.ui-state-hover {
color: $md-link-color;
background-color: $calendar-hover-bg;
}
.ui-datepicker-prev,
.ui-datepicker-next {
top: 0;
height: 15px;
cursor: pointer;
&:hover {
background-color: transparent;
border: 0;
.ui-icon:before {
color: $md-link-color;
}
}
}
.ui-datepicker-prev {
left: 0;
.ui-icon:before {
content: '\f104';
text-align: left;
}
}
.ui-datepicker-next {
right: 0;
.ui-icon:before {
content: '\f105';
text-align: right;
}
}
td {
padding: 0;
border: 1px solid $calendar-border-color;
&:first-child {
border-left: 0;
}
&:last-child {
border-right: 0;
}
a {
line-height: 17px;
border: 0;
border-radius: 0;
}
}
.ui-datepicker-title {
color: $gl-gray;
font-size: 15px;
line-height: 1;
font-weight: normal;
}
}
th {
padding: 2px 0;
color: $calendar-header-color;
font-weight: normal;
text-transform: lowercase;
border-top: 1px solid $calendar-border-color;
}
.ui-datepicker-unselectable {
background-color: $calendar-unselectable-bg;
}
}

View File

@ -38,12 +38,14 @@
.filename { .filename {
&.old { &.old {
display: inline-block;
span.idiff { span.idiff {
background-color: #f8cbcb; background-color: #f8cbcb;
} }
} }
&.new { &.new {
display: inline-block;
span.idiff { span.idiff {
background-color: #a6f3a6; background-color: #a6f3a6;
} }
@ -82,10 +84,6 @@
} }
} }
&.blob_file {
}
&.blob-no-preview { &.blob-no-preview {
background: #eee; background: #eee;
text-shadow: 0 1px 2px #fff; text-shadow: 0 1px 2px #fff;
@ -129,6 +127,11 @@
td.line-numbers { td.line-numbers {
float: none; float: none;
border-left: 1px solid #ddd; border-left: 1px solid #ddd;
i {
float: none;
margin-right: 0;
}
} }
td.lines { td.lines {
padding: 0; padding: 0;

View File

@ -78,6 +78,24 @@ label {
border-radius: 3px; border-radius: 3px;
} }
.select-wrapper {
position: relative;
.caret {
position: absolute;
right: 10px;
top: $gl-padding;
color: $gray-darkest;
pointer-events: none;
}
}
.select-control {
padding-left: 10px;
padding-right: 10px;
-webkit-appearance: none;
}
.form-control-inline { .form-control-inline {
display: inline; display: inline;
} }

View File

@ -24,6 +24,10 @@
background-color: $color-darker; background-color: $color-darker;
a { a {
color: #fff; color: #fff;
h3 {
color: #fff;
}
} }
} }
} }

View File

@ -8,7 +8,7 @@ header {
&.navbar-empty { &.navbar-empty {
height: 58px; height: 58px;
background: #fff; background: #fff;
border-bottom: 1px solid #eee; border-bottom: 1px solid $btn-gray-hover;
.center-logo { .center-logo {
margin: 11px 0; margin: 11px 0;
@ -22,13 +22,21 @@ header {
} }
&.navbar-gitlab { &.navbar-gitlab {
padding: 0 20px; padding: 0 16px;
z-index: 100; z-index: 100;
margin-bottom: 0; margin-bottom: 0;
min-height: $header-height; height: $header-height;
background-color: #fff; background-color: $background-color;
border: none; border: none;
border-bottom: 1px solid #eee; border-bottom: 1px solid $border-color;
@media (max-width: $screen-xs-min) {
padding: 0 16px;
}
&.with-horizontal-nav {
border-bottom: none;
}
.container-fluid { .container-fluid {
width: 100% !important; width: 100% !important;
@ -47,7 +55,7 @@ header {
text-align: center; text-align: center;
&:hover, &:focus, &:active { &:hover, &:focus, &:active {
background-color: #fff; background-color: $background-color;
} }
} }
@ -56,22 +64,54 @@ header {
margin: 6px 0; margin: 6px 0;
border-radius: 0; border-radius: 0;
position: absolute; position: absolute;
right: 2px; right: -10px;
padding: 6px 10px;
&:hover { &:hover {
background-color: #eee; background-color: $btn-gray-hover;
} }
&.active { &.active {
color: $gl-icon-color; color: $gl-icon-color;
} }
} }
} }
&.header-collapsed {
padding: 0 16px;
}
.side-nav-toggle {
display: none;
position: absolute;
left: -10px;
margin: 6px 0;
padding: 6px 10px;
border: none;
background-color: $background-color;
&:hover {
background-color: $btn-gray-hover;
}
&:focus {
outline: none;
}
@media (max-width: $screen-xs-min) {
display: block;
}
}
} }
.header-content { .header-content {
position: relative; position: relative;
height: $header-height; height: $header-height;
padding-right: 20px; padding-right: 40px;
@media (max-width: $screen-xs-min) {
padding-left: 40px;
}
@media (min-width: $screen-sm-min) { @media (min-width: $screen-sm-min) {
padding-right: 0; padding-right: 0;
@ -122,6 +162,10 @@ header {
} }
} }
.project-item-select-holder {
display: inline;
}
.impersonation i { .impersonation i {
color: $red-normal; color: $red-normal;
} }
@ -137,6 +181,10 @@ header {
@media (min-width: $screen-md-min) { @media (min-width: $screen-md-min) {
@include collapsed-header; @include collapsed-header;
} }
@media (max-width: $screen-xs-min) {
margin-left: 0;
}
} }
.header-expanded { .header-expanded {
@ -145,6 +193,10 @@ header {
@media (min-width: $screen-md-min) { @media (min-width: $screen-md-min) {
margin-left: $sidebar_width; margin-left: $sidebar_width;
} }
@media (max-width: $screen-xs-min) {
margin-left: 0;
}
} }
@media (max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {

View File

@ -5,7 +5,7 @@
*/ */
.status-box { .status-box {
/* Extra small devices (phones, less than 768px) */ /* Extra small devices (phones, less than 768px) */
/* No media query since this is the default in Bootstrap */ /* No media query since this is the default in Bootstrap */
padding: 5px 11px; padding: 5px 11px;

View File

@ -90,3 +90,12 @@
box-shadow: none; box-shadow: none;
width: 100%; width: 100%;
} }
.md {
&.md-preview-holder {
code {
white-space: pre-wrap;
word-break: keep-all;
}
}
}

View File

@ -30,7 +30,7 @@
} }
.rss-btn { .rss-btn {
display: none !important; display: none;
} }
.project-home-links { .project-home-links {
@ -70,13 +70,6 @@
display: none; display: none;
} }
.issue-details {
.creator,
.page-title .btn-close {
display: none;
}
}
%ul.notes .note-role, .note-actions { %ul.notes .note-role, .note-actions {
display: none; display: none;
} }

View File

@ -0,0 +1,22 @@
.modal-body {
position: relative;
overflow-y: auto;
padding: 15px;
.form-actions {
margin: -$gl-padding+1;
margin-top: 15px;
}
.text-danger {
font-weight: bold;
}
}
body.modal-open {
overflow: hidden;
}
.modal .modal-dialog {
width: 860px;
}

View File

@ -26,8 +26,8 @@
} }
&.active a { &.active a {
color: #000; border-bottom: 2px solid $link-underline-blue;
border-bottom: 2px solid #4688f1; color: $black;
} }
.badge { .badge {
@ -140,6 +140,12 @@
} }
} }
.project-filter-form {
input {
background-color: $background-color;
}
}
@media (max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {
padding-bottom: 0; padding-bottom: 0;
@ -185,3 +191,73 @@
} }
} }
} }
.layout-nav {
position: fixed;
top: $header-height;
width: 100%;
z-index: 1;
background: $background-color;
border-bottom: 1px solid $border-color;
transition-duration: .3s;
.container-fluid {
position: relative;
}
.controls {
float: right;
padding: 7px 0 0;
i {
color: $layout-link-gray;
}
.fa-rss,
.fa-cog {
font-size: 16px;
}
.fa-caret-down {
margin-left: 5px;
color: $gl-icon-color;
}
.dropdown {
margin-left: 7px;
}
}
.nav-links {
border-bottom: none;
height: 51px;
white-space: nowrap;
overflow-x: auto;
li {
a {
padding-top: 10px;
}
a, i {
color: $layout-link-gray;
}
&.active {
a, i {
color: $black;
}
}
.badge {
color: $gl-icon-color;
}
}
}
}
.page-with-layout-nav {
margin-top: 50px;
}

View File

@ -7,13 +7,11 @@
.select2-choice { .select2-choice {
background: #fff; background: #fff;
border-color: $input-border; border-color: $input-border;
border-color: $border-white-light;
height: 35px; height: 35px;
padding: $gl-vert-padding $gl-btn-padding; padding: $gl-vert-padding $gl-btn-padding;
font-size: $gl-font-size; font-size: $gl-font-size;
line-height: 1.42857143; line-height: 1.42857143;
border-radius: $border-radius-base;
@include border-radius($border-radius-default);
.select2-arrow { .select2-arrow {
background-image: none; background-image: none;
@ -121,9 +119,6 @@
} }
} }
.select2-container-multi .select2-choices .select2-search-choice {
}
.select2-drop-active { .select2-drop-active {
margin-top: 6px; margin-top: 6px;
font-size: 14px; font-size: 14px;
@ -202,6 +197,14 @@
} }
} }
.select2-highlighted {
.group-result {
.group-path {
color: #fff;
}
}
}
.group-result { .group-result {
.group-image { .group-image {
float: left; float: left;

View File

@ -3,6 +3,7 @@
position: absolute; position: absolute;
width: 58px; width: 58px;
cursor: pointer; cursor: pointer;
margin-top: 8px;
} }
.page-with-sidebar { .page-with-sidebar {
@ -62,7 +63,7 @@
float: left; float: left;
height: $header-height; height: $header-height;
width: 100%; width: 100%;
padding: 11px 0 11px 22px; padding-left: 22px;
overflow: hidden; overflow: hidden;
outline: none; outline: none;
transition-duration: .3s; transition-duration: .3s;
@ -85,7 +86,7 @@
margin: 0; margin: 0;
margin-left: 50px; margin-left: 50px;
font-size: 19px; font-size: 19px;
line-height: 41px; line-height: 50px;
font-weight: normal; font-weight: normal;
} }
} }
@ -97,7 +98,7 @@
} }
.sidebar-user { .sidebar-user {
padding: 9px 22px; padding: 7px 22px;
position: fixed; position: fixed;
bottom: 40px; bottom: 40px;
width: $sidebar_width; width: $sidebar_width;
@ -209,15 +210,33 @@
} }
} }
.sidebar-wrapper {
&.hidden-nav {
width: 0;
}
}
.page-sidebar-collapsed { .page-sidebar-collapsed {
padding-left: $sidebar_collapsed_width; padding-left: $sidebar_collapsed_width;
@media (max-width: $screen-xs-min) {
padding-left: 0;
}
.sidebar-wrapper { .sidebar-wrapper {
width: $sidebar_collapsed_width; width: $sidebar_collapsed_width;
@media (max-width: $screen-xs-min) {
width: 0;
}
.header-logo { .header-logo {
width: $sidebar_collapsed_width; width: $sidebar_collapsed_width;
@media (max-width: $screen-xs-min) {
width: 0;
}
a { a {
padding-left: ($sidebar_collapsed_width - 36) / 2; padding-left: ($sidebar_collapsed_width - 36) / 2;
@ -243,17 +262,35 @@
.collapse-nav a { .collapse-nav a {
width: $sidebar_collapsed_width; width: $sidebar_collapsed_width;
@media (max-width: $screen-xs-min) {
width: 0;
}
} }
.sidebar-user { .sidebar-user {
padding-left: ($sidebar_collapsed_width - 36) / 2; padding-left: ($sidebar_collapsed_width - 36) / 2;
width: $sidebar_collapsed_width; width: $sidebar_collapsed_width;
@media (max-width: $screen-xs-min) {
width: 0;
padding-left: 0;
padding-right: 0;
}
.username { .username {
display: none; display: none;
} }
} }
} }
.layout-nav {
padding-right: $sidebar_collapsed_width;
@media (max-width: $screen-xs-min) {
padding-right: 0;;
}
}
} }
.page-sidebar-expanded { .page-sidebar-expanded {
@ -263,6 +300,10 @@
padding-left: $sidebar_width; padding-left: $sidebar_width;
} }
@media (max-width: $screen-xs-min) {
padding-left: 0;
}
.sidebar-wrapper { .sidebar-wrapper {
width: $sidebar_width; width: $sidebar_width;
@ -280,6 +321,20 @@
} }
} }
} }
.layout-nav {
@media (max-width: $screen-xs-min) {
padding-right: 0;;
}
@media (min-width: $screen-xs-min) and (max-width: $screen-md-min) {
padding-right: 62px;
}
@media (min-width: $screen-md-min) {
padding-right: $sidebar_width;
}
}
} }
.right-sidebar-collapsed { .right-sidebar-collapsed {

View File

@ -32,13 +32,11 @@ table {
th { th {
background-color: $background-color; background-color: $background-color;
font-weight: normal; font-weight: normal;
font-size: 15px; border-bottom: none;
border-bottom: 1px solid $border-color;
} }
td { td {
border-color: $table-border-color; border-color: $table-border-color;
border-bottom: 1px solid $border-color;
} }
} }
} }

View File

@ -11,7 +11,7 @@
border-bottom: 1px solid $border-white-light; border-bottom: 1px solid $border-white-light;
&:target { &:target {
background: $row-hover; background: $line-target-blue;
} }
.avatar { .avatar {
@ -39,8 +39,7 @@
.diff-file { .diff-file {
border: 1px solid $border-color; border: 1px solid $border-color;
border-bottom: none; border-bottom: none;
margin-left: 0; margin: 0;
margin-right: 0;
} }
} }

View File

@ -81,7 +81,7 @@
// Labels // Labels
.label { .label {
padding: 2px 4px; padding: 4px 5px;
font-size: 13px; font-size: 13px;
font-style: normal; font-style: normal;
font-weight: normal; font-weight: normal;

View File

@ -153,8 +153,8 @@ $nav-link-padding: 13px $gl-padding;
//== Code //== Code
// //
//## //##
$pre-bg: #f8fafc !default; $pre-bg: $background-color !default;
$pre-color: $gl-gray !default; $pre-color: $gl-gray !default;
$pre-border-color: #e7e9ed; $pre-border-color: $border-color;
$table-bg-accent: $background-color; $table-bg-accent: $background-color;

View File

@ -42,14 +42,14 @@
margin: 24px 0 12px; margin: 24px 0 12px;
padding: 0 0 10px; padding: 0 0 10px;
border-bottom: 1px solid #e7e9ed; border-bottom: 1px solid #e7e9ed;
color: #313236; color: $gl-gray-dark;
} }
h2 { h2 {
font-size: 1.2em; font-size: 1.2em;
font-weight: 600; font-weight: 600;
margin: 24px 0 12px; margin: 24px 0 12px;
color: #313236; color: $gl-gray-dark;
} }
h3 { h3 {
@ -205,6 +205,10 @@ h1, h2, h3, h4, h5, h6 {
font-weight: 600; font-weight: 600;
} }
.light-header {
font-weight: 600;
}
/** CODE **/ /** CODE **/
pre { pre {
font-family: $monospace_font; font-family: $monospace_font;
@ -259,3 +263,9 @@ h1, h2, h3, h4 {
color: $gl-gray; color: $gl-gray;
} }
} }
.text-right-lg {
@media (min-width: $screen-lg-min) {
text-align: right;
}
}

View File

@ -12,7 +12,7 @@ $gutter_inner_width: 258px;
*/ */
$border-color: #e5e5e5; $border-color: #e5e5e5;
$focus-border-color: #3aabf0; $focus-border-color: #3aabf0;
$table-border-color: #eef0f2; $table-border-color: #ececec;
$background-color: #fafafa; $background-color: #fafafa;
/* /*
@ -20,7 +20,7 @@ $background-color: #fafafa;
*/ */
$gl-font-size: 15px; $gl-font-size: 15px;
$gl-title-color: #333; $gl-title-color: #333;
$gl-text-color: #555; $gl-text-color: #5c5c5c;
$gl-text-green: #4a2; $gl-text-green: #4a2;
$gl-text-red: #d12f19; $gl-text-red: #d12f19;
$gl-text-orange: #d90; $gl-text-orange: #d90;
@ -30,6 +30,7 @@ $gl-placeholder-color: #8f8f8f;
$gl-icon-color: $gl-placeholder-color; $gl-icon-color: $gl-placeholder-color;
$gl-grayish-blue: #7f8fa4; $gl-grayish-blue: #7f8fa4;
$gl-gray: $gl-text-color; $gl-gray: $gl-text-color;
$gl-gray-dark: #313236;
$gl-header-color: $gl-title-color; $gl-header-color: $gl-title-color;
/* /*
@ -65,16 +66,18 @@ $gl-padding-top: 10px;
$row-hover: #f4f8fe; $row-hover: #f4f8fe;
$progress-color: #c0392b; $progress-color: #c0392b;
$avatar_radius: 50%; $avatar_radius: 50%;
$header-height: 58px; $header-height: 50px;
$fixed-layout-width: 1280px; $fixed-layout-width: 1280px;
$gl-avatar-size: 40px; $gl-avatar-size: 40px;
$error-exclamation-point: #e62958; $error-exclamation-point: #e62958;
$border-radius-default: 2px; $border-radius-default: 2px;
$btn-transparent-color: #8f8f8f; $btn-transparent-color: #8f8f8f;
$ssh-key-icon-color: #8f8f8f; $settings-icon-size: 18px;
$ssh-key-icon-size: 18px;
$provider-btn-group-border: #e5e5e5; $provider-btn-group-border: #e5e5e5;
$provider-btn-not-active-color: #4688f1; $provider-btn-not-active-color: #4688f1;
$link-underline-blue: #4a8bee;
$layout-link-gray: #7e7c7c;
$todo-alert-blue: #428bca;
/* /*
* Color schema * Color schema
@ -109,6 +112,7 @@ $red-light: #e52c5a;
$red-normal: #d22852; $red-normal: #d22852;
$red-dark: darken($red-normal, 5%); $red-dark: darken($red-normal, 5%);
$black: #000;
$black-transparent: rgba(0, 0, 0, 0.3); $black-transparent: rgba(0, 0, 0, 0.3);
$border-white-light: #f1f2f4; $border-white-light: #f1f2f4;
@ -168,8 +172,12 @@ $line-removed: #fbe9eb;
$line-removed-dark: #fac5cd; $line-removed-dark: #fac5cd;
$line-number-old: #f9d7dc; $line-number-old: #f9d7dc;
$line-number-new: #ddfbe6; $line-number-new: #ddfbe6;
$line-number-select: #fbf2da;
$match-line: #fafafa; $match-line: #fafafa;
$table-border-gray: #f0f0f0; $table-border-gray: #f0f0f0;
$line-target-blue: #eaf3fc;
$line-select-yellow: #fcf8e7;
$line-select-yellow-dark: #f0e2bd;
/* /*
* Fonts * Fonts
*/ */
@ -179,7 +187,6 @@ $regular_font: 'Source Sans Pro', "Helvetica Neue", Helvetica, Arial, sans-serif
/* /*
* Dropdowns * Dropdowns
*/ */
$dropdown-border-radius: 2px;
$dropdown-width: 300px; $dropdown-width: 300px;
$dropdown-bg: #fff; $dropdown-bg: #fff;
$dropdown-link-color: #555; $dropdown-link-color: #555;
@ -208,6 +215,7 @@ $dropdown-toggle-hover-icon-color: $dropdown-toggle-hover-border-color;
$btn-active-gray: #ececec; $btn-active-gray: #ececec;
$btn-placeholder-gray: #c7c7c7; $btn-placeholder-gray: #c7c7c7;
$btn-white-active: #848484; $btn-white-active: #848484;
$btn-gray-hover: #eee;
/* /*
* Award emoji * Award emoji
@ -241,3 +249,8 @@ $note-form-border-color: #e5e5e5;
$note-toolbar-color: #959494; $note-toolbar-color: #959494;
$zen-control-hover-color: #111; $zen-control-hover-color: #111;
$calendar-header-color: #b8b8b8;
$calendar-hover-bg: #ecf3fe;
$calendar-border-color: rgba(#000, .1);
$calendar-unselectable-bg: #faf9f9;

View File

@ -111,8 +111,6 @@
.vg { color: #f8f8f2 } /* Name.Variable.Global */ .vg { color: #f8f8f2 } /* Name.Variable.Global */
.vi { color: #f8f8f2 } /* Name.Variable.Instance */ .vi { color: #f8f8f2 } /* Name.Variable.Instance */
.il { color: #ae81ff } /* Literal.Number.Integer.Long */ .il { color: #ae81ff } /* Literal.Number.Integer.Long */
.gh { } /* Generic Heading & Diff Header */
.gu { color: #75715e; } /* Generic.Subheading & Diff Unified/Comment? */ .gu { color: #75715e; } /* Generic.Subheading & Diff Unified/Comment? */
.gd { color: #f92672; } /* Generic.Deleted & Diff Deleted */ .gd { color: #f92672; } /* Generic.Deleted & Diff Deleted */
.gi { color: #a6e22e; } /* Generic.Inserted & Diff Inserted */ .gi { color: #a6e22e; } /* Generic.Inserted & Diff Inserted */

View File

@ -21,11 +21,6 @@
// Diff line // Diff line
.line_holder { .line_holder {
td.diff-line-num.hll:not(.empty-cell),
td.line_content.hll:not(.empty-cell) {
background-color: #f8eec7;
border-color: darken(#f8eec7, 15%);
}
.diff-line-num { .diff-line-num {
&.old { &.old {
@ -37,11 +32,16 @@
background-color: $line-number-new; background-color: $line-number-new;
border-color: $line-added-dark; border-color: $line-added-dark;
} }
&.hll:not(.empty-cell) {
background-color: $line-number-select;
border-color: $line-select-yellow-dark;
}
} }
.line_content { .line_content {
&.old { &.old {
background: $line-removed; background-color: $line-removed;
span.idiff { span.idiff {
background-color: $line-removed-dark; background-color: $line-removed-dark;
@ -58,7 +58,11 @@
&.match { &.match {
color: $black-transparent; color: $black-transparent;
background: $match-line; background-color: $match-line;
}
&.hll:not(.empty-cell) {
background-color: $line-select-yellow;
} }
} }
} }

View File

@ -83,3 +83,12 @@
} }
} }
} }
table.builds {
.build-link {
a {
color: $gl-dark-link-color;
}
}
}

View File

@ -31,9 +31,23 @@
} }
.commit-committer-link, .commit-committer-link,
.commit-author-link { .commit-author-link {
color: #444; color: $gl-gray;
font-weight: bold; font-weight: bold;
} }
.time_ago {
margin-left: 8px;
}
.fa-clipboard {
color: $dropdown-title-btn-color;
}
.commit-info {
&.branches {
margin-left: 8px;
}
}
} }
.commit-box { .commit-box {
@ -42,7 +56,7 @@
.commit-title { .commit-title {
margin: 0; margin: 0;
font-size: 23px; font-size: 23px;
color: #313236; color: $gl-gray-dark;
} }
.commit-description { .commit-description {
@ -83,6 +97,14 @@
} }
} }
.commit-action-buttons {
i {
color: $gl-icon-color;
font-size: 13px;
margin-right: 3px;
}
}
/* /*
* Commit message textarea for web editor and * Commit message textarea for web editor and
* custom merge request message * custom merge request message

View File

@ -0,0 +1,18 @@
.well-confirmation {
margin-bottom: 20px;
border-bottom: 1px solid #eee;
> h1 {
font-weight: 400;
}
.lead {
margin-bottom: 20px;
}
}
.confirmation-content {
a {
color: $md-link-color;
}
}

View File

@ -1,5 +1,5 @@
.detail-page-header { .detail-page-header {
padding: 11px 0; padding: $gl-padding-top 0;
border-bottom: 1px solid $border-color; border-bottom: 1px solid $border-color;
color: #5c5d5e; color: #5c5d5e;
font-size: 16px; font-size: 16px;
@ -16,18 +16,13 @@
.issue_created_ago, .author_link { .issue_created_ago, .author_link {
white-space: nowrap; white-space: nowrap;
} }
.issue-meta {
display: inline-block;
line-height: 20px;
}
} }
.detail-page-description { .detail-page-description {
.title { .title {
margin: 0; margin: 0;
font-size: 23px; font-size: 23px;
color: #313236; color: $gl-gray-dark;
} }
.description { .description {
@ -41,4 +36,11 @@
} }
} }
} }
.wiki {
code {
white-space: pre-wrap;
word-break: keep-all;
}
}
} }

View File

@ -34,6 +34,7 @@
background: #fff; background: #fff;
color: #333; color: #333;
border-radius: 0 0 3px 3px; border-radius: 0 0 3px 3px;
-webkit-overflow-scrolling: auto;
.unfold { .unfold {
cursor: pointer; cursor: pointer;
@ -86,7 +87,7 @@
} }
span { span {
white-space: pre; white-space: pre-wrap;
} }
} }
} }
@ -97,7 +98,11 @@
} }
td.line_content.parallel { td.line_content.parallel {
width: 50%; width: 46%;
}
.add-diff-note {
margin-left: -65px;
} }
} }
@ -126,8 +131,13 @@
margin: 0; margin: 0;
padding: 0 0.5em; padding: 0 0.5em;
border: none; border: none;
&.parallel { &.parallel {
display: table-cell; display: table-cell;
span {
word-break: break-all;
}
} }
} }
@ -335,7 +345,7 @@
} }
.diff-file .line_content { .diff-file .line_content {
white-space: pre; white-space: pre-wrap;
} }
.diff-wrap-lines .line_content { .diff-wrap-lines .line_content {

View File

@ -26,6 +26,10 @@
line-height: 42px; line-height: 42px;
padding-top: 7px; padding-top: 7px;
padding-bottom: 7px; padding-bottom: 7px;
.pull-right {
height: 20px;
}
} }
.editor-ref { .editor-ref {
@ -53,4 +57,9 @@
.select2 { .select2 {
float: right; float: right;
} }
.encoding-selector,
.license-selector {
display: inline-block;
}
} }

View File

@ -18,9 +18,6 @@
} }
.graphs { .graphs {
.graph-author-commits-count {
}
.graph-author-email { .graph-author-email {
float: right; float: right;
color: #777; color: #777;

View File

@ -55,23 +55,6 @@
} }
} }
.modal-body {
position: relative;
overflow-y: auto;
padding: 15px;
.form-actions {
margin: -$gl-padding+1;
}
}
body.modal-open {
overflow: hidden;
}
.modal .modal-dialog {
width: 860px;
}
.documentation { .documentation {
padding: 7px; padding: 7px;
} }

View File

@ -16,3 +16,24 @@ i.icon-gitorious-big {
width: 18px; width: 18px;
height: 18px; height: 18px;
} }
.import-jobs-from-col,
.import-jobs-to-col {
width: 40%;
}
.import-jobs-status-col {
width: 20%;
}
.btn-import {
.loading-icon {
display: none;
}
&.is-loading {
.loading-icon {
display: inline-block;
}
}
}

View File

@ -128,6 +128,7 @@
top: 58px; top: 58px;
bottom: 0; bottom: 0;
right: 0; right: 0;
z-index: 10;
transition: width .3s; transition: width .3s;
background: $gray-light; background: $gray-light;
padding: 10px 20px; padding: 10px 20px;
@ -241,16 +242,20 @@
} }
} }
.btn { .issuable-pager {
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;
} }
&.btn-primary {
@extend .btn-primary
}
} }
a:not(.btn) { a:not(.issuable-pager) {
&:hover { &:hover {
color: $md-link-color; color: $md-link-color;
text-decoration: none; text-decoration: none;
@ -273,10 +278,6 @@
} }
} }
.btn-default.gutter-toggle {
margin-top: 4px;
}
.detail-page-description { .detail-page-description {
small { small {
color: $gray-darkest; color: $gray-darkest;
@ -322,3 +323,50 @@
padding-top: 7px; padding-top: 7px;
} }
} }
.issuable-status-box {
float: none;
display: inline-block;
margin-top: 0;
@media (max-width: $screen-xs-max) {
position: absolute;
top: 0;
left: 0;
}
}
.issuable-header {
position: relative;
padding-left: 45px;
padding-right: 45px;
line-height: 35px;
@media (min-width: $screen-sm-min) {
float: left;
padding-left: 0;
padding-right: 0;
}
}
.issuable-actions {
padding-top: 10px;
@media (min-width: $screen-sm-min) {
float: right;
padding-top: 0;
}
}
.issuable-gutter-toggle {
@media (max-width: $screen-sm-max) {
position: absolute;
top: 0;
right: 0;
}
}
.issuable-meta {
display: inline-block;
line-height: 18px;
}

View File

@ -86,41 +86,9 @@ form.edit-issue {
@media (max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {
.issue-btn-group { .issue-btn-group {
width: 100%; width: 100%;
margin-top: 5px;
.btn-group {
width: 100%;
ul {
width: 100%;
text-align: center;
}
}
.btn { .btn {
width: 100%; width: 100%;
&:first-child:not(:last-child) {
}
&:not(:first-child):not(:last-child) {
margin-top: 10px;
}
&:last-child:not(:first-child) {
margin-top: 10px;
}
}
}
.issue {
&:hover .issue-actions {
display: none !important;
}
.issue-updated-at {
display: none;
} }
} }
} }
@ -133,11 +101,3 @@ form.edit-issue {
color: $gl-text-color; color: $gl-text-color;
margin-left: 52px; margin-left: 52px;
} }
.editor-details {
display: block;
@media (min-width: $screen-sm-min) {
display: inline-block;
}
}

View File

@ -104,7 +104,7 @@
font-weight: 600; font-weight: 600;
font-size: 17px; font-size: 17px;
margin: 5px 0; margin: 5px 0;
color: #313236; color: $gl-gray-dark;
} }
p:last-child { p:last-child {
@ -136,7 +136,7 @@
} }
.label-branch { .label-branch {
color: #313236; color: $gl-gray-dark;
font-family: $monospace_font; font-family: $monospace_font;
font-weight: bold; font-weight: bold;
overflow: hidden; overflow: hidden;
@ -272,3 +272,19 @@
display: inline-block; display: inline-block;
width: 250px; width: 250px;
} }
.table-holder {
.builds {
th {
background-color: $white-light;
color: $gl-placeholder-color;
}
th,
td {
padding: 16px;
}
}
}

View File

@ -28,7 +28,7 @@ li.milestone {
// Issue title // Issue title
span a { span a {
color: rgba(0,0,0,0.64); color: $gl-text-color;
} }
} }
} }
@ -51,7 +51,7 @@ li.milestone {
margin-top: 7px; margin-top: 7px;
.issuable-number { .issuable-number {
color: rgba(0,0,0,0.44); color: $gl-placeholder-color;
margin-right: 5px; margin-right: 5px;
} }
.avatar { .avatar {

View File

@ -42,6 +42,7 @@
.note-textarea { .note-textarea {
display: block; display: block;
padding: 10px 0; padding: 10px 0;
color: $gl-gray;
font-family: $regular_font; font-family: $regular_font;
border: 0; border: 0;
@ -61,11 +62,11 @@
padding: $gl-padding-top $gl-padding; padding: $gl-padding-top $gl-padding;
border: 1px solid $note-form-border-color; border: 1px solid $note-form-border-color;
border-radius: $border-radius-base; border-radius: $border-radius-base;
transition: border-color ease-in-out 0.15s,
box-shadow ease-in-out 0.15s;
&.is-focused { &.is-focused {
border-color: $focus-border-color; @extend .form-control:focus;
box-shadow: 0 0 2px $black-transparent,
0 0 4px rgba($focus-border-color, .4);
.comment-toolbar, .comment-toolbar,
.nav-links { .nav-links {
@ -83,18 +84,6 @@
border-color: $gl-success; border-color: $gl-success;
} }
} }
p {
code {
white-space: normal;
}
pre {
code {
white-space: pre;
}
}
}
} }
} }

View File

@ -80,16 +80,8 @@ ul.notes {
@include md-typography; @include md-typography;
// On diffs code should wrap nicely and not overflow // On diffs code should wrap nicely and not overflow
p { code {
code { white-space: pre-wrap;
white-space: normal;
}
pre {
code {
white-space: pre;
}
}
} }
// Reset ul style types since we're nested inside a ul already // Reset ul style types since we're nested inside a ul already
@ -116,10 +108,10 @@ ul.notes {
border-color: darken(#f5f5f5, 8%); border-color: darken(#f5f5f5, 8%);
margin: 10px 0; margin: 10px 0;
} }
}
a { code {
word-break: break-all; word-break: keep-all;
}
} }
} }
@ -136,7 +128,7 @@ ul.notes {
margin-right: 10px; margin-right: 10px;
} }
.line_content { .line_content {
white-space: pre; white-space: pre-wrap;
} }
} }
@ -175,6 +167,11 @@ ul.notes {
.notes { .notes {
background-color: $white-light; background-color: $white-light;
} }
a code {
top: 0;
margin-right: 0;
}
} }
} }
} }
@ -190,6 +187,9 @@ ul.notes {
} }
} }
.author_link {
color: $gl-gray;
}
} }
.note-headline-light, .note-headline-light,
@ -197,6 +197,12 @@ ul.notes {
color: $notes-light-color; color: $notes-light-color;
} }
.discussion-headline-light {
a {
color: $gl-link-color;
}
}
/** /**
* Actions for Discussions/Notes * Actions for Discussions/Notes
*/ */
@ -208,6 +214,17 @@ ul.notes {
color: $notes-action-color; color: $notes-action-color;
} }
.discussion-actions {
@media (max-width: $screen-md-max) {
float: none;
margin-left: 0;
.note-action-button {
margin-left: 0;
}
}
}
.note-action-button, .note-action-button,
.discussion-action-button { .discussion-action-button {
display: inline-block; display: inline-block;
@ -285,7 +302,7 @@ ul.notes {
padding: 4px; padding: 4px;
font-size: 16px; font-size: 16px;
color: $gl-link-color; color: $gl-link-color;
margin-left: -60px; margin-left: -56px;
position: absolute; position: absolute;
z-index: 10; z-index: 10;
width: 32px; width: 32px;

View File

@ -18,7 +18,8 @@
} }
.account-btn-link, .account-btn-link,
.profile-settings-sidebar a { .profile-settings-sidebar a,
.settings-sidebar a {
color: $md-link-color; color: $md-link-color;
} }
@ -123,12 +124,6 @@
} }
} }
.key-icon {
color: $ssh-key-icon-color;
font-size: $ssh-key-icon-size;
line-height: 42px;
}
.key-created-at { .key-created-at {
line-height: 42px; line-height: 42px;
} }
@ -180,14 +175,6 @@
} }
} }
.profile-settings-message {
line-height: 32px;
color: $warning-message-color;
background-color: $warning-message-bg;
border: 1px solid $warning-message-border;
border-radius: $border-radius-base;
}
.oauth-applications { .oauth-applications {
form { form {
display: inline-block; display: inline-block;
@ -218,3 +205,21 @@
text-align: center; text-align: center;
} }
} }
.user-profile {
@media (max-width: $screen-xs-max) {
.cover-block {
padding-top: 20px;
}
.cover-controls {
position: static;
margin-bottom: 20px;
.btn {
display: inline-block;
width: 46%;
}
}
}
}

View File

@ -178,7 +178,7 @@
.option-title { .option-title {
font-weight: normal; font-weight: normal;
display: inline-block; display: inline-block;
color: #313236; color: $gl-gray-dark;
} }
.option-descr { .option-descr {
@ -202,8 +202,31 @@
min-width: 200px; min-width: 200px;
} }
.deploy-project-label { .deploy-key-content {
margin: 1px; @media (min-width: $screen-sm-min) {
float: left;
&:last-child {
float: right;
}
}
}
.deploy-key-projects {
@media (min-width: $screen-sm-min) {
line-height: 42px;
}
}
a.deploy-project-label {
padding: 5px;
margin-right: 5px;
color: $gl-gray;
background-color: $row-hover;
&:hover {
color: $gl-link-color;
}
} }
.vs-public { .vs-public {
@ -256,12 +279,6 @@
} }
} }
table.table.protected-branches-list tr.no-border {
th, td {
border: 0;
}
}
.project-import .btn { .project-import .btn {
float: left; float: left;
margin-right: 10px; margin-right: 10px;
@ -474,3 +491,14 @@ pre.light-well {
color: #fff; color: #fff;
} }
} }
.protected-branches-list {
a {
color: $gl-gray;
font-weight: 600;
&:hover {
color: $gl-link-color;
}
}
}

View File

@ -10,17 +10,6 @@
} }
} }
.search-holder {
max-width: 600px;
margin: 0 auto;
margin-bottom: 20px;
input {
border-color: #bbb;
font-weight: bold;
}
}
.search { .search {
margin-right: 10px; margin-right: 10px;
margin-left: 10px; margin-left: 10px;
@ -159,7 +148,85 @@
&.has-location-badge { &.has-location-badge {
.search-input-wrap { .search-input-wrap {
width: 78%; width: 68%;
} }
} }
} }
.search-holder {
@media (min-width: $screen-sm-min) {
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
}
.search-field-holder {
-webkit-flex: 1 0 auto;
-ms-flex: 1 0 auto;
flex: 1 0 auto;
position: relative;
margin-right: 0;
@media (min-width: $screen-sm-min) {
margin-right: 5px;
}
}
.search-icon {
position: absolute;
left: 10px;
top: 10px;
color: $gray-darkest;
pointer-events: none;
}
.search-text-input {
padding-left: $gl-padding + 15px;
padding-right: $gl-padding + 15px;
}
.btn-search {
width: 100%;
margin-top: 5px;
@media (min-width: $screen-sm-min) {
width: auto;
margin-top: 0;
margin-left: 5px;
}
}
.dropdown {
@media (min-width: $screen-sm-min) {
margin-left: 5px;
margin-right: 5px;
}
}
.dropdown-menu-toggle {
width: 100%;
margin-top: 5px;
@media (min-width: $screen-sm-min) {
width: 160px;
margin-top: 0;
}
}
}
.search-clear {
position: absolute;
right: 10px;
top: 10px;
padding: 0;
color: $gray-darkest;
line-height: 0;
background: none;
border: 0;
&:hover,
&:focus {
color: $gl-link-color;
outline: none;
}
}

View File

@ -0,0 +1,14 @@
.settings-list-icon {
color: $gl-placeholder-color;
font-size: $settings-icon-size;
line-height: 42px;
}
.settings-message {
padding: 5px;
line-height: 1.3;
color: $warning-message-color;
background-color: $warning-message-bg;
border: 1px solid $warning-message-border;
border-radius: $border-radius-base;
}

View File

@ -1,7 +1,7 @@
.container-fluid { .container-fluid {
.ci-status { .ci-status {
padding: 2px 7px; padding: 2px 7px;
margin-right: 5px; margin-right: 10px;
border: 1px solid #eee; border: 1px solid #eee;
white-space: nowrap; white-space: nowrap;
@include border-radius(4px); @include border-radius(4px);

View File

@ -6,9 +6,16 @@
.navbar-nav { .navbar-nav {
li { li {
.badge.todos-pending-count { .badge.todos-pending-count {
background-color: $gl-icon-color;
margin-top: -5px; margin-top: -5px;
font-weight: normal; font-weight: normal;
background: $todo-alert-blue;
margin-left: -17px;
font-size: 11px;
color: white;
padding: 3px;
padding-top: 1px;
padding-bottom: 1px;
border-radius: 3px;
} }
} }
} }

View File

@ -16,7 +16,7 @@
tr { tr {
> td, > th { > td, > th {
line-height: 26px; line-height: 23px;
} }
&:hover { &:hover {

View File

@ -1,17 +1,37 @@
/* Generic print styles */
header, nav, nav.main-nav, nav.navbar-collapse, nav.navbar-collapse.collapse {display: none!important;}
.profiler-results {display: none;}
/* Styles targeted specifically at printing files */
.tree-ref-holder, .tree-holder .breadcrumb, .blob-commit-info {display: none;}
.file-title {display: none;}
.file-holder {border: none;}
.wiki h1, .wiki h2, .wiki h3, .wiki h4, .wiki h5, .wiki h6 {margin-top: 17px; } .wiki h1, .wiki h2, .wiki h3, .wiki h4, .wiki h5, .wiki h6 {margin-top: 17px; }
.wiki h1 {font-size: 30px;} .wiki h1 {font-size: 30px;}
.wiki h2 {font-size: 22px;} .wiki h2 {font-size: 22px;}
.wiki h3 {font-size: 18px; font-weight: bold; } .wiki h3 {font-size: 18px; font-weight: bold; }
.sidebar-wrapper { display: none; } header,
.nav { display: none; } nav,
.btn { display: none; } nav.main-nav,
nav.navbar-collapse,
nav.navbar-collapse.collapse,
.profiler-results,
.tree-ref-holder,
.tree-holder .breadcrumb,
.blob-commit-info,
.file-title,
.file-holder,
.sidebar-wrapper,
.nav,
.btn,
ul.notes-form,
.merge-request-ci-status .ci-status-link:after,
.issuable-gutter-toggle,
.gutter-toggle,
.issuable-details .content-block-small,
.edit-link,
.note-action-button {
display: none!important;
}
.page-gutter {
padding-top: 0;
padding-left: 0;
}
.right-sidebar {
top: 0;
}

View File

@ -6,12 +6,6 @@ class Admin::ApplicationController < ApplicationController
layout 'admin' layout 'admin'
def authenticate_admin! def authenticate_admin!
return render_404 unless current_user.is_admin? render_404 unless current_user.is_admin?
end
def authorize_impersonator!
if session[:impersonator_id]
User.find_by!(username: session[:impersonator_id]).admin?
end
end end
end end

View File

@ -75,6 +75,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:admin_notification_email, :admin_notification_email,
:user_oauth_applications, :user_oauth_applications,
:shared_runners_enabled, :shared_runners_enabled,
:shared_runners_text,
:max_artifacts_size, :max_artifacts_size,
:metrics_enabled, :metrics_enabled,
:metrics_host, :metrics_host,
@ -92,6 +93,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:akismet_api_key, :akismet_api_key,
:email_author_in_body, :email_author_in_body,
:repository_checks_enabled, :repository_checks_enabled,
:metrics_packet_size,
restricted_visibility_levels: [], restricted_visibility_levels: [],
import_sources: [] import_sources: []
) )

View File

@ -39,6 +39,12 @@ class Admin::HooksController < Admin::ApplicationController
end end
def hook_params def hook_params
params.require(:hook).permit(:url, :enable_ssl_verification) params.require(:hook).permit(
:enable_ssl_verification,
:push_events,
:tag_push_events,
:token,
:url
)
end end
end end

View File

@ -1,38 +0,0 @@
class Admin::ImpersonationController < Admin::ApplicationController
skip_before_action :authenticate_admin!, only: :destroy
before_action :user
before_action :authorize_impersonator!
def create
if @user.blocked?
flash[:alert] = "You cannot impersonate a blocked user"
redirect_to admin_user_path(@user)
else
session[:impersonator_id] = current_user.username
session[:impersonator_return_to] = admin_user_path(@user)
warden.set_user(user, scope: 'user')
flash[:alert] = "You are impersonating #{user.username}."
redirect_to root_path
end
end
def destroy
redirect = session[:impersonator_return_to]
warden.set_user(user, scope: 'user')
session[:impersonator_return_to] = nil
session[:impersonator_id] = nil
redirect_to redirect || root_path
end
def user
@user ||= User.find_by!(username: params[:id] || session[:impersonator_id])
end
end

View File

@ -0,0 +1,26 @@
class Admin::ImpersonationsController < Admin::ApplicationController
skip_before_action :authenticate_admin!
before_action :authenticate_impersonator!
def destroy
original_user = current_user
warden.set_user(impersonator, scope: :user)
Gitlab::AppLogger.info("User #{original_user.username} has stopped impersonating #{impersonator.username}")
session[:impersonator_id] = nil
redirect_to admin_user_path(original_user)
end
private
def impersonator
@impersonator ||= User.find(session[:impersonator_id]) if session[:impersonator_id]
end
def authenticate_impersonator!
render_404 unless impersonator && impersonator.is_admin? && !impersonator.blocked?
end
end

View File

@ -31,6 +31,24 @@ class Admin::UsersController < Admin::ApplicationController
user user
end end
def impersonate
if user.blocked?
flash[:alert] = "You cannot impersonate a blocked user"
redirect_to admin_user_path(user)
else
session[:impersonator_id] = current_user.id
warden.set_user(user, scope: :user)
Gitlab::AppLogger.info("User #{current_user.username} has started impersonating #{user.username}")
flash[:alert] = "You are now impersonating #{user.username}"
redirect_to root_path
end
end
def block def block
if user.block if user.block
redirect_back_or_admin_user(notice: "Successfully blocked") redirect_back_or_admin_user(notice: "Successfully blocked")

View File

@ -117,7 +117,7 @@ class ApplicationController < ActionController::Base
end end
def after_sign_out_path_for(resource) def after_sign_out_path_for(resource)
current_application_settings.after_sign_out_path || new_user_session_path current_application_settings.after_sign_out_path.presence || new_user_session_path
end end
def abilities def abilities

Some files were not shown because too many files have changed in this diff Show More