Merge branch 'master' into awardables
This commit is contained in:
		
						commit
						7e6dcf9cd0
					
				|  | @ -115,6 +115,11 @@ bundler:audit: | |||
|   script: | ||||
|     - "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 | ||||
| 
 | ||||
| spec:feature:ruby22: | ||||
|  |  | |||
|  | @ -728,7 +728,7 @@ Metrics/ParameterLists: | |||
| # A complexity metric geared towards measuring complexity for a human reader. | ||||
| Metrics/PerceivedComplexity: | ||||
|   Enabled: true | ||||
|   Max: 17 | ||||
|   Max: 18 | ||||
| 
 | ||||
| 
 | ||||
| #################### Lint ################################ | ||||
|  | @ -953,10 +953,9 @@ Performance/DoubleStartEndWith: | |||
| Performance/EndWith: | ||||
|   Enabled: false | ||||
| 
 | ||||
| # TODO: Enable LstripRstrip Cop. | ||||
| # Use `strip` instead of `lstrip.rstrip`. | ||||
| Performance/LstripRstrip: | ||||
|   Enabled: false | ||||
|   Enabled: true | ||||
| 
 | ||||
| # TODO: Enable RangeInclude Cop. | ||||
| # Use `Range#cover?` instead of `Range#include?`. | ||||
|  |  | |||
|  | @ -65,7 +65,7 @@ linters: | |||
|    | ||||
|   # Reports when you have an empty rule set. | ||||
|   EmptyRule: | ||||
|     enabled: false | ||||
|     enabled: true | ||||
|    | ||||
|   # Reports when you have an @extend directive. | ||||
|   ExtendDirective: | ||||
|  | @ -244,11 +244,11 @@ linters: | |||
|    | ||||
|   # URLs should be valid and not contain protocols or domain names. | ||||
|   UrlFormat: | ||||
|     enabled: false | ||||
|     enabled: true | ||||
| 
 | ||||
|   # URLs should always be enclosed within quotes. | ||||
|   UrlQuotes: | ||||
|     enabled: false | ||||
|     enabled: true | ||||
| 
 | ||||
|   # Properties, like color and font, are easier to read and maintain | ||||
|   # when defined using variables rather than literals. | ||||
|  |  | |||
							
								
								
									
										181
									
								
								CHANGELOG
								
								
								
								
							
							
						
						
									
										181
									
								
								CHANGELOG
								
								
								
								
							|  | @ -1,26 +1,105 @@ | |||
| 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 | ||||
|   - All service classes (those residing in app/services) are now instrumented | ||||
|   - Developers can now add custom tags to transactions | ||||
|   - 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) | ||||
|   - Add support to cherry-pick any commit into any branch in the web interface (Minqi Pan) | ||||
|   - 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) | ||||
|   - 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). | ||||
|   - 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 | ||||
|   - 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 | ||||
|   - 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) | ||||
|   - API: Ability to subscribe and unsubscribe from issues and merge requests (Robert Schilling) | ||||
|   - Expose project badges in project settings | ||||
|   - Make /profile/keys/new redirect to /profile/keys for back-compat. !3717 | ||||
|   - Preserve time notes/comments have been updated at when moving issue | ||||
|   - 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) | ||||
|   - API: Ability to update a group (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) | ||||
|   - Allow back dating on issues when created or updated 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 | ||||
|   - API: Expose `subscribed` for issues and merge requests (Robert Schilling) | ||||
|   - 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 | ||||
|   - Fix a bug whith trailing slash in bamboo_url | ||||
|   - 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) | ||||
|   - 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: Ability to star and unstar a project (Robert Schilling) | ||||
|   - 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) | ||||
|   - API: Ability to filter milestones by state `active` and `closed` (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) | ||||
|   - Implement 'Groups View' as an option for dashboard preferences !3379 (Elias W.) | ||||
|   - 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 | ||||
|   - Implement 'TODOs View' as an option for dashboard preferences !3379 (Elias W.) | ||||
|   - 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) | ||||
|   - Decouple membership and notifications | ||||
|   - Fix creation of merge requests for orphaned branches (Stan Hu) | ||||
|   - 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) | ||||
|   - Remove "Congratulations!" tweet button on newly-created project. (Connor Shea) | ||||
|   - Fix admin/projects when using visibility levels on search (PotHix) | ||||
|   - Build status notifications | ||||
|   - Update email confirmation interface | ||||
|   - API: Expose user location (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 | ||||
|   - 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: 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: Allow empty recipients list for builds emails service when pushed is added (Frank Groeneveld) | ||||
|   - 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) | ||||
|   - Add Slack notifications when Wiki is edited (Sebastian Klier) | ||||
|   - Diffs load at the correct point when linking from from number | ||||
|   - Selected diff rows highlight | ||||
|   - 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 | ||||
|   - Properly format all merge request references with ! rather than # !3740 (Ben Bodenmiller) | ||||
|   - 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 | ||||
|   - 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 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 | ||||
|   - Fix importing from GitHub Enterprise. !3529 | ||||
|  | @ -209,6 +335,20 @@ v 8.6.0 | |||
|   - Trigger a todo for mentions on commits page | ||||
|   - 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 | ||||
|   - 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) | ||||
|   - 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 | ||||
|   - Fix a 2FA authentication spoofing vulnerability. | ||||
| 
 | ||||
|  | @ -478,6 +632,18 @@ v 8.4.0 | |||
|   - Add IP check against DNSBLs at account sign-up | ||||
|   - 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 | ||||
|   - Fix a 2FA authentication spoofing vulnerability. | ||||
| 
 | ||||
|  | @ -584,6 +750,17 @@ v 8.3.0 | |||
|   - Expose Git's version in the admin area | ||||
|   - 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 | ||||
|   - Fix application settings cache not expiring after changes (Stan Hu) | ||||
|   - Fix Error 500s when creating global milestones with Unicode characters (Stan Hu) | ||||
|  |  | |||
|  | @ -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 | ||||
| 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). | ||||
| 
 | ||||
| ## 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 | ||||
| [`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] | ||||
| members to add the label `feature proposal` to the issue. | ||||
| 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 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 | ||||
| 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. | ||||
| If you want to create something yourself, consider opening an issue first to | ||||
| 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) | ||||
| 1. If your code creates new files on disk please read the | ||||
|    [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 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 | ||||
| [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 | ||||
| [core team][core-team] or one of the | ||||
| [Merge request coaches](https://about.gitlab.com/team/). | ||||
| [core team] or one of the [Merge request coaches](https://about.gitlab.com/team/). | ||||
| Please ensure that your merge request meets the contribution acceptance criteria. | ||||
| 
 | ||||
| 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 | ||||
| 
 | ||||
|  | @ -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, | ||||
| 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/ | ||||
| [codetriage]: http://www.codetriage.com/gitlabhq/gitlabhq | ||||
| [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 | ||||
| [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/ | ||||
| [Thoughtbot code review guide]: https://github.com/thoughtbot/guides/tree/master/code-review | ||||
|  |  | |||
							
								
								
									
										18
									
								
								Gemfile
								
								
								
								
							
							
						
						
									
										18
									
								
								Gemfile
								
								
								
								
							|  | @ -19,8 +19,8 @@ gem "pg", '~> 0.18.2', group: :postgres | |||
| 
 | ||||
| # Authentication libraries | ||||
| gem 'devise',                 '~> 3.5.4' | ||||
| gem 'doorkeeper',             '~> 3.1' | ||||
| gem 'devise-async',           '~> 0.9.0' | ||||
| gem 'doorkeeper',             '~> 2.2.0' | ||||
| gem 'omniauth',               '~> 1.3.1' | ||||
| gem 'omniauth-auth0',         '~> 1.4.1' | ||||
| gem 'omniauth-azure-oauth2',  '~> 0.0.6' | ||||
|  | @ -178,7 +178,7 @@ gem 'ruby-fogbugz', '~> 0.2.1' | |||
| gem 'd3_rails', '~> 3.5.0' | ||||
| 
 | ||||
| #cal-heatmap | ||||
| gem 'cal-heatmap-rails', '~> 3.5.0' | ||||
| gem 'cal-heatmap-rails', '~> 3.6.0' | ||||
| 
 | ||||
| # underscore-rails | ||||
| gem "underscore-rails", "~> 1.8.0" | ||||
|  | @ -190,6 +190,9 @@ gem 'babosa', '~> 1.0.2' | |||
| # Sanitizes SVG input | ||||
| gem "loofah", "~> 2.0.3" | ||||
| 
 | ||||
| # Working with license | ||||
| gem 'licensee', '~> 8.0.0' | ||||
| 
 | ||||
| # Protect against bruteforcing | ||||
| gem "rack-attack", '~> 4.3.1' | ||||
| 
 | ||||
|  | @ -214,7 +217,7 @@ gem 'font-awesome-rails', '~> 4.2' | |||
| gem 'gitlab_emoji',       '~> 0.3.0' | ||||
| gem 'gon',                '~> 6.0.1' | ||||
| 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-ui-rails',    '~> 5.0.0' | ||||
| gem 'raphael-rails',      '~> 2.1.2' | ||||
|  | @ -239,8 +242,7 @@ group :development do | |||
|   gem "foreman" | ||||
|   gem 'brakeman', '~> 3.2.0', require: false | ||||
| 
 | ||||
|   gem "annotate", "~> 2.7.0" | ||||
|   gem "letter_opener", '~> 1.1.2' | ||||
|   gem 'letter_opener_web', '~> 1.3.0' | ||||
|   gem 'quiet_assets', '~> 1.0.2' | ||||
|   gem 'rerun', '~> 0.11.0' | ||||
|   gem 'bullet', require: false | ||||
|  | @ -267,7 +269,7 @@ group :development, :test do | |||
| 
 | ||||
|   gem 'database_cleaner',   '~> 1.4.0' | ||||
|   gem 'factory_girl_rails', '~> 4.6.0' | ||||
|   gem 'rspec-rails',        '~> 3.3.0' | ||||
|   gem 'rspec-rails',        '~> 3.4.0' | ||||
|   gem 'rspec-retry' | ||||
|   gem 'spinach-rails',      '~> 0.2.1' | ||||
|   gem 'spinach-rerun-reporter', '~> 0.0.2' | ||||
|  | @ -315,9 +317,9 @@ end | |||
| 
 | ||||
| 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' | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										98
									
								
								Gemfile.lock
								
								
								
								
							
							
						
						
									
										98
									
								
								Gemfile.lock
								
								
								
								
							|  | @ -51,9 +51,6 @@ GEM | |||
|       activerecord (>= 3.0) | ||||
|     akismet (2.0.0) | ||||
|     allocations (1.0.4) | ||||
|     annotate (2.7.0) | ||||
|       activerecord (>= 3.2, < 6.0) | ||||
|       rake (~> 10.4) | ||||
|     arel (6.0.3) | ||||
|     asana (0.4.0) | ||||
|       faraday (~> 0.9) | ||||
|  | @ -103,7 +100,7 @@ GEM | |||
|       bundler (~> 1.2) | ||||
|       thor (~> 0.18) | ||||
|     byebug (8.2.1) | ||||
|     cal-heatmap-rails (3.5.1) | ||||
|     cal-heatmap-rails (3.6.0) | ||||
|     capybara (2.6.2) | ||||
|       addressable | ||||
|       mime-types (>= 1.16) | ||||
|  | @ -134,7 +131,7 @@ GEM | |||
|       execjs | ||||
|     coffee-script-source (1.10.0) | ||||
|     colorize (0.7.7) | ||||
|     concurrent-ruby (1.0.0) | ||||
|     concurrent-ruby (1.0.2) | ||||
|     connection_pool (2.2.0) | ||||
|     coveralls (0.8.13) | ||||
|       json (~> 1.8) | ||||
|  | @ -175,7 +172,7 @@ GEM | |||
|     diff-lcs (1.2.5) | ||||
|     diffy (3.0.7) | ||||
|     docile (1.1.5) | ||||
|     doorkeeper (2.2.2) | ||||
|     doorkeeper (3.1.0) | ||||
|       railties (>= 3.2) | ||||
|     dropzonejs-rails (0.7.2) | ||||
|       rails (> 3.1) | ||||
|  | @ -186,7 +183,7 @@ GEM | |||
|     encryptor (1.3.0) | ||||
|     equalizer (0.0.11) | ||||
|     erubis (2.7.0) | ||||
|     escape_utils (1.1.0) | ||||
|     escape_utils (1.1.1) | ||||
|     eventmachine (1.0.8) | ||||
|     excon (0.45.4) | ||||
|     execjs (2.6.0) | ||||
|  | @ -336,7 +333,7 @@ GEM | |||
|       json | ||||
|     get_process_mem (0.2.0) | ||||
|     gherkin-ruby (0.3.2) | ||||
|     github-linguist (4.7.5) | ||||
|     github-linguist (4.7.6) | ||||
|       charlock_holmes (~> 0.7.3) | ||||
|       escape_utils (~> 1.1.0) | ||||
|       mime-types (>= 1.19) | ||||
|  | @ -346,14 +343,14 @@ GEM | |||
|       flowdock (~> 0.7) | ||||
|       gitlab-grit (>= 2.4.1) | ||||
|       multi_json | ||||
|     gitlab-grit (2.7.3) | ||||
|     gitlab-grit (2.8.1) | ||||
|       charlock_holmes (~> 0.6) | ||||
|       diff-lcs (~> 1.1) | ||||
|       mime-types (~> 1.15) | ||||
|       mime-types (>= 1.16, < 3) | ||||
|       posix-spawn (~> 0.3) | ||||
|     gitlab_emoji (0.3.1) | ||||
|       gemojione (~> 2.2, >= 2.2.1) | ||||
|     gitlab_git (10.0.0) | ||||
|     gitlab_git (10.1.0) | ||||
|       activesupport (~> 4.0) | ||||
|       charlock_holmes (~> 0.7.3) | ||||
|       github-linguist (~> 4.7.0) | ||||
|  | @ -431,8 +428,8 @@ GEM | |||
|       json | ||||
|     ipaddress (0.8.2) | ||||
|     jquery-atwho-rails (1.3.2) | ||||
|     jquery-rails (4.0.5) | ||||
|       rails-dom-testing (~> 1.0) | ||||
|     jquery-rails (4.1.1) | ||||
|       rails-dom-testing (>= 1, < 3) | ||||
|       railties (>= 4.2.0) | ||||
|       thor (>= 0.14, < 2.0) | ||||
|     jquery-scrollto-rails (1.4.3) | ||||
|  | @ -450,8 +447,14 @@ GEM | |||
|     kgio (2.10.0) | ||||
|     launchy (2.4.3) | ||||
|       addressable (~> 2.3) | ||||
|     letter_opener (1.1.2) | ||||
|     letter_opener (1.4.1) | ||||
|       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) | ||||
|       rb-fsevent (>= 0.9.3) | ||||
|       rb-inotify (>= 0.9) | ||||
|  | @ -461,9 +464,9 @@ GEM | |||
|       systemu (~> 2.6.2) | ||||
|     mail (2.6.4) | ||||
|       mime-types (>= 1.16, < 4) | ||||
|     mail_room (0.6.1) | ||||
|     mail_room (0.7.0) | ||||
|     method_source (0.8.2) | ||||
|     mime-types (1.25.1) | ||||
|     mime-types (2.99.1) | ||||
|     mimemagic (0.3.0) | ||||
|     mini_portile2 (2.0.0) | ||||
|     minitest (5.7.0) | ||||
|  | @ -485,8 +488,8 @@ GEM | |||
|       multi_json (~> 1.3) | ||||
|       multi_xml (~> 0.5) | ||||
|       rack (~> 1.2) | ||||
|     octokit (3.8.0) | ||||
|       sawyer (~> 0.6.0, >= 0.5.3) | ||||
|     octokit (4.3.0) | ||||
|       sawyer (~> 0.7.0, >= 0.5.3) | ||||
|     omniauth (1.3.1) | ||||
|       hashie (>= 1.2, < 4) | ||||
|       rack (>= 1.0, < 3) | ||||
|  | @ -627,7 +630,7 @@ GEM | |||
|     recaptcha (1.0.2) | ||||
|       json | ||||
|     redcarpet (3.3.3) | ||||
|     redis (3.2.2) | ||||
|     redis (3.3.0) | ||||
|     redis-actionpack (4.0.1) | ||||
|       actionpack (~> 4) | ||||
|       redis-rack (~> 1.5.0) | ||||
|  | @ -658,29 +661,29 @@ GEM | |||
|       chunky_png | ||||
|     rqrcode-rails3 (0.1.7) | ||||
|       rqrcode (>= 0.4.2) | ||||
|     rspec (3.3.0) | ||||
|       rspec-core (~> 3.3.0) | ||||
|       rspec-expectations (~> 3.3.0) | ||||
|       rspec-mocks (~> 3.3.0) | ||||
|     rspec-core (3.3.2) | ||||
|       rspec-support (~> 3.3.0) | ||||
|     rspec-expectations (3.3.1) | ||||
|     rspec (3.4.0) | ||||
|       rspec-core (~> 3.4.0) | ||||
|       rspec-expectations (~> 3.4.0) | ||||
|       rspec-mocks (~> 3.4.0) | ||||
|     rspec-core (3.4.4) | ||||
|       rspec-support (~> 3.4.0) | ||||
|     rspec-expectations (3.4.0) | ||||
|       diff-lcs (>= 1.2.0, < 2.0) | ||||
|       rspec-support (~> 3.3.0) | ||||
|     rspec-mocks (3.3.2) | ||||
|       rspec-support (~> 3.4.0) | ||||
|     rspec-mocks (3.4.1) | ||||
|       diff-lcs (>= 1.2.0, < 2.0) | ||||
|       rspec-support (~> 3.3.0) | ||||
|     rspec-rails (3.3.3) | ||||
|       rspec-support (~> 3.4.0) | ||||
|     rspec-rails (3.4.2) | ||||
|       actionpack (>= 3.0, < 4.3) | ||||
|       activesupport (>= 3.0, < 4.3) | ||||
|       railties (>= 3.0, < 4.3) | ||||
|       rspec-core (~> 3.3.0) | ||||
|       rspec-expectations (~> 3.3.0) | ||||
|       rspec-mocks (~> 3.3.0) | ||||
|       rspec-support (~> 3.3.0) | ||||
|       rspec-core (~> 3.4.0) | ||||
|       rspec-expectations (~> 3.4.0) | ||||
|       rspec-mocks (~> 3.4.0) | ||||
|       rspec-support (~> 3.4.0) | ||||
|     rspec-retry (0.4.5) | ||||
|       rspec-core | ||||
|     rspec-support (3.3.0) | ||||
|     rspec-support (3.4.1) | ||||
|     rubocop (0.38.0) | ||||
|       parser (>= 2.3.0.6, < 3.0) | ||||
|       powerpack (~> 0.1) | ||||
|  | @ -712,8 +715,8 @@ GEM | |||
|       sprockets (>= 2.8, < 4.0) | ||||
|       sprockets-rails (>= 2.0, < 4.0) | ||||
|       tilt (>= 1.1, < 3) | ||||
|     sawyer (0.6.0) | ||||
|       addressable (~> 2.3.5) | ||||
|     sawyer (0.7.0) | ||||
|       addressable (>= 2.3.5, < 2.5) | ||||
|       faraday (~> 0.8, < 0.10) | ||||
|     scss_lint (0.47.1) | ||||
|       rake (>= 0.9, < 11) | ||||
|  | @ -734,10 +737,9 @@ GEM | |||
|       rack | ||||
|     shoulda-matchers (2.8.0) | ||||
|       activesupport (>= 3.0.0) | ||||
|     sidekiq (4.0.1) | ||||
|     sidekiq (4.1.2) | ||||
|       concurrent-ruby (~> 1.0) | ||||
|       connection_pool (~> 2.2, >= 2.2.0) | ||||
|       json (~> 1.0) | ||||
|       redis (~> 3.2, >= 3.2.1) | ||||
|     sidekiq-cron (0.4.0) | ||||
|       redis-namespace (>= 1.5.2) | ||||
|  | @ -888,7 +890,6 @@ DEPENDENCIES | |||
|   after_commit_queue | ||||
|   akismet (~> 2.0) | ||||
|   allocations (~> 1.0) | ||||
|   annotate (~> 2.7.0) | ||||
|   asana (~> 0.4.0) | ||||
|   asciidoctor (~> 1.5.2) | ||||
|   attr_encrypted (~> 1.3.4) | ||||
|  | @ -903,7 +904,7 @@ DEPENDENCIES | |||
|   bullet | ||||
|   bundler-audit | ||||
|   byebug | ||||
|   cal-heatmap-rails (~> 3.5.0) | ||||
|   cal-heatmap-rails (~> 3.6.0) | ||||
|   capybara (~> 2.6.2) | ||||
|   capybara-screenshot (~> 1.0.0) | ||||
|   carrierwave (~> 0.10.0) | ||||
|  | @ -920,7 +921,7 @@ DEPENDENCIES | |||
|   devise-async (~> 0.9.0) | ||||
|   devise-two-factor (~> 2.0.0) | ||||
|   diffy (~> 3.0.3) | ||||
|   doorkeeper (~> 2.2.0) | ||||
|   doorkeeper (~> 3.1) | ||||
|   dropzonejs-rails (~> 0.7.1) | ||||
|   email_reply_parser (~> 0.5.8) | ||||
|   email_spec (~> 1.6.0) | ||||
|  | @ -951,14 +952,15 @@ DEPENDENCIES | |||
|   httparty (~> 0.13.3) | ||||
|   influxdb (~> 0.2) | ||||
|   jquery-atwho-rails (~> 1.3.2) | ||||
|   jquery-rails (~> 4.0.0) | ||||
|   jquery-rails (~> 4.1.0) | ||||
|   jquery-scrollto-rails (~> 1.4.3) | ||||
|   jquery-turbolinks (~> 2.1.0) | ||||
|   jquery-ui-rails (~> 5.0.0) | ||||
|   kaminari (~> 0.16.3) | ||||
|   letter_opener (~> 1.1.2) | ||||
|   letter_opener_web (~> 1.3.0) | ||||
|   licensee (~> 8.0.0) | ||||
|   loofah (~> 2.0.3) | ||||
|   mail_room (~> 0.6.1) | ||||
|   mail_room (~> 0.7) | ||||
|   method_source (~> 0.8) | ||||
|   minitest (~> 5.7.0) | ||||
|   mousetrap-rails (~> 1.4.6) | ||||
|  | @ -968,7 +970,7 @@ DEPENDENCIES | |||
|   newrelic_rpm (~> 3.14) | ||||
|   nokogiri (~> 1.6.7, >= 1.6.7.2) | ||||
|   oauth2 (~> 1.0.0) | ||||
|   octokit (~> 3.8.0) | ||||
|   octokit (~> 4.3.0) | ||||
|   omniauth (~> 1.3.1) | ||||
|   omniauth-auth0 (~> 1.4.1) | ||||
|   omniauth-azure-oauth2 (~> 0.0.6) | ||||
|  | @ -1008,7 +1010,7 @@ DEPENDENCIES | |||
|   responders (~> 2.0) | ||||
|   rouge (~> 1.10.1) | ||||
|   rqrcode-rails3 (~> 0.1.7) | ||||
|   rspec-rails (~> 3.3.0) | ||||
|   rspec-rails (~> 3.4.0) | ||||
|   rspec-retry | ||||
|   rubocop (~> 0.38.0) | ||||
|   ruby-fogbugz (~> 0.2.1) | ||||
|  | @ -1055,4 +1057,4 @@ DEPENDENCIES | |||
|   wikicloth (= 0.8.1) | ||||
| 
 | ||||
| BUNDLED WITH | ||||
|    1.11.2 | ||||
|    1.12.3 | ||||
|  |  | |||
							
								
								
									
										22
									
								
								PROCESS.md
								
								
								
								
							
							
						
						
									
										22
									
								
								PROCESS.md
								
								
								
								
							|  | @ -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 | ||||
| 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 | ||||
| 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. | ||||
| - ~customer is an issue reported by enterprise subscribers. This label should | ||||
| 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. | ||||
| 
 | ||||
| ## 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 | ||||
| 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 | ||||
| 
 | ||||
| ### Improperly formatted issue | ||||
|  |  | |||
							
								
								
									
										12
									
								
								README.md
								
								
								
								
							
							
						
						
									
										12
									
								
								README.md
								
								
								
								
							|  | @ -1,6 +1,6 @@ | |||
| # GitLab | ||||
| 
 | ||||
| [](https://ci.gitlab.com/projects/1?ref=master) | ||||
| [](https://gitlab.com/gitlab-org/gitlab-ce/commits/master) | ||||
| [](https://semaphoreci.com/gitlabhq/gitlabhq) | ||||
| [](https://codeclimate.com/github/gitlabhq/gitlabhq) | ||||
| [](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) | ||||
| - 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 | ||||
| 
 | ||||
| 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: | ||||
| 
 | ||||
| - [Subscriptions](https://about.gitlab.com/subscription/) | ||||
| - [Subscriptions](https://about.gitlab.com/pricing/) | ||||
| - [Consultancy](https://about.gitlab.com/consultancy/) | ||||
| - [Community](https://about.gitlab.com/community/) | ||||
| - [Hosted GitLab.com](https://about.gitlab.com/gitlab-com/) use GitLab as a free service | ||||
| - [GitLab Enterprise Edition](https://about.gitlab.com/gitlab-ee/) with additional features aimed at larger organizations. | ||||
| - [GitLab 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. | ||||
| 
 | ||||
| ## Requirements | ||||
|  | @ -80,7 +84,7 @@ There are a lot of [third-party applications integrating with GitLab](https://ab | |||
| 
 | ||||
| ## 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 | ||||
| 
 | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ | |||
|   group_projects_path: "/api/:version/groups/:id/projects.json" | ||||
|   projects_path: "/api/:version/projects.json" | ||||
|   labels_path: "/api/:version/projects/:id/labels" | ||||
|   license_path: "/api/:version/licenses/:key" | ||||
| 
 | ||||
|   group: (group_id, callback) -> | ||||
|     url = Api.buildUrl(Api.group_path) | ||||
|  | @ -92,6 +93,16 @@ | |||
|     ).done (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) -> | ||||
|     url = gon.relative_url_root + url if gon.relative_url_root? | ||||
|     return url.replace(':version', gon.api_version) | ||||
|  |  | |||
|  | @ -174,7 +174,7 @@ $ -> | |||
|   $('.trigger-submit').on 'change', -> | ||||
|     $(@).parents('form').submit() | ||||
| 
 | ||||
|   gl.utils.localTimeAgo($('abbr.timeago, .js-timeago'), false) | ||||
|   gl.utils.localTimeAgo($('abbr.timeago, .js-timeago'), true) | ||||
| 
 | ||||
|   # Flash | ||||
|   if (flash = $(".flash-container")).length > 0 | ||||
|  | @ -204,6 +204,7 @@ $ -> | |||
|     $('.header-content .title').toggle() | ||||
|     $('.header-content .navbar-collapse').toggle() | ||||
|     $('.navbar-toggle').toggleClass('active') | ||||
|     $('.navbar-toggle i').toggleClass("fa-angle-right fa-angle-left") | ||||
| 
 | ||||
|   # Show/hide comments on diff | ||||
|   $("body").on "click", ".js-toggle-diff-comments", (e) -> | ||||
|  |  | |||
|  | @ -105,7 +105,7 @@ class @AwardsHandler | |||
|     @postEmoji awardUrl, emoji, => | ||||
|       @addAwardToEmojiBar(emoji) | ||||
| 
 | ||||
|     $(".emoji-menu").removeClass "is-visible" | ||||
|     $('.emoji-menu').removeClass 'is-visible' | ||||
| 
 | ||||
|   addAwardToEmojiBar: (emoji) -> | ||||
|     @addEmojiToFrequentlyUsedList(emoji) | ||||
|  | @ -168,7 +168,7 @@ class @AwardsHandler | |||
|     @resetTooltip(award_block) | ||||
| 
 | ||||
|   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. | ||||
|     setTimeout (-> | ||||
|  | @ -194,13 +194,13 @@ class @AwardsHandler | |||
|       $currentBlock.removeClass 'hidden' | ||||
| 
 | ||||
|   resolveNameToCssClass: (emoji) -> | ||||
|     emoji_icon = $(".emoji-menu-content [data-emoji='#{emoji}']") | ||||
|     emojiIcon = $(".emoji-menu-content [data-emoji='#{emoji}']") | ||||
| 
 | ||||
|     if emoji_icon.length > 0 | ||||
|       unicodeName = emoji_icon.data("unicode-name") | ||||
|     if emojiIcon.length > 0 | ||||
|       unicodeName = emojiIcon.data('unicode-name') | ||||
|     else | ||||
|       # 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}" | ||||
| 
 | ||||
|  | @ -217,45 +217,42 @@ class @AwardsHandler | |||
|       scrollTop: $('.awards').offset().top - 80 | ||||
|     }, 200) | ||||
| 
 | ||||
|   normilizeEmojiName: (emoji) -> | ||||
|     @aliases[emoji] || emoji | ||||
| 
 | ||||
|   addEmojiToFrequentlyUsedList: (emoji) -> | ||||
|     frequently_used_emojis = @getFrequentlyUsedEmojis() | ||||
|     frequently_used_emojis.push(emoji) | ||||
|     $.cookie('frequently_used_emojis', frequently_used_emojis.join(","), { expires: 365 }) | ||||
|     frequentlyUsedEmojis = @getFrequentlyUsedEmojis() | ||||
|     frequentlyUsedEmojis.push(emoji) | ||||
|     $.cookie('frequently_used_emojis', frequentlyUsedEmojis.join(','), { expires: 365 }) | ||||
| 
 | ||||
|   getFrequentlyUsedEmojis: -> | ||||
|     frequently_used_emojis = ($.cookie('frequently_used_emojis') || "").split(",") | ||||
|     _.compact(_.uniq(frequently_used_emojis)) | ||||
|     frequentlyUsedEmojis = ($.cookie('frequently_used_emojis') || '').split(',') | ||||
|     _.compact(_.uniq(frequentlyUsedEmojis)) | ||||
| 
 | ||||
|   renderFrequentlyUsedBlock: -> | ||||
|     if $.cookie('frequently_used_emojis') | ||||
|       frequently_used_emojis = @getFrequentlyUsedEmojis() | ||||
|       frequentlyUsedEmojis = @getFrequentlyUsedEmojis() | ||||
| 
 | ||||
|       ul = $("<ul class='clearfix emoji-menu-list'>") | ||||
| 
 | ||||
|       for emoji in frequently_used_emojis | ||||
|         $(".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: -> | ||||
|     $("input.emoji-search").on 'keyup', (ev) => | ||||
|     $('input.emoji-search').keyup (ev) => | ||||
|       term = $(ev.target).val() | ||||
| 
 | ||||
|       # Clean previous search results | ||||
|       $("ul.emoji-menu-search, h5.emoji-search").remove() | ||||
|       $('ul.emoji-menu-search, h5.emoji-search').remove() | ||||
| 
 | ||||
|       if term | ||||
|         # Generate a search result block | ||||
|         h5 = $("<h5>").text("Search results").addClass("emoji-search") | ||||
|         found_emojis = @searchEmojis(term).show() | ||||
|         ul = $("<ul>").addClass("emoji-menu-list emoji-menu-search").append(found_emojis) | ||||
|         $(".emoji-menu-content ul, .emoji-menu-content h5").hide() | ||||
|         $(".emoji-menu-content").append(h5).append(ul) | ||||
|         h5 = $('<h5>').text('Search results').addClass('emoji-search') | ||||
|         foundEmojis = @searchEmojis(term).show() | ||||
|         ul = $('<ul>').addClass('emoji-menu-list emoji-menu-search').append(foundEmojis) | ||||
|         $('.emoji-menu-content ul, .emoji-menu-content h5').hide() | ||||
|         $('.emoji-menu-content').append(h5).append(ul) | ||||
|       else | ||||
|         $(".emoji-menu-content").children().show() | ||||
|         $('.emoji-menu-content').children().show() | ||||
| 
 | ||||
|   searchEmojis: (term)-> | ||||
|     $(".emoji-menu-content [data-emoji*='#{term}']").closest("li").clone() | ||||
|  |  | |||
|  | @ -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() | ||||
|  | @ -1,44 +1,39 @@ | |||
| class @EditBlob | ||||
|   constructor: (assets_path, mode)-> | ||||
|     ace.config.set "modePath", assets_path + '/ace' | ||||
|   constructor: (assets_path, ace_mode = null) -> | ||||
|     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 | ||||
|     @editor = ace.edit("editor") | ||||
|     @editor.focus() | ||||
|     @editor.getSession().setMode "ace/mode/#{ace_mode}" if ace_mode | ||||
| 
 | ||||
|     # Before a form submission, move the content from the Ace editor into the | ||||
|     # submitted textarea | ||||
|     $('form').submit -> | ||||
|       $("#file-content").val(editor.getValue()) | ||||
|     $('form').submit => | ||||
|       $("#file-content").val(@editor.getValue()) | ||||
| 
 | ||||
|     editModePanes = $(".js-edit-mode-pane") | ||||
|     editModeLinks = $(".js-edit-mode a") | ||||
|     editModeLinks.click (event) -> | ||||
|     @initModePanesAndLinks() | ||||
|     new BlobLicenseSelector(@editor) | ||||
| 
 | ||||
|   initModePanesAndLinks: -> | ||||
|     @$editModePanes = $(".js-edit-mode-pane") | ||||
|     @$editModeLinks = $(".js-edit-mode a") | ||||
|     @$editModeLinks.click @editModeLinkClickHandler | ||||
| 
 | ||||
|   editModeLinkClickHandler: (event) => | ||||
|     event.preventDefault() | ||||
|       currentLink = $(this) | ||||
|     currentLink = $(event.target) | ||||
|     paneId = currentLink.attr("href") | ||||
|       currentPane = editModePanes.filter(paneId) | ||||
|       editModeLinks.parent().removeClass "active hover" | ||||
|     currentPane = @$editModePanes.filter(paneId) | ||||
|     @$editModeLinks.parent().removeClass "active hover" | ||||
|     currentLink.parent().addClass "active hover" | ||||
|       editModePanes.hide() | ||||
|       if paneId is "#preview" | ||||
|     @$editModePanes.hide() | ||||
|     currentPane.fadeIn 200 | ||||
|     if paneId is "#preview" | ||||
|       $.post currentLink.data("preview-url"), | ||||
|           content: editor.getValue() | ||||
|         content: @editor.getValue() | ||||
|       , (response) -> | ||||
|         currentPane.empty().append response | ||||
|         currentPane.syntaxHighlight() | ||||
|           return | ||||
| 
 | ||||
|     else | ||||
|         currentPane.fadeIn 200 | ||||
|         editor.focus() | ||||
|       return | ||||
| 
 | ||||
|   editor: -> | ||||
|     return @editor | ||||
|       @editor.focus() | ||||
|  |  | |||
|  | @ -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 | ||||
|  | @ -1,7 +1,7 @@ | |||
| class @CommitsList | ||||
|   @timer = null | ||||
| 
 | ||||
|   @init: (ref, limit) -> | ||||
|   @init: (limit) -> | ||||
|     $("body").on "click", ".day-commits-table li.commit", (event) -> | ||||
|       if event.target.nodeName != "A" | ||||
|         location.href = $(this).attr("url") | ||||
|  |  | |||
|  | @ -17,6 +17,7 @@ class Dispatcher | |||
|     switch page | ||||
|       when 'projects:issues:index' | ||||
|         Issues.init() | ||||
|         Issuable.init() | ||||
|         shortcut_handler = new ShortcutsNavigation() | ||||
|       when 'projects:issues:show' | ||||
|         new Issue() | ||||
|  | @ -59,7 +60,7 @@ class Dispatcher | |||
|         new ZenMode() | ||||
|       when 'projects:merge_requests:index' | ||||
|         shortcut_handler = new ShortcutsNavigation() | ||||
|         MergeRequests.init() | ||||
|         Issuable.init() | ||||
|       when 'dashboard:activity' | ||||
|         new Activities() | ||||
|       when 'dashboard:projects:starred' | ||||
|  | @ -109,6 +110,8 @@ class Dispatcher | |||
|         new BuildArtifacts() | ||||
|       when 'projects:group_links:index' | ||||
|         new GroupsSelect() | ||||
|       when 'search:show' | ||||
|         new Search() | ||||
| 
 | ||||
|     switch path.first() | ||||
|       when 'admin' | ||||
|  |  | |||
|  | @ -61,6 +61,7 @@ class @DropzoneInput | |||
|         return | ||||
| 
 | ||||
|       drop: -> | ||||
|         $mdArea.removeClass 'is-dropzone-hover' | ||||
|         form.find(".div-dropzone-hover").css "opacity", 0 | ||||
|         form_textarea.focus() | ||||
|         return | ||||
|  |  | |||
|  | @ -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() | ||||
|  | @ -32,10 +32,8 @@ class GitLabDropdownFilter | |||
|       else if @input.val() is "" and $inputContainer.hasClass HAS_VALUE_CLASS | ||||
|         $inputContainer.removeClass HAS_VALUE_CLASS | ||||
| 
 | ||||
|       if keyCode is 13 and @input.val() isnt "" | ||||
|         if @options.enterCallback | ||||
|           @options.enterCallback() | ||||
|         return | ||||
|       if keyCode is 13 | ||||
|         return false | ||||
| 
 | ||||
|       clearTimeout timeout | ||||
|       timeout = setTimeout => | ||||
|  | @ -132,7 +130,6 @@ class GitLabDropdown | |||
|       @filterInput = @getElement(FILTER_INPUT) | ||||
|       @highlight = false | ||||
|       @filterInputBlur = true | ||||
|       @enterCallback = true | ||||
|     } = @options | ||||
| 
 | ||||
|     self = @ | ||||
|  | @ -157,6 +154,9 @@ class GitLabDropdown | |||
|             @fullData = data | ||||
| 
 | ||||
|             @parseData @fullData | ||||
| 
 | ||||
|             if @options.filterable | ||||
|               @filterInput.trigger 'keyup' | ||||
|         } | ||||
| 
 | ||||
|     # Init filterable | ||||
|  | @ -178,15 +178,15 @@ class GitLabDropdown | |||
|         callback: (data) => | ||||
|           currentIndex = -1 | ||||
|           @parseData data | ||||
|         enterCallback: => | ||||
|           if @enterCallback | ||||
|             @selectRowAtIndex 0 | ||||
| 
 | ||||
|     # Event listeners | ||||
| 
 | ||||
|     @dropdown.on "shown.bs.dropdown", @opened | ||||
|     @dropdown.on "hidden.bs.dropdown", @hidden | ||||
|     @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 | ||||
|       @dropdown.find(".dropdown-toggle-page, .dropdown-menu-back").on "click", (e) => | ||||
|  | @ -224,6 +224,9 @@ class GitLabDropdown | |||
| 
 | ||||
|     menu.toggleClass PAGE_TWO_CLASS | ||||
| 
 | ||||
|     # Focus first visible input on active page | ||||
|     @dropdown.find('[class^="dropdown-page-"]:visible :text:visible:first').focus() | ||||
| 
 | ||||
|   parseData: (data) -> | ||||
|     @renderedData = data | ||||
| 
 | ||||
|  | @ -243,7 +246,8 @@ class GitLabDropdown | |||
|   shouldPropagate: (e) => | ||||
|     if @options.multiSelect | ||||
|       $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() | ||||
|         return false | ||||
|       else | ||||
|  | @ -378,7 +382,6 @@ class GitLabDropdown | |||
|       selectedObject = @renderedData[selectedIndex] | ||||
|     value = if @options.id then @options.id(selectedObject, el) else selectedObject.id | ||||
|     field = @dropdown.parent().find("input[name='#{fieldName}'][value='#{value}']") | ||||
| 
 | ||||
|     if el.hasClass(ACTIVE_CLASS) | ||||
|       el.removeClass(ACTIVE_CLASS) | ||||
|       field.remove() | ||||
|  | @ -389,13 +392,13 @@ class GitLabDropdown | |||
|       else | ||||
|         selectedObject | ||||
|     else | ||||
|       if !value? | ||||
|         field.remove() | ||||
| 
 | ||||
|       if not @options.multiSelect | ||||
|       if not @options.multiSelect or el.hasClass('dropdown-clear-active') | ||||
|         @dropdown.find(".#{ACTIVE_CLASS}").removeClass ACTIVE_CLASS | ||||
|         @dropdown.parent().find("input[name='#{fieldName}']").remove() | ||||
| 
 | ||||
|       if !value? | ||||
|         field.remove() | ||||
| 
 | ||||
|       # Toggle active class for the tick mark | ||||
|       el.addClass ACTIVE_CLASS | ||||
| 
 | ||||
|  |  | |||
|  | @ -4,18 +4,33 @@ class @ImporterStatus | |||
|     this.setAutoUpdate() | ||||
| 
 | ||||
|   initStatusPage: -> | ||||
|     $(".js-add-to-import").click (event) => | ||||
|     $('.js-add-to-import') | ||||
|       .off 'click' | ||||
|       .on 'click', (e) => | ||||
|         new_namespace = null | ||||
|       tr = $(event.currentTarget).closest("tr") | ||||
|       id = tr.attr("id").replace("repo_", "") | ||||
|       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")) | ||||
|         $btn = $(e.currentTarget) | ||||
|         $tr = $btn.closest('tr') | ||||
|         id = $tr.attr('id').replace('repo_', '') | ||||
|         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')}") | ||||
| 
 | ||||
|         $btn | ||||
|           .disable() | ||||
|           .addClass 'is-loading' | ||||
| 
 | ||||
|         $.post @import_url, {repo_id: id, new_namespace: new_namespace}, dataType: 'script' | ||||
| 
 | ||||
|     $(".js-import-all").click (event) => | ||||
|       $(".js-add-to-import").each -> | ||||
|         $(this).click() | ||||
|     $('.js-import-all') | ||||
|       .off 'click' | ||||
|       .on 'click', (e) -> | ||||
|         $btn = $(@) | ||||
|         $btn | ||||
|           .disable() | ||||
|           .addClass 'is-loading' | ||||
| 
 | ||||
|         $('.js-add-to-import').each -> | ||||
|           $(this).trigger('click') | ||||
| 
 | ||||
|   setAutoUpdate: -> | ||||
|     setInterval (=> | ||||
|  |  | |||
|  | @ -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 | ||||
|  | @ -9,7 +9,16 @@ class @IssuableContext | |||
|     $(".issuable-sidebar .inline-update").on "change", ".js-assignee", -> | ||||
|       $(this).submit() | ||||
| 
 | ||||
|     $(document).off("click", ".edit-link").on "click",".edit-link", (e) -> | ||||
|     $(document) | ||||
|       .off 'click', '.issuable-sidebar .dropdown-content a' | ||||
|       .on 'click', '.issuable-sidebar .dropdown-content a', (e) -> | ||||
|         e.preventDefault() | ||||
| 
 | ||||
|     $(document) | ||||
|       .off 'click', '.edit-link' | ||||
|       .on 'click', '.edit-link', (e) -> | ||||
|         e.preventDefault() | ||||
| 
 | ||||
|         $block = $(@).parents('.block') | ||||
|         $selectbox = $block.find('.selectbox') | ||||
|         if $selectbox.is(':visible') | ||||
|  | @ -20,10 +29,9 @@ class @IssuableContext | |||
|           $block.find('.value').hide() | ||||
| 
 | ||||
|         if $selectbox.is(':visible') | ||||
|         setTimeout (-> | ||||
|           setTimeout -> | ||||
|             $block.find('.dropdown-menu-toggle').trigger 'click' | ||||
|         ), 0 | ||||
|        | ||||
|           , 0 | ||||
| 
 | ||||
|     $(".right-sidebar").niceScroll() | ||||
| 
 | ||||
|  |  | |||
|  | @ -12,6 +12,7 @@ class @Issue | |||
| 
 | ||||
|     @initMergeRequests() | ||||
|     @initRelatedBranches() | ||||
|     @initCanCreateBranch() | ||||
| 
 | ||||
|   initTaskList: -> | ||||
|     $('.detail-page-description .js-task-list-container').taskList('enable') | ||||
|  | @ -92,3 +93,25 @@ class @Issue | |||
|       .success (data) -> | ||||
|         if 'html' of data | ||||
|           $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() | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| @Issues = | ||||
|   init: -> | ||||
|     Issues.initSearch() | ||||
|     Issues.created = true | ||||
|     Issues.initChecks() | ||||
| 
 | ||||
|     $("body").on "ajax:success", ".close_issue, .reopen_issue", -> | ||||
|  | @ -15,10 +15,6 @@ | |||
|         else | ||||
|           $(this).html totalIssues - 1 | ||||
| 
 | ||||
|   reload: -> | ||||
|     Issues.initChecks() | ||||
|     $('#filter_issue_search').val($('#issue_search').val()) | ||||
| 
 | ||||
|   initChecks: -> | ||||
|     $(".check_all_issues").click -> | ||||
|       $(".selected_issue").prop("checked", @checked) | ||||
|  | @ -26,51 +22,6 @@ | |||
| 
 | ||||
|     $(".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: -> | ||||
|     checked_issues = $(".selected_issue:checked") | ||||
|     if checked_issues.length > 0 | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ class @LabelsSelect | |||
|       labelUrl = $dropdown.data('labels') | ||||
|       issueUpdateURL = $dropdown.data('issueUpdate') | ||||
|       selectedLabel = $dropdown.data('selected') | ||||
|       if selectedLabel? | ||||
|       if selectedLabel? and not $dropdown.hasClass 'js-multiselect' | ||||
|         selectedLabel = selectedLabel.split(',') | ||||
|       newLabelField = $('#new_label_name') | ||||
|       newColorField = $('#new_label_color') | ||||
|  | @ -16,33 +16,32 @@ class @LabelsSelect | |||
|       abilityName = $dropdown.data('ability-name') | ||||
|       $selectbox = $dropdown.closest('.selectbox') | ||||
|       $block = $selectbox.closest('.block') | ||||
|       $form = $dropdown.closest('form') | ||||
|       $sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon span') | ||||
|       $value = $block.find('.value') | ||||
|       $loading = $block.find('.block-loading').fadeOut() | ||||
| 
 | ||||
|       if newLabelField.length | ||||
|         $newLabelCreateButton = $('.js-new-label-btn') | ||||
|       $newLabelError = $('.js-label-error') | ||||
|       $colorPreview = $('.js-dropdown-label-color-preview') | ||||
|         $newLabelError = $dropdown.parent().find('.js-label-error') | ||||
|         $newLabelError.hide() | ||||
|       $newLabelCreateButton = $('.js-new-label-btn') | ||||
| 
 | ||||
|         # Suggested colors in the dropdown to chose from pre-chosen colors | ||||
|         $('.suggest-colors-dropdown a').on 'click', (e) -> | ||||
|       $newLabelError.hide() | ||||
|       $loading = $block.find('.block-loading').fadeOut() | ||||
| 
 | ||||
|       issueURLSplit = issueUpdateURL.split('/') if issueUpdateURL? | ||||
|       if issueUpdateURL | ||||
|         labelHTMLTemplate = _.template( | ||||
|             '<% _.each(labels, function(label){ %> | ||||
|             <a href="<%= ["",issueURLSplit[1], issueURLSplit[2],""].join("/") %>issues?label_name=<%= label.title %>"> | ||||
|             <span class="label has-tooltip color-label" title="<%= label.description %>" style="background-color: <%= label.color %>;"> | ||||
|             <%= label.title %> | ||||
|             <a href="<%= ["",issueURLSplit[1], issueURLSplit[2],""].join("/") %>issues?label_name[]=<%= _.escape(label.title) %>"> | ||||
|             <span class="label has-tooltip color-label" title="<%= _.escape(label.description) %>" style="background-color: <%= label.color %>; color: <%= label.text_color %>;"> | ||||
|             <%= _.escape(label.title) %> | ||||
|             </span> | ||||
|             </a> | ||||
|             <% }); %>' | ||||
|         ); | ||||
|         ) | ||||
|         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) -> | ||||
|           e.preventDefault() | ||||
|           e.stopPropagation() | ||||
|  | @ -81,14 +80,17 @@ class @LabelsSelect | |||
|         enableLabelCreateButton = -> | ||||
|           if newLabelField.val() isnt '' and newColorField.val() isnt '' | ||||
|             $newLabelError.hide() | ||||
|             $('.js-new-label-btn').disable() | ||||
|             $newLabelCreateButton.enable() | ||||
|           else | ||||
|             $newLabelCreateButton.disable() | ||||
| 
 | ||||
|         saveLabel = -> | ||||
|           # Create new label with API | ||||
|           Api.newLabel projectId, { | ||||
|             name: newLabelField.val() | ||||
|             color: newColorField.val() | ||||
|           }, (label) -> | ||||
|               $('.js-new-label-btn').enable() | ||||
|             $newLabelCreateButton.enable() | ||||
| 
 | ||||
|             if label.message? | ||||
|               $newLabelError | ||||
|  | @ -97,10 +99,6 @@ class @LabelsSelect | |||
|             else | ||||
|               $('.dropdown-menu-back', $dropdown.parent()).trigger 'click' | ||||
| 
 | ||||
|             $newLabelCreateButton.enable() | ||||
|           else | ||||
|             $newLabelCreateButton.disable() | ||||
| 
 | ||||
|         newLabelField.on 'keyup change', enableLabelCreateButton | ||||
| 
 | ||||
|         newColorField.on 'keyup change', enableLabelCreateButton | ||||
|  | @ -111,24 +109,7 @@ class @LabelsSelect | |||
|           .on 'click', (e) -> | ||||
|             e.preventDefault() | ||||
|             e.stopPropagation() | ||||
| 
 | ||||
|             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' | ||||
|             saveLabel() | ||||
| 
 | ||||
|       saveLabelData = -> | ||||
|         selected = $dropdown | ||||
|  | @ -171,7 +152,7 @@ class @LabelsSelect | |||
|             .find('a') | ||||
|             .each((i) -> | ||||
|               setTimeout(=> | ||||
|                 glAnimate($(@), 'pulse') | ||||
|                 gl.animate.animate($(@), 'pulse') | ||||
|               ,200 * i | ||||
|               ) | ||||
|             ) | ||||
|  | @ -182,6 +163,21 @@ class @LabelsSelect | |||
|           $.ajax( | ||||
|             url: labelUrl | ||||
|           ).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 showNo | ||||
|                 data.unshift( | ||||
|  | @ -197,21 +193,47 @@ class @LabelsSelect | |||
| 
 | ||||
|               if data.length > 2 | ||||
|                 data.splice 2, 0, 'divider' | ||||
| 
 | ||||
|             callback data | ||||
| 
 | ||||
|         renderRow: (label) -> | ||||
|           selectedClass = '' | ||||
|           if $selectbox.find("input[type='hidden']\ | ||||
|             [name='#{$dropdown.data('field-name')}']\ | ||||
|             [value='#{label.id}']").length | ||||
|             selectedClass = 'is-active' | ||||
|           removesAll = label.id is 0 or not label.id? | ||||
| 
 | ||||
|           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> | ||||
|             <a href='#' class='#{selectedClass}'> | ||||
|               #{color} | ||||
|               #{label.title} | ||||
|             <a href='#' class='#{selectedClass.join(' ')}'> | ||||
|               #{colorEl} | ||||
|               #{_.escape(label.title)} | ||||
|             </a> | ||||
|           </li>" | ||||
|         filterable: true | ||||
|  | @ -219,37 +241,56 @@ class @LabelsSelect | |||
|           fields: ['title'] | ||||
|         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_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 | ||||
|             defaultLabel | ||||
|         fieldName: $dropdown.data('field-name') | ||||
|         id: (label) -> | ||||
|           if label.isAny? | ||||
|             '' | ||||
|           else if $dropdown.hasClass "js-filter-submit" | ||||
|             label.title | ||||
|           if $dropdown.hasClass("js-filter-submit") and not label.isAny? | ||||
|             _.escape label.title | ||||
|           else | ||||
|             label.id | ||||
| 
 | ||||
|         hidden: -> | ||||
|           page = $('body').data 'page' | ||||
|           isIssueIndex = page is 'projects:issues:index' | ||||
|           isMRIndex = page is 'projects:merge_requests:index' | ||||
| 
 | ||||
|           $selectbox.hide() | ||||
|           # display:block overrides the hide-collapse rule | ||||
|           $value.removeAttr('style') | ||||
|           if $dropdown.hasClass 'js-multiselect' | ||||
|             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' | ||||
|         clicked: (label) -> | ||||
|           page = $('body').data 'page' | ||||
|           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 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' | ||||
|             $dropdown.closest('form').submit() | ||||
|           else | ||||
|  |  | |||
|  | @ -1,13 +1,39 @@ | |||
| ((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 | ||||
|       .removeClass() | ||||
|       .removeClass(animation + ' animated') | ||||
|       .addClass(animation + ' animated') | ||||
|       .one 'webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', -> | ||||
|         $(this).removeClass() | ||||
|         return | ||||
|         $(this).removeClass(animation + ' animated') | ||||
|         if done? | ||||
|           done() | ||||
|         if options?.cssEnd? | ||||
|           $el.css(options.cssEnd) | ||||
|         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 | ||||
|  | @ -3,16 +3,20 @@ | |||
|   w.gl ?= {} | ||||
|   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)) | ||||
|     sURLVariables = sPageURL.split('&') | ||||
|     sParameterName = undefined | ||||
|     values = [] | ||||
|     i = 0 | ||||
|     while i < sURLVariables.length | ||||
|       sParameterName = sURLVariables[i].split('=') | ||||
|       if sParameterName[0] is sParam | ||||
|         return if sParameterName[1] is undefined then true else sParameterName[1] | ||||
|         values.push(sParameterName[1]) | ||||
|       i++ | ||||
|     values | ||||
| 
 | ||||
|   # # | ||||
|   #  @param {Object} params - url keys and value to merge | ||||
|  | @ -28,4 +32,12 @@ | |||
|         newUrl = "#{newUrl}#{(if newUrl.indexOf('?') > 0 then '&' else '?')}#{paramName}=#{paramValue}" | ||||
|     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 | ||||
|  |  | |||
|  | @ -87,8 +87,8 @@ class @MergeRequestTabs | |||
|     if window.location.hash | ||||
|       navBarHeight = $('.navbar-gitlab').outerHeight() | ||||
| 
 | ||||
|       $el = $("#{container} #{window.location.hash}") | ||||
|       $.scrollTo("#{container} #{window.location.hash}", offset: -navBarHeight) if $el.length | ||||
|       $el = $("#{container} #{window.location.hash}:not(.match)") | ||||
|       $.scrollTo("#{container} #{window.location.hash}:not(.match)", offset: -navBarHeight) if $el.length | ||||
| 
 | ||||
|   # Activate a tab based on the current action | ||||
|   activateTab: (action) -> | ||||
|  | @ -176,13 +176,14 @@ class @MergeRequestTabs | |||
| 
 | ||||
|     if locationHash isnt '' | ||||
|       hashClassString = ".#{locationHash.replace('#', '')}" | ||||
|       $diffLine = $(locationHash) | ||||
|       $diffLine = $("#{locationHash}:not(.match)", $('#diffs')) | ||||
| 
 | ||||
|       if $diffLine.is ':not(tr)' | ||||
|         $diffLine = $("td#{locationHash}, td#{hashClassString}") | ||||
|       if not $diffLine.is 'tr' | ||||
|         $diffLine = $('#diffs').find("td#{locationHash}, td#{hashClassString}") | ||||
|       else | ||||
|         $diffLine = $('td', $diffLine) | ||||
|         $diffLine = $diffLine.find('td') | ||||
| 
 | ||||
|       if $diffLine.length | ||||
|         $diffLine.addClass 'hll' | ||||
|         diffLineTop = $diffLine.offset().top | ||||
|         navBarHeight = $('.navbar-gitlab').outerHeight() | ||||
|  |  | |||
|  | @ -9,11 +9,12 @@ class @MergeRequestWidget | |||
|   constructor: (@opts) -> | ||||
|     $('#modal_merge_info').modal(show: false) | ||||
|     @firstCICheck = true | ||||
|     @readyForCICheck = true | ||||
|     @readyForCICheck = false | ||||
|     clearInterval @fetchBuildStatusInterval | ||||
| 
 | ||||
|     @clearEventListeners() | ||||
|     @addEventListeners() | ||||
|     @getCIStatus(false) | ||||
|     @pollCIStatus() | ||||
|     notifyPermissions() | ||||
| 
 | ||||
|  | @ -68,20 +69,18 @@ class @MergeRequestWidget | |||
|     $.getJSON @opts.ci_status_url, (data) => | ||||
|       @readyForCICheck = true | ||||
| 
 | ||||
|       if @firstCICheck | ||||
|         @firstCICheck = false | ||||
|         @opts.ci_status = data.status | ||||
| 
 | ||||
|       if @opts.ci_status is '' | ||||
|         @opts.ci_status = data.status | ||||
|       if data.status is '' | ||||
|         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 | ||||
|         if 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) | ||||
| 
 | ||||
|           if status is "preparing" | ||||
|  | @ -104,8 +103,7 @@ class @MergeRequestWidget | |||
|               @close() | ||||
|               Turbolinks.visit _this.opts.builds_path | ||||
|           ) | ||||
| 
 | ||||
|         @opts.ci_status = data.status | ||||
|         @firstCICheck = false | ||||
| 
 | ||||
|   showCIStatus: (state) -> | ||||
|     $('.ci_widget').hide() | ||||
|  |  | |||
|  | @ -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()) | ||||
|  | @ -24,7 +24,7 @@ class @MilestoneSelect | |||
| 
 | ||||
|       if issueUpdateURL | ||||
|         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>' | ||||
|  | @ -71,7 +71,7 @@ class @MilestoneSelect | |||
|             defaultLabel | ||||
|         fieldName: $dropdown.data('field-name') | ||||
|         text: (milestone) -> | ||||
|           milestone.title | ||||
|           _.escape(milestone.title) | ||||
|         id: (milestone) -> | ||||
|           if !useId | ||||
|             milestone.name | ||||
|  | @ -97,7 +97,7 @@ class @MilestoneSelect | |||
|               selectedMilestone = selected.name | ||||
|             else | ||||
|               selectedMilestone = '' | ||||
|             Issues.filterResults $dropdown.closest('form') | ||||
|             Issuable.filterResults $dropdown.closest('form') | ||||
|           else if $dropdown.hasClass('js-filter-submit') | ||||
|             $dropdown.closest('form').submit() | ||||
|           else | ||||
|  |  | |||
|  | @ -75,6 +75,9 @@ class @Notes | |||
|     # when issue status changes, we need to refresh data | ||||
|     $(document).on "issuable:change", @refresh | ||||
| 
 | ||||
|     # when a key is clicked on the notes | ||||
|     $(document).on "keydown", ".js-note-text", @keydownNoteText | ||||
| 
 | ||||
|   cleanBinding: -> | ||||
|     $(document).off "ajax:success", ".js-main-target-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-close" | ||||
|     $(document).off "click", ".js-note-discard" | ||||
|     $(document).off "keydown", ".js-note-text" | ||||
| 
 | ||||
|     $('.note .js-task-list-container').taskList('disable') | ||||
|     $(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: -> | ||||
|     clearInterval(Notes.interval) | ||||
|     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 | ||||
|   if the user cancels | ||||
|   ### | ||||
|   showEditForm: (e) -> | ||||
|   showEditForm: (e, scrollTo, myLastNote) -> | ||||
|     e.preventDefault() | ||||
|     note = $(this).closest(".note") | ||||
|     note.addClass "is-editting" | ||||
|  | @ -354,9 +366,27 @@ class @Notes | |||
|     # Show the attachment delete link | ||||
|     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 | ||||
|  |  | |||
|  | @ -45,9 +45,10 @@ class @Profile | |||
| 
 | ||||
|   saveForm: -> | ||||
|     self = @ | ||||
| 
 | ||||
|     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 | ||||
|       url: @form.attr('action') | ||||
|  |  | |||
|  | @ -1,10 +1,12 @@ | |||
| class @Sidebar | ||||
|   constructor: (currentUser) -> | ||||
|     @sidebar = $('aside') | ||||
| 
 | ||||
|     @addEventListeners() | ||||
| 
 | ||||
|   addEventListeners: -> | ||||
|     $('aside').on('click', '.sidebar-collapsed-icon', @sidebarCollapseClicked) | ||||
|     $('.dropdown').on('hidden.gl.dropdown', @sidebarDropdownHidden) | ||||
|     @sidebar.on('click', '.sidebar-collapsed-icon', @, @sidebarCollapseClicked) | ||||
|     $('.dropdown').on('hidden.gl.dropdown', @, @onSidebarDropdownHidden) | ||||
|     $('.dropdown').on('loading.gl.dropdown', @sidebarDropdownLoading) | ||||
|     $('.dropdown').on('loaded.gl.dropdown', @sidebarDropdownLoaded) | ||||
| 
 | ||||
|  | @ -30,26 +32,56 @@ class @Sidebar | |||
|     else | ||||
|       i.show() | ||||
| 
 | ||||
| 
 | ||||
|   sidebarCollapseClicked: (e) -> | ||||
|     sidebar = e.data | ||||
|     e.preventDefault() | ||||
|     $block = $(@).closest('.block') | ||||
|     sidebar.openDropdown($block); | ||||
| 
 | ||||
|     $('aside') | ||||
|       .find('.gutter-toggle') | ||||
|       .trigger('click') | ||||
|     $editLink = $block.find('.edit-link') | ||||
|   openDropdown: (blockOrName) -> | ||||
|     $block = if _.isString(blockOrName) then @getBlock(blockOrName) else blockOrName | ||||
| 
 | ||||
|     if $editLink.length | ||||
|       $editLink.trigger('click') | ||||
|     $block.find('.edit-link').trigger('click') | ||||
| 
 | ||||
|     if not @isOpen() | ||||
|       @setCollapseAfterUpdate($block) | ||||
|       @toggleSidebar('open') | ||||
| 
 | ||||
|   setCollapseAfterUpdate: ($block) -> | ||||
|     $block.addClass('collapse-after-update') | ||||
|     $('.page-with-sidebar').addClass('with-overlay') | ||||
| 
 | ||||
|   sidebarDropdownHidden: (e) -> | ||||
|   onSidebarDropdownHidden: (e) -> | ||||
|     sidebar = e.data | ||||
|     e.preventDefault() | ||||
|     $block = $(@).closest('.block') | ||||
|     sidebar.sidebarDropdownHidden($block) | ||||
| 
 | ||||
|   sidebarDropdownHidden: ($block) -> | ||||
|     if $block.hasClass('collapse-after-update') | ||||
|       $block.removeClass('collapse-after-update') | ||||
|       $('.page-with-sidebar').removeClass('with-overlay') | ||||
|       $('aside') | ||||
|         .find('.gutter-toggle') | ||||
|       @toggleSidebar('hide') | ||||
| 
 | ||||
|   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}") | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -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() | ||||
|  | @ -2,25 +2,27 @@ class @Shortcuts | |||
|   constructor: -> | ||||
|     @enabledHelp = [] | ||||
|     Mousetrap.reset() | ||||
|     Mousetrap.bind('?', @selectiveHelp) | ||||
|     Mousetrap.bind('?', @onToggleHelp) | ||||
|     Mousetrap.bind('s', Shortcuts.focusSearch) | ||||
|     Mousetrap.bind(['ctrl+shift+p', 'command+shift+p'], @toggleMarkdownPreview) | ||||
|     Mousetrap.bind('t', -> Turbolinks.visit(findFileURL)) if findFileURL? | ||||
| 
 | ||||
|   selectiveHelp: (e) => | ||||
|     Shortcuts.showHelp(e, @enabledHelp) | ||||
|   onToggleHelp: (e) => | ||||
|     e.preventDefault() | ||||
|     @toggleHelp(@enabledHelp) | ||||
| 
 | ||||
|   toggleMarkdownPreview: (e) => | ||||
|     $(document).triggerHandler('markdown-preview:toggle', [e]) | ||||
| 
 | ||||
|   @showHelp: (e, location) -> | ||||
|     if $('#modal-shortcuts').length > 0 | ||||
|       $('#modal-shortcuts').modal('show') | ||||
|     else | ||||
|       url = '/help/shortcuts' | ||||
|       url = gon.relative_url_root + url if gon.relative_url_root? | ||||
|   toggleHelp: (location) -> | ||||
|     $modal = $('#modal-shortcuts') | ||||
| 
 | ||||
|     if $modal.length | ||||
|       $modal.modal('toggle') | ||||
|       return | ||||
| 
 | ||||
|     $.ajax( | ||||
|         url: url, | ||||
|       url: gon.shortcuts_path, | ||||
|       dataType: 'script', | ||||
|       success: (e) -> | ||||
|         if location and location.length > 0 | ||||
|  | @ -29,7 +31,6 @@ class @Shortcuts | |||
|           $('.hidden-shortcut').show() | ||||
|           $('.js-more-help-button').remove() | ||||
|     ) | ||||
|       e.preventDefault() | ||||
| 
 | ||||
|   @focusSearch: (e) -> | ||||
|     $('#search').focus() | ||||
|  |  | |||
|  | @ -4,18 +4,8 @@ | |||
| class @ShortcutsIssuable extends ShortcutsNavigation | ||||
|   constructor: (isMergeRequest) -> | ||||
|     super() | ||||
|     Mousetrap.bind('a', -> | ||||
|       $('.block.assignee .edit-link').trigger('click') | ||||
|       return false | ||||
|     ) | ||||
|     Mousetrap.bind('m', -> | ||||
|       $('.block.milestone .edit-link').trigger('click') | ||||
|       return false | ||||
|     ) | ||||
|     Mousetrap.bind('r', => | ||||
|       @replyWithSelectedText() | ||||
|       return false | ||||
|     ) | ||||
|     Mousetrap.bind('a', @openSidebarDropdown.bind(@, 'assignee')) | ||||
|     Mousetrap.bind('m', @openSidebarDropdown.bind(@, 'milestone')) | ||||
|     Mousetrap.bind('j', => | ||||
|       @prevIssue() | ||||
|       return false | ||||
|  | @ -28,7 +18,7 @@ class @ShortcutsIssuable extends ShortcutsNavigation | |||
|       @editIssue() | ||||
|       return false | ||||
|     ) | ||||
| 
 | ||||
|     Mousetrap.bind('l', @openSidebarDropdown.bind(@, 'labels')) | ||||
| 
 | ||||
|     if isMergeRequest | ||||
|       @enabledHelp.push('.hidden-shortcut.merge_requests') | ||||
|  | @ -71,3 +61,7 @@ class @ShortcutsIssuable extends ShortcutsNavigation | |||
|   editIssue: -> | ||||
|     $editBtn = $('.issuable-edit') | ||||
|     Turbolinks.visit($editBtn.attr('href')) | ||||
| 
 | ||||
|   openSidebarDropdown: (name) -> | ||||
|     sidebar.openDropdown(name) | ||||
|     return false | ||||
|  |  | |||
|  | @ -14,6 +14,7 @@ class @ShortcutsNavigation extends Shortcuts | |||
|     Mousetrap.bind('g m', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-merge_requests')) | ||||
|     Mousetrap.bind('g w', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-wiki')) | ||||
|     Mousetrap.bind('g s', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-snippets')) | ||||
|     Mousetrap.bind('i', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-new-issue')) | ||||
|     @enabledHelp.push('.hidden-shortcut.project') | ||||
| 
 | ||||
|   @findAndFollowLink: (selector) -> | ||||
|  |  | |||
|  | @ -12,7 +12,7 @@ toggleSidebar = -> | |||
|     niceScrollBars.updateScrollBar(); | ||||
|   ), 300 | ||||
| 
 | ||||
| $(document).on("click", '.toggle-nav-collapse', (e) -> | ||||
| $(document).on("click", '.toggle-nav-collapse, .side-nav-toggle', (e) -> | ||||
|   e.preventDefault() | ||||
| 
 | ||||
|   toggleSidebar() | ||||
|  |  | |||
|  | @ -1,5 +1,11 @@ | |||
| class @Todos | ||||
|   constructor: (@name) -> | ||||
|   constructor: (opts = {}) -> | ||||
|     { | ||||
|       @el = $('.js-todos-options') | ||||
|     } = opts | ||||
| 
 | ||||
|     @perPage = @el.data('perPage') | ||||
| 
 | ||||
|     @clearListeners() | ||||
|     @initBtnListeners() | ||||
| 
 | ||||
|  | @ -26,6 +32,7 @@ class @Todos | |||
|       dataType: 'json' | ||||
|       data: '_method': 'delete' | ||||
|       success: (data) => | ||||
|         @redirectIfNeeded data.count | ||||
|         @clearDone $this.closest('li') | ||||
|         @updateBadges data | ||||
| 
 | ||||
|  | @ -57,11 +64,46 @@ class @Todos | |||
|     $('.todos-pending .badge, .todos-pending-count').text data.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)-> | ||||
|     todoLink = $(this).data('url') | ||||
|     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() | ||||
|       window.open(todoLink,'_blank') | ||||
|     else | ||||
|  |  | |||
|  | @ -92,7 +92,7 @@ class @UserTabs | |||
|     @setCurrentAction(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) -> | ||||
|     return if @loaded[action] is true | ||||
|  |  | |||
|  | @ -158,7 +158,7 @@ class @UsersSelect | |||
| 
 | ||||
|           if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex) | ||||
|             selectedId = user.id | ||||
|             Issues.filterResults $dropdown.closest('form') | ||||
|             Issuable.filterResults $dropdown.closest('form') | ||||
|           else if $dropdown.hasClass 'js-filter-submit' | ||||
|             $dropdown.closest('form').submit() | ||||
|           else | ||||
|  |  | |||
|  | @ -25,6 +25,7 @@ | |||
| @import "framework/lists.scss"; | ||||
| @import "framework/markdown_area.scss"; | ||||
| @import "framework/mobile.scss"; | ||||
| @import "framework/modal.scss"; | ||||
| @import "framework/nav.scss"; | ||||
| @import "framework/pagination.scss"; | ||||
| @import "framework/progress.scss"; | ||||
|  |  | |||
|  | @ -28,6 +28,7 @@ | |||
|   &.s46 { width: 46px; height: 46px; margin-right: 15px; } | ||||
|   &.s48 { width: 48px; height: 48px; margin-right: 10px; } | ||||
|   &.s60 { width: 60px; height: 60px; margin-right: 12px; } | ||||
|   &.s70 { width: 70px; height: 70px; margin-right: 14px; } | ||||
|   &.s90 { width: 90px; height: 90px; margin-right: 15px; } | ||||
|   &.s110 { width: 110px; height: 110px; margin-right: 15px; } | ||||
|   &.s140 { width: 140px; height: 140px; margin-right: 20px; } | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| .light-well { | ||||
|   background-color: #f8fafc; | ||||
|   background-color: $background-color; | ||||
|   padding: 15px; | ||||
| } | ||||
| 
 | ||||
|  | @ -18,7 +18,7 @@ | |||
|   line-height: 36px; | ||||
| } | ||||
| 
 | ||||
| .gray-content-block { | ||||
| .row-content-block { | ||||
|   margin-top: 0; | ||||
|   margin-bottom: -$gl-padding; | ||||
|   background-color: $background-color; | ||||
|  | @ -81,6 +81,11 @@ | |||
|       margin-left: 10px; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   &.build-content { | ||||
|     background-color: $white-light; | ||||
|     border-top: none; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .cover-block { | ||||
|  | @ -113,7 +118,7 @@ | |||
|     line-height: 1.1; | ||||
| 
 | ||||
|     h1 { | ||||
|       color: #313236; | ||||
|       color: $gl-gray-dark; | ||||
|       margin-bottom: 6px; | ||||
|       font-size: 23px; | ||||
|     } | ||||
|  | @ -150,6 +155,41 @@ | |||
|       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 { | ||||
|  |  | |||
|  | @ -59,7 +59,7 @@ | |||
| } | ||||
| 
 | ||||
| @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 { | ||||
|  | @ -139,11 +139,19 @@ | |||
|     pointer-events: auto !important; | ||||
|   } | ||||
| 
 | ||||
|   &[disabled] { | ||||
|     pointer-events: none !important; | ||||
|   } | ||||
| 
 | ||||
|   .caret { | ||||
|     margin-left: 5px; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .btn-lg { | ||||
|   padding: 12px 20px; | ||||
| } | ||||
| 
 | ||||
| .btn-transparent { | ||||
|   color: $btn-transparent-color; | ||||
|   background-color: transparent; | ||||
|  | @ -243,3 +251,10 @@ | |||
| .btn-file-option { | ||||
|   background: linear-gradient(180deg, $white-light 25%, $gray-light 100%); | ||||
| } | ||||
| 
 | ||||
| .btn-build { | ||||
|   margin-left: 10px; | ||||
|   i { | ||||
|     color: $gl-icon-color; | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -54,6 +54,10 @@ | |||
|     fill: #254e77 !important; | ||||
|   } | ||||
| 
 | ||||
|   .future { | ||||
|     visibility: hidden; | ||||
|   } | ||||
| 
 | ||||
|   .domain-background { | ||||
|     fill: none; | ||||
|     shape-rendering: crispedges; | ||||
|  |  | |||
|  | @ -11,6 +11,7 @@ | |||
| .prepend-top-10 { margin-top: 10px } | ||||
| .prepend-top-default { margin-top: $gl-padding !important; } | ||||
| .prepend-top-20 { margin-top: 20px } | ||||
| .prepend-left-5 { margin-left: 5px } | ||||
| .prepend-left-10 { margin-left: 10px } | ||||
| .prepend-left-default { margin-left: $gl-padding; } | ||||
| .prepend-left-20 { margin-left: 20px } | ||||
|  |  | |||
|  | @ -42,7 +42,7 @@ | |||
|   font-size: 15px; | ||||
|   text-align: left; | ||||
|   border: 1px solid $dropdown-toggle-border-color; | ||||
|   border-radius: $dropdown-border-radius; | ||||
|   border-radius: $border-radius-base; | ||||
|   outline: 0; | ||||
|   text-overflow: ellipsis; | ||||
|   white-space: nowrap; | ||||
|  | @ -80,7 +80,7 @@ | |||
|   padding: 10px 0; | ||||
|   background-color: $dropdown-bg; | ||||
|   border: 1px solid $dropdown-border-color; | ||||
|   border-radius: $dropdown-border-radius; | ||||
|   border-radius: $border-radius-base; | ||||
|   box-shadow: 0 2px 4px $dropdown-shadow-color; | ||||
| 
 | ||||
|   &.is-loading { | ||||
|  | @ -248,7 +248,7 @@ | |||
| 
 | ||||
| .dropdown-title { | ||||
|   position: relative; | ||||
|   padding: 0 25px 15px; | ||||
|   padding: 0 25px 10px; | ||||
|   margin: 0 10px 10px; | ||||
|   font-weight: 600; | ||||
|   line-height: 1; | ||||
|  | @ -278,7 +278,7 @@ | |||
|   right: 5px; | ||||
|   width: 20px; | ||||
|   height: 20px; | ||||
|   top: -1px; | ||||
|   top: -3px; | ||||
| } | ||||
| 
 | ||||
| .dropdown-menu-back { | ||||
|  | @ -320,7 +320,7 @@ | |||
|   } | ||||
| } | ||||
| 
 | ||||
| .dropdown-input-field { | ||||
| .dropdown-input-field, .default-dropdown-input { | ||||
|   width: 100%; | ||||
|   padding: 0 7px; | ||||
|   color: $dropdown-input-color; | ||||
|  | @ -358,6 +358,13 @@ | |||
|   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 { | ||||
|   font-size: 14px; | ||||
| 
 | ||||
|  | @ -395,3 +402,122 @@ | |||
|   height: 15px; | ||||
|   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; | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -38,12 +38,14 @@ | |||
| 
 | ||||
|     .filename { | ||||
|       &.old { | ||||
|         display: inline-block; | ||||
|         span.idiff { | ||||
|           background-color: #f8cbcb; | ||||
|         } | ||||
|       } | ||||
| 
 | ||||
|       &.new { | ||||
|         display: inline-block; | ||||
|         span.idiff { | ||||
|           background-color: #a6f3a6; | ||||
|         } | ||||
|  | @ -82,10 +84,6 @@ | |||
|       } | ||||
|     } | ||||
| 
 | ||||
|     &.blob_file { | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     &.blob-no-preview { | ||||
|       background: #eee; | ||||
|       text-shadow: 0 1px 2px #fff; | ||||
|  | @ -129,6 +127,11 @@ | |||
|       td.line-numbers { | ||||
|         float: none; | ||||
|         border-left: 1px solid #ddd; | ||||
| 
 | ||||
|         i { | ||||
|           float: none; | ||||
|           margin-right: 0; | ||||
|         } | ||||
|       } | ||||
|       td.lines { | ||||
|         padding: 0; | ||||
|  |  | |||
|  | @ -78,6 +78,24 @@ label { | |||
|   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 { | ||||
|   display: inline; | ||||
| } | ||||
|  |  | |||
|  | @ -24,6 +24,10 @@ | |||
|         background-color: $color-darker; | ||||
|         a { | ||||
|           color: #fff; | ||||
| 
 | ||||
|           h3 { | ||||
|             color: #fff; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  |  | |||
|  | @ -8,7 +8,7 @@ header { | |||
|   &.navbar-empty { | ||||
|     height: 58px; | ||||
|     background: #fff; | ||||
|     border-bottom: 1px solid #eee; | ||||
|     border-bottom: 1px solid $btn-gray-hover; | ||||
| 
 | ||||
|     .center-logo { | ||||
|       margin: 11px 0; | ||||
|  | @ -22,13 +22,21 @@ header { | |||
|   } | ||||
| 
 | ||||
|   &.navbar-gitlab { | ||||
|     padding: 0 20px; | ||||
|     padding: 0 16px; | ||||
|     z-index: 100; | ||||
|     margin-bottom: 0; | ||||
|     min-height: $header-height; | ||||
|     background-color: #fff; | ||||
|     height: $header-height; | ||||
|     background-color: $background-color; | ||||
|     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 { | ||||
|       width: 100% !important; | ||||
|  | @ -47,7 +55,7 @@ header { | |||
|         text-align: center; | ||||
| 
 | ||||
|         &:hover, &:focus, &:active { | ||||
|           background-color: #fff; | ||||
|           background-color: $background-color; | ||||
|         } | ||||
|       } | ||||
| 
 | ||||
|  | @ -56,22 +64,54 @@ header { | |||
|         margin: 6px 0; | ||||
|         border-radius: 0; | ||||
|         position: absolute; | ||||
|         right: 2px; | ||||
|         right: -10px; | ||||
|         padding: 6px 10px; | ||||
| 
 | ||||
|         &:hover { | ||||
|           background-color: #eee; | ||||
|           background-color: $btn-gray-hover; | ||||
|         } | ||||
| 
 | ||||
|         &.active { | ||||
|           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 { | ||||
|     position: relative; | ||||
|     height: $header-height; | ||||
|     padding-right: 20px; | ||||
|     padding-right: 40px; | ||||
| 
 | ||||
|     @media (max-width: $screen-xs-min) { | ||||
|       padding-left: 40px; | ||||
|     } | ||||
| 
 | ||||
|     @media (min-width: $screen-sm-min) { | ||||
|       padding-right: 0; | ||||
|  | @ -122,6 +162,10 @@ header { | |||
|     } | ||||
|   } | ||||
| 
 | ||||
|   .project-item-select-holder { | ||||
|     display: inline; | ||||
|   } | ||||
| 
 | ||||
|   .impersonation i { | ||||
|     color: $red-normal; | ||||
|   } | ||||
|  | @ -137,6 +181,10 @@ header { | |||
|   @media (min-width: $screen-md-min) { | ||||
|     @include collapsed-header; | ||||
|   } | ||||
| 
 | ||||
|   @media (max-width: $screen-xs-min) { | ||||
|     margin-left: 0; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .header-expanded { | ||||
|  | @ -145,6 +193,10 @@ header { | |||
|   @media (min-width: $screen-md-min) { | ||||
|     margin-left: $sidebar_width; | ||||
|   } | ||||
| 
 | ||||
|   @media (max-width: $screen-xs-min) { | ||||
|     margin-left: 0; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| @media (max-width: $screen-xs-max) { | ||||
|  |  | |||
|  | @ -90,3 +90,12 @@ | |||
|   box-shadow: none; | ||||
|   width: 100%; | ||||
| } | ||||
| 
 | ||||
| .md { | ||||
|   &.md-preview-holder { | ||||
|     code { | ||||
|       white-space: pre-wrap; | ||||
|       word-break: keep-all; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -30,7 +30,7 @@ | |||
|   } | ||||
| 
 | ||||
|   .rss-btn { | ||||
|     display: none !important; | ||||
|     display: none; | ||||
|   } | ||||
| 
 | ||||
|   .project-home-links { | ||||
|  | @ -70,13 +70,6 @@ | |||
|     display: none; | ||||
|   } | ||||
| 
 | ||||
|   .issue-details { | ||||
|     .creator, | ||||
|     .page-title .btn-close { | ||||
|       display: none; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   %ul.notes .note-role, .note-actions { | ||||
|     display: none; | ||||
|   } | ||||
|  |  | |||
|  | @ -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; | ||||
| } | ||||
|  | @ -26,8 +26,8 @@ | |||
|     } | ||||
| 
 | ||||
|     &.active a { | ||||
|       color: #000; | ||||
|       border-bottom: 2px solid #4688f1; | ||||
|       border-bottom: 2px solid $link-underline-blue; | ||||
|       color: $black; | ||||
|     } | ||||
| 
 | ||||
|     .badge { | ||||
|  | @ -140,6 +140,12 @@ | |||
|       } | ||||
|     } | ||||
| 
 | ||||
|     .project-filter-form { | ||||
|       input { | ||||
|         background-color: $background-color; | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     @media (max-width: $screen-xs-max) { | ||||
|       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; | ||||
| } | ||||
|  |  | |||
|  | @ -7,13 +7,11 @@ | |||
|   .select2-choice { | ||||
|     background: #fff; | ||||
|     border-color: $input-border; | ||||
|     border-color: $border-white-light; | ||||
|     height: 35px; | ||||
|     padding: $gl-vert-padding $gl-btn-padding; | ||||
|     font-size: $gl-font-size; | ||||
|     line-height: 1.42857143; | ||||
| 
 | ||||
|     @include border-radius($border-radius-default); | ||||
|     border-radius: $border-radius-base; | ||||
| 
 | ||||
|     .select2-arrow { | ||||
|       background-image: none; | ||||
|  | @ -121,9 +119,6 @@ | |||
|   } | ||||
| } | ||||
| 
 | ||||
| .select2-container-multi .select2-choices .select2-search-choice { | ||||
| } | ||||
| 
 | ||||
| .select2-drop-active { | ||||
|   margin-top: 6px; | ||||
|   font-size: 14px; | ||||
|  | @ -202,6 +197,14 @@ | |||
|   } | ||||
| } | ||||
| 
 | ||||
| .select2-highlighted { | ||||
|   .group-result { | ||||
|     .group-path { | ||||
|       color: #fff; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .group-result { | ||||
|   .group-image { | ||||
|     float: left; | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ | |||
|   position: absolute; | ||||
|   width: 58px; | ||||
|   cursor: pointer; | ||||
|   margin-top: 8px; | ||||
| } | ||||
| 
 | ||||
| .page-with-sidebar { | ||||
|  | @ -62,7 +63,7 @@ | |||
|       float: left; | ||||
|       height: $header-height; | ||||
|       width: 100%; | ||||
|       padding: 11px 0 11px 22px; | ||||
|       padding-left: 22px; | ||||
|       overflow: hidden; | ||||
|       outline: none; | ||||
|       transition-duration: .3s; | ||||
|  | @ -85,7 +86,7 @@ | |||
|           margin: 0; | ||||
|           margin-left: 50px; | ||||
|           font-size: 19px; | ||||
|           line-height: 41px; | ||||
|           line-height: 50px; | ||||
|           font-weight: normal; | ||||
|         } | ||||
|       } | ||||
|  | @ -97,7 +98,7 @@ | |||
|   } | ||||
| 
 | ||||
|   .sidebar-user { | ||||
|     padding: 9px 22px; | ||||
|     padding: 7px 22px; | ||||
|     position: fixed; | ||||
|     bottom: 40px; | ||||
|     width: $sidebar_width; | ||||
|  | @ -209,15 +210,33 @@ | |||
|   } | ||||
| } | ||||
| 
 | ||||
| .sidebar-wrapper { | ||||
|   &.hidden-nav { | ||||
|     width: 0; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .page-sidebar-collapsed { | ||||
|   padding-left: $sidebar_collapsed_width; | ||||
| 
 | ||||
|   @media (max-width: $screen-xs-min) { | ||||
|     padding-left: 0; | ||||
|   } | ||||
| 
 | ||||
|   .sidebar-wrapper { | ||||
|     width: $sidebar_collapsed_width; | ||||
| 
 | ||||
|     @media (max-width: $screen-xs-min) { | ||||
|       width: 0; | ||||
|     } | ||||
| 
 | ||||
|     .header-logo { | ||||
|       width: $sidebar_collapsed_width; | ||||
| 
 | ||||
|       @media (max-width: $screen-xs-min) { | ||||
|         width: 0; | ||||
|       } | ||||
| 
 | ||||
|       a { | ||||
|         padding-left: ($sidebar_collapsed_width - 36) / 2; | ||||
| 
 | ||||
|  | @ -243,17 +262,35 @@ | |||
| 
 | ||||
|     .collapse-nav a { | ||||
|       width: $sidebar_collapsed_width; | ||||
| 
 | ||||
|       @media (max-width: $screen-xs-min) { | ||||
|         width: 0; | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     .sidebar-user { | ||||
|       padding-left: ($sidebar_collapsed_width - 36) / 2; | ||||
|       width: $sidebar_collapsed_width; | ||||
| 
 | ||||
|       @media (max-width: $screen-xs-min) { | ||||
|         width: 0; | ||||
|         padding-left: 0; | ||||
|         padding-right: 0; | ||||
|       } | ||||
| 
 | ||||
|       .username { | ||||
|         display: none; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   .layout-nav { | ||||
|     padding-right: $sidebar_collapsed_width; | ||||
| 
 | ||||
|     @media (max-width: $screen-xs-min) { | ||||
|       padding-right: 0;; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .page-sidebar-expanded { | ||||
|  | @ -263,6 +300,10 @@ | |||
|     padding-left: $sidebar_width; | ||||
|   } | ||||
| 
 | ||||
|   @media (max-width: $screen-xs-min) { | ||||
|     padding-left: 0; | ||||
|   } | ||||
| 
 | ||||
|   .sidebar-wrapper { | ||||
|     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 { | ||||
|  |  | |||
|  | @ -32,13 +32,11 @@ table { | |||
|       th { | ||||
|         background-color: $background-color; | ||||
|         font-weight: normal; | ||||
|         font-size: 15px; | ||||
|         border-bottom: 1px solid $border-color; | ||||
|         border-bottom: none; | ||||
|       } | ||||
| 
 | ||||
|       td { | ||||
|         border-color: $table-border-color; | ||||
|         border-bottom: 1px solid $border-color; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ | |||
|     border-bottom: 1px solid $border-white-light; | ||||
| 
 | ||||
|     &:target { | ||||
|       background: $row-hover; | ||||
|       background: $line-target-blue; | ||||
|     } | ||||
| 
 | ||||
|     .avatar { | ||||
|  | @ -39,8 +39,7 @@ | |||
|   .diff-file { | ||||
|     border: 1px solid $border-color; | ||||
|     border-bottom: none; | ||||
|     margin-left: 0; | ||||
|     margin-right: 0; | ||||
|     margin: 0; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -81,7 +81,7 @@ | |||
| 
 | ||||
| // Labels | ||||
| .label { | ||||
|   padding: 2px 4px; | ||||
|   padding: 4px 5px; | ||||
|   font-size: 13px; | ||||
|   font-style: normal; | ||||
|   font-weight: normal; | ||||
|  |  | |||
|  | @ -153,8 +153,8 @@ $nav-link-padding: 13px $gl-padding; | |||
| //== Code | ||||
| // | ||||
| //## | ||||
| $pre-bg:           #f8fafc !default; | ||||
| $pre-bg:           $background-color !default; | ||||
| $pre-color:        $gl-gray !default; | ||||
| $pre-border-color: #e7e9ed; | ||||
| $pre-border-color: $border-color; | ||||
| 
 | ||||
| $table-bg-accent: $background-color; | ||||
|  |  | |||
|  | @ -42,14 +42,14 @@ | |||
|     margin: 24px 0 12px; | ||||
|     padding: 0 0 10px; | ||||
|     border-bottom: 1px solid #e7e9ed; | ||||
|     color: #313236; | ||||
|     color: $gl-gray-dark; | ||||
|   } | ||||
| 
 | ||||
|   h2 { | ||||
|     font-size: 1.2em; | ||||
|     font-weight: 600; | ||||
|     margin: 24px 0 12px; | ||||
|     color: #313236; | ||||
|     color: $gl-gray-dark; | ||||
|   } | ||||
| 
 | ||||
|   h3 { | ||||
|  | @ -205,6 +205,10 @@ h1, h2, h3, h4, h5, h6 { | |||
|   font-weight: 600; | ||||
| } | ||||
| 
 | ||||
| .light-header { | ||||
|   font-weight: 600; | ||||
| } | ||||
| 
 | ||||
| /** CODE **/ | ||||
| pre { | ||||
|   font-family: $monospace_font; | ||||
|  | @ -259,3 +263,9 @@ h1, h2, h3, h4 { | |||
|     color: $gl-gray; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .text-right-lg { | ||||
|   @media (min-width: $screen-lg-min) { | ||||
|     text-align: right; | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -12,7 +12,7 @@ $gutter_inner_width: 258px; | |||
|  */ | ||||
| $border-color:       #e5e5e5; | ||||
| $focus-border-color: #3aabf0; | ||||
| $table-border-color: #eef0f2; | ||||
| $table-border-color: #ececec; | ||||
| $background-color:   #fafafa; | ||||
| 
 | ||||
| /* | ||||
|  | @ -20,7 +20,7 @@ $background-color:   #fafafa; | |||
|  */ | ||||
| $gl-font-size:         15px; | ||||
| $gl-title-color:       #333; | ||||
| $gl-text-color:        #555; | ||||
| $gl-text-color:        #5c5c5c; | ||||
| $gl-text-green:        #4a2; | ||||
| $gl-text-red:          #d12f19; | ||||
| $gl-text-orange:       #d90; | ||||
|  | @ -30,6 +30,7 @@ $gl-placeholder-color: #8f8f8f; | |||
| $gl-icon-color:        $gl-placeholder-color; | ||||
| $gl-grayish-blue:      #7f8fa4; | ||||
| $gl-gray:              $gl-text-color; | ||||
| $gl-gray-dark:         #313236; | ||||
| $gl-header-color:      $gl-title-color; | ||||
| 
 | ||||
| /* | ||||
|  | @ -65,16 +66,18 @@ $gl-padding-top: 10px; | |||
| $row-hover: #f4f8fe; | ||||
| $progress-color: #c0392b; | ||||
| $avatar_radius: 50%; | ||||
| $header-height: 58px; | ||||
| $header-height: 50px; | ||||
| $fixed-layout-width: 1280px; | ||||
| $gl-avatar-size: 40px; | ||||
| $error-exclamation-point: #e62958; | ||||
| $border-radius-default: 2px; | ||||
| $btn-transparent-color: #8f8f8f; | ||||
| $ssh-key-icon-color: #8f8f8f; | ||||
| $ssh-key-icon-size: 18px; | ||||
| $settings-icon-size: 18px; | ||||
| $provider-btn-group-border: #e5e5e5; | ||||
| $provider-btn-not-active-color: #4688f1; | ||||
| $link-underline-blue: #4a8bee; | ||||
| $layout-link-gray: #7e7c7c; | ||||
| $todo-alert-blue: #428bca; | ||||
| 
 | ||||
| /* | ||||
|  * Color schema | ||||
|  | @ -109,6 +112,7 @@ $red-light: #e52c5a; | |||
| $red-normal: #d22852; | ||||
| $red-dark: darken($red-normal, 5%); | ||||
| 
 | ||||
| $black: #000; | ||||
| $black-transparent: rgba(0, 0, 0, 0.3); | ||||
| 
 | ||||
| $border-white-light: #f1f2f4; | ||||
|  | @ -168,8 +172,12 @@ $line-removed: #fbe9eb; | |||
| $line-removed-dark: #fac5cd; | ||||
| $line-number-old: #f9d7dc; | ||||
| $line-number-new: #ddfbe6; | ||||
| $line-number-select: #fbf2da; | ||||
| $match-line: #fafafa; | ||||
| $table-border-gray: #f0f0f0; | ||||
| $line-target-blue: #eaf3fc; | ||||
| $line-select-yellow: #fcf8e7; | ||||
| $line-select-yellow-dark: #f0e2bd; | ||||
| /* | ||||
|  * Fonts | ||||
|  */ | ||||
|  | @ -179,7 +187,6 @@ $regular_font: 'Source Sans Pro', "Helvetica Neue", Helvetica, Arial, sans-serif | |||
| /* | ||||
| * Dropdowns | ||||
| */ | ||||
| $dropdown-border-radius: 2px; | ||||
| $dropdown-width: 300px; | ||||
| $dropdown-bg: #fff; | ||||
| $dropdown-link-color: #555; | ||||
|  | @ -208,6 +215,7 @@ $dropdown-toggle-hover-icon-color: $dropdown-toggle-hover-border-color; | |||
| $btn-active-gray: #ececec; | ||||
| $btn-placeholder-gray: #c7c7c7; | ||||
| $btn-white-active: #848484; | ||||
| $btn-gray-hover: #eee; | ||||
| 
 | ||||
| /* | ||||
|  *  Award emoji | ||||
|  | @ -241,3 +249,8 @@ $note-form-border-color: #e5e5e5; | |||
| $note-toolbar-color: #959494; | ||||
| 
 | ||||
| $zen-control-hover-color: #111; | ||||
| 
 | ||||
| $calendar-header-color: #b8b8b8; | ||||
| $calendar-hover-bg: #ecf3fe; | ||||
| $calendar-border-color: rgba(#000, .1); | ||||
| $calendar-unselectable-bg: #faf9f9; | ||||
|  |  | |||
|  | @ -111,8 +111,6 @@ | |||
|   .vg { color: #f8f8f2 } /* Name.Variable.Global */ | ||||
|   .vi { color: #f8f8f2 } /* Name.Variable.Instance */ | ||||
|   .il { color: #ae81ff } /* Literal.Number.Integer.Long */ | ||||
| 
 | ||||
|   .gh { } /* Generic Heading & Diff Header */ | ||||
|   .gu { color: #75715e; } /* Generic.Subheading & Diff Unified/Comment? */ | ||||
|   .gd { color: #f92672; } /* Generic.Deleted & Diff Deleted */ | ||||
|   .gi { color: #a6e22e; } /* Generic.Inserted & Diff Inserted */ | ||||
|  |  | |||
|  | @ -21,11 +21,6 @@ | |||
| 
 | ||||
|   // Diff line | ||||
|   .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 { | ||||
|       &.old { | ||||
|  | @ -37,11 +32,16 @@ | |||
|         background-color: $line-number-new; | ||||
|         border-color: $line-added-dark; | ||||
|       } | ||||
| 
 | ||||
|       &.hll:not(.empty-cell) { | ||||
|         background-color: $line-number-select; | ||||
|         border-color: $line-select-yellow-dark; | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     .line_content { | ||||
|       &.old { | ||||
|         background: $line-removed; | ||||
|         background-color: $line-removed; | ||||
| 
 | ||||
|         span.idiff { | ||||
|           background-color: $line-removed-dark; | ||||
|  | @ -58,7 +58,11 @@ | |||
| 
 | ||||
|       &.match { | ||||
|         color: $black-transparent; | ||||
|         background: $match-line; | ||||
|         background-color: $match-line; | ||||
|       } | ||||
| 
 | ||||
|       &.hll:not(.empty-cell) { | ||||
|         background-color: $line-select-yellow; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  |  | |||
|  | @ -83,3 +83,12 @@ | |||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| table.builds { | ||||
| 
 | ||||
|   .build-link { | ||||
|     a { | ||||
|       color: $gl-dark-link-color; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -31,9 +31,23 @@ | |||
|   } | ||||
|   .commit-committer-link, | ||||
|   .commit-author-link { | ||||
|     color: #444; | ||||
|     color: $gl-gray; | ||||
|     font-weight: bold; | ||||
|   } | ||||
| 
 | ||||
|   .time_ago { | ||||
|     margin-left: 8px; | ||||
|   } | ||||
| 
 | ||||
|   .fa-clipboard { | ||||
|     color: $dropdown-title-btn-color; | ||||
|   } | ||||
| 
 | ||||
|   .commit-info { | ||||
|     &.branches { | ||||
|       margin-left: 8px; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .commit-box { | ||||
|  | @ -42,7 +56,7 @@ | |||
|   .commit-title { | ||||
|     margin: 0; | ||||
|     font-size: 23px; | ||||
|     color: #313236; | ||||
|     color: $gl-gray-dark; | ||||
|   } | ||||
| 
 | ||||
|   .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 | ||||
|  * custom merge request message | ||||
|  |  | |||
|  | @ -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; | ||||
|   } | ||||
| } | ||||
|  | @ -1,5 +1,5 @@ | |||
| .detail-page-header { | ||||
|   padding: 11px 0; | ||||
|   padding: $gl-padding-top 0; | ||||
|   border-bottom: 1px solid $border-color; | ||||
|   color: #5c5d5e; | ||||
|   font-size: 16px; | ||||
|  | @ -16,18 +16,13 @@ | |||
|   .issue_created_ago, .author_link { | ||||
|     white-space: nowrap; | ||||
|   } | ||||
| 
 | ||||
|   .issue-meta { | ||||
|     display: inline-block; | ||||
|     line-height: 20px; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .detail-page-description { | ||||
|   .title { | ||||
|     margin: 0; | ||||
|     font-size: 23px; | ||||
|     color: #313236; | ||||
|     color: $gl-gray-dark; | ||||
|   } | ||||
| 
 | ||||
|   .description { | ||||
|  | @ -41,4 +36,11 @@ | |||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   .wiki { | ||||
|     code { | ||||
|       white-space: pre-wrap; | ||||
|       word-break: keep-all; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -34,6 +34,7 @@ | |||
|     background: #fff; | ||||
|     color: #333; | ||||
|     border-radius: 0 0 3px 3px; | ||||
|     -webkit-overflow-scrolling: auto; | ||||
| 
 | ||||
|     .unfold { | ||||
|       cursor: pointer; | ||||
|  | @ -86,7 +87,7 @@ | |||
|         } | ||||
| 
 | ||||
|         span { | ||||
|           white-space: pre; | ||||
|           white-space: pre-wrap; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | @ -97,7 +98,11 @@ | |||
|       } | ||||
| 
 | ||||
|       td.line_content.parallel { | ||||
|         width: 50%; | ||||
|         width: 46%; | ||||
|       } | ||||
| 
 | ||||
|       .add-diff-note { | ||||
|         margin-left: -65px; | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|  | @ -126,8 +131,13 @@ | |||
|       margin: 0; | ||||
|       padding: 0 0.5em; | ||||
|       border: none; | ||||
| 
 | ||||
|       &.parallel { | ||||
|         display: table-cell; | ||||
| 
 | ||||
|         span { | ||||
|           word-break: break-all; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|  | @ -335,7 +345,7 @@ | |||
| } | ||||
| 
 | ||||
| .diff-file .line_content { | ||||
|   white-space: pre; | ||||
|   white-space: pre-wrap; | ||||
| } | ||||
| 
 | ||||
| .diff-wrap-lines .line_content { | ||||
|  |  | |||
|  | @ -26,6 +26,10 @@ | |||
|     line-height: 42px; | ||||
|     padding-top: 7px; | ||||
|     padding-bottom: 7px; | ||||
| 
 | ||||
|     .pull-right { | ||||
|       height: 20px; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   .editor-ref { | ||||
|  | @ -53,4 +57,9 @@ | |||
|   .select2 { | ||||
|     float: right; | ||||
|   } | ||||
| 
 | ||||
|   .encoding-selector, | ||||
|   .license-selector { | ||||
|     display: inline-block; | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -18,9 +18,6 @@ | |||
| } | ||||
| 
 | ||||
| .graphs { | ||||
|   .graph-author-commits-count { | ||||
|   } | ||||
| 
 | ||||
|   .graph-author-email { | ||||
|     float: right; | ||||
|     color: #777; | ||||
|  |  | |||
|  | @ -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 { | ||||
|   padding: 7px; | ||||
| } | ||||
|  |  | |||
|  | @ -16,3 +16,24 @@ i.icon-gitorious-big { | |||
|   width: 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; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -128,6 +128,7 @@ | |||
|   top: 58px; | ||||
|   bottom: 0; | ||||
|   right: 0; | ||||
|   z-index: 10; | ||||
|   transition: width .3s; | ||||
|   background: $gray-light; | ||||
|   padding: 10px 20px; | ||||
|  | @ -241,16 +242,20 @@ | |||
|     } | ||||
|   } | ||||
| 
 | ||||
|   .btn { | ||||
|   .issuable-pager { | ||||
|     background: $gray-normal; | ||||
|     border: 1px solid $border-gray-normal; | ||||
|     &:hover { | ||||
|       background: $gray-dark; | ||||
|       border: 1px solid $border-gray-dark; | ||||
|     } | ||||
| 
 | ||||
|     &.btn-primary { | ||||
|       @extend .btn-primary | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   a:not(.btn) { | ||||
|   a:not(.issuable-pager) { | ||||
|     &:hover { | ||||
|       color: $md-link-color; | ||||
|       text-decoration: none; | ||||
|  | @ -273,10 +278,6 @@ | |||
|   } | ||||
| } | ||||
| 
 | ||||
| .btn-default.gutter-toggle { | ||||
|   margin-top: 4px; | ||||
| } | ||||
| 
 | ||||
| .detail-page-description { | ||||
|   small { | ||||
|     color: $gray-darkest; | ||||
|  | @ -322,3 +323,50 @@ | |||
|     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; | ||||
| } | ||||
|  |  | |||
|  | @ -86,41 +86,9 @@ form.edit-issue { | |||
| @media (max-width: $screen-xs-max) { | ||||
|   .issue-btn-group { | ||||
|     width: 100%; | ||||
|     margin-top: 5px; | ||||
| 
 | ||||
|     .btn-group { | ||||
|       width: 100%; | ||||
| 
 | ||||
|       ul { | ||||
|         width: 100%; | ||||
|         text-align: center; | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     .btn { | ||||
|       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; | ||||
|   margin-left: 52px; | ||||
| } | ||||
| 
 | ||||
| .editor-details { | ||||
|   display: block; | ||||
| 
 | ||||
|   @media (min-width: $screen-sm-min) { | ||||
|     display: inline-block; | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -104,7 +104,7 @@ | |||
|       font-weight: 600; | ||||
|       font-size: 17px; | ||||
|       margin: 5px 0; | ||||
|       color: #313236; | ||||
|       color: $gl-gray-dark; | ||||
|     } | ||||
| 
 | ||||
|     p:last-child { | ||||
|  | @ -136,7 +136,7 @@ | |||
| } | ||||
| 
 | ||||
| .label-branch { | ||||
|   color: #313236; | ||||
|   color: $gl-gray-dark; | ||||
|   font-family: $monospace_font; | ||||
|   font-weight: bold; | ||||
|   overflow: hidden; | ||||
|  | @ -272,3 +272,19 @@ | |||
|   display: inline-block; | ||||
|   width: 250px; | ||||
| } | ||||
| 
 | ||||
| .table-holder { | ||||
|   .builds { | ||||
| 
 | ||||
|     th { | ||||
|       background-color: $white-light; | ||||
|       color: $gl-placeholder-color; | ||||
|     } | ||||
| 
 | ||||
|     th, | ||||
|     td { | ||||
|       padding: 16px; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -28,7 +28,7 @@ li.milestone { | |||
| 
 | ||||
|     // Issue title | ||||
|     span a { | ||||
|       color: rgba(0,0,0,0.64); | ||||
|       color: $gl-text-color; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | @ -51,7 +51,7 @@ li.milestone { | |||
|     margin-top: 7px; | ||||
| 
 | ||||
|     .issuable-number { | ||||
|       color: rgba(0,0,0,0.44); | ||||
|       color: $gl-placeholder-color; | ||||
|       margin-right: 5px; | ||||
|     } | ||||
|     .avatar { | ||||
|  |  | |||
|  | @ -42,6 +42,7 @@ | |||
| .note-textarea { | ||||
|   display: block; | ||||
|   padding: 10px 0; | ||||
|   color: $gl-gray; | ||||
|   font-family: $regular_font; | ||||
|   border: 0; | ||||
| 
 | ||||
|  | @ -61,11 +62,11 @@ | |||
|     padding: $gl-padding-top $gl-padding; | ||||
|     border: 1px solid $note-form-border-color; | ||||
|     border-radius: $border-radius-base; | ||||
|     transition: border-color ease-in-out 0.15s, | ||||
|                 box-shadow ease-in-out 0.15s; | ||||
| 
 | ||||
|     &.is-focused { | ||||
|       border-color: $focus-border-color; | ||||
|       box-shadow: 0 0 2px $black-transparent, | ||||
|                   0 0 4px rgba($focus-border-color, .4); | ||||
|       @extend .form-control:focus; | ||||
| 
 | ||||
|       .comment-toolbar, | ||||
|       .nav-links { | ||||
|  | @ -83,18 +84,6 @@ | |||
|         border-color: $gl-success; | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     p { | ||||
|       code { | ||||
|         white-space: normal; | ||||
|       } | ||||
| 
 | ||||
|       pre { | ||||
|         code { | ||||
|           white-space: pre; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -80,16 +80,8 @@ ul.notes { | |||
|         @include md-typography; | ||||
| 
 | ||||
|         // On diffs code should wrap nicely and not overflow | ||||
|         p { | ||||
|         code { | ||||
|             white-space: normal; | ||||
|           } | ||||
| 
 | ||||
|           pre { | ||||
|             code { | ||||
|               white-space: pre; | ||||
|             } | ||||
|           } | ||||
|           white-space: pre-wrap; | ||||
|         } | ||||
| 
 | ||||
|         // Reset ul style types since we're nested inside a ul already | ||||
|  | @ -116,10 +108,10 @@ ul.notes { | |||
|           border-color: darken(#f5f5f5, 8%); | ||||
|           margin: 10px 0; | ||||
|         } | ||||
|       } | ||||
| 
 | ||||
|       a { | ||||
|         word-break: break-all; | ||||
|         code { | ||||
|           word-break: keep-all; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|  | @ -136,7 +128,7 @@ ul.notes { | |||
|     margin-right: 10px; | ||||
|   } | ||||
|   .line_content { | ||||
|     white-space: pre; | ||||
|     white-space: pre-wrap; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
|  | @ -175,6 +167,11 @@ ul.notes { | |||
|       .notes { | ||||
|         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, | ||||
|  | @ -197,6 +197,12 @@ ul.notes { | |||
|   color: $notes-light-color; | ||||
| } | ||||
| 
 | ||||
| .discussion-headline-light { | ||||
|   a { | ||||
|     color: $gl-link-color; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Actions for Discussions/Notes | ||||
|  */ | ||||
|  | @ -208,6 +214,17 @@ ul.notes { | |||
|   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, | ||||
| .discussion-action-button { | ||||
|   display: inline-block; | ||||
|  | @ -285,7 +302,7 @@ ul.notes { | |||
|     padding: 4px; | ||||
|     font-size: 16px; | ||||
|     color: $gl-link-color; | ||||
|     margin-left: -60px; | ||||
|     margin-left: -56px; | ||||
|     position: absolute; | ||||
|     z-index: 10; | ||||
|     width: 32px; | ||||
|  |  | |||
|  | @ -18,7 +18,8 @@ | |||
| } | ||||
| 
 | ||||
| .account-btn-link, | ||||
| .profile-settings-sidebar a { | ||||
| .profile-settings-sidebar a, | ||||
| .settings-sidebar a { | ||||
|   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 { | ||||
|   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 { | ||||
|   form { | ||||
|     display: inline-block; | ||||
|  | @ -218,3 +205,21 @@ | |||
|     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%; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -178,7 +178,7 @@ | |||
|     .option-title { | ||||
|       font-weight: normal; | ||||
|       display: inline-block; | ||||
|       color: #313236; | ||||
|       color: $gl-gray-dark; | ||||
|     } | ||||
| 
 | ||||
|     .option-descr { | ||||
|  | @ -202,8 +202,31 @@ | |||
|   min-width: 200px; | ||||
| } | ||||
| 
 | ||||
| .deploy-project-label { | ||||
|   margin: 1px; | ||||
| .deploy-key-content { | ||||
|   @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 { | ||||
|  | @ -256,12 +279,6 @@ | |||
|   } | ||||
| } | ||||
| 
 | ||||
| table.table.protected-branches-list tr.no-border { | ||||
|   th, td { | ||||
|     border: 0; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .project-import .btn { | ||||
|   float: left; | ||||
|   margin-right: 10px; | ||||
|  | @ -474,3 +491,14 @@ pre.light-well { | |||
|     color: #fff; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .protected-branches-list { | ||||
|   a { | ||||
|     color: $gl-gray; | ||||
|     font-weight: 600; | ||||
| 
 | ||||
|     &:hover { | ||||
|       color: $gl-link-color; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -10,17 +10,6 @@ | |||
|   } | ||||
| } | ||||
| 
 | ||||
| .search-holder { | ||||
|   max-width: 600px; | ||||
|   margin: 0 auto; | ||||
|   margin-bottom: 20px; | ||||
| 
 | ||||
|   input { | ||||
|     border-color: #bbb; | ||||
|     font-weight: bold; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .search { | ||||
|   margin-right: 10px; | ||||
|   margin-left: 10px; | ||||
|  | @ -159,7 +148,85 @@ | |||
| 
 | ||||
|   &.has-location-badge { | ||||
|     .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; | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -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; | ||||
| } | ||||
|  | @ -1,7 +1,7 @@ | |||
| .container-fluid { | ||||
|   .ci-status { | ||||
|     padding: 2px 7px; | ||||
|     margin-right: 5px; | ||||
|     margin-right: 10px; | ||||
|     border: 1px solid #eee; | ||||
|     white-space: nowrap; | ||||
|     @include border-radius(4px); | ||||
|  |  | |||
|  | @ -6,9 +6,16 @@ | |||
| .navbar-nav { | ||||
|   li { | ||||
|     .badge.todos-pending-count { | ||||
|       background-color: $gl-icon-color; | ||||
|       margin-top: -5px; | ||||
|       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; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -16,7 +16,7 @@ | |||
| 
 | ||||
|     tr { | ||||
|       > td, > th { | ||||
|         line-height: 26px; | ||||
|         line-height: 23px; | ||||
|       } | ||||
| 
 | ||||
|       &:hover { | ||||
|  |  | |||
|  | @ -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 {font-size: 30px;} | ||||
| .wiki h2 {font-size: 22px;} | ||||
| .wiki h3 {font-size: 18px; font-weight: bold; } | ||||
| 
 | ||||
| .sidebar-wrapper { display: none; } | ||||
| .nav { display: none; } | ||||
| .btn { display: none; } | ||||
| header, | ||||
| nav, | ||||
| 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; | ||||
| } | ||||
|  |  | |||
|  | @ -6,12 +6,6 @@ class Admin::ApplicationController < ApplicationController | |||
|   layout 'admin' | ||||
| 
 | ||||
|   def authenticate_admin! | ||||
|     return render_404 unless current_user.is_admin? | ||||
|   end | ||||
| 
 | ||||
|   def authorize_impersonator! | ||||
|     if session[:impersonator_id] | ||||
|       User.find_by!(username: session[:impersonator_id]).admin? | ||||
|     end | ||||
|     render_404 unless current_user.is_admin? | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -75,6 +75,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController | |||
|       :admin_notification_email, | ||||
|       :user_oauth_applications, | ||||
|       :shared_runners_enabled, | ||||
|       :shared_runners_text, | ||||
|       :max_artifacts_size, | ||||
|       :metrics_enabled, | ||||
|       :metrics_host, | ||||
|  | @ -92,6 +93,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController | |||
|       :akismet_api_key, | ||||
|       :email_author_in_body, | ||||
|       :repository_checks_enabled, | ||||
|       :metrics_packet_size, | ||||
|       restricted_visibility_levels: [], | ||||
|       import_sources: [] | ||||
|     ) | ||||
|  |  | |||
|  | @ -39,6 +39,12 @@ class Admin::HooksController < Admin::ApplicationController | |||
|   end | ||||
| 
 | ||||
|   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 | ||||
|  |  | |||
|  | @ -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 | ||||
|  | @ -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 | ||||
|  | @ -31,6 +31,24 @@ class Admin::UsersController < Admin::ApplicationController | |||
|     user | ||||
|   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 | ||||
|     if user.block | ||||
|       redirect_back_or_admin_user(notice: "Successfully blocked") | ||||
|  |  | |||
|  | @ -117,7 +117,7 @@ class ApplicationController < ActionController::Base | |||
|   end | ||||
| 
 | ||||
|   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 | ||||
| 
 | ||||
|   def abilities | ||||
|  |  | |||
|  | @ -10,6 +10,8 @@ module FilterProjects | |||
|   def filter_projects(projects) | ||||
|     projects = projects.search(params[:filter_projects]) if params[:filter_projects].present? | ||||
|     projects = projects.non_archived if params[:archived].blank? | ||||
|     projects = projects.personal(current_user) if params[:personal].present? && current_user | ||||
| 
 | ||||
|     projects | ||||
|   end | ||||
| end | ||||
|  |  | |||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue