Imported Upstream version 7.2.1

This commit is contained in:
Cédric Boutillier 2014-09-02 14:37:02 +02:00
commit 7e8e16d0ba
1525 changed files with 83996 additions and 0 deletions

1
.foreman Normal file
View File

@ -0,0 +1 @@
port: 3000

40
.gitignore vendored Normal file
View File

@ -0,0 +1,40 @@
.bundle
.rbx/
db/*.sqlite3
db/*.sqlite3-journal
log/*.log*
tmp/
.sass-cache/
coverage/*
backups/*
*.swp
public/uploads/
.ruby-version
.ruby-gemset
.rvmrc
.rbenv-version
.directory
nohup.out
Vagrantfile
.vagrant
config/gitlab.yml
config/database.yml
config/initializers/omniauth.rb
config/initializers/rack_attack.rb
config/initializers/smtp_settings.rb
config/unicorn.rb
config/resque.yml
config/aws.yml
db/data.yml
.idea
.DS_Store
.chef
vendor/bundle/*
rails_best_practices_output.html
doc/code/*
.secret
*.log
public/uploads.*
public/assets/
.envrc
dump.rdb

4
.hound.yml Normal file
View File

@ -0,0 +1,4 @@
# Prefer single quotes
StringLiterals:
EnforcedStyle: single_quotes
Enabled: true

26
.pkgr.yml Normal file
View File

@ -0,0 +1,26 @@
user: git
group: git
before_precompile: ./bin/pkgr_before_precompile.sh
targets:
debian-7: &wheezy
build_dependencies:
- libicu-dev
dependencies:
- libicu48
- libpcre3
- git
ubuntu-12.04: *wheezy
ubuntu-14.04:
build_dependencies:
- libicu-dev
dependencies:
- libicu52
- libpcre3
- git
centos-6:
build_dependencies:
- libicu-devel
dependencies:
- libicu
- pcre
- git

1
.rspec Normal file
View File

@ -0,0 +1 @@
--color

4
.simplecov Normal file
View File

@ -0,0 +1,4 @@
# .simplecov
SimpleCov.start 'rails' do
merge_timeout 3600
end

8
.teatro.yml Normal file
View File

@ -0,0 +1,8 @@
stage:
before:
- cp config/gitlab.teatro.yml config/gitlab.yml
- mkdir /apps/gitlab-satellites
- mkdir /apps/repositories
database:
- RAILS_ENV=development force=yes bundle exec rake db:create gitlab:setup

36
.travis.yml Normal file
View File

@ -0,0 +1,36 @@
language: ruby
env:
global:
- TRAVIS=true
matrix:
- TASK=spinach_project DB=mysql
- TASK=spinach_other DB=mysql
- TASK=spec:api DB=mysql
- TASK=spec:feature DB=mysql
- TASK=spec:other DB=mysql
- TASK=jasmine:ci DB=mysql
- TASK=spinach_project DB=postgresql
- TASK=spinach_other DB=postgresql
- TASK=spec:api DB=postgresql
- TASK=spec:feature DB=postgresql
- TASK=spec:other DB=postgresql
- TASK=jasmine:ci DB=postgresql
before_install:
- sudo apt-get install libicu-dev -y
install:
- "travis_retry bundle install --deployment --without production --retry 5"
branches:
only:
- 'master'
rvm:
- 2.0.0
services:
- redis-server
before_script:
- "cp config/database.yml.$DB config/database.yml"
- "cp config/gitlab.yml.example config/gitlab.yml"
- "bundle exec rake db:setup"
- "bundle exec rake db:seed_fu"
script: "bundle exec rake $TASK --trace"
notifications:
email: false

858
CHANGELOG Normal file
View File

@ -0,0 +1,858 @@
v 7.2.1
- Delete orphaned labels during label migration (James Brooks)
- Security: prevent XSS with stricter MIME types for raw repo files
v 7.2.0
- Explore page
- Add project stars (Ciro Santilli)
- Log Sidekiq arguments
- Better labels: colors, ability to rename and remove
- Improve the way merge request collects diffs
- Improve compare page for large diffs
- Expose the full commit message via API
- Fix 500 error on repository rename
- Fix bug when MR download patch return invalid diff
- Test gitlab-shell integration
- Repository import timeout increased from 2 to 4 minutes allowing larger repos to be imported
- API for labels (Robert Schilling)
- API: ability to set an import url when creating project for specific user
v 7.1.1
- Fix cpu usage issue in Firefox
- Fix redirect loop when changing password by new user
- Fix 500 error on new merge request page
v 7.1.0
- Remove observers
- Improve MR discussions
- Filter by description on Issues#index page
- Fix bug with namespace select when create new project page
- Show README link after description for non-master members
- Add @all mention for comments
- Dont show reply button if user is not signed in
- Expose more information for issues with webhook
- Add a mention of the merge request into the default merge request commit message
- Improve code highlight, introduce support for more languages like Go, Clojure, Erlang etc
- Fix concurrency issue in repository download
- Dont allow repository name start with ?
- Improve email threading (Pierre de La Morinerie)
- Cleaner help page
- Group milestones
- Improved email notifications
- Contributors API (sponsored by Mobbr)
- Fix LDAP TLS authentication (Boris HUISGEN)
- Show VERSION information on project sidebar
- Improve branch removal logic when accept MR
- Fix bug where comment form is spawned inside the Reply button
- Remove Dir.chdir from Satellite#lock for thread-safety
- Increased default git max_size value from 5MB to 20MB in gitlab.yml. Please update your configs!
- Show error message in case of timeout in satellite when create MR
- Show first 100 files for huge diff instead of hiding all
- Change default admin email from admin@local.host to admin@example.com
v 7.0.0
- The CPU no longer overheats when you hold down the spacebar
- Improve edit file UI
- Add ability to upload group avatar when create
- Protected branch cannot be removed
- Developers can remove normal branches with UI
- Remove branch via API (sponsored by O'Reilly Media)
- Move protected branches page to Project settings area
- Redirect to Files view when create new branch via UI
- Drag and drop upload of image in every markdown-area (Earle Randolph Bunao and Neil Francis Calabroso)
- Refactor the markdown relative links processing
- Make it easier to implement other CI services for GitLab
- Group masters can create projects in group
- Deprecate ruby 1.9.3 support
- Only masters can rewrite/remove git tags
- Add X-Frame-Options SAMEORIGIN to Nginx config so Sidekiq admin is visible
- UI improvements
- Case-insensetive search for issues
- Update to rails 4.1
- Improve performance of application for projects and groups with a lot of members
- Formally support Ruby 2.1
- Include Nginx gitlab-ssl config
- Add manual language detection for highlight.js
- Added example.com/:username routing
- Show notice if your profile is public
- UI improvements for mobile devices
- Improve diff rendering performance
- Drag-n-drop for issues and merge requests between states at milestone page
- Fix '0 commits' message for huge repositories on project home page
- Prevent 500 error page when visit commit page from large repo
- Add notice about huge push over http to unicorn config
- File action in satellites uses default 30 seconds timeout instead of old 10 seconds one
- Overall performance improvements
- Skip init script check on omnibus-gitlab
- Be more selective when killing stray Sidekiqs
- Check LDAP user filter during sign-in
- Remove wall feature (no data loss - you can take it from database)
- Dont expose user emails via API unless you are admin
- Detect issues closed by Merge Request description
- Better email subject lines from email on push service (Alex Elman)
- Enable identicon for gravatar be default
v 6.9.2
- Revert the commit that broke the LDAP user filter
v 6.9.1
- Fix scroll to highlighted line
- Fix the pagination on load for commits page
v 6.9.0
- Store Rails cache data in the Redis `cache:gitlab` namespace
- Adjust MySQL limits for existing installations
- Add db index on project_id+iid column. This prevents duplicate on iid (During migration duplicates will be removed)
- Markdown preview or diff during editing via web editor (Evgeniy Sokovikov)
- Give the Rails cache its own Redis namespace
- Add ability to set different ssh host, if different from http/https
- Fix syntax highlighting for code comments blocks
- Improve comments loading logic
- Stop refreshing comments when the tab is hidden
- Improve issue and merge request mobile UI (Drew Blessing)
- Document how to convert a backup to PostgreSQL
- Fix locale bug in backup manager
- Fix can not automerge when MR description is too long
- Fix wiki backup skip bug
- Two Step MR creation process
- Remove unwanted files from satellite working directory with git clean -fdx
- Accept merge request via API (sponsored by O'Reilly Media)
- Add more access checks during API calls
- Block SSH access for 'disabled' Active Directory users
- Labels for merge requests (Drew Blessing)
- Threaded emails by setting a Message-ID (Philip Blatter)
v 6.8.0
- Ability to at mention users that are participating in issue and merge req. discussion
- Enabled GZip Compression for assets in example Nginx, make sure that Nginx is compiled with --with-http_gzip_static_module flag (this is default in Ubuntu)
- Make user search case-insensitive (Christopher Arnold)
- Remove omniauth-ldap nickname bug workaround
- Drop all tables before restoring a Postgres backup
- Make the repository downloads path configurable
- Create branches via API (sponsored by O'Reilly Media)
- Changed permission of gitlab-satellites directory not to be world accessible
- Protected branch does not allow force push
- Fix popen bug in `rake gitlab:satellites:create`
- Disable connection reaping for MySQL
- Allow oauth signup without email for twitter and github
- Fix faulty namespace names that caused 500 on user creation
- Option to disable standard login
- Clean old created archives from repository downloads directory
- Fix download link for huge MR diffs
- Expose event and mergerequest timestamps in API
- Fix emails on push service when only one commit is pushed
v 6.7.3
- Fix the merge notification email not being sent (Pierre de La Morinerie)
- Drop all tables before restoring a Postgres backup
- Remove yanked modernizr gem
v 6.7.2
- Fix upgrader script
v 6.7.1
- Fix GitLab CI integration
v 6.7.0
- Increased the example Nginx client_max_body_size from 5MB to 20MB, consider updating it manually on existing installations
- Add support for Gemnasium as a Project Service (Olivier Gonzalez)
- Add edit file button to MergeRequest diff
- Public groups (Jason Hollingsworth)
- Cleaner headers in Notification Emails (Pierre de La Morinerie)
- Blob and tree gfm links to anchors work
- Piwik Integration (Sebastian Winkler)
- Show contribution guide link for new issue form (Jeroen van Baarsen)
- Fix CI status for merge requests from fork
- Added option to remove issue assignee on project issue page and issue edit page (Jason Blanchard)
- New page load indicator that includes a spinner that scrolls with the page
- Converted all the help sections into markdown
- LDAP user filters
- Streamline the content of notification emails (Pierre de La Morinerie)
- Fixes a bug with group member administration (Matt DeTullio)
- Sort tag names using VersionSorter (Robert Speicher)
- Add GFM autocompletion for MergeRequests (Robert Speicher)
- Add webhook when a new tag is pushed (Jeroen van Baarsen)
- Add button for toggling inline comments in diff view
- Add retry feature for repository import
- Reuse the GitLab LDAP connection within each request
- Changed markdown new line behaviour to conform to markdown standards
- Fix global search
- Faster authorized_keys rebuilding in `rake gitlab:shell:setup` (requires gitlab-shell 1.8.5)
- Create and Update MR calls now support the description parameter (Greg Messner)
- Markdown relative links in the wiki link to wiki pages, markdown relative links in repositories link to files in the repository
- Added Slack service integration (Federico Ravasio)
- Better API responses for access_levels (sponsored by O'Reilly Media)
- Requires at least 2 unicorn workers
- Requires gitlab-shell v1.9+
- Replaced gemoji(due to closed licencing problem) with Phantom Open Emoji library(combined SIL Open Font License, MIT License and the CC 3.0 License)
- Fix `/:username.keys` response content type (Dmitry Medvinsky)
v 6.6.5
- Added option to remove issue assignee on project issue page and issue edit page (Jason Blanchard)
- Hide mr close button for comment form if merge request was closed or inline comment
- Adds ability to reopen closed merge request
v 6.6.4
- Add missing html escape for highlighted code blocks in comments, issues
v 6.6.3
- Fix 500 error when edit yourself from admin area
- Hide private groups for public profiles
v 6.6.2
- Fix 500 error on branch/tag create or remove via UI
v 6.6.1
- Fix 500 error on files tab if submodules presents
v 6.6.0
- Retrieving user ssh keys publically(github style): http://__HOST__/__USERNAME__.keys
- Permissions: Developer now can manage issue tracker (modify any issue)
- Improve Code Compare page performance
- Group avatar
- Pygments.rb replaced with highlight.js
- Improve Merge request diff store logic
- Improve render performnace for MR show page
- Fixed Assembla hardcoded project name
- Jira integration documentation
- Refactored app/services
- Remove snippet expiration
- Mobile UI improvements (Drew Blessing)
- Fix block/remove UI for admin::users#show page
- Show users' group membership on users' activity page (Robert Djurasaj)
- User pages are visible without login if user is authorized to a public project
- Markdown rendered headers have id derived from their name and link to their id
- Improve application to work faster with large groups (100+ members)
- Multiple emails per user
- Show last commit for file when view file source
- Restyle Issue#show page and MR#show page
- Ability to filter by multiple labels for Issues page
- Rails version to 4.0.3
- Fixed attachment identifier displaying underneath note text (Jason Blanchard)
v 6.5.1
- Fix branch selectbox when create merge request from fork
v 6.5.0
- Dropdown menus on issue#show page for assignee and milestone (Jason Blanchard)
- Add color custimization and previewing to broadcast messages
- Fixed notes anchors
- Load new comments in issues dynamically
- Added sort options to Public page
- New filters (assigned/authored/all) for Dashboard#issues/merge_requests (sponsored by Say Media)
- Add project visibility icons to dashboard
- Enable secure cookies if https used
- Protect users/confirmation with rack_attack
- Default HTTP headers to protect against MIME-sniffing, force https if enabled
- Bootstrap 3 with responsive UI
- New repository download formats: tar.bz2, zip, tar (Jason Hollingsworth)
- Restyled accept widgets for MR
- SCSS refactored
- Use jquery timeago plugin
- Fix 500 error for rdoc files
- Ability to customize merge commit message (sponsored by Say Media)
- Search autocomplete via ajax
- Add website url to user profile
- Files API supports base64 encoded content (sponsored by O'Reilly Media)
- Added support for Go's repository retrieval (Bruno Albuquerque)
v6.4.3
- Don't use unicorn worker killer if PhusionPassenger is defined
v6.4.2
- Fixed wrong behaviour of script/upgrade.rb
v6.4.1
- Fixed bug with repository rename
- Fixed bug with project transfer
v 6.4.0
- Added sorting to project issues page (Jason Blanchard)
- Assembla integration (Carlos Paramio)
- Fixed another 500 error with submodules
- UI: More compact issues page
- Minimal password length increased to 8 symbols
- Side-by-side diff view (Steven Thonus)
- Internal projects (Jason Hollingsworth)
- Allow removal of avatar (Drew Blessing)
- Project web hooks now support issues and merge request events
- Visiting project page while not logged in will redirect to sign-in instead of 404 (Jason Hollingsworth)
- Expire event cache on avatar creation/removal (Drew Blessing)
- Archiving old projects (Steven Thonus)
- Rails 4
- Add time ago tooltips to show actual date/time
- UI: Fixed UI for admin system hooks
- Ruby script for easier GitLab upgrade
- Do not remove Merge requests if fork project was removed
- Improve sign-in/signup UX
- Add resend confirmation link to sign-in page
- Set noreply@HOSTNAME for reply_to field in all emails
- Show GitLab API version on Admin#dashboard
- API Cross-origin resource sharing
- Show READMe link at project home page
- Show repo size for projects in Admin area
v 6.3.0
- API for adding gitlab-ci service
- Init script now waits for pids to appear after (re)starting before reporting status (Rovanion Luckey)
- Restyle project home page
- Grammar fixes
- Show branches list (which branches contains commit) on commit page (Andrew Kumanyaev)
- Security improvements
- Added support for GitLab CI 4.0
- Fixed issue with 500 error when group did not exist
- Ability to leave project
- You can create file in repo using UI
- You can remove file from repo using UI
- API: dropped default_branch attribute from project during creation
- Project default_branch is not stored in db any more. It takes from repo now.
- Admin broadcast messages
- UI improvements
- Dont show last push widget if user removed this branch
- Fix 500 error for repos with newline in file name
- Extended html titles
- API: create/update/delete repo files
- Admin can transfer project to any namespace
- API: projects/all for admin users
- Fix recent branches order
v 6.2.4
- Security: Cast API private_token to string (CVE-2013-4580)
- Security: Require gitlab-shell 1.7.8 (CVE-2013-4581, CVE-2013-4582, CVE-2013-4583)
- Fix for Git SSH access for LDAP users
v 6.2.3
- Security: More protection against CVE-2013-4489
- Security: Require gitlab-shell 1.7.4 (CVE-2013-4490, CVE-2013-4546)
- Fix sidekiq rake tasks
v 6.2.2
- Security: Update gitlab_git (CVE-2013-4489)
v 6.2.1
- Security: Fix issue with generated passwords for new users
v 6.2.0
- Public project pages are now visible to everyone (files, issues, wik, etc.)
THIS MEANS YOUR ISSUES AND WIKI FOR PUBLIC PROJECTS ARE PUBLICLY VISIBLE AFTER THE UPGRADE
- Add group access to permissions page
- Require current password to change one
- Group owner or admin can remove other group owners
- Remove group transfer since we have multiple owners
- Respect authorization in Repository API
- Improve UI for Project#files page
- Add more security specs
- Added search for projects by name to api (Izaak Alpert)
- Make default user theme configurable (Izaak Alpert)
- Update logic for validates_merge_request for tree of MR (Andrew Kumanyaev)
- Rake tasks for web hooks management (Jonhnny Weslley)
- Extended User API to expose admin and can_create_group for user creation/updating (Boyan Tabakov)
- API: Remove group
- API: Remove project
- Avatar upload on profile page with a maximum of 100KB (Steven Thonus)
- Store the sessions in Redis instead of the cookie store
- Fixed relative links in markdown
- User must confirm their email if signup enabled
- User must confirm changed email
v 6.1.0
- Project specific IDs for issues, mr, milestones
Above items will get a new id and for example all bookmarked issue urls will change.
Old issue urls are redirected to the new one if the issue id is too high for an internal id.
- Description field added to Merge Request
- API: Sudo api calls (Izaak Alpert)
- API: Group membership api (Izaak Alpert)
- Improved commit diff
- Improved large commit handling (Boyan Tabakov)
- Rewrite: Init script now less prone to errors and keeps better track of the service (Rovanion Luckey)
- Link issues, merge requests, and commits when they reference each other with GFM (Ash Wilson)
- Close issues automatically when pushing commits with a special message
- Improve user removal from admin area
- Invalidate events cache when project was moved
- Remove deprecated classes and rake tasks
- Add event filter for group and project show pages
- Add links to create branch/tag from project home page
- Add public-project? checkbox to new-project view
- Improved compare page. Added link to proceed into Merge Request
- Send an email to a user when they are added to group
- New landing page when you have 0 projects
v 6.0.0
- Feature: Replace teams with group membership
We introduce group membership in 6.0 as a replacement for teams.
The old combination of groups and teams was confusing for a lot of people.
And when the members of a team where changed this wasn't reflected in the project permissions.
In GitLab 6.0 you will be able to add members to a group with a permission level for each member.
These group members will have access to the projects in that group.
Any changes to group members will immediately be reflected in the project permissions.
You can even have multiple owners for a group, greatly simplifying administration.
- Feature: Ability to have multiple owners for group
- Feature: Merge Requests between fork and project (Izaak Alpert)
- Feature: Generate fingerprint for ssh keys
- Feature: Ability to create and remove branches with UI
- Feature: Ability to create and remove git tags with UI
- Feature: Groups page in profile. You can leave group there
- API: Allow login with LDAP credentials
- Redesign: project settings navigation
- Redesign: snippets area
- Redesign: ssh keys page
- Redesign: buttons, blocks and other ui elements
- Add comment title to rss feed
- You can use arrows to navigate at tree view
- Add project filter on dashboard
- Cache project graph
- Drop support of root namespaces
- Default theme is classic now
- Cache result of methods like authorize_projects, project.team.members etc
- Remove $.ready events
- Fix onclick events being double binded
- Add notification level to group membership
- Move all project controllers/views under Projects:: module
- Move all profile controllers/views under Profiles:: module
- Apply user project limit only for personal projects
- Unicorn is default web server again
- Store satellites lock files inside satellites dir
- Disabled threadsafety mode in rails
- Fixed bug with loosing MR comments
- Improved MR comments logic
- Render readme file for projects in public area
v 5.4.2
- Security: Cast API private_token to string (CVE-2013-4580)
- Security: Require gitlab-shell 1.7.8 (CVE-2013-4581, CVE-2013-4582, CVE-2013-4583)
v 5.4.1
- Security: Fixes for CVE-2013-4489
- Security: Require gitlab-shell 1.7.4 (CVE-2013-4490, CVE-2013-4546)
v 5.4.0
- Ability to edit own comments
- Documentation improvements
- Improve dashboard projects page
- Fixed nav for empty repos
- GitLab Markdown help page
- Misspelling fixes
- Added support of unicorn and fog gems
- Added client list to API doc
- Fix PostgreSQL database restoration problem
- Increase snippet content column size
- allow project import via git:// url
- Show participants on issues, including mentions
- Notify mentioned users with email
v 5.3.0
- Refactored services
- Campfire service added
- HipChat service added
- Fixed bug with LDAP + git over http
- Fixed bug with google analytics code being ignored
- Improve sign-in page if ldap enabled
- Respect newlines in wall messages
- Generate the Rails secret token on first run
- Rename repo feature
- Init.d: remove gitlab.socket on service start
- Api: added teams api
- Api: Prevent blob content being escaped
- Api: Smart deploy key add behaviour
- Api: projects/owned.json return user owned project
- Fix bug with team assignation on project from #4109
- Advanced snippets: public/private, project/personal (Andrew Kulakov)
- Repository Graphs (Karlo Nicholas T. Soriano)
- Fix dashboard lost if comment on commit
- Update gitlab-grack. Fixes issue with --depth option
- Fix project events duplicate on project page
- Fix postgres error when displaying network graph.
- Fix dashboard event filter when navigate via turbolinks
- init.d: Ensure socket is removed before starting service
- Admin area: Style teams:index, group:show pages
- Own page for failed forking
- Scrum view for milestone
v 5.2.0
- Turbolinks
- Git over http with ldap credentials
- Diff with better colors and some spacing on the corners
- Default values for project features
- Fixed huge_commit view
- Restyle project clone panel
- Move Gitlab::Git code to gitlab_git gem
- Move update docs in repo
- Requires gitlab-shell v1.4.0
- Fixed submodules listing under file tab
- Fork feature (Angus MacArthur)
- git version check in gitlab:check
- Shared deploy keys feature
- Ability to generate default labels set for issues
- Improve gfm autocomplete (Harold Luo)
- Added support for Google Analytics
- Code search feature (Javier Castro)
v 5.1.0
- You can login with email or username now
- Corrected project transfer rollback when repository cannot be moved
- Move both repo and wiki when project transfer requested
- Admin area: project editing was removed from admin namespace
- Access: admin user has now access to any project.
- Notification settings
- Gitlab::Git set of objects to abstract from grit library
- Replace Unicorn web server with Puma
- Backup/Restore refactored. Backup dump project wiki too now
- Restyled Issues list. Show milestone version in issue row
- Restyled Merge Request list
- Backup now dump/restore uploads
- Improved performance of dashboard (Andrew Kumanyaev)
- File history now tracks renames (Akzhan Abdulin)
- Drop wiki migration tools
- Drop sqlite migration tools
- project tagging
- Paginate users in API
- Restyled network graph (Hiroyuki Sato)
v 5.0.1
- Fixed issue with gitlab-grit being overridden by grit
v 5.0.0
- Replaced gitolite with gitlab-shell
- Removed gitolite-related libraries
- State machine added
- Setup gitlab as git user
- Internal API
- Show team tab for empty projects
- Import repository feature
- Updated rails
- Use lambda for scopes
- Redesign admin area -> users
- Redesign admin area -> user
- Secure link to file attachments
- Add validations for Group and Team names
- Restyle team page for project
- Update capybara, rspec-rails, poltergeist to recent versions
- Wiki on git using Gollum
- Added Solarized Dark theme for code review
- Don't show user emails in autocomplete lists, profile pages
- Added settings tab for group, team, project
- Replace user popup with icons in header
- Handle project moving with gitlab-shell
- Added select2-rails for selectboxes with ajax data load
- Fixed search field on projects page
- Added teams to search autocomplete
- Move groups and teams on dashboard sidebar to sub-tabs
- API: improved return codes and docs. (Felix Gilcher, Sebastian Ziebell)
- Redesign wall to be more like chat
- Snippets, Wall features are disabled by default for new projects
v 4.2.0
- Teams
- User show page. Via /u/username
- Show help contents on pages for better navigation
- Async gitolite calls
- added satellites logs
- can_create_group, can_create_team booleans for User
- Process web hooks async
- GFM: Fix images escaped inside links
- Network graph improved
- Switchable branches for network graph
- API: Groups
- Fixed project download
v 4.1.0
- Optional Sign-Up
- Discussions
- Satellites outside of tmp
- Line numbers for blame
- Project public mode
- Public area with unauthorized access
- Load dashboard events with ajax
- remember dashboard filter in cookies
- replace resque with sidekiq
- fix routing issues
- cleanup rake tasks
- fix backup/restore
- scss cleanup
- show preview for note images
- improved network-graph
- get rid of app/roles/
- added new classes Team, Repository
- Reduce amount of gitolite calls
- Ability to add user in all group projects
- remove deprecated configs
- replaced Korolev font with open font
- restyled admin/dashboard page
- restyled admin/projects page
v 4.0.0
- Remove project code and path from API. Use id instead
- Return valid cloneable url to repo for web hook
- Fixed backup issue
- Reorganized settings
- Fixed commits compare
- Refactored scss
- Improve status checks
- Validates presence of User#name
- Fixed postgres support
- Removed sqlite support
- Modified post-receive hook
- Milestones can be closed now
- Show comment events on dashboard
- Quick add team members via group#people page
- [API] expose created date for hooks and SSH keys
- [API] list, create issue notes
- [API] list, create snippet notes
- [API] list, create wall notes
- Remove project code - use path instead
- added username field to user
- rake task to fill usernames based on emails create namespaces for users
- STI Group < Namespace
- Project has namespace_id
- Projects with namespaces also namespaced in gitolite and stored in subdir
- Moving project to group will move it under group namespace
- Ability to move project from namespaces to another
- Fixes commit patches getting escaped (see #2036)
- Support diff and patch generation for commits and merge request
- MergeReqest doesn't generate a temporary file for the patch any more
- Update the UI to allow downloading Patch or Diff
v 3.1.0
- Updated gems
- Services: Gitlab CI integration
- Events filter on dashboard
- Own namespace for redis/resque
- Optimized commit diff views
- add alphabetical order for projects admin page
- Improved web editor
- Commit stats page
- Documentation split and cleanup
- Link to commit authors everywhere
- Restyled milestones list
- added Milestone to Merge Request
- Restyled Top panel
- Refactored Satellite Code
- Added file line links
- moved from capybara-webkit to poltergeist + phantomjs
v 3.0.3
- Fixed bug with issues list in Chrome
- New Feature: Import team from another project
v 3.0.2
- Fixed gitlab:app:setup
- Fixed application error on empty project in admin area
- Restyled last push widget
v 3.0.1
- Fixed git over http
v 3.0.0
- Projects groups
- Web Editor
- Fixed bug with gitolite keys
- UI improved
- Increased performance of application
- Show user avatar in last commit when browsing Files
- Refactored Gitlab::Merge
- Use Font Awesome for icons
- Separate observing of Note and MergeRequests
- Milestone "All Issues" filter
- Fix issue close and reopen button text and styles
- Fix forward/back while browsing Tree hierarchy
- Show number of notes for commits and merge requests
- Added support pg from box and update installation doc
- Reject ssh keys that break gitolite
- [API] list one project hook
- [API] edit project hook
- [API] list project snippets
- [API] allow to authorize using private token in HTTP header
- [API] add user creation
v 2.9.1
- Fixed resque custom config init
v 2.9.0
- fixed inline notes bugs
- refactored rspecs
- refactored gitolite backend
- added factory_girl
- restyled projects list on dashboard
- ssh keys validation to prevent gitolite crash
- send notifications if changed permission in project
- scss refactoring. gitlab_bootstrap/ dir
- fix git push http body bigger than 112k problem
- list of labels page under issues tab
- API for milestones, keys
- restyled buttons
- OAuth
- Comment order changed
v 2.8.1
- ability to disable gravatars
- improved MR diff logic
- ssh key help page
v 2.8.0
- Gitlab Flavored Markdown
- Bulk issues update
- Issues API
- Cucumber coverage increased
- Post-receive files fixed
- UI improved
- Application cleanup
- more cucumber
- capybara-webkit + headless
v 2.7.0
- Issue Labels
- Inline diff
- Git HTTP
- API
- UI improved
- System hooks
- UI improved
- Dashboard events endless scroll
- Source performance increased
v 2.6.0
- UI polished
- Improved network graph + keyboard nav
- Handle huge commits
- Last Push widget
- Bugfix
- Better performance
- Email in resque
- Increased test coverage
- Ability to remove branch with MR accept
- a lot of code refactored
v 2.5.0
- UI polished
- Git blame for file
- Bugfix
- Email in resque
- Better test coverage
v 2.4.0
- Admin area stats page
- Ability to block user
- Simplified dashboard area
- Improved admin area
- Bootstrap 2.0
- Responsive layout
- Big commits handling
- Performance improved
- Milestones
v 2.3.1
- Issues pagination
- ssl fixes
- Merge Request pagination
v 2.3.0
- Dashboard r1
- Search r1
- Project page
- Close merge request on push
- Persist MR diff after merge
- mysql support
- Documentation
v 2.2.0
- Weve added support of LDAP auth
- Improved permission logic (4 roles system)
- Protected branches (now only masters can push to protected branches)
- Usability improved
- twitter bootstrap integrated
- compare view between commits
- wiki feature
- now you can enable/disable issues, wiki, wall features per project
- security fixes
- improved code browsing (ajax branch switch etc)
- improved per-line commenting
- git submodules displayed
- moved to rails 3.2
- help section improved
v 2.1.0
- Project tab r1
- List branches/tags
- per line comments
- mass user import
v 2.0.0
- gitolite as main git host system
- merge requests
- project/repo access
- link to commit/issue feed
- design tab
- improved email notifications
- restyled dashboard
- bugfix
v 1.2.2
- common config file gitlab.yml
- issues restyle
- snippets restyle
- clickable news feed header on dashboard
- bugfix
v 1.2.1
- bugfix
v 1.2.0
- new design
- user dashboard
- network graph
- markdown support for comments
- encoding issues
- wall like twitter timeline
v 1.1.0
- project dashboard
- wall redesigned
- feature: code snippets
- fixed horizontal scroll on file preview
- fixed app crash if commit message has invalid chars
- bugfix & code cleaning
v 1.0.2
- fixed bug with empty project
- added adv validation for project path & code
- feature: issues can be sortable
- bugfix
- username displayed on top panel
v 1.0.1
- fixed: with invalid source code for commit
- fixed: lose branch/tag selection when use tree navigation
- when history clicked - display path
- bug fix & code cleaning
v 1.0.0
- bug fix
- projects preview mode
v 0.9.6
- css fix
- new repo empty tree until restart server - fixed
v 0.9.4
- security improved
- authorization improved
- html escaping
- bug fix
- increased test coverage
- design improvements
v 0.9.1
- increased test coverage
- design improvements
- new issue email notification
- updated app name
- issue redesigned
- issue can be edit
v 0.8.0
- syntax highlight for main file types
- redesign
- stability
- security fixes
- increased test coverage
- email notification

117
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,117 @@
# Contribute to GitLab
Thank you for your interest in contributing to GitLab.
This guide details how contribute to GitLab in a way that is efficient for everyone.
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
By submitting code as an individual you agree to the [individual contributor license agreement](doc/legal/individual_contributor_license_agreement.md). By submitting code as an entity you agree to the [corporate contributor license agreement](doc/legal/corporate_contributor_license_agreement.md).
## Security vulnerability disclosure
Please report suspected security vulnerabilities in private to support@gitlab.com, also see the [disclosure section on the GitLab.com website](http://www.gitlab.com/disclosure/). Please do NOT create publicly viewable issues for suspected security vulnerabilities.
## Closing policy for issues and merge requests
GitLab is a popular open source project and the capacity to deal with issues and merge requests is limited. Out of respect for our volunteers, issues and merge requests not in line with the guidelines listed in this document may be closed without notice.
Please treat our volunteers with courtesy and respect, it will go a long way towards getting your issue resolved.
Issues and merge requests should be in English and contain appropriate language for audiences of all ages.
## Issue tracker
To get support for your particular problem please use the channels as detailed in the [getting help section of the readme](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/README.md#getting-help). Professional [support subscriptions](http://www.gitlab.com/subscription/) and [consulting services](http://www.gitlab.com/consultancy/) are available from [GitLab.com](http://www.gitlab.com/).
The [issue tracker](https://gitlab.com/gitlab-org/gitlab-ce/issues) is only for obvious errors in the latest [stable or development release of GitLab](MAINTENANCE.md). If something is wrong but it is not a regression compared to older versions of GitLab please do not open an issue but a feature request. When submitting an issue please conform to the issue submission guidelines listed below. Not all issues will be addressed and your issue is more likely to be addressed if you submit a merge request which partially or fully addresses the issue.
Issues can be filed either at [gitlab.com](https://gitlab.com/gitlab-org/gitlab-ce/issues) or [github.com](https://github.com/gitlabhq/gitlabhq/issues).
Do not use the issue tracker for feature requests. We have a specific [feature request forum](http://feedback.gitlab.com) for this purpose. Please keep feature requests as small and simple as possible, complex ones might be edited to make them small and simple.
Please send a merge request with a tested solution or a merge request with a failing test instead of opening an issue if you can. If you're unsure where to post, post to the [mailing list](https://groups.google.com/forum/#!forum/gitlabhq) or [Stack Overflow](http://stackoverflow.com/questions/tagged/gitlab) first. There are a lot of helpful GitLab users there who may be able to help you quickly. If your particular issue turns out to be a bug, it will find its way from there.
### Issue tracker guidelines
**[Search the issues](https://gitlab.com/gitlab-org/gitlab-ce/issues)** for similar entries before submitting your own, there's a good chance somebody else had the same issue. Show your support with `:+1:` and/or join the discussion. Please submit issues in the following format (as the first post):
1. **Summary:** Summarize your issue in one sentence (what goes wrong, what did you expect to happen)
1. **Steps to reproduce:** How can we reproduce the issue, preferably on the [GitLab development virtual machine with vagrant](https://gitlab.com/gitlab-org/cookbook-gitlab/blob/master/doc/development.md) (start your issue with: `vagrant destroy && vagrant up && vagrant ssh`)
1. **Expected behavior:** Describe your issue in detail
1. **Observed behavior**
1. **Relevant logs and/or screenshots:** Please use code blocks (\`\`\`) to format console output, logs, and code as it's very hard to read otherwise.
1. **Output of checks**
* Results of GitLab [Application Check](doc/install/installation.md#check-application-status) (`sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production SANITIZE=true`); we will only investigate if the tests are passing
* Version of GitLab you are running; we will only investigate issues in the latest stable and development releases as per the [maintenance policy](MAINTENANCE.md)
* Add the last commit sha1 of the GitLab version you used to replicate the issue (obtainable from the help page)
* Describe your setup (use relevant parts from `sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production`)
1. **Possible fixes**: If you can, link to the line of code that might be responsible for the problem
## Merge requests
We welcome merge requests with fixes and improvements to GitLab code, tests, and/or documentation. The features we would really like a merge request for are listed with the [status 'accepting merge requests' on our feature request forum](http://feedback.gitlab.com/forums/176466-general/status/796455) but other improvements are also welcome. If you want to add a new feature that is not marked it is best to first create a feedback issue (if there isn't one already) and leave a comment asking for it to be marked accepting merge requests. Please include screenshots or wireframes if the feature will also change the UI.
Merge requests can be filed either at [gitlab.com](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests) or [github.com](https://github.com/gitlabhq/gitlabhq/pulls).
### Merge request guidelines
If you can, please submit a merge request with the fix or improvements including tests. If you don't know how to fix the issue but can write a test that exposes the issue we will accept that as well. In general bug fixes that include a regression test are merged quickly while new features without proper tests are least likely to receive timely feedback. The workflow to make a merge request is as follows:
1. Fork the project on GitLab Cloud
1. Create a feature branch
1. Write [tests](README.md#run-the-tests) and code
1. Add your changes to the [CHANGELOG](CHANGELOG)
1. If you have multiple commits please combine them into one commit by [squashing them](http://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits)
1. Push the commit to your fork
1. Submit a merge request (MR) to the master branch
1. The MR title should describe the change you want to make
1. The MR description should give a motive for your change and the method you used to achieve it
1. If the MR changes the UI it should include before and after screenshots
1. If the MR changes CSS classes please include the list of affected pages `grep css-class ./app -R`
1. Link relevant [issues](https://gitlab.com/gitlab-org/gitlab-ce/issues) and/or [feature requests](http://feedback.gitlab.com/) from the merge request description and leave a comment on them with a link back to the MR
1. Be prepared to answer questions and incorporate feedback even if requests for this arrive weeks or months after your MR submission
1. If your MR touches code that executes shell commands, make sure it adheres to the [shell command guidelines]( doc/development/shell_commands.md).
The **official merge window** is in the beginning of the month from the 1st to the 7th day of the month. The best time to submit a MR and get feedback fast. Before this time the GitLab B.V. team is still dealing with work that is created by the monthly release such as assisting subscribers with upgrade issues, the release of Enterprise Edition and the upgrade of GitLab Cloud. After the 7th it is already getting closer to the release date of the next version. This means there is less time to fix the issues created by merging large new features.
Please keep the change in a single MR **as small as possible**. If you want to contribute a large feature think very hard what the minimum viable change is. Can you split functionality? Can you only submit the backend/API code? Can you start with a very simple UI? Can you do part of the refactor? The increased reviewability of small MR's that leads to higher code quality is more important to us than having a mimimal commit log. The smaller a MR is the more likely it is it will be merged (quickly), after that you can send more MR's to enhance it.
For examples of feedback on merge requests please look at already [closed merge requests](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests?assignee_id=&label_name=&milestone_id=&scope=&sort=&state=closed). If you would like quick feedback on your merge request feel free to mention one of the Merge Marshalls of [the core-team](https://about.gitlab.com/core-team/). Please ensure that your merge request meets the following contribution acceptance criteria.
**Please format your merge request description as follows:**
1. What does this MR do?
1. Are there points in the code the reviewer needs to double check?
1. Why was this MR needed?
1. What are the relevant issue numbers / [Feature requests](http://feedback.gitlab.com/)?
1. Screenshots (If appropiate)
## Contribution acceptance criteria
1. The change is as small as possible (see the above paragraph for details)
1. Include proper tests and make all tests pass (unless it contains a test exposing a bug in existing code)
1. Can merge without problems (if not please use: `git rebase master`)
1. Does not break any existing functionality
1. Fixes one specific issue or implements one specific feature (do not combine things, send separate merge requests if needed)
1. Keeps the GitLab code base clean and well structured
1. Contains functionality we think other users will benefit from too
1. Doesn't add configuration options since they complicate future changes
1. Initially contains a single commit (please use `git rebase -i` to squash commits)
1. Changes after submitting the merge request should be in separate commits (no squashing). You will be asked to squash when the review is over, before merging.
1. It conforms to the following style guides
## Style guides
1. [Ruby](https://github.com/bbatsov/ruby-style-guide).
Important sections include [Source Code Layout](https://github.com/bbatsov/ruby-style-guide#source-code-layout)
and [Naming](https://github.com/bbatsov/ruby-style-guide#naming). Use:
- multi-line method chaining style **Option B**: dot `.` on previous line
- string literal quoting style **Option A**: single quoted by default
1. [Rails](https://github.com/bbatsov/rails-style-guide)
1. [Testing](https://github.com/thoughtbot/guides/tree/master/style#testing)
1. [CoffeeScript](https://github.com/thoughtbot/guides/tree/master/style#coffeescript)
1. [Shell commands](doc/development/shell_commands.md) created by GitLab contributors to enhance security
1. [Markdown](http://www.cirosantilli.com/markdown-styleguide)
This is also the style used by linting tools such as [Rubocop](https://github.com/bbatsov/rubocop), [PullReview](https://www.pullreview.com/) and [Hound CI](https://houndci.com).

1
GITLAB_SHELL_VERSION Normal file
View File

@ -0,0 +1 @@
1.9.7

249
Gemfile Normal file
View File

@ -0,0 +1,249 @@
source "https://rubygems.org"
def darwin_only(require_as)
RUBY_PLATFORM.include?('darwin') && require_as
end
def linux_only(require_as)
RUBY_PLATFORM.include?('linux') && require_as
end
gem "rails", "~> 4.1.0"
# Make links from text
gem 'rails_autolink', '~> 1.1'
# Default values for AR models
gem "default_value_for", "~> 3.0.0"
# Supported DBs
gem "mysql2", group: :mysql
gem "pg", group: :postgres
# Auth
gem "devise", '3.2.4'
gem "devise-async", '0.9.0'
gem 'omniauth', "~> 1.1.3"
gem 'omniauth-google-oauth2'
gem 'omniauth-twitter'
gem 'omniauth-github'
# Extracting information from a git repository
# Provide access to Gitlab::Git library
gem "gitlab_git", '~> 6.0'
# Ruby/Rack Git Smart-HTTP Server Handler
gem 'gitlab-grack', '~> 2.0.0.pre', require: 'grack'
# LDAP Auth
gem 'gitlab_omniauth-ldap', '1.0.4', require: "omniauth-ldap"
# Git Wiki
gem 'gollum-lib', '~> 3.0.0'
# Language detection
gem "gitlab-linguist", "~> 3.0.0", require: "linguist"
# API
gem "grape", "~> 0.6.1"
gem "grape-entity", "~> 0.4.2"
gem 'rack-cors', require: 'rack/cors'
# Format dates and times
# based on human-friendly examples
gem "stamp"
# Enumeration fields
gem 'enumerize'
# Pagination
gem "kaminari", "~> 0.15.1"
# HAML
gem "haml-rails"
# Files attachments
gem "carrierwave"
# Drag and Drop UI
gem 'dropzonejs-rails'
# for aws storage
gem "fog", "~> 1.14", group: :aws
gem "unf", group: :aws
# Authorization
gem "six"
# Seed data
gem "seed-fu"
# Markdown to HTML
gem "github-markup"
# Required markup gems by github-markdown
gem 'redcarpet', '~> 2.2.2'
gem 'RedCloth'
gem 'rdoc', '~>3.6'
gem 'org-ruby'
gem 'creole', '~>0.3.6'
gem 'wikicloth', '=0.8.1'
gem 'asciidoctor', '= 0.1.4'
# Diffs
gem 'diffy', '~> 3.0.3'
# Application server
group :unicorn do
gem "unicorn", '~> 4.6.3'
gem 'unicorn-worker-killer'
end
# State machine
gem "state_machine"
# Issue tags
gem "acts-as-taggable-on"
# Background jobs
gem 'slim'
gem 'sinatra', require: nil
gem 'sidekiq', '2.17.0'
# HTTP requests
gem "httparty"
# Colored output to console
gem "colored"
# GitLab settings
gem 'settingslogic'
# Misc
gem "foreman"
gem 'version_sorter'
# Cache
gem "redis-rails"
# Campfire integration
gem 'tinder', '~> 1.9.2'
# HipChat integration
gem "hipchat", "~> 0.14.0"
# Flowdock integration
gem "gitlab-flowdock-git-hook", "~> 0.4.2"
# Gemnasium integration
gem "gemnasium-gitlab-service", "~> 0.2"
# Slack integration
gem "slack-notifier", "~> 0.3.2"
# d3
gem "d3_rails", "~> 3.1.4"
# underscore-rails
gem "underscore-rails", "~> 1.4.4"
# Sanitize user input
gem "sanitize", '~> 2.0'
# Protect against bruteforcing
gem "rack-attack"
# Ace editor
gem 'ace-rails-ap'
# Semantic UI Sass for Sidebar
gem 'semantic-ui-sass', '~> 0.16.1.0'
gem "sass-rails", '~> 4.0.2'
gem "coffee-rails"
gem "uglifier"
gem "therubyracer"
gem 'turbolinks'
gem 'jquery-turbolinks'
gem 'select2-rails'
gem 'jquery-atwho-rails', "~> 0.3.3"
gem "jquery-rails"
gem "jquery-ui-rails"
gem "jquery-scrollto-rails"
gem "raphael-rails", "~> 2.1.2"
gem 'bootstrap-sass', '~> 3.0'
gem "font-awesome-rails", '~> 3.2'
gem "gitlab_emoji", "~> 0.0.1.1"
gem "gon", '~> 5.0.0'
gem 'nprogress-rails'
gem 'request_store'
gem "virtus"
group :development do
gem "annotate", "~> 2.6.0.beta2"
gem "letter_opener"
gem 'quiet_assets', '~> 1.0.1'
gem 'rack-mini-profiler', require: false
# Better errors handler
gem 'better_errors'
gem 'binding_of_caller'
gem 'rails_best_practices'
# Docs generator
gem "sdoc"
# thin instead webrick
gem 'thin'
end
group :development, :test do
gem 'coveralls', require: false
# gem 'rails-dev-tweaks'
gem 'spinach-rails'
gem "rspec-rails"
gem "capybara", '~> 2.2.1'
gem "pry"
gem "awesome_print"
gem "database_cleaner"
gem "launchy"
gem 'factory_girl_rails'
# Prevent occasions where minitest is not bundled in packaged versions of ruby (see #3826)
gem 'minitest', '~> 5.3.0'
# Generate Fake data
gem "ffaker"
# Guard
gem 'guard-rspec'
gem 'guard-spinach'
# Notification
gem 'rb-fsevent', require: darwin_only('rb-fsevent')
gem 'growl', require: darwin_only('growl')
gem 'rb-inotify', require: linux_only('rb-inotify')
# PhantomJS driver for Capybara
gem 'poltergeist', '~> 1.5.1'
gem 'jasmine', '2.0.2'
gem "spring", '1.1.1'
gem "spring-commands-rspec", '1.0.1'
gem "spring-commands-spinach", '1.0.0'
end
group :test do
gem "simplecov", require: false
gem "shoulda-matchers", "~> 2.1.0"
gem 'email_spec'
gem "webmock"
gem 'test_after_commit'
end
group :production do
gem "gitlab_meta", '7.0'
end

697
Gemfile.lock Normal file
View File

@ -0,0 +1,697 @@
GEM
remote: https://rubygems.org/
specs:
RedCloth (4.2.9)
ace-rails-ap (2.0.1)
actionmailer (4.1.1)
actionpack (= 4.1.1)
actionview (= 4.1.1)
mail (~> 2.5.4)
actionpack (4.1.1)
actionview (= 4.1.1)
activesupport (= 4.1.1)
rack (~> 1.5.2)
rack-test (~> 0.6.2)
actionview (4.1.1)
activesupport (= 4.1.1)
builder (~> 3.1)
erubis (~> 2.7.0)
activemodel (4.1.1)
activesupport (= 4.1.1)
builder (~> 3.1)
activerecord (4.1.1)
activemodel (= 4.1.1)
activesupport (= 4.1.1)
arel (~> 5.0.0)
activesupport (4.1.1)
i18n (~> 0.6, >= 0.6.9)
json (~> 1.7, >= 1.7.7)
minitest (~> 5.1)
thread_safe (~> 0.1)
tzinfo (~> 1.1)
acts-as-taggable-on (2.4.1)
rails (>= 3, < 5)
addressable (2.3.5)
annotate (2.6.0)
activerecord (>= 2.3.0)
rake (>= 0.8.7)
arel (5.0.1.20140414130214)
asciidoctor (0.1.4)
awesome_print (1.2.0)
axiom-types (0.0.5)
descendants_tracker (~> 0.0.1)
ice_nine (~> 0.9)
bcrypt (3.1.7)
better_errors (1.0.1)
coderay (>= 1.0.0)
erubis (>= 2.6.6)
binding_of_caller (0.7.2)
debug_inspector (>= 0.0.1)
bootstrap-sass (3.0.3.0)
sass (~> 3.2)
builder (3.2.2)
capybara (2.2.1)
mime-types (>= 1.16)
nokogiri (>= 1.3.3)
rack (>= 1.0.0)
rack-test (>= 0.5.4)
xpath (~> 2.0)
carrierwave (0.9.0)
activemodel (>= 3.2.0)
activesupport (>= 3.2.0)
json (>= 1.7)
celluloid (0.15.2)
timers (~> 1.1.0)
charlock_holmes (0.6.9.4)
cliver (0.3.2)
code_analyzer (0.4.3)
sexp_processor
coderay (1.1.0)
coercible (1.0.0)
descendants_tracker (~> 0.0.1)
coffee-rails (4.0.1)
coffee-script (>= 2.2.0)
railties (>= 4.0.0, < 5.0)
coffee-script (2.2.0)
coffee-script-source
execjs
coffee-script-source (1.6.3)
colored (1.2)
colorize (0.5.8)
connection_pool (1.2.0)
coveralls (0.7.0)
multi_json (~> 1.3)
rest-client
simplecov (>= 0.7)
term-ansicolor
thor
crack (0.4.1)
safe_yaml (~> 0.9.0)
creole (0.3.8)
d3_rails (3.1.10)
railties (>= 3.1.0)
daemons (1.1.9)
database_cleaner (1.3.0)
debug_inspector (0.0.2)
default_value_for (3.0.0)
activerecord (>= 3.2.0, < 5.0)
descendants_tracker (0.0.3)
devise (3.2.4)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 3.2.6, < 5)
thread_safe (~> 0.1)
warden (~> 1.2.3)
devise-async (0.9.0)
devise (~> 3.2)
diff-lcs (1.2.5)
diffy (3.0.3)
docile (1.1.1)
dotenv (0.9.0)
dropzonejs-rails (0.4.14)
rails (> 3.1)
email_spec (1.5.0)
launchy (~> 2.1)
mail (~> 2.2)
emoji (1.0.1)
json
enumerize (0.7.0)
activesupport (>= 3.2)
equalizer (0.0.8)
erubis (2.7.0)
escape_utils (0.2.4)
eventmachine (1.0.3)
excon (0.32.1)
execjs (2.0.2)
expression_parser (0.9.0)
factory_girl (4.3.0)
activesupport (>= 3.0.0)
factory_girl_rails (4.3.0)
factory_girl (~> 4.3.0)
railties (>= 3.0.0)
faraday (0.8.9)
multipart-post (~> 1.2.0)
faraday_middleware (0.9.0)
faraday (>= 0.7.4, < 0.9)
ffaker (1.22.1)
ffi (1.9.3)
fog (1.21.0)
fog-brightbox
fog-core (~> 1.21, >= 1.21.1)
fog-json
nokogiri (~> 1.5, >= 1.5.11)
fog-brightbox (0.0.1)
fog-core
fog-json
fog-core (1.21.1)
builder
excon (~> 0.32)
formatador (~> 0.2.0)
mime-types
net-scp (~> 1.1)
net-ssh (>= 2.1.3)
fog-json (1.0.0)
multi_json (~> 1.0)
font-awesome-rails (3.2.1.3)
railties (>= 3.2, < 5.0)
foreman (0.63.0)
dotenv (>= 0.7)
thor (>= 0.13.6)
formatador (0.2.4)
gemnasium-gitlab-service (0.2.2)
rugged (~> 0.19)
gherkin-ruby (0.3.1)
racc
github-markup (1.1.0)
gitlab-flowdock-git-hook (0.4.2.2)
gitlab-grit (>= 2.4.1)
multi_json
gitlab-grack (2.0.0.pre)
rack (~> 1.5.1)
gitlab-grit (2.6.10)
charlock_holmes (~> 0.6)
diff-lcs (~> 1.1)
mime-types (~> 1.15)
posix-spawn (~> 0.3)
gitlab-linguist (3.0.0)
charlock_holmes (~> 0.6.6)
escape_utils (~> 0.2.4)
mime-types (~> 1.19)
gitlab_emoji (0.0.1.1)
emoji (~> 1.0.1)
gitlab_git (6.2.1)
activesupport (~> 4.0)
charlock_holmes (~> 0.6)
gitlab-grit (~> 2.6)
gitlab-linguist (~> 3.0)
rugged (~> 0.21.0)
gitlab_meta (7.0)
gitlab_omniauth-ldap (1.0.4)
net-ldap (~> 0.3.1)
omniauth (~> 1.0)
pyu-ruby-sasl (~> 0.0.3.1)
rubyntlm (~> 0.1.1)
gollum-lib (3.0.0)
github-markup (~> 1.1.0)
gitlab-grit (~> 2.6.5)
nokogiri (~> 1.6.1)
rouge (~> 1.3.3)
sanitize (~> 2.1.0)
stringex (~> 2.5.1)
gon (5.0.1)
actionpack (>= 2.3.0)
json
grape (0.6.1)
activesupport
builder
hashie (>= 1.2.0)
multi_json (>= 1.3.2)
multi_xml (>= 0.5.2)
rack (>= 1.3.0)
rack-accept
rack-mount
virtus (>= 1.0.0)
grape-entity (0.4.2)
activesupport
multi_json (>= 1.3.2)
growl (1.0.3)
guard (2.2.4)
formatador (>= 0.2.4)
listen (~> 2.1)
lumberjack (~> 1.0)
pry (>= 0.9.12)
thor (>= 0.18.1)
guard-rspec (4.2.0)
guard (>= 2.1.1)
rspec (>= 2.14, < 4.0)
guard-spinach (0.0.2)
guard (>= 1.1)
spinach
haml (4.0.5)
tilt
haml-rails (0.5.3)
actionpack (>= 4.0.1)
activesupport (>= 4.0.1)
haml (>= 3.1, < 5.0)
railties (>= 4.0.1)
hashie (2.1.2)
hike (1.2.3)
hipchat (0.14.0)
httparty
httparty
http_parser.rb (0.5.3)
httparty (0.13.0)
json (~> 1.8)
multi_xml (>= 0.5.2)
httpauth (0.2.1)
i18n (0.6.11)
ice_nine (0.10.0)
jasmine (2.0.2)
jasmine-core (~> 2.0.0)
phantomjs
rack (>= 1.2.1)
rake
jasmine-core (2.0.0)
jquery-atwho-rails (0.3.3)
jquery-rails (3.1.0)
railties (>= 3.0, < 5.0)
thor (>= 0.14, < 2.0)
jquery-scrollto-rails (1.4.3)
railties (> 3.1, < 5.0)
jquery-turbolinks (2.0.1)
railties (>= 3.1.0)
turbolinks
jquery-ui-rails (4.2.1)
railties (>= 3.2.16)
json (1.8.1)
jwt (0.1.13)
multi_json (>= 1.5)
kaminari (0.15.1)
actionpack (>= 3.0.0)
activesupport (>= 3.0.0)
kgio (2.8.1)
launchy (2.4.2)
addressable (~> 2.3)
letter_opener (1.1.2)
launchy (~> 2.2)
libv8 (3.16.14.3)
listen (2.3.1)
celluloid (>= 0.15.2)
rb-fsevent (>= 0.9.3)
rb-inotify (>= 0.9)
lumberjack (1.0.4)
mail (2.5.4)
mime-types (~> 1.16)
treetop (~> 1.4.8)
method_source (0.8.2)
mime-types (1.25.1)
mini_portile (0.6.0)
minitest (5.3.5)
multi_json (1.10.1)
multi_xml (0.5.5)
multipart-post (1.2.0)
mysql2 (0.3.16)
net-ldap (0.3.1)
net-scp (1.1.2)
net-ssh (>= 2.6.5)
net-ssh (2.8.0)
nokogiri (1.6.2.1)
mini_portile (= 0.6.0)
nprogress-rails (0.1.2.3)
oauth (0.4.7)
oauth2 (0.8.1)
faraday (~> 0.8)
httpauth (~> 0.1)
jwt (~> 0.1.4)
multi_json (~> 1.0)
rack (~> 1.2)
omniauth (1.1.4)
hashie (>= 1.2, < 3)
rack
omniauth-github (1.1.1)
omniauth (~> 1.0)
omniauth-oauth2 (~> 1.1)
omniauth-google-oauth2 (0.2.5)
omniauth (> 1.0)
omniauth-oauth2 (~> 1.1)
omniauth-oauth (1.0.1)
oauth
omniauth (~> 1.0)
omniauth-oauth2 (1.1.1)
oauth2 (~> 0.8.0)
omniauth (~> 1.0)
omniauth-twitter (1.0.1)
multi_json (~> 1.3)
omniauth-oauth (~> 1.0)
org-ruby (0.9.8)
rubypants (~> 0.2)
orm_adapter (0.5.0)
pg (0.15.1)
phantomjs (1.9.2.0)
poltergeist (1.5.1)
capybara (~> 2.1)
cliver (~> 0.3.1)
multi_json (~> 1.0)
websocket-driver (>= 0.2.0)
polyglot (0.3.4)
posix-spawn (0.3.9)
pry (0.9.12.4)
coderay (~> 1.0)
method_source (~> 0.8)
slop (~> 3.4)
pyu-ruby-sasl (0.0.3.3)
quiet_assets (1.0.2)
railties (>= 3.1, < 5.0)
racc (1.4.10)
rack (1.5.2)
rack-accept (0.4.5)
rack (>= 0.4)
rack-attack (2.3.0)
rack
rack-cors (0.2.9)
rack-mini-profiler (0.9.0)
rack (>= 1.1.3)
rack-mount (0.8.3)
rack (>= 1.0.0)
rack-protection (1.5.1)
rack
rack-test (0.6.2)
rack (>= 1.0)
rails (4.1.1)
actionmailer (= 4.1.1)
actionpack (= 4.1.1)
actionview (= 4.1.1)
activemodel (= 4.1.1)
activerecord (= 4.1.1)
activesupport (= 4.1.1)
bundler (>= 1.3.0, < 2.0)
railties (= 4.1.1)
sprockets-rails (~> 2.0)
rails_autolink (1.1.6)
rails (> 3.1)
rails_best_practices (1.14.4)
activesupport
awesome_print
code_analyzer (>= 0.4.3)
colored
erubis
i18n
require_all
ruby-progressbar
railties (4.1.1)
actionpack (= 4.1.1)
activesupport (= 4.1.1)
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
raindrops (0.12.0)
rake (10.3.2)
raphael-rails (2.1.2)
rb-fsevent (0.9.3)
rb-inotify (0.9.2)
ffi (>= 0.5.0)
rdoc (3.12.2)
json (~> 1.4)
redcarpet (2.2.2)
redis (3.0.6)
redis-actionpack (4.0.0)
actionpack (~> 4)
redis-rack (~> 1.5.0)
redis-store (~> 1.1.0)
redis-activesupport (4.0.0)
activesupport (~> 4)
redis-store (~> 1.1.0)
redis-namespace (1.4.1)
redis (~> 3.0.4)
redis-rack (1.5.0)
rack (~> 1.5)
redis-store (~> 1.1.0)
redis-rails (4.0.0)
redis-actionpack (~> 4)
redis-activesupport (~> 4)
redis-store (~> 1.1.0)
redis-store (1.1.4)
redis (>= 2.2)
ref (1.0.5)
request_store (1.0.5)
require_all (1.3.2)
rest-client (1.6.7)
mime-types (>= 1.16)
rinku (1.7.3)
rouge (1.3.3)
rspec (2.14.1)
rspec-core (~> 2.14.0)
rspec-expectations (~> 2.14.0)
rspec-mocks (~> 2.14.0)
rspec-core (2.14.7)
rspec-expectations (2.14.4)
diff-lcs (>= 1.1.3, < 2.0)
rspec-mocks (2.14.4)
rspec-rails (2.14.0)
actionpack (>= 3.0)
activesupport (>= 3.0)
railties (>= 3.0)
rspec-core (~> 2.14.0)
rspec-expectations (~> 2.14.0)
rspec-mocks (~> 2.14.0)
ruby-progressbar (1.2.0)
rubyntlm (0.1.1)
rubypants (0.2.0)
rugged (0.21.0)
safe_yaml (0.9.7)
sanitize (2.1.0)
nokogiri (>= 1.4.4)
sass (3.2.19)
sass-rails (4.0.3)
railties (>= 4.0.0, < 5.0)
sass (~> 3.2.0)
sprockets (~> 2.8, <= 2.11.0)
sprockets-rails (~> 2.0)
sdoc (0.3.20)
json (>= 1.1.3)
rdoc (~> 3.10)
seed-fu (2.3.1)
activerecord (>= 3.1, < 4.2)
activesupport (>= 3.1, < 4.2)
select2-rails (3.5.2)
thor (~> 0.14)
semantic-ui-sass (0.16.1.0)
sass (~> 3.2)
settingslogic (2.0.9)
sexp_processor (4.4.0)
shoulda-matchers (2.1.0)
activesupport (>= 3.0.0)
sidekiq (2.17.0)
celluloid (>= 0.15.2)
connection_pool (>= 1.0.0)
json
redis (>= 3.0.4)
redis-namespace (>= 1.3.1)
simple_oauth (0.1.9)
simplecov (0.8.2)
docile (~> 1.1.0)
multi_json
simplecov-html (~> 0.8.0)
simplecov-html (0.8.0)
sinatra (1.4.4)
rack (~> 1.4)
rack-protection (~> 1.4)
tilt (~> 1.3, >= 1.3.4)
six (0.2.0)
slack-notifier (0.3.2)
slim (2.0.2)
temple (~> 0.6.6)
tilt (>= 1.3.3, < 2.1)
slop (3.4.7)
spinach (0.8.7)
colorize (= 0.5.8)
gherkin-ruby (>= 0.3.1)
spinach-rails (0.2.1)
capybara (>= 2.0.0)
railties (>= 3)
spinach (>= 0.4)
spring (1.1.1)
spring-commands-rspec (1.0.1)
spring (>= 0.9.1)
spring-commands-spinach (1.0.0)
spring (>= 0.9.1)
sprockets (2.11.0)
hike (~> 1.2)
multi_json (~> 1.0)
rack (~> 1.0)
tilt (~> 1.1, != 1.3.0)
sprockets-rails (2.1.3)
actionpack (>= 3.0)
activesupport (>= 3.0)
sprockets (~> 2.8)
stamp (0.5.0)
state_machine (1.2.0)
stringex (2.5.1)
temple (0.6.7)
term-ansicolor (1.2.2)
tins (~> 0.8)
test_after_commit (0.2.2)
therubyracer (0.12.0)
libv8 (~> 3.16.14.0)
ref
thin (1.6.1)
daemons (>= 1.0.9)
eventmachine (>= 1.0.0)
rack (>= 1.0.0)
thor (0.19.1)
thread_safe (0.3.4)
tilt (1.4.1)
timers (1.1.0)
tinder (1.9.3)
eventmachine (~> 1.0)
faraday (~> 0.8)
faraday_middleware (~> 0.9)
hashie (>= 1.0, < 3)
json (~> 1.8.0)
mime-types (~> 1.19)
multi_json (~> 1.7)
twitter-stream (~> 0.1)
tins (0.13.1)
treetop (1.4.15)
polyglot
polyglot (>= 0.3.1)
turbolinks (2.0.0)
coffee-rails
twitter-stream (0.1.16)
eventmachine (>= 0.12.8)
http_parser.rb (~> 0.5.1)
simple_oauth (~> 0.1.4)
tzinfo (1.2.2)
thread_safe (~> 0.1)
uglifier (2.3.2)
execjs (>= 0.3.0)
json (>= 1.8.0)
underscore-rails (1.4.4)
unf (0.1.4)
unf_ext
unf_ext (0.0.6)
unicorn (4.6.3)
kgio (~> 2.6)
rack
raindrops (~> 0.7)
unicorn-worker-killer (0.4.2)
unicorn (~> 4)
version_sorter (1.1.0)
virtus (1.0.1)
axiom-types (~> 0.0.5)
coercible (~> 1.0)
descendants_tracker (~> 0.0.1)
equalizer (~> 0.0.7)
warden (1.2.3)
rack (>= 1.0)
webmock (1.16.0)
addressable (>= 2.2.7)
crack (>= 0.3.2)
websocket-driver (0.3.3)
wikicloth (0.8.1)
builder
expression_parser
rinku
xpath (2.0.0)
nokogiri (~> 1.3)
PLATFORMS
ruby
DEPENDENCIES
RedCloth
ace-rails-ap
acts-as-taggable-on
annotate (~> 2.6.0.beta2)
asciidoctor (= 0.1.4)
awesome_print
better_errors
binding_of_caller
bootstrap-sass (~> 3.0)
capybara (~> 2.2.1)
carrierwave
coffee-rails
colored
coveralls
creole (~> 0.3.6)
d3_rails (~> 3.1.4)
database_cleaner
default_value_for (~> 3.0.0)
devise (= 3.2.4)
devise-async (= 0.9.0)
diffy (~> 3.0.3)
dropzonejs-rails
email_spec
enumerize
factory_girl_rails
ffaker
fog (~> 1.14)
font-awesome-rails (~> 3.2)
foreman
gemnasium-gitlab-service (~> 0.2)
github-markup
gitlab-flowdock-git-hook (~> 0.4.2)
gitlab-grack (~> 2.0.0.pre)
gitlab-linguist (~> 3.0.0)
gitlab_emoji (~> 0.0.1.1)
gitlab_git (~> 6.0)
gitlab_meta (= 7.0)
gitlab_omniauth-ldap (= 1.0.4)
gollum-lib (~> 3.0.0)
gon (~> 5.0.0)
grape (~> 0.6.1)
grape-entity (~> 0.4.2)
growl
guard-rspec
guard-spinach
haml-rails
hipchat (~> 0.14.0)
httparty
jasmine (= 2.0.2)
jquery-atwho-rails (~> 0.3.3)
jquery-rails
jquery-scrollto-rails
jquery-turbolinks
jquery-ui-rails
kaminari (~> 0.15.1)
launchy
letter_opener
minitest (~> 5.3.0)
mysql2
nprogress-rails
omniauth (~> 1.1.3)
omniauth-github
omniauth-google-oauth2
omniauth-twitter
org-ruby
pg
poltergeist (~> 1.5.1)
pry
quiet_assets (~> 1.0.1)
rack-attack
rack-cors
rack-mini-profiler
rails (~> 4.1.0)
rails_autolink (~> 1.1)
rails_best_practices
raphael-rails (~> 2.1.2)
rb-fsevent
rb-inotify
rdoc (~> 3.6)
redcarpet (~> 2.2.2)
redis-rails
request_store
rspec-rails
sanitize (~> 2.0)
sass-rails (~> 4.0.2)
sdoc
seed-fu
select2-rails
semantic-ui-sass (~> 0.16.1.0)
settingslogic
shoulda-matchers (~> 2.1.0)
sidekiq (= 2.17.0)
simplecov
sinatra
six
slack-notifier (~> 0.3.2)
slim
spinach-rails
spring (= 1.1.1)
spring-commands-rspec (= 1.0.1)
spring-commands-spinach (= 1.0.0)
stamp
state_machine
test_after_commit
therubyracer
thin
tinder (~> 1.9.2)
turbolinks
uglifier
underscore-rails (~> 1.4.4)
unf
unicorn (~> 4.6.3)
unicorn-worker-killer
version_sorter
virtus
webmock
wikicloth (= 0.8.1)

27
Guardfile Normal file
View File

@ -0,0 +1,27 @@
# A sample Guardfile
# More info at https://github.com/guard/guard#readme
guard 'rspec', cmd: "spring rspec", version: 2, all_on_start: false, all_after_pass: false do
watch(%r{^spec/.+_spec\.rb$})
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
watch(%r{^lib/api/(.+)\.rb$}) { |m| "spec/requests/api/#{m[1]}_spec.rb" }
watch('spec/spec_helper.rb') { "spec" }
# Rails example
watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
watch('config/routes.rb') { "spec/routing" }
watch('app/controllers/application_controller.rb') { "spec/controllers" }
# Capybara request specs
watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/requests/#{m[1]}_spec.rb" }
end
guard 'spinach' do
watch(%r|^features/(.*)\.feature|)
watch(%r|^features/steps/(.*)([^/]+)\.rb|) do |m|
"features/#{m[1]}#{m[2]}.feature"
end
end

19
LICENSE Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2011-2014 GitLab B.V.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

15
MAINTENANCE.md Normal file
View File

@ -0,0 +1,15 @@
# GitLab Maintenance Policy
GitLab is a fast moving and evolving project. We currently don't have the resources to support many releases concurrently. We support exactly one stable release at any given time.
GitLab follows the [Semantic Versioning](http://semver.org/) for its releases: `(Major).(Minor).(Patch)`.
- **Major version**: Whenever there is something significant or any backwards incompatible changes are introduced to the public API.
- **Minor version**: When new, backwards compatible functionality is introduced to the public API or a minor feature is introduced, or when a set of smaller features is rolled out.
- **Patch number**: When backwards compatible bug fixes are introduced that fix incorrect behavior.
The current stable release will receive security patches and bug fixes (eg. `5.0` -> `5.0.1`). Feature releases will mark the next supported stable release where the minor version is increased numerically by increments of one (eg. `5.0 -> 5.1`).
We encourage everyone to run the latest stable release to ensure that you can easily upgrade to the most secure and feature rich GitLab experience. In order to make sure you can easily run the most recent stable release, we are working hard to keep the update process simple and reliable.
More information about the release procedures can be found in the doc/release directory.

106
PROCESS.md Normal file
View File

@ -0,0 +1,106 @@
# GitLab Contributing Process
## Purpose of describing the contributing process
Below we describe the contributing process to GitLab for two reasons. So that contributors know what to expect from maintainers (possible responses, friendly treatment, etc.). And so that maintainers know what to expect from contributors (use the latest version, ensure that the issue is addressed, friendly treatment, etc.).
## Common actions
### Issue team
- Looks for issues without [workflow labels](#how-we-handle-issues) and triages issue
- Closes invalid issues with a comment (duplicates, [feature requests](#feature-requests), [fixed in newer version](#issue-fixed-in-newer-version), [issue report for old version](#issue-report-for-old-version), not a problem in GitLab, etc.)
- Asks for feedback from issue reporter ([invalid issue reports](#improperly-formatted-issue), [format code](#code-format), etc.)
- Monitors all issues for feedback (but especially ones commented on since automatically watching them)
- Closes issues with no feedback from the reporter for two weeks
### Merge marshal
- Responds to merge requests the issue team mentions them in and monitors for new merge requests
- Provides feedback to the merge request submitter to improve the merge request (style, tests, etc.)
- Mark merge requests 'ready-for-merge' when they meet the contribution guidelines
- Mention developer(s) based on the [list of members and their specialities](https://www.gitlab.com/core-team/)
- Closes merge requests with no feedback from the reporter for two weeks
## Priorities of the issue team
1. Mentioning people (critical)
1. Workflow labels (normal)
1. Functional labels (minor)
1. Assigning issues (avoid if possible)
## Mentioning people
The most important thing is making sure valid issues receive feedback from the development team. Therefore the priority is mentioning developers that can help on those issue. Please select someone with relevant experience from [GitLab core team](https://www.gitlab.com/core-team/). If there is nobody mentioned with that expertise look in the commit history for the affected files to find someone. Avoid mentioning the lead developer, this is the person that is least likely to give a timely response. If the involvement of the lead developer is needed the other core team members will mention this person.
## Workflow labels
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 overview (for example all issues related to RVM, to tackle them in one go) and to add details to the issue.
- *Awaiting feedback*: Feedback pending from the reporter
- *Awaiting confirmation of fix*: The issue should already be solved in **master** (generally you can avoid this workflow item and just close the issue right away)
- *Attached MR*: There is a MR attached and the discussion should happen there
- We need to let issues stay in sync with the MR's. We can do this with a "Closing #XXXX" or "Fixes #XXXX" comment in the MR. We can't close the issue when there is a merge request because sometimes a MR is not good and we just close the MR, then the issue must stay.
- *Awaiting developer action/feedback*: Issue needs to be fixed or clarified by a developer
## Functional labels
These labels describe what development specialities are involved such as: PostgreSQL, UX, LDAP.
## Assigning issues
If an issue is complex and needs the attention of a specific person, assignment is a good option but assigning issues might discourage other people from contributing to that issue. We need all the contributions we can get so this should never be discouraged. Also, an assigned person might not have time for a few weeks, so others should feel free to takeover.
## Label colors
- Light orange `#fef2c0`: workflow labels for issue team members (awaiting feedback, awaiting confirmation of fix)
- Bright orange `#eb6420`: workflow labels for core team members (attached MR, awaiting developer action/feedback)
- Light blue `#82C5FF`: functional labels
- Green labels `#009800`: issues that can generally be ignored. For example, issues given the following labels normally can be closed immediately:
- Feature request (see copy & paste response: [Feature requests](#feature-requests))
- Support (see copy & paste response: [Support requests and configuration questions](#support-requests-and-configuration-questions)
## Be kind
Be kind to people trying to contribute. Be aware that people may be a non-native English speaker, they might not understand things or they might be very 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](https://github.com/thoughtbot/guides/tree/master/code-review).
## Copy & paste responses
### Improperly formatted issue
Thanks for the issue report. Please reformat your issue to conform to the issue tracker guidelines found in our \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker-guidelines).
### Feature requests
Thank you for your interest in improving GitLab. We don't use the issue tracker for feature requests. Things that are wrong but are not a regression compared to older versions of GitLab are considered feature requests and not issues. Please use the [feature request forum](http://feedback.gitlab.com/) for this purpose or create a merge request implementing this feature. Have a look at the \[contribution guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md) for more information.
### Issue report for old version
Thanks for the issue report but we only support issues for the latest stable version of GitLab. I'm closing this issue but if you still experience this problem in the latest stable version, please open a new issue (but also reference the old issue(s)). Make sure to also include the necessary debugging information conforming to the issue tracker guidelines found in our \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker-guidelines).
### Support requests and configuration questions
Thanks for your interest in GitLab. We don't use the issue tracker for support requests and configuration questions. Please use the \[support forum\]\(https://groups.google.com/forum/#!forum/gitlabhq), \[Stack Overflow\]\(http://stackoverflow.com/questions/tagged/gitlab), the #gitlab IRC channel on Freenode or the http://www.gitlab.com paid services for this purpose. Have a look at the \[contribution guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md) for more information.
### Code format
Please use ``` to format console output, logs, and code as it's very hard to read otherwise.
### Issue fixed in newer version
Thanks for the issue report. This issue has already been fixed in newer versions of GitLab. Due to the size of this project and our limited resources we are only able to support the latest stable release as outlined in our \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker). In order to get this bug fix and enjoy many new features please \[upgrade\]\(https://github.com/gitlabhq/gitlabhq/tree/master/doc/update). If you still experience issues at that time please open a new issue following our issue tracker guidelines found in the \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker-guidelines).
### Improperly formatted merge request
Thanks for your interest in improving the GitLab codebase! Please update your merge request according to the \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#pull-request-guidelines).
### Inactivity close of an issue
It's been at least 2 weeks (and a new release) since we heard from you. I'm closing this issue but if you still experience this problem, please open a new issue (but also reference the old issue(s)). Make sure to also include the necessary debugging information conforming to the issue tracker guidelines found in our \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker-guidelines).
### Inactivity close of a merge request
This merge request has been closed because a request for more information has not been reacted to for more than 2 weeks. If you respond and conform to the merge request guidelines in our \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#pull-requests) we will reopen this merge request.
### Accepting merge requests
Is there a request on [the feature request forum](http://feedback.gitlab.com/forums/176466-general) that is similar to this? If so, can you make a comment with a link to it? Please be aware that new functionality that is not marked [accepting merge/pull requests](http://feedback.gitlab.com/forums/176466-general/status/796455) on the forum might not make it into GitLab. You might be asked to make changes and even after implementing them your feature might still be declined. If you want to reduce the chance of this happening please have a discussion in the forum first.

2
Procfile Normal file
View File

@ -0,0 +1,2 @@
web: bundle exec unicorn_rails -p ${PORT:="3000"} -E ${RAILS_ENV:="development"} -c ${UNICORN_CONFIG:="config/unicorn.rb"}
worker: bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,common,default,gitlab_shell

138
README.md Normal file
View File

@ -0,0 +1,138 @@
# GitLab
## Open source software to collaborate on code
![logo](https://gitlab.com/gitlab-org/gitlab-ce/raw/master/public/gitlab_logo.png)
![animated-screenshots](https://gist.github.com/fnkr/2f9badd56bfe0ed04ee7/raw/4f48806fbae97f556c2f78d8c2d299c04500cb0d/compiled.gif)
- Manage Git repositories with fine grained access controls that keep your code secure
- Perform code reviews and enhance collaboration with merge requests
- Each project can also have an issue tracker and a wiki
- Used by more than 100,000 organizations, GitLab is the most popular solution to manage Git repositories on-premises
- Completely free and open source (MIT Expat license)
- Powered by Ruby on Rails
## Canonical source
- The source of GitLab Community Edition is [hosted on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/) and there are mirrors to make [contributing](CONTRIBUTING.md) as easy as possible.
## Code status
- [![build status](https://ci.gitlab.org/projects/1/status.png?ref=master)](https://ci.gitlab.org/projects/1?ref=master) on ci.gitlab.org (master branch)
- [![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.png)](https://codeclimate.com/github/gitlabhq/gitlabhq)
- [![Coverage Status](https://coveralls.io/repos/gitlabhq/gitlabhq/badge.png?branch=master)](https://coveralls.io/r/gitlabhq/gitlabhq)
- [![PullReview stats](https://www.pullreview.com/gitlab/gitlab-org/gitlab-ce/badges/master.svg?)](https://www.pullreview.com/gitlab.gitlab.com/gitlab-org/gitlab-ce/reviews/master)
## Website
On [www.gitlab.com](https://www.gitlab.com/) you can find more information about:
- [Subscriptions](https://www.gitlab.com/subscription/)
- [Consultancy](https://www.gitlab.com/consultancy/)
- [Community](https://www.gitlab.com/community/)
- [Hosted GitLab.com](https://www.gitlab.com/gitlab-com/) use GitLab as a free service
- [GitLab Enterprise Edition](https://www.gitlab.com/gitlab-ee/) with additional features aimed at larger organizations.
- [GitLab CI](https://www.gitlab.com/gitlab-ci/) a continuous integration (CI) server that is easy to integrate with GitLab.
## Third-party applications
Access GitLab from multiple platforms with applications below.
These applications are maintained by contributors, GitLab B.V. does not offer support for them.
- [iPhone app](http://gitlabcontrol.com/)
- [Android app](https://play.google.com/store/apps/details?id=com.bd.gitlab&hl=en)
- [Chrome app](https://chrome.google.com/webstore/detail/chrome-gitlab-notifier/eageapgbnjicdjjihgclpclilenjbobi)
- [Command line client](https://github.com/drewblessing/gitlab-cli)
- [Ruby API wrapper](https://github.com/NARKOZ/gitlab)
## Requirements
- Ubuntu/Debian/CentOS/RHEL**
- ruby 2.0+
- git 1.7.10+
- redis 2.0+
- MySQL or PostgreSQL
** More details are in the [requirements doc](doc/install/requirements.md).
## Installation
Please see [the installation page on the GitLab website](https://www.gitlab.com/installation/).
### New versions
Since 2011 a minor or major version of GitLab is released on the 22nd of every month. Patch and security releases come out when needed. New features are detailed on the [blog](https://www.gitlab.com/blog/) and in the [changelog](CHANGELOG). For more information about the release process see the release [documentation](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/release). Features that will likely be in the next releases can be found on the [feature request forum](http://feedback.gitlab.com/forums/176466-general) with the status [started](http://feedback.gitlab.com/forums/176466-general/status/796456) and [completed](http://feedback.gitlab.com/forums/176466-general/status/796457).
### Upgrading
For updating the the Omnibus installation please see the [update documentation](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/update.md). For manual installations there is an [upgrader script](doc/update/upgrader.md) and there are [upgrade guides](doc/update).
## Run in production mode
The Installation guide contains instructions on how to download an init script and run it automatically on boot. You can also start the init script manually:
sudo service gitlab start
or by directly calling the script:
sudo /etc/init.d/gitlab start
Please login with `root` / `5iveL!fe`
## Install a development environment
We recommend setting up your development environment with [the cookbook](https://gitlab.com/gitlab-org/cookbook-gitlab/blob/master/README.md#installation). If you do not use the cookbook you might need to copy the example development unicorn configuration file
cp config/unicorn.rb.example.development config/unicorn.rb
## Run in development mode
Start it with [Foreman](https://github.com/ddollar/foreman)
bundle exec foreman start -p 3000
or start each component separately:
bundle exec rails s
bin/background_jobs start
And surf to [localhost:3000](http://localhost:3000/) and login with `root` / `5iveL!fe`.
## Run the tests
- Run all tests:
bundle exec rake test
- [RSpec](http://rspec.info/) unit and functional tests.
All RSpec tests: `bundle exec rake spec`
Single RSpec file: `bundle exec rspec spec/controllers/commit_controller_spec.rb`
- [Spinach](https://github.com/codegram/spinach) integration tests.
All Spinach tests: `bundle exec rake spinach`
Single Spinach test: `bundle exec spinach features/project/issues/milestones.feature`
## Documentation
All documentation can be found on [doc.gitlab.com/ce/](http://doc.gitlab.com/ce/).
## Getting help
Please see [Getting help for GitLab](https://www.gitlab.com/getting-help/) on our website for the many options to get help.
## Is it any good?
[Yes](https://news.ycombinator.com/item?id=3067434)
## Is it awesome?
Thanks for [asking this question](https://twitter.com/supersloth/status/489462789384056832) Joshua.
[These people](https://twitter.com/gitlabhq/favorites) seem to like it.

7
Rakefile Normal file
View File

@ -0,0 +1,7 @@
#!/usr/bin/env rake
# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
require File.expand_path('../config/application', __FILE__)
Gitlab::Application.load_tasks

1
VERSION Normal file
View File

@ -0,0 +1 @@
7.2.1

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 396 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 691 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1019 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 331 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
app/assets/images/move.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 704 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 548 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 494 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 505 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

View File

@ -0,0 +1,31 @@
class Activities
constructor: ->
Pager.init 20, true
$(".event_filter_link").bind "click", (event) =>
event.preventDefault()
@toggleFilter($(event.currentTarget))
@reloadActivities()
reloadActivities: ->
$(".content_list").html ''
Pager.init 20, true
toggleFilter: (sender) ->
sender.parent().toggleClass "inactive"
event_filters = $.cookie("event_filter")
filter = sender.attr("id").split("_")[0]
if event_filters
event_filters = event_filters.split(",")
else
event_filters = new Array()
index = event_filters.indexOf(filter)
if index is -1
event_filters.push filter
else
event_filters.splice index, 1
$.cookie "event_filter", event_filters.join(","), { path: '/' }
@Activities = Activities

View File

@ -0,0 +1,55 @@
class Admin
constructor: ->
$('input#user_force_random_password').on 'change', (elem) ->
elems = $('#user_password, #user_password_confirmation')
if $(@).attr 'checked'
elems.val('').attr 'disabled', true
else
elems.removeAttr 'disabled'
$('body').on 'click', '.js-toggle-colors-link', (e) ->
e.preventDefault()
$('.js-toggle-colors-link').hide()
$('.js-toggle-colors-container').show()
$('input#broadcast_message_color').on 'input', ->
previewColor = $('input#broadcast_message_color').val()
$('div.broadcast-message-preview').css('background-color', previewColor)
$('input#broadcast_message_font').on 'input', ->
previewColor = $('input#broadcast_message_font').val()
$('div.broadcast-message-preview').css('color', previewColor)
$('textarea#broadcast_message_message').on 'input', ->
previewMessage = $('textarea#broadcast_message_message').val()
$('div.broadcast-message-preview span').text(previewMessage)
$('.log-tabs a').click (e) ->
e.preventDefault()
$(this).tab('show')
$('.log-bottom').click (e) ->
e.preventDefault()
visible_log = $(".file-content:visible")
visible_log.animate({ scrollTop: visible_log.find('ol').height() }, "fast")
modal = $('.change-owner-holder')
$('.change-owner-link').bind "click", (e) ->
e.preventDefault()
$(this).hide()
modal.show()
$('.change-owner-cancel-link').bind "click", (e) ->
e.preventDefault()
modal.hide()
$('.change-owner-link').show()
$('li.users_project').bind 'ajax:success', ->
Turbolinks.visit(location.href)
$('li.users_group').bind 'ajax:success', ->
Turbolinks.visit(location.href)
@Admin = Admin

View File

@ -0,0 +1,87 @@
@Api =
users_path: "/api/:version/users.json"
user_path: "/api/:version/users/:id.json"
notes_path: "/api/:version/projects/:id/notes.json"
namespaces_path: "/api/:version/namespaces.json"
project_users_path: "/api/:version/projects/:id/users.json"
# Get 20 (depends on api) recent notes
# and sort the ascending from oldest to newest
notes: (project_id, callback) ->
url = Api.buildUrl(Api.notes_path)
url = url.replace(':id', project_id)
$.ajax(
url: url,
data:
private_token: gon.api_token
gfm: true
recent: true
dataType: "json"
).done (notes) ->
notes.sort (a, b) ->
return a.id - b.id
callback(notes)
user: (user_id, callback) ->
url = Api.buildUrl(Api.user_path)
url = url.replace(':id', user_id)
$.ajax(
url: url
data:
private_token: gon.api_token
dataType: "json"
).done (user) ->
callback(user)
# Return users list. Filtered by query
# Only active users retrieved
users: (query, callback) ->
url = Api.buildUrl(Api.users_path)
$.ajax(
url: url
data:
private_token: gon.api_token
search: query
per_page: 20
active: true
dataType: "json"
).done (users) ->
callback(users)
# Return project users list. Filtered by query
# Only active users retrieved
projectUsers: (project_id, query, callback) ->
url = Api.buildUrl(Api.project_users_path)
url = url.replace(':id', project_id)
$.ajax(
url: url
data:
private_token: gon.api_token
search: query
per_page: 20
active: true
dataType: "json"
).done (users) ->
callback(users)
# Return namespaces list. Filtered by query
namespaces: (query, callback) ->
url = Api.buildUrl(Api.namespaces_path)
$.ajax(
url: url
data:
private_token: gon.api_token
search: query
per_page: 20
dataType: "json"
).done (namespaces) ->
callback(namespaces)
buildUrl: (url) ->
url = gon.relative_url_root + url if gon.relative_url_root?
return url.replace(':version', gon.api_version)

View File

@ -0,0 +1,188 @@
# This is a manifest file that'll be compiled into including all the files listed below.
# Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
# be included in the compiled file accessible from http://example.com/assets/application.js
# It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
# the compiled file.
#
#= require jquery
#= require jquery.ui.all
#= require jquery_ujs
#= require jquery.cookie
#= require jquery.endless-scroll
#= require jquery.highlight
#= require jquery.history
#= require jquery.waitforimages
#= require jquery.atwho
#= require jquery.scrollTo
#= require jquery.blockUI
#= require turbolinks
#= require jquery.turbolinks
#= require bootstrap
#= require select2
#= require raphael
#= require g.raphael-min
#= require g.bar-min
#= require branch-graph
#= require highlight.pack
#= require ace/ace
#= require d3
#= require underscore
#= require nprogress
#= require nprogress-turbolinks
#= require dropzone
#= require semantic-ui/sidebar
#= require_tree .
window.slugify = (text) ->
text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase()
window.ajaxGet = (url) ->
$.ajax({type: "GET", url: url, dataType: "script"})
window.showAndHide = (selector) ->
window.errorMessage = (message) ->
ehtml = $("<p>")
ehtml.addClass("error_message")
ehtml.html(message)
ehtml
window.split = (val) ->
return val.split( /,\s*/ )
window.extractLast = (term) ->
return split( term ).pop()
window.rstrip = (val) ->
return val.replace(/\s+$/, '')
# Disable button if text field is empty
window.disableButtonIfEmptyField = (field_selector, button_selector) ->
field = $(field_selector)
closest_submit = field.closest('form').find(button_selector)
closest_submit.disable() if rstrip(field.val()) is ""
field.on 'input', ->
if rstrip($(@).val()) is ""
closest_submit.disable()
else
closest_submit.enable()
# Disable button if any input field with given selector is empty
window.disableButtonIfAnyEmptyField = (form, form_selector, button_selector) ->
closest_submit = form.find(button_selector)
empty = false
form.find('input').filter(form_selector).each ->
empty = true if rstrip($(this).val()) is ""
if empty
closest_submit.disable()
else
closest_submit.enable()
form.keyup ->
empty = false
form.find('input').filter(form_selector).each ->
empty = true if rstrip($(this).val()) is ""
if empty
closest_submit.disable()
else
closest_submit.enable()
window.sanitize = (str) ->
return str.replace(/<(?:.|\n)*?>/gm, '')
window.linkify = (str) ->
exp = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig
return str.replace(exp,"<a href='$1'>$1</a>")
window.simpleFormat = (str) ->
linkify(sanitize(str).replace(/\n/g, '<br />'))
window.unbindEvents = ->
$(document).unbind('scroll')
$(document).off('scroll')
document.addEventListener("page:fetch", unbindEvents)
$ ->
# Click a .one_click_select field, select the contents
$(".one_click_select").on 'click', -> $(@).select()
$('.remove-row').bind 'ajax:success', ->
$(this).closest('li').fadeOut()
# Initialize select2 selects
$('select.select2').select2(width: 'resolve', dropdownAutoWidth: true)
# Initialize tooltips
$('.has_tooltip').tooltip()
# Bottom tooltip
$('.has_bottom_tooltip').tooltip(placement: 'bottom')
# Form submitter
$('.trigger-submit').on 'change', ->
$(@).parents('form').submit()
$("abbr.timeago").timeago()
$('.js-timeago').timeago()
# Flash
if (flash = $(".flash-container")).length > 0
flash.click -> $(@).fadeOut()
flash.show()
setTimeout (-> flash.fadeOut()), 5000
# Disable form buttons while a form is submitting
$('body').on 'ajax:complete, ajax:beforeSend, submit', 'form', (e) ->
buttons = $('[type="submit"]', @)
switch e.type
when 'ajax:beforeSend', 'submit'
buttons.disable()
else
buttons.enable()
# Show/Hide the profile menu when hovering the account box
$('.account-box').hover -> $(@).toggleClass('hover')
# Focus search field by pressing 's' key
$(document).keypress (e) ->
# Don't do anything if typing in an input
return if $(e.target).is(":input")
switch e.which
when 115
$("#search").focus()
e.preventDefault()
when 63
new Shortcuts()
e.preventDefault()
# Commit show suppressed diff
$(".diff-content").on "click", ".supp_diff_link", ->
$(@).next('table').show()
$(@).remove()
# Show/hide comments on diff
$("body").on "click", ".js-toggle-diff-comments", (e) ->
$(@).find('i').
toggleClass('icon-chevron-down').
toggleClass('icon-chevron-up')
$(@).closest(".diff-file").find(".notes_holder").toggle()
e.preventDefault()
(($) ->
# Disable an element and add the 'disabled' Bootstrap class
$.fn.extend disable: ->
$(@).attr('disabled', 'disabled').addClass('disabled')
# Enable an element and remove the 'disabled' Bootstrap class
$.fn.extend enable: ->
$(@).removeAttr('disabled').removeClass('disabled')
)(jQuery)

View File

@ -0,0 +1,15 @@
$ ->
$("body").on "click", ".js-details-target", ->
container = $(@).closest(".js-details-container")
container.toggleClass("open")
# Show details content. Hides link after click.
#
# %div
# %a.js-details-expand
# %div.js-details-content
#
$("body").on "click", ".js-details-expand", (e) ->
$(@).next('.js-details-content').removeClass("hide")
$(@).hide()
e.preventDefault()

View File

@ -0,0 +1,14 @@
$ ->
# Toggle button. Show/hide content inside parent container.
# Button does not change visibility. If button has icon - it changes chevron style.
#
# %div.js-toggle-container
# %a.js-toggle-button
# %div.js-toggle-content
#
$("body").on "click", ".js-toggle-button", (e) ->
$(@).find('i').
toggleClass('icon-chevron-down').
toggleClass('icon-chevron-up')
$(@).closest(".js-toggle-container").find(".js-toggle-content").toggle()
e.preventDefault()

View File

@ -0,0 +1,76 @@
class BlobView
constructor: ->
# handle multi-line select
handleMultiSelect = (e) ->
[ first_line, last_line ] = parseSelectedLines()
[ line_number ] = parseSelectedLines($(this).attr("id"))
hash = "L#{line_number}"
if e.shiftKey and not isNaN(first_line) and not isNaN(line_number)
if line_number < first_line
last_line = first_line
first_line = line_number
else
last_line = line_number
hash = if first_line == last_line then "L#{first_line}" else "L#{first_line}-#{last_line}"
setHash(hash)
e.preventDefault()
# See if there are lines selected
# "#L12" and "#L34-56" supported
highlightBlobLines = (e) ->
[ first_line, last_line ] = parseSelectedLines()
unless isNaN first_line
$("#tree-content-holder .highlight .line").removeClass("hll")
$("#LC#{line}").addClass("hll") for line in [first_line..last_line]
$.scrollTo("#L#{first_line}") unless e?
# parse selected lines from hash
# always return first and last line (initialized to NaN)
parseSelectedLines = (str) ->
first_line = NaN
last_line = NaN
hash = str || window.location.hash
if hash isnt ""
matches = hash.match(/\#?L(\d+)(\-(\d+))?/)
first_line = parseInt(matches?[1])
last_line = parseInt(matches?[3])
last_line = first_line if isNaN(last_line)
[ first_line, last_line ]
setHash = (hash) ->
hash = hash.replace(/^\#/, "")
nodes = $("#" + hash)
# if any nodes are using this id, they must be temporarily changed
# also, add a temporary div at the top of the screen to prevent scrolling
if nodes.length > 0
scroll_top = $(document).scrollTop()
nodes.attr("id", "")
tmp = $("<div></div>")
.css({ position: "absolute", visibility: "hidden", top: scroll_top + "px" })
.attr("id", hash)
.appendTo(document.body)
window.location.hash = hash
# restore the nodes
if nodes.length > 0
tmp.remove()
nodes.attr("id", hash)
# initialize multi-line select
$("#tree-content-holder .line-numbers a[id^=L]").on("click", handleMultiSelect)
# Highlight the correct lines on load
highlightBlobLines()
# Highlight the correct lines when the hash part of the URL changes
$(window).on("hashchange", highlightBlobLines)
@BlobView = BlobView

View File

@ -0,0 +1,329 @@
class BranchGraph
constructor: (@element, @options) ->
@preparedCommits = {}
@mtime = 0
@mspace = 0
@parents = {}
@colors = ["#000"]
@offsetX = 150
@offsetY = 20
@unitTime = 30
@unitSpace = 10
@prev_start = -1
@load()
load: ->
$.ajax
url: @options.url
method: "get"
dataType: "json"
success: $.proxy((data) ->
$(".loading", @element).hide()
@prepareData data.days, data.commits
@buildGraph()
, this)
prepareData: (@days, @commits) ->
@collectParents()
@graphHeight = $(@element).height()
@graphWidth = $(@element).width()
ch = Math.max(@graphHeight, @offsetY + @unitTime * @mtime + 150)
cw = Math.max(@graphWidth, @offsetX + @unitSpace * @mspace + 300)
@r = Raphael(@element.get(0), cw, ch)
@top = @r.set()
@barHeight = Math.max(@graphHeight, @unitTime * @days.length + 320)
for c in @commits
c.isParent = true if c.id of @parents
@preparedCommits[c.id] = c
@markCommit(c)
@collectColors()
collectParents: ->
for c in @commits
@mtime = Math.max(@mtime, c.time)
@mspace = Math.max(@mspace, c.space)
for p in c.parents
@parents[p[0]] = true
@mspace = Math.max(@mspace, p[1])
collectColors: ->
k = 0
while k < @mspace
@colors.push Raphael.getColor(.8)
# Skipping a few colors in the spectrum to get more contrast between colors
Raphael.getColor()
Raphael.getColor()
k++
buildGraph: ->
r = @r
cuday = 0
cumonth = ""
r.rect(0, 0, 40, @barHeight).attr fill: "#222"
r.rect(40, 0, 30, @barHeight).attr fill: "#444"
for day, mm in @days
if cuday isnt day[0]
# Dates
r.text(55, @offsetY + @unitTime * mm, day[0])
.attr(
font: "12px Monaco, monospace"
fill: "#BBB"
)
cuday = day[0]
if cumonth isnt day[1]
# Months
r.text(20, @offsetY + @unitTime * mm, day[1])
.attr(
font: "12px Monaco, monospace"
fill: "#EEE"
)
cumonth = day[1]
@renderPartialGraph()
@bindEvents()
renderPartialGraph: ->
start = Math.floor((@element.scrollTop() - @offsetY) / @unitTime) - 10
start = 0 if start < 0
end = start + 40
end = @commits.length if @commits.length < end
if @prev_start == -1 or Math.abs(@prev_start - start) > 10
i = start
@prev_start = start
while i < end
commit = @commits[i]
i += 1
if commit.hasDrawn isnt true
x = @offsetX + @unitSpace * (@mspace - commit.space)
y = @offsetY + @unitTime * commit.time
@drawDot(x, y, commit)
@drawLines(x, y, commit)
@appendLabel(x, y, commit)
@appendAnchor(x, y, commit)
commit.hasDrawn = true
@top.toFront()
bindEvents: ->
drag = {}
element = @element
$(element).scroll (event) =>
@renderPartialGraph()
$(window).on
keydown: (event) =>
# left
element.scrollLeft element.scrollLeft() - 50 if event.keyCode is 37
# top
element.scrollTop element.scrollTop() - 50 if event.keyCode is 38
# right
element.scrollLeft element.scrollLeft() + 50 if event.keyCode is 39
# bottom
element.scrollTop element.scrollTop() + 50 if event.keyCode is 40
@renderPartialGraph()
appendLabel: (x, y, commit) ->
return unless commit.refs
r = @r
shortrefs = commit.refs
# Truncate if longer than 15 chars
shortrefs = shortrefs.substr(0, 15) + "" if shortrefs.length > 17
text = r.text(x + 4, y, shortrefs).attr(
"text-anchor": "start"
font: "10px Monaco, monospace"
fill: "#FFF"
title: commit.refs
)
textbox = text.getBBox()
# Create rectangle based on the size of the textbox
rect = r.rect(x, y - 7, textbox.width + 5, textbox.height + 5, 4).attr(
fill: "#000"
"fill-opacity": .5
stroke: "none"
)
triangle = r.path(["M", x - 5, y, "L", x - 15, y - 4, "L", x - 15, y + 4, "Z"]).attr(
fill: "#000"
"fill-opacity": .5
stroke: "none"
)
label = r.set(rect, text)
label.transform(["t", -rect.getBBox().width - 15, 0])
# Set text to front
text.toFront()
appendAnchor: (x, y, commit) ->
r = @r
top = @top
options = @options
anchor = r.circle(x, y, 10).attr(
fill: "#000"
opacity: 0
cursor: "pointer"
).click(->
window.open options.commit_url.replace("%s", commit.id), "_blank"
).hover(->
@tooltip = r.commitTooltip(x + 5, y, commit)
top.push @tooltip.insertBefore(this)
, ->
@tooltip and @tooltip.remove() and delete @tooltip
)
top.push anchor
drawDot: (x, y, commit) ->
r = @r
r.circle(x, y, 3).attr(
fill: @colors[commit.space]
stroke: "none"
)
avatar_box_x = @offsetX + @unitSpace * @mspace + 10
avatar_box_y = y - 10
r.rect(avatar_box_x, avatar_box_y, 20, 20).attr(
stroke: @colors[commit.space]
"stroke-width": 2
)
r.image(gon.relative_url_root + commit.author.icon, avatar_box_x, avatar_box_y, 20, 20)
r.text(@offsetX + @unitSpace * @mspace + 35, y, commit.message.split("\n")[0]).attr(
"text-anchor": "start"
font: "14px Monaco, monospace"
)
drawLines: (x, y, commit) ->
r = @r
for parent, i in commit.parents
parentCommit = @preparedCommits[parent[0]]
parentY = @offsetY + @unitTime * parentCommit.time
parentX1 = @offsetX + @unitSpace * (@mspace - parentCommit.space)
parentX2 = @offsetX + @unitSpace * (@mspace - parent[1])
# Set line color
if parentCommit.space <= commit.space
color = @colors[commit.space]
else
color = @colors[parentCommit.space]
# Build line shape
if parent[1] is commit.space
offset = [0, 5]
arrow = "l-2,5,4,0,-2,-5,0,5"
else if parent[1] < commit.space
offset = [3, 3]
arrow = "l5,0,-2,4,-3,-4,4,2"
else
offset = [-3, 3]
arrow = "l-5,0,2,4,3,-4,-4,2"
# Start point
route = ["M", x + offset[0], y + offset[1]]
# Add arrow if not first parent
if i > 0
route.push(arrow)
# Circumvent if overlap
if commit.space isnt parentCommit.space or commit.space isnt parent[1]
route.push(
"L", parentX2, y + 10,
"L", parentX2, parentY - 5,
)
# End point
route.push("L", parentX1, parentY)
r
.path(route)
.attr(
stroke: color
"stroke-width": 2)
markCommit: (commit) ->
if commit.id is @options.commit_id
r = @r
x = @offsetX + @unitSpace * (@mspace - commit.space)
y = @offsetY + @unitTime * commit.time
r.path(["M", x + 5, y, "L", x + 15, y + 4, "L", x + 15, y - 4, "Z"]).attr(
fill: "#000"
"fill-opacity": .5
stroke: "none"
)
# Displayed in the center
@element.scrollTop(y - @graphHeight / 2)
Raphael::commitTooltip = (x, y, commit) ->
boxWidth = 300
boxHeight = 200
icon = @image(gon.relative_url_root + commit.author.icon, x, y, 20, 20)
nameText = @text(x + 25, y + 10, commit.author.name)
idText = @text(x, y + 35, commit.id)
messageText = @text(x, y + 50, commit.message)
textSet = @set(icon, nameText, idText, messageText).attr(
"text-anchor": "start"
font: "12px Monaco, monospace"
)
nameText.attr(
font: "14px Arial"
"font-weight": "bold"
)
idText.attr fill: "#AAA"
@textWrap messageText, boxWidth - 50
rect = @rect(x - 10, y - 10, boxWidth, 100, 4).attr(
fill: "#FFF"
stroke: "#000"
"stroke-linecap": "round"
"stroke-width": 2
)
tooltip = @set(rect, textSet)
rect.attr(
height: tooltip.getBBox().height + 10
width: tooltip.getBBox().width + 10
)
tooltip.transform ["t", 20, 20]
tooltip
Raphael::textWrap = (t, width) ->
content = t.attr("text")
abc = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
t.attr text: abc
letterWidth = t.getBBox().width / abc.length
t.attr text: content
words = content.split(" ")
x = 0
s = []
for word in words
if x + (word.length * letterWidth) > width
s.push "\n"
x = 0
x += word.length * letterWidth
s.push word + " "
t.attr text: s.join("")
b = t.getBBox()
h = Math.abs(b.y2) - Math.abs(b.y) + 1
t.attr y: b.y + h
@BranchGraph = BranchGraph

View File

@ -0,0 +1,21 @@
@Chart =
labels: []
values: []
init: (labels, values, title) ->
r = Raphael('activity-chart')
fin = ->
@flag = r.popup(@bar.x, @bar.y, @bar.value or "0").insertBefore(this)
fout = ->
@flag.animate
opacity: 0, 300, -> @remove()
r.text(160, 10, title).attr font: "13px sans-serif"
r.barchart(
10, 20, 560, 200,
[values],
{colors:["#456"]}
).label(labels, true)
.hover(fin, fout)

View File

@ -0,0 +1,6 @@
class Commit
constructor: ->
$('.files .diff-file').each ->
new CommitFile(this)
@Commit = Commit

View File

@ -0,0 +1,7 @@
class CommitFile
constructor: (file) ->
if $('.image', file).length
new ImageFile(file)
@CommitFile = CommitFile

View File

@ -0,0 +1,128 @@
class ImageFile
# Width where images must fits in, for 2-up this gets divided by 2
@availWidth = 900
@viewModes = ['two-up', 'swipe']
constructor: (@file) ->
# Determine if old and new file has same dimensions, if not show 'two-up' view
this.requestImageInfo $('.two-up.view .frame.deleted img', @file), (deletedWidth, deletedHeight) =>
this.requestImageInfo $('.two-up.view .frame.added img', @file), (width, height) =>
if width == deletedWidth && height == deletedHeight
this.initViewModes()
else
this.initView('two-up')
initViewModes: ->
viewMode = ImageFile.viewModes[0]
$('.view-modes', @file).removeClass 'hide'
$('.view-modes-menu', @file).on 'click', 'li', (event) =>
unless $(event.currentTarget).hasClass('active')
this.activateViewMode(event.currentTarget.className)
this.activateViewMode(viewMode)
activateViewMode: (viewMode) ->
$('.view-modes-menu li', @file)
.removeClass('active')
.filter(".#{viewMode}").addClass 'active'
$(".view:visible:not(.#{viewMode})", @file).fadeOut 200, =>
$(".view.#{viewMode}", @file).fadeIn(200)
this.initView viewMode
initView: (viewMode) ->
this.views[viewMode].call(this)
prepareFrames = (view) ->
maxWidth = 0
maxHeight = 0
$('.frame', view).each (index, frame) =>
width = $(frame).width()
height = $(frame).height()
maxWidth = if width > maxWidth then width else maxWidth
maxHeight = if height > maxHeight then height else maxHeight
.css
width: maxWidth
height: maxHeight
[maxWidth, maxHeight]
views:
'two-up': ->
$('.two-up.view .wrap', @file).each (index, wrap) =>
$('img', wrap).each ->
currentWidth = $(this).width()
if currentWidth > ImageFile.availWidth / 2
$(this).width ImageFile.availWidth / 2
this.requestImageInfo $('img', wrap), (width, height) ->
$('.image-info .meta-width', wrap).text "#{width}px"
$('.image-info .meta-height', wrap).text "#{height}px"
$('.image-info', wrap).removeClass('hide')
'swipe': ->
maxWidth = 0
maxHeight = 0
$('.swipe.view', @file).each (index, view) =>
[maxWidth, maxHeight] = prepareFrames(view)
$('.swipe-frame', view).css
width: maxWidth + 16
height: maxHeight + 28
$('.swipe-wrap', view).css
width: maxWidth + 1
height: maxHeight + 2
$('.swipe-bar', view).css
left: 0
.draggable
axis: 'x'
containment: 'parent'
drag: (event) ->
$('.swipe-wrap', view).width (maxWidth + 1) - $(this).position().left
stop: (event) ->
$('.swipe-wrap', view).width (maxWidth + 1) - $(this).position().left
'onion-skin': ->
maxWidth = 0
maxHeight = 0
dragTrackWidth = $('.drag-track', @file).width() - $('.dragger', @file).width()
$('.onion-skin.view', @file).each (index, view) =>
[maxWidth, maxHeight] = prepareFrames(view)
$('.onion-skin-frame', view).css
width: maxWidth + 16
height: maxHeight + 28
$('.swipe-wrap', view).css
width: maxWidth + 1
height: maxHeight + 2
$('.dragger', view).css
left: dragTrackWidth
.draggable
axis: 'x'
containment: 'parent'
drag: (event) ->
$('.frame.added', view).css('opacity', $(this).position().left / dragTrackWidth)
stop: (event) ->
$('.frame.added', view).css('opacity', $(this).position().left / dragTrackWidth)
requestImageInfo: (img, callback) ->
domImg = img.get(0)
if domImg.complete
callback.call(this, domImg.naturalWidth, domImg.naturalHeight)
else
img.on 'load', =>
callback.call(this, domImg.naturalWidth, domImg.naturalHeight)
@ImageFile = ImageFile

View File

@ -0,0 +1,57 @@
class CommitsList
@data =
ref: null
limit: 0
offset: 0
@disable = false
@showProgress: ->
$('.loading').show()
@hideProgress: ->
$('.loading').hide()
@init: (ref, limit) ->
$("body").on "click", ".day-commits-table li.commit", (event) ->
if event.target.nodeName != "A"
location.href = $(this).attr("url")
e.stopPropagation()
return false
@data.ref = ref
@data.limit = limit
@data.offset = limit
this.initLoadMore()
this.showProgress()
@getOld: ->
this.showProgress()
$.ajax
type: "GET"
url: location.href
data: @data
complete: this.hideProgress
success: (data) ->
CommitsList.append(data.count, data.html)
dataType: "json"
@append: (count, html) ->
$("#commits-list").append(html)
if count > 0
@data.offset += count
else
@disable = true
@initLoadMore: ->
$(document).unbind('scroll')
$(document).endlessScroll
bottomPixels: 400
fireDelay: 1000
fireOnce: true
ceaseFire: =>
@disable
callback: =>
this.getOld()
this.CommitsList = CommitsList

View File

@ -0,0 +1,33 @@
class Dashboard
constructor: ->
@initSidebarTab()
$(".dash-filter").keyup ->
terms = $(this).val()
uiBox = $(this).parents('.panel').first()
if terms == "" || terms == undefined
uiBox.find(".dash-list li").show()
else
uiBox.find(".dash-list li").each (index) ->
name = $(this).find(".filter-title").text()
if name.toLowerCase().search(terms.toLowerCase()) == -1
$(this).hide()
else
$(this).show()
initSidebarTab: ->
key = "dashboard_sidebar_filter"
# store selection in cookie
$('.dash-sidebar-tabs a').on 'click', (e) ->
$.cookie(key, $(e.target).attr('id'))
# show tab from cookie
sidebar_filter = $.cookie(key)
$("#" + sidebar_filter).tab('show') if sidebar_filter
@Dashboard = Dashboard

View File

@ -0,0 +1,46 @@
class Diff
UNFOLD_COUNT = 20
constructor: ->
$(document).on('click', '.js-unfold', (event) =>
target = $(event.target)
unfoldBottom = target.hasClass('js-unfold-bottom')
unfold = true
[old_line, line_number] = @lineNumbers(target.parent())
offset = line_number - old_line
if unfoldBottom
line_number += 1
since = line_number
to = line_number + UNFOLD_COUNT
else
[prev_old_line, prev_new_line] = @lineNumbers(target.parent().prev())
line_number -= 1
to = line_number
if line_number - UNFOLD_COUNT > prev_new_line + 1
since = line_number - UNFOLD_COUNT
else
since = prev_new_line + 1
unfold = false
link = target.parents('.diff-file').attr('data-blob-diff-path')
params =
since: since
to: to
bottom: unfoldBottom
offset: offset
unfold: unfold
$.get(link, params, (response) =>
target.parent().replaceWith(response)
)
)
lineNumbers: (line) ->
return ([0, 0]) unless line.children().length
lines = line.children().slice(0, 2)
line_numbers = ($(l).attr('data-linenumber') for l in lines)
(parseInt(line_number) for line_number in line_numbers)
@Diff = Diff

View File

@ -0,0 +1,75 @@
$ ->
new Dispatcher()
class Dispatcher
constructor: () ->
@initSearch()
@initHighlight()
@initPageScripts()
initPageScripts: ->
page = $('body').attr('data-page')
project_id = $('body').attr('data-project-id')
unless page
return false
path = page.split(':')
switch page
when 'projects:issues:index'
Issues.init()
when 'projects:issues:show'
new Issue()
when 'projects:milestones:show'
new Milestone()
when 'projects:issues:new'
GitLab.GfmAutoComplete.setup()
when 'projects:merge_requests:new'
GitLab.GfmAutoComplete.setup()
new Diff()
when 'projects:merge_requests:show'
new Diff()
when "projects:merge_requests:diffs"
new Diff()
when 'dashboard:show'
new Dashboard()
new Activities()
when 'projects:commit:show'
new Commit()
new Diff()
when 'groups:show', 'projects:show'
new Activities()
when 'projects:new', 'projects:edit'
new Project()
when 'projects:teams:members:index'
new TeamMembers()
when 'groups:members'
new GroupMembers()
when 'projects:tree:show'
new TreeView()
when 'projects:blob:show'
new BlobView()
when 'projects:labels:new', 'projects:labels:edit'
new Labels()
switch path.first()
when 'admin' then new Admin()
when 'projects'
new Wikis() if path[1] == 'wikis'
initSearch: ->
opts = $('.search-autocomplete-opts')
path = opts.data('autocomplete-path')
project_id = opts.data('autocomplete-project-id')
project_ref = opts.data('autocomplete-project-ref')
new SearchAutocomplete(path, project_id, project_ref)
initHighlight: ->
$('.highlight pre code').each (i, e) ->
$(e).html($.map($(e).html().split("\n"), (line, i) ->
"<span class='line' id='LC" + (i + 1) + "'>" + line + "</span>"
).join("\n"))
hljs.highlightBlock(e)

View File

@ -0,0 +1,7 @@
Array.prototype.first = function() {
return this[0];
}
Array.prototype.last = function() {
return this[this.length-1];
}

View File

@ -0,0 +1,13 @@
$.fn.showAndHide = ->
$(@).show().
delay(3000).
fadeOut()
$.fn.enableButton = ->
$(@).removeAttr('disabled').
removeClass('disabled')
$.fn.disableButton = ->
$(@).attr('disabled', 'disabled').
addClass('disabled')

View File

@ -0,0 +1,15 @@
class Flash
constructor: (message, type)->
flash = $(".flash-container")
flash.html("")
$('<div/>',
class: "flash-#{type}",
text: message
).appendTo(".flash-container")
flash.click -> $(@).fadeOut()
flash.show()
setTimeout (-> flash.fadeOut()), 5000
@Flash = Flash

View File

@ -0,0 +1,67 @@
# Creates the variables for setting up GFM auto-completion
window.GitLab ?= {}
GitLab.GfmAutoComplete =
# private_token: ''
dataSource: ''
# Emoji
Emoji:
template: '<li data-value="${insert}">${name} <img alt="${name}" height="20" src="${image}" width="20" /></li>'
# Team Members
Members:
template: '<li data-value="${username}">${username} <small>${name}</small></li>'
# Issues and MergeRequests
Issues:
template: '<li data-value="${id}"><small>${id}</small> ${title} </li>'
# Add GFM auto-completion to all input fields, that accept GFM input.
setup: ->
input = $('.js-gfm-input')
# Emoji
input.atwho
at: ':'
tpl: @Emoji.template
callbacks:
before_save: (emojis) =>
$.map emojis, (em) => name: em.name, insert: em.name+ ':', image: em.path
# Team Members
input.atwho
at: '@'
tpl: @Members.template
search_key: 'search'
callbacks:
before_save: (members) =>
$.map members, (m) => name: m.name, username: m.username, search: "#{m.username} #{m.name}"
input.atwho
at: '#'
alias: 'issues'
search_key: 'search'
tpl: @Issues.template
callbacks:
before_save: (issues) ->
$.map issues, (i) -> id: i.iid, title: sanitize(i.title), search: "#{i.iid} #{i.title}"
input.atwho
at: '!'
alias: 'mergerequests'
search_key: 'search'
tpl: @Issues.template
callbacks:
before_save: (merges) ->
$.map merges, (m) -> id: m.iid, title: sanitize(m.title), search: "#{m.iid} #{m.title}"
input.one "focus", =>
$.getJSON(@dataSource).done (data) ->
# load members
input.atwho 'load', "@", data.members
# load issues
input.atwho 'load', "issues", data.issues
# load merge requests
input.atwho 'load', "mergerequests", data.mergerequests
# load emojis
input.atwho 'load', ":", data.emojis

View File

@ -0,0 +1,17 @@
class GroupMembers
constructor: ->
$('li.users_group').bind 'ajax:success', ->
$(this).fadeOut()
@GroupMembers = GroupMembers
$ ->
# avatar
$('.js-choose-group-avatar-button').bind "click", ->
form = $(this).closest("form")
form.find(".js-group-avatar-input").click()
$('.js-group-avatar-input').bind "change", ->
form = $(this).closest("form")
filename = $(this).val().replace(/^.*[\\\/]/, '')
form.find(".js-avatar-filename").text(filename)

View File

@ -0,0 +1,9 @@
class Issue
constructor: ->
$('.edit-issue.inline-update input[type="submit"]').hide()
$(".issue-box .inline-update").on "change", "select", ->
$(this).submit()
$(".issue-box .inline-update").on "change", "#issue_assignee_id", ->
$(this).submit()
@Issue = Issue

View File

@ -0,0 +1,79 @@
@Issues =
init: ->
Issues.initSearch()
Issues.initSelects()
Issues.initChecks()
$("body").on "ajax:success", ".close_issue, .reopen_issue", ->
t = $(this)
totalIssues = undefined
reopen = t.hasClass("reopen_issue")
$(".issue_counter").each ->
issue = $(this)
totalIssues = parseInt($(this).html(), 10)
if reopen and issue.closest(".main_menu").length
$(this).html totalIssues + 1
else
$(this).html totalIssues - 1
$("body").on "click", ".issues-filters .dropdown-menu a", ->
$('.issues-list').block(
message: null,
overlayCSS:
backgroundColor: '#DDD'
opacity: .4
)
reload: ->
Issues.initSelects()
Issues.initChecks()
$('#filter_issue_search').val($('#issue_search').val())
initSelects: ->
$("select#update_status").select2(width: 'resolve', dropdownAutoWidth: true)
$("select#update_assignee_id").select2(width: 'resolve', dropdownAutoWidth: true)
$("select#update_milestone_id").select2(width: 'resolve', dropdownAutoWidth: true)
$("select#label_name").select2(width: 'resolve', dropdownAutoWidth: true)
$("#milestone_id, #assignee_id, #label_name").on "change", ->
$(this).closest("form").submit()
initChecks: ->
$(".check_all_issues").click ->
$(".selected_issue").prop("checked", @checked)
Issues.checkChanged()
$(".selected_issue").bind "change", Issues.checkChanged
initSearch: ->
form = $("#issue_search_form")
last_terms = ""
$("#issue_search").keyup ->
terms = $(this).val()
unless terms is last_terms
last_terms = terms
if terms.length >= 2 or terms.length is 0
$.ajax
type: "GET"
url: location.href
data: "issue_search=" + terms
complete: ->
$(".loading").hide()
success: (data) ->
$('.issues-holder').html(data.html)
Issues.reload()
dataType: "json"
checkChanged: ->
checked_issues = $(".selected_issue:checked")
if checked_issues.length > 0
ids = []
$.each checked_issues, (index, value) ->
ids.push $(value).attr("data-id")
$("#update_issues_ids").val ids
$(".issues-filters").hide()
$(".issues_bulk_update").show()
else
$("#update_issues_ids").val []
$(".issues_bulk_update").hide()
$(".issues-filters").show()

View File

@ -0,0 +1,35 @@
class Labels
constructor: ->
form = $('.label-form')
@setupLabelForm(form)
@cleanBinding()
@addBinding()
@updateColorPreview()
addBinding: ->
$(document).on 'click', '.suggest-colors a', @setSuggestedColor
$(document).on 'input', 'input#label_color', @updateColorPreview
cleanBinding: ->
$(document).off 'click', '.suggest-colors a'
$(document).off 'input', 'input#label_color'
# Initializes the form to disable the save button if no color or title is entered
setupLabelForm: (form) ->
disableButtonIfAnyEmptyField form, '.form-control', form.find('.js-save-button')
# Updates the the preview color with the hex-color input
updateColorPreview: =>
previewColor = $('input#label_color').val()
$('div.label-color-preview').css('background-color', previewColor)
# Updates the preview color with a click on a suggested color
setSuggestedColor: (e) =>
color = $(e.currentTarget).data('color')
$('input#label_color').val(color)
@updateColorPreview()
# Notify the form, that color has changed
$('.label-form').trigger('keyup')
e.preventDefault()
@Labels = Labels

View File

@ -0,0 +1,181 @@
/**
* Timeago is a jQuery plugin that makes it easy to support automatically
* updating fuzzy timestamps (e.g. "4 minutes ago" or "about 1 day ago").
*
* @name timeago
* @version 1.1.0
* @requires jQuery v1.2.3+
* @author Ryan McGeary
* @license MIT License - http://www.opensource.org/licenses/mit-license.php
*
* For usage and examples, visit:
* http://timeago.yarp.com/
*
* Copyright (c) 2008-2013, Ryan McGeary (ryan -[at]- mcgeary [*dot*] org)
*/
(function (factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['jquery'], factory);
} else {
// Browser globals
factory(jQuery);
}
}(function ($) {
$.timeago = function(timestamp) {
if (timestamp instanceof Date) {
return inWords(timestamp);
} else if (typeof timestamp === "string") {
return inWords($.timeago.parse(timestamp));
} else if (typeof timestamp === "number") {
return inWords(new Date(timestamp));
} else {
return inWords($.timeago.datetime(timestamp));
}
};
var $t = $.timeago;
$.extend($.timeago, {
settings: {
refreshMillis: 60000,
allowFuture: false,
strings: {
prefixAgo: null,
prefixFromNow: null,
suffixAgo: "ago",
suffixFromNow: "from now",
seconds: "less than a minute",
minute: "about a minute",
minutes: "%d minutes",
hour: "about an hour",
hours: "about %d hours",
day: "a day",
days: "%d days",
month: "about a month",
months: "%d months",
year: "about a year",
years: "%d years",
wordSeparator: " ",
numbers: []
}
},
inWords: function(distanceMillis) {
var $l = this.settings.strings;
var prefix = $l.prefixAgo;
var suffix = $l.suffixAgo;
if (this.settings.allowFuture) {
if (distanceMillis < 0) {
prefix = $l.prefixFromNow;
suffix = $l.suffixFromNow;
}
}
var seconds = Math.abs(distanceMillis) / 1000;
var minutes = seconds / 60;
var hours = minutes / 60;
var days = hours / 24;
var years = days / 365;
function substitute(stringOrFunction, number) {
var string = $.isFunction(stringOrFunction) ? stringOrFunction(number, distanceMillis) : stringOrFunction;
var value = ($l.numbers && $l.numbers[number]) || number;
return string.replace(/%d/i, value);
}
var words = seconds < 45 && substitute($l.seconds, Math.round(seconds)) ||
seconds < 90 && substitute($l.minute, 1) ||
minutes < 45 && substitute($l.minutes, Math.round(minutes)) ||
minutes < 90 && substitute($l.hour, 1) ||
hours < 24 && substitute($l.hours, Math.round(hours)) ||
hours < 42 && substitute($l.day, 1) ||
days < 30 && substitute($l.days, Math.round(days)) ||
days < 45 && substitute($l.month, 1) ||
days < 365 && substitute($l.months, Math.round(days / 30)) ||
years < 1.5 && substitute($l.year, 1) ||
substitute($l.years, Math.round(years));
var separator = $l.wordSeparator || "";
if ($l.wordSeparator === undefined) { separator = " "; }
return $.trim([prefix, words, suffix].join(separator));
},
parse: function(iso8601) {
var s = $.trim(iso8601);
s = s.replace(/\.\d+/,""); // remove milliseconds
s = s.replace(/-/,"/").replace(/-/,"/");
s = s.replace(/T/," ").replace(/Z/," UTC");
s = s.replace(/([\+\-]\d\d)\:?(\d\d)/," $1$2"); // -04:00 -> -0400
return new Date(s);
},
datetime: function(elem) {
var iso8601 = $t.isTime(elem) ? $(elem).attr("datetime") : $(elem).attr("title");
return $t.parse(iso8601);
},
isTime: function(elem) {
// jQuery's `is()` doesn't play well with HTML5 in IE
return $(elem).get(0).tagName.toLowerCase() === "time"; // $(elem).is("time");
}
});
// functions that can be called via $(el).timeago('action')
// init is default when no action is given
// functions are called with context of a single element
var functions = {
init: function(){
var refresh_el = $.proxy(refresh, this);
refresh_el();
var $s = $t.settings;
if ($s.refreshMillis > 0) {
setInterval(refresh_el, $s.refreshMillis);
}
},
update: function(time){
$(this).data('timeago', { datetime: $t.parse(time) });
refresh.apply(this);
}
};
$.fn.timeago = function(action, options) {
var fn = action ? functions[action] : functions.init;
if(!fn){
throw new Error("Unknown function name '"+ action +"' for timeago");
}
// each over objects here and call the requested function
this.each(function(){
fn.call(this, options);
});
return this;
};
function refresh() {
var data = prepareData(this);
if (!isNaN(data.datetime)) {
$(this).text(inWords(data.datetime));
}
return this;
}
function prepareData(element) {
element = $(element);
if (!element.data("timeago")) {
element.data("timeago", { datetime: $t.datetime(element) });
var text = $.trim(element.text());
if (text.length > 0 && !($t.isTime(element) && element.attr("title"))) {
element.attr("title", text);
}
}
return element.data("timeago");
}
function inWords(date) {
return $t.inWords(distance(date));
}
function distance(date) {
return (new Date().getTime() - date.getTime());
}
// fix for IE6 suckage
document.createElement("abbr");
document.createElement("time");
}));

View File

@ -0,0 +1,211 @@
function md5 (str) {
// http://kevin.vanzonneveld.net
// + original by: Webtoolkit.info (http://www.webtoolkit.info/)
// + namespaced by: Michael White (http://getsprink.com)
// + tweaked by: Jack
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + input by: Brett Zamir (http://brett-zamir.me)
// + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// - depends on: utf8_encode
// * example 1: md5('Kevin van Zonneveld');
// * returns 1: '6e658d4bfcb59cc13f96c14450ac40b9'
var xl;
var rotateLeft = function (lValue, iShiftBits) {
return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits));
};
var addUnsigned = function (lX, lY) {
var lX4, lY4, lX8, lY8, lResult;
lX8 = (lX & 0x80000000);
lY8 = (lY & 0x80000000);
lX4 = (lX & 0x40000000);
lY4 = (lY & 0x40000000);
lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF);
if (lX4 & lY4) {
return (lResult ^ 0x80000000 ^ lX8 ^ lY8);
}
if (lX4 | lY4) {
if (lResult & 0x40000000) {
return (lResult ^ 0xC0000000 ^ lX8 ^ lY8);
} else {
return (lResult ^ 0x40000000 ^ lX8 ^ lY8);
}
} else {
return (lResult ^ lX8 ^ lY8);
}
};
var _F = function (x, y, z) {
return (x & y) | ((~x) & z);
};
var _G = function (x, y, z) {
return (x & z) | (y & (~z));
};
var _H = function (x, y, z) {
return (x ^ y ^ z);
};
var _I = function (x, y, z) {
return (y ^ (x | (~z)));
};
var _FF = function (a, b, c, d, x, s, ac) {
a = addUnsigned(a, addUnsigned(addUnsigned(_F(b, c, d), x), ac));
return addUnsigned(rotateLeft(a, s), b);
};
var _GG = function (a, b, c, d, x, s, ac) {
a = addUnsigned(a, addUnsigned(addUnsigned(_G(b, c, d), x), ac));
return addUnsigned(rotateLeft(a, s), b);
};
var _HH = function (a, b, c, d, x, s, ac) {
a = addUnsigned(a, addUnsigned(addUnsigned(_H(b, c, d), x), ac));
return addUnsigned(rotateLeft(a, s), b);
};
var _II = function (a, b, c, d, x, s, ac) {
a = addUnsigned(a, addUnsigned(addUnsigned(_I(b, c, d), x), ac));
return addUnsigned(rotateLeft(a, s), b);
};
var convertToWordArray = function (str) {
var lWordCount;
var lMessageLength = str.length;
var lNumberOfWords_temp1 = lMessageLength + 8;
var lNumberOfWords_temp2 = (lNumberOfWords_temp1 - (lNumberOfWords_temp1 % 64)) / 64;
var lNumberOfWords = (lNumberOfWords_temp2 + 1) * 16;
var lWordArray = new Array(lNumberOfWords - 1);
var lBytePosition = 0;
var lByteCount = 0;
while (lByteCount < lMessageLength) {
lWordCount = (lByteCount - (lByteCount % 4)) / 4;
lBytePosition = (lByteCount % 4) * 8;
lWordArray[lWordCount] = (lWordArray[lWordCount] | (str.charCodeAt(lByteCount) << lBytePosition));
lByteCount++;
}
lWordCount = (lByteCount - (lByteCount % 4)) / 4;
lBytePosition = (lByteCount % 4) * 8;
lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition);
lWordArray[lNumberOfWords - 2] = lMessageLength << 3;
lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29;
return lWordArray;
};
var wordToHex = function (lValue) {
var wordToHexValue = "",
wordToHexValue_temp = "",
lByte, lCount;
for (lCount = 0; lCount <= 3; lCount++) {
lByte = (lValue >>> (lCount * 8)) & 255;
wordToHexValue_temp = "0" + lByte.toString(16);
wordToHexValue = wordToHexValue + wordToHexValue_temp.substr(wordToHexValue_temp.length - 2, 2);
}
return wordToHexValue;
};
var x = [],
k, AA, BB, CC, DD, a, b, c, d, S11 = 7,
S12 = 12,
S13 = 17,
S14 = 22,
S21 = 5,
S22 = 9,
S23 = 14,
S24 = 20,
S31 = 4,
S32 = 11,
S33 = 16,
S34 = 23,
S41 = 6,
S42 = 10,
S43 = 15,
S44 = 21;
str = this.utf8_encode(str);
x = convertToWordArray(str);
a = 0x67452301;
b = 0xEFCDAB89;
c = 0x98BADCFE;
d = 0x10325476;
xl = x.length;
for (k = 0; k < xl; k += 16) {
AA = a;
BB = b;
CC = c;
DD = d;
a = _FF(a, b, c, d, x[k + 0], S11, 0xD76AA478);
d = _FF(d, a, b, c, x[k + 1], S12, 0xE8C7B756);
c = _FF(c, d, a, b, x[k + 2], S13, 0x242070DB);
b = _FF(b, c, d, a, x[k + 3], S14, 0xC1BDCEEE);
a = _FF(a, b, c, d, x[k + 4], S11, 0xF57C0FAF);
d = _FF(d, a, b, c, x[k + 5], S12, 0x4787C62A);
c = _FF(c, d, a, b, x[k + 6], S13, 0xA8304613);
b = _FF(b, c, d, a, x[k + 7], S14, 0xFD469501);
a = _FF(a, b, c, d, x[k + 8], S11, 0x698098D8);
d = _FF(d, a, b, c, x[k + 9], S12, 0x8B44F7AF);
c = _FF(c, d, a, b, x[k + 10], S13, 0xFFFF5BB1);
b = _FF(b, c, d, a, x[k + 11], S14, 0x895CD7BE);
a = _FF(a, b, c, d, x[k + 12], S11, 0x6B901122);
d = _FF(d, a, b, c, x[k + 13], S12, 0xFD987193);
c = _FF(c, d, a, b, x[k + 14], S13, 0xA679438E);
b = _FF(b, c, d, a, x[k + 15], S14, 0x49B40821);
a = _GG(a, b, c, d, x[k + 1], S21, 0xF61E2562);
d = _GG(d, a, b, c, x[k + 6], S22, 0xC040B340);
c = _GG(c, d, a, b, x[k + 11], S23, 0x265E5A51);
b = _GG(b, c, d, a, x[k + 0], S24, 0xE9B6C7AA);
a = _GG(a, b, c, d, x[k + 5], S21, 0xD62F105D);
d = _GG(d, a, b, c, x[k + 10], S22, 0x2441453);
c = _GG(c, d, a, b, x[k + 15], S23, 0xD8A1E681);
b = _GG(b, c, d, a, x[k + 4], S24, 0xE7D3FBC8);
a = _GG(a, b, c, d, x[k + 9], S21, 0x21E1CDE6);
d = _GG(d, a, b, c, x[k + 14], S22, 0xC33707D6);
c = _GG(c, d, a, b, x[k + 3], S23, 0xF4D50D87);
b = _GG(b, c, d, a, x[k + 8], S24, 0x455A14ED);
a = _GG(a, b, c, d, x[k + 13], S21, 0xA9E3E905);
d = _GG(d, a, b, c, x[k + 2], S22, 0xFCEFA3F8);
c = _GG(c, d, a, b, x[k + 7], S23, 0x676F02D9);
b = _GG(b, c, d, a, x[k + 12], S24, 0x8D2A4C8A);
a = _HH(a, b, c, d, x[k + 5], S31, 0xFFFA3942);
d = _HH(d, a, b, c, x[k + 8], S32, 0x8771F681);
c = _HH(c, d, a, b, x[k + 11], S33, 0x6D9D6122);
b = _HH(b, c, d, a, x[k + 14], S34, 0xFDE5380C);
a = _HH(a, b, c, d, x[k + 1], S31, 0xA4BEEA44);
d = _HH(d, a, b, c, x[k + 4], S32, 0x4BDECFA9);
c = _HH(c, d, a, b, x[k + 7], S33, 0xF6BB4B60);
b = _HH(b, c, d, a, x[k + 10], S34, 0xBEBFBC70);
a = _HH(a, b, c, d, x[k + 13], S31, 0x289B7EC6);
d = _HH(d, a, b, c, x[k + 0], S32, 0xEAA127FA);
c = _HH(c, d, a, b, x[k + 3], S33, 0xD4EF3085);
b = _HH(b, c, d, a, x[k + 6], S34, 0x4881D05);
a = _HH(a, b, c, d, x[k + 9], S31, 0xD9D4D039);
d = _HH(d, a, b, c, x[k + 12], S32, 0xE6DB99E5);
c = _HH(c, d, a, b, x[k + 15], S33, 0x1FA27CF8);
b = _HH(b, c, d, a, x[k + 2], S34, 0xC4AC5665);
a = _II(a, b, c, d, x[k + 0], S41, 0xF4292244);
d = _II(d, a, b, c, x[k + 7], S42, 0x432AFF97);
c = _II(c, d, a, b, x[k + 14], S43, 0xAB9423A7);
b = _II(b, c, d, a, x[k + 5], S44, 0xFC93A039);
a = _II(a, b, c, d, x[k + 12], S41, 0x655B59C3);
d = _II(d, a, b, c, x[k + 3], S42, 0x8F0CCC92);
c = _II(c, d, a, b, x[k + 10], S43, 0xFFEFF47D);
b = _II(b, c, d, a, x[k + 1], S44, 0x85845DD1);
a = _II(a, b, c, d, x[k + 8], S41, 0x6FA87E4F);
d = _II(d, a, b, c, x[k + 15], S42, 0xFE2CE6E0);
c = _II(c, d, a, b, x[k + 6], S43, 0xA3014314);
b = _II(b, c, d, a, x[k + 13], S44, 0x4E0811A1);
a = _II(a, b, c, d, x[k + 4], S41, 0xF7537E82);
d = _II(d, a, b, c, x[k + 11], S42, 0xBD3AF235);
c = _II(c, d, a, b, x[k + 2], S43, 0x2AD7D2BB);
b = _II(b, c, d, a, x[k + 9], S44, 0xEB86D391);
a = addUnsigned(a, AA);
b = addUnsigned(b, BB);
c = addUnsigned(c, CC);
d = addUnsigned(d, DD);
}
var temp = wordToHex(a) + wordToHex(b) + wordToHex(c) + wordToHex(d);
return temp.toLowerCase();
}

View File

@ -0,0 +1,70 @@
function utf8_encode (argString) {
// http://kevin.vanzonneveld.net
// + original by: Webtoolkit.info (http://www.webtoolkit.info/)
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + improved by: sowberry
// + tweaked by: Jack
// + bugfixed by: Onno Marsman
// + improved by: Yves Sucaet
// + bugfixed by: Onno Marsman
// + bugfixed by: Ulrich
// + bugfixed by: Rafal Kukawski
// + improved by: kirilloid
// + bugfixed by: kirilloid
// * example 1: utf8_encode('Kevin van Zonneveld');
// * returns 1: 'Kevin van Zonneveld'
if (argString === null || typeof argString === "undefined") {
return "";
}
var string = (argString + ''); // .replace(/\r\n/g, "\n").replace(/\r/g, "\n");
var utftext = '',
start, end, stringl = 0;
start = end = 0;
stringl = string.length;
for (var n = 0; n < stringl; n++) {
var c1 = string.charCodeAt(n);
var enc = null;
if (c1 < 128) {
end++;
} else if (c1 > 127 && c1 < 2048) {
enc = String.fromCharCode(
(c1 >> 6) | 192,
( c1 & 63) | 128
);
} else if (c1 & 0xF800 != 0xD800) {
enc = String.fromCharCode(
(c1 >> 12) | 224,
((c1 >> 6) & 63) | 128,
( c1 & 63) | 128
);
} else { // surrogate pairs
if (c1 & 0xFC00 != 0xD800) { throw new RangeError("Unmatched trail surrogate at " + n); }
var c2 = string.charCodeAt(++n);
if (c2 & 0xFC00 != 0xDC00) { throw new RangeError("Unmatched lead surrogate at " + (n-1)); }
c1 = ((c1 & 0x3FF) << 10) + (c2 & 0x3FF) + 0x10000;
enc = String.fromCharCode(
(c1 >> 18) | 240,
((c1 >> 12) & 63) | 128,
((c1 >> 6) & 63) | 128,
( c1 & 63) | 128
);
}
if (enc !== null) {
if (end > start) {
utftext += string.slice(start, end);
}
utftext += enc;
start = end = n + 1;
}
}
if (end > start) {
utftext += string.slice(start, stringl);
}
return utftext;
}

View File

@ -0,0 +1,196 @@
formatLink = (str) ->
"![" + str.alt + "](" + str.url + ")"
$(document).ready ->
alertClass = "alert alert-danger alert-dismissable div-dropzone-alert"
alertAttr = "class=\"close\" data-dismiss=\"alert\"" + "aria-hidden=\"true\""
divHover = "<div class=\"div-dropzone-hover\"></div>"
divSpinner = "<div class=\"div-dropzone-spinner\"></div>"
divAlert = "<div class=\"" + alertClass + "\"></div>"
iconPicture = "<i class=\"icon-picture div-dropzone-icon\"></i>"
iconSpinner = "<i class=\"icon-spinner icon-spin div-dropzone-icon\"></i>"
btnAlert = "<button type=\"button\"" + alertAttr + ">&times;</button>"
project_image_path_upload = window.project_image_path_upload or null
$("textarea.markdown-area").wrap "<div class=\"div-dropzone\"></div>"
$(".div-dropzone").parent().addClass "div-dropzone-wrapper"
$(".div-dropzone").append divHover
$(".div-dropzone-hover").append iconPicture
$(".div-dropzone").append divSpinner
$(".div-dropzone-spinner").append iconSpinner
$(".div-dropzone-spinner").css
"opacity": 0
"display": "none"
dropzone = $(".div-dropzone").dropzone(
url: project_image_path_upload
dictDefaultMessage: ""
clickable: true
paramName: "markdown_img"
maxFilesize: 10
uploadMultiple: false
acceptedFiles: "image/jpg,image/jpeg,image/gif,image/png"
headers:
"X-CSRF-Token": $("meta[name=\"csrf-token\"]").attr("content")
previewContainer: false
processing: ->
$(".div-dropzone-alert").alert "close"
dragover: ->
$(".div-dropzone > textarea").addClass "div-dropzone-focus"
$(".div-dropzone-hover").css "opacity", 0.7
return
dragleave: ->
$(".div-dropzone > textarea").removeClass "div-dropzone-focus"
$(".div-dropzone-hover").css "opacity", 0
return
drop: ->
$(".div-dropzone > textarea").removeClass "div-dropzone-focus"
$(".div-dropzone-hover").css "opacity", 0
$(".div-dropzone > textarea").focus()
return
success: (header, response) ->
child = $(dropzone[0]).children("textarea")
$(child).val $(child).val() + formatLink(response.link) + "\n"
return
error: (temp, errorMessage) ->
checkIfMsgExists = $(".error-alert").children().length
if checkIfMsgExists is 0
$(".error-alert").append divAlert
$(".div-dropzone-alert").append btnAlert + errorMessage
return
sending: ->
$(".div-dropzone-spinner").css
"opacity": 0.7
"display": "inherit"
return
complete: ->
$(".dz-preview").remove()
$(".markdown-area").trigger "input"
$(".div-dropzone-spinner").css
"opacity": 0
"display": "none"
return
)
child = $(dropzone[0]).children("textarea")
formatLink = (str) ->
"![" + str.alt + "](" + str.url + ")"
handlePaste = (e) ->
e.preventDefault()
my_event = e.originalEvent
if my_event.clipboardData and my_event.clipboardData.items
processItem(my_event)
processItem = (e) ->
image = isImage(e)
if image
filename = getFilename(e) or "image.png"
text = "{{" + filename + "}}"
pasteText(text)
uploadFile image.getAsFile(), filename
else
text = e.clipboardData.getData("text/plain")
pasteText(text)
isImage = (data) ->
i = 0
while i < data.clipboardData.items.length
item = data.clipboardData.items[i]
if item.type.indexOf("image") isnt -1
return item
i++
return false
pasteText = (text) ->
caretStart = $(child)[0].selectionStart
caretEnd = $(child)[0].selectionEnd
textEnd = $(child).val().length
beforeSelection = $(child).val().substring 0, caretStart
afterSelection = $(child).val().substring caretEnd, textEnd
$(child).val beforeSelection + text + afterSelection
$(".markdown-area").trigger "input"
getFilename = (e) ->
if window.clipboardData and window.clipboardData.getData
value = window.clipboardData.getData("Text")
else if e.clipboardData and e.clipboardData.getData
value = e.clipboardData.getData("text/plain")
value = value.split("\r")
value.first()
uploadFile = (item, filename) ->
formData = new FormData()
formData.append "markdown_img", item, filename
$.ajax
url: project_image_path_upload
type: "POST"
data: formData
dataType: "json"
processData: false
contentType: false
headers:
"X-CSRF-Token": $("meta[name=\"csrf-token\"]").attr("content")
beforeSend: ->
showSpinner()
closeAlertMessage()
success: (e, textStatus, response) ->
insertToTextArea(filename, formatLink(response.responseJSON.link))
error: (response) ->
showError(response.responseJSON.message)
complete: ->
closeSpinner()
insertToTextArea = (filename, url) ->
$(child).val (index, val) ->
val.replace("{{" + filename + "}}", url + "\n")
appendToTextArea = (url) ->
$(child).val (index, val) ->
val + url + "\n"
showSpinner = (e) ->
$(".div-dropzone-spinner").css
"opacity": 0.7
"display": "inherit"
closeSpinner = ->
$(".div-dropzone-spinner").css
"opacity": 0
"display": "none"
showError = (message) ->
checkIfMsgExists = $(".error-alert").children().length
if checkIfMsgExists is 0
$(".error-alert").append divAlert
$(".div-dropzone-alert").append btnAlert + message
closeAlertMessage = ->
$(".div-dropzone-alert").alert "close"
$(".markdown-selector").click (e) ->
e.preventDefault()
$(@).closest(".div-dropzone-wrapper").find(".div-dropzone").click()
return
return

View File

@ -0,0 +1,129 @@
class MergeRequest
constructor: (@opts) ->
@initContextWidget()
this.$el = $('.merge-request')
@diffs_loaded = if @opts.action == 'diffs' then true else false
@commits_loaded = false
this.activateTab(@opts.action)
this.bindEvents()
this.initMergeWidget()
this.$('.show-all-commits').on 'click', =>
this.showAllCommits()
modal = $('#modal_merge_info').modal(show: false)
disableButtonIfEmptyField '#merge_commit_message', '.accept_merge_request'
# Local jQuery finder
$: (selector) ->
this.$el.find(selector)
initContextWidget: ->
$('.edit-merge_request.inline-update input[type="submit"]').hide()
$(".issue-box .inline-update").on "change", "select", ->
$(this).submit()
$(".issue-box .inline-update").on "change", "#merge_request_assignee_id", ->
$(this).submit()
initMergeWidget: ->
this.showState( @opts.current_status )
if this.$('.automerge_widget').length and @opts.check_enable
$.get @opts.url_to_automerge_check, (data) =>
this.showState( data.merge_status )
, 'json'
if @opts.ci_enable
$.get @opts.url_to_ci_check, (data) =>
this.showCiState data.status
, 'json'
bindEvents: ->
this.$('.merge-request-tabs').on 'click', 'a', (event) =>
a = $(event.currentTarget)
href = a.attr('href')
History.replaceState {path: href}, document.title, href
event.preventDefault()
this.$('.merge-request-tabs').on 'click', 'li', (event) =>
this.activateTab($(event.currentTarget).data('action'))
this.$('.accept_merge_request').on 'click', ->
$('.automerge_widget.can_be_merged').hide()
$('.merge-in-progress').show()
this.$('.remove_source_branch').on 'click', ->
$('.remove_source_branch_widget').hide()
$('.remove_source_branch_in_progress').show()
this.$(".remove_source_branch").on "ajax:success", (e, data, status, xhr) ->
location.reload()
this.$(".remove_source_branch").on "ajax:error", (e, data, status, xhr) =>
this.$('.remove_source_branch_widget').hide()
this.$('.remove_source_branch_in_progress').hide()
this.$('.remove_source_branch_widget.failed').show()
activateTab: (action) ->
this.$('.merge-request-tabs li').removeClass 'active'
this.$('.tab-content').hide()
switch action
when 'diffs'
this.$('.merge-request-tabs .diffs-tab').addClass 'active'
this.loadDiff() unless @diffs_loaded
this.$('.diffs').show()
else
this.$('.merge-request-tabs .notes-tab').addClass 'active'
this.$('.notes').show()
showState: (state) ->
$('.automerge_widget').hide()
$('.automerge_widget.' + state).show()
showCiState: (state) ->
$('.ci_widget').hide()
allowed_states = ["failed", "running", "pending", "success"]
if state in allowed_states
$('.ci_widget.ci-' + state).show()
else
$('.ci_widget.ci-error').show()
switch state
when "success"
$('.mr-state-widget').addClass("panel-success")
when "failed"
$('.mr-state-widget').addClass("panel-danger")
when "running", "pending"
$('.mr-state-widget').addClass("panel-warning")
loadDiff: (event) ->
$.ajax
type: 'GET'
url: this.$('.merge-request-tabs .diffs-tab a').attr('href')
beforeSend: =>
this.$('.mr-loading-status .loading').show()
complete: =>
@diffs_loaded = true
this.$('.mr-loading-status .loading').hide()
success: (data) =>
this.$(".diffs").html(data.html)
dataType: 'json'
showAllCommits: ->
this.$('.first-commits').remove()
this.$('.all-commits').removeClass 'hide'
alreadyOrCannotBeMerged: ->
this.$('.automerge_widget').hide()
this.$('.merge-in-progress').hide()
this.$('.automerge_widget.already_cannot_be_merged').show()
this.MergeRequest = MergeRequest

View File

@ -0,0 +1,8 @@
#
# * Filter merge requests
#
@merge_requestsPage = ->
$('#assignee_id').select2()
$('#milestone_id').select2()
$('#milestone_id, #assignee_id').on 'change', ->
$(this).closest('form').submit()

View File

@ -0,0 +1,119 @@
class Milestone
@updateIssue: (li, issue_url, data) ->
$.ajax
type: "PUT"
url: issue_url
data: data
success: (data) ->
if data.saved == true
if data.assignee_avatar_url
img_tag = $('<img/>')
img_tag.attr('src', data.assignee_avatar_url)
img_tag.addClass('avatar s16')
$(li).find('.assignee-icon').html(img_tag)
else
$(li).find('.assignee-icon').html('')
$(li).effect 'highlight'
else
new Flash("Issue update failed", 'alert')
dataType: "json"
@sortIssues: (data) ->
sort_issues_url = location.href + "/sort_issues"
$.ajax
type: "PUT"
url: sort_issues_url
data: data
success: (data) ->
if data.saved != true
new Flash("Issues update failed", 'alert')
dataType: "json"
@sortMergeRequests: (data) ->
sort_mr_url = location.href + "/sort_merge_requests"
$.ajax
type: "PUT"
url: sort_mr_url
data: data
success: (data) ->
if data.saved != true
new Flash("MR update failed", 'alert')
dataType: "json"
@updateMergeRequest: (li, merge_request_url, data) ->
$.ajax
type: "PUT"
url: merge_request_url
data: data
success: (data) ->
if data.saved == true
$(li).effect 'highlight'
else
new Flash("Issue update failed", 'alert')
dataType: "json"
constructor: ->
@bindIssuesSorting()
@bindMergeRequestSorting()
bindIssuesSorting: ->
$("#issues-list-unassigned, #issues-list-ongoing, #issues-list-closed").sortable(
connectWith: ".issues-sortable-list",
dropOnEmpty: true,
items: "li:not(.ui-sort-disabled)",
update: (event, ui) ->
data = $(this).sortable("serialize")
Milestone.sortIssues(data)
receive: (event, ui) ->
new_state = $(this).data('state')
issue_id = ui.item.data('iid')
issue_url = ui.item.data('url')
data = switch new_state
when 'ongoing'
"issue[assignee_id]=" + gon.current_user_id
when 'unassigned'
"issue[assignee_id]="
when 'closed'
"issue[state_event]=close"
if $(ui.sender).data('state') == "closed"
data += "&issue[state_event]=reopen"
Milestone.updateIssue(ui.item, issue_url, data)
).disableSelection()
bindMergeRequestSorting: ->
$("#merge_requests-list-unassigned, #merge_requests-list-ongoing, #merge_requests-list-closed").sortable(
connectWith: ".merge_requests-sortable-list",
dropOnEmpty: true,
items: "li:not(.ui-sort-disabled)",
update: (event, ui) ->
data = $(this).sortable("serialize")
Milestone.sortMergeRequests(data)
receive: (event, ui) ->
new_state = $(this).data('state')
merge_request_id = ui.item.data('iid')
merge_request_url = ui.item.data('url')
data = switch new_state
when 'ongoing'
"merge_request[assignee_id]=" + gon.current_user_id
when 'unassigned'
"merge_request[assignee_id]="
when 'closed'
"merge_request[state_event]=close"
if $(ui.sender).data('state') == "closed"
data += "&merge_request[state_event]=reopen"
Milestone.updateMergeRequest(ui.item, merge_request_url, data)
).disableSelection()
@Milestone = Milestone

View File

@ -0,0 +1,24 @@
$ ->
namespaceFormatResult = (namespace) ->
markup = "<div class='namespace-result'>"
markup += "<span class='namespace-kind'>" + namespace.kind + "</span>"
markup += "<span class='namespace-path'>" + namespace.path + "</span>"
markup += "</div>"
markup
formatSelection = (namespace) ->
namespace.kind + ": " + namespace.path
$('.ajax-namespace-select').each (i, select) ->
$(select).select2
placeholder: "Search for namespace"
multiple: $(select).hasClass('multiselect')
minimumInputLength: 0
query: (query) ->
Api.namespaces query.term, (namespaces) ->
data = { results: namespaces }
query.callback(data)
dropdownCssClass: "ajax-namespace-dropdown"
formatResult: namespaceFormatResult
formatSelection: formatSelection

View File

@ -0,0 +1,11 @@
class Network
constructor: (opts) ->
$("#filter_ref").click ->
$(this).closest('form').submit()
branch_graph = new BranchGraph($(".network-graph"), opts)
vph = $(window).height() - 250
$('.network-graph').css 'height': (vph + 'px')
@Network = Network

View File

@ -0,0 +1,505 @@
class Notes
@interval: null
constructor: (notes_url, note_ids, last_fetched_at) ->
@notes_url = notes_url
@notes_url = gon.relative_url_root + @notes_url if gon.relative_url_root?
@note_ids = note_ids
@last_fetched_at = last_fetched_at
@initRefresh()
@setupMainTargetNoteForm()
@cleanBinding()
@addBinding()
addBinding: ->
# add note to UI after creation
$(document).on "ajax:success", ".js-main-target-form", @addNote
$(document).on "ajax:success", ".js-discussion-note-form", @addDiscussionNote
# change note in UI after update
$(document).on "ajax:success", "form.edit_note", @updateNote
# Edit note link
$(document).on "click", ".js-note-edit", @showEditForm
$(document).on "click", ".note-edit-cancel", @cancelEdit
# remove a note (in general)
$(document).on "click", ".js-note-delete", @removeNote
# delete note attachment
$(document).on "click", ".js-note-attachment-delete", @removeAttachment
# Preview button
$(document).on "click", ".js-note-preview-button", @previewNote
# Preview button
$(document).on "click", ".js-note-write-button", @writeNote
# reset main target form after submit
$(document).on "ajax:complete", ".js-main-target-form", @resetMainTargetForm
# attachment button
$(document).on "click", ".js-choose-note-attachment-button", @chooseNoteAttachment
# update the file name when an attachment is selected
$(document).on "change", ".js-note-attachment-input", @updateFormAttachment
# reply to diff/discussion notes
$(document).on "click", ".js-discussion-reply-button", @replyToDiscussionNote
# add diff note
$(document).on "click", ".js-add-diff-note-button", @addDiffNote
# hide diff note form
$(document).on "click", ".js-close-discussion-note-form", @cancelDiscussionForm
# fetch notes when tab becomes visible
$(document).on "visibilitychange", @visibilityChange
@notes_forms = '.js-main-target-form textarea, .js-discussion-note-form textarea'
$(document).on('keypress', @notes_forms, (e)->
if e.keyCode == 10 || (e.ctrlKey && e.keyCode == 13)
$(@).parents('form').submit()
)
cleanBinding: ->
$(document).off "ajax:success", ".js-main-target-form"
$(document).off "ajax:success", ".js-discussion-note-form"
$(document).off "ajax:success", "form.edit_note"
$(document).off "click", ".js-note-edit"
$(document).off "click", ".note-edit-cancel"
$(document).off "click", ".js-note-delete"
$(document).off "click", ".js-note-attachment-delete"
$(document).off "click", ".js-note-preview-button"
$(document).off "click", ".js-note-write-button"
$(document).off "ajax:complete", ".js-main-target-form"
$(document).off "click", ".js-choose-note-attachment-button"
$(document).off "click", ".js-discussion-reply-button"
$(document).off "click", ".js-add-diff-note-button"
$(document).off "visibilitychange"
$(document).off "keypress", @notes_forms
initRefresh: ->
clearInterval(Notes.interval)
Notes.interval = setInterval =>
@refresh()
, 15000
refresh: ->
@getContent() unless document.hidden
getContent: ->
$.ajax
url: @notes_url
data: "last_fetched_at=" + @last_fetched_at
dataType: "json"
success: (data) =>
notes = data.notes
@last_fetched_at = data.last_fetched_at
$.each notes, (i, note) =>
@renderNote(note)
###
Render note in main comments area.
Note: for rendering inline notes use renderDiscussionNote
###
renderNote: (note) ->
# render note if it not present in loaded list
# or skip if rendered
if @isNewNote(note)
@note_ids.push(note.id)
$('ul.main-notes-list').append(note.html)
code = "#note_" + note.id + " .highlight pre code"
$(code).each (i, e) ->
hljs.highlightBlock(e)
###
Check if note does not exists on page
###
isNewNote: (note) ->
$.inArray(note.id, @note_ids) == -1
###
Render note in discussion area.
Note: for rendering inline notes use renderDiscussionNote
###
renderDiscussionNote: (note) ->
@note_ids.push(note.id)
form = $("form[rel='" + note.discussion_id + "']")
row = form.closest("tr")
# is this the first note of discussion?
if row.is(".js-temp-notes-holder")
# insert the note and the reply button after the temp row
row.after note.discussion_html
# remove the note (will be added again below)
row.next().find(".note").remove()
# Add note to 'Changes' page discussions
$(".notes[rel='" + note.discussion_id + "']").append note.html
# Init discussion on 'Discussion' page if it is merge request page
if $('body').attr('data-page').indexOf('projects:merge_request') == 0
$('ul.main-notes-list').append(note.discussion_with_diff_html)
else
# append new note to all matching discussions
$(".notes[rel='" + note.discussion_id + "']").append note.html
# cleanup after successfully creating a diff/discussion note
@removeDiscussionNoteForm(form)
###
Shows write note textarea.
###
writeNote: (e) ->
e.preventDefault()
form = $(this).closest("form")
# toggle tabs
form.find(".js-note-write-button").parent().addClass "active"
form.find(".js-note-preview-button").parent().removeClass "active"
# toggle content
form.find(".note-write-holder").show()
form.find(".note-preview-holder").hide()
###
Shows the note preview.
Lets the server render GFM into Html and displays it.
###
previewNote: (e) ->
e.preventDefault()
form = $(this).closest("form")
# toggle tabs
form.find(".js-note-write-button").parent().removeClass "active"
form.find(".js-note-preview-button").parent().addClass "active"
# toggle content
form.find(".note-write-holder").hide()
form.find(".note-preview-holder").show()
preview = form.find(".js-note-preview")
noteText = form.find(".js-note-text").val()
if noteText.trim().length is 0
preview.text "Nothing to preview."
else
preview.text "Loading..."
$.post($(this).data("url"),
note: noteText
).success (previewData) ->
preview.html previewData
###
Called in response the main target form has been successfully submitted.
Removes any errors.
Resets text and preview.
Resets buttons.
###
resetMainTargetForm: ->
form = $(".js-main-target-form")
# remove validation errors
form.find(".js-errors").remove()
# reset text and preview
form.find(".js-note-write-button").click()
form.find(".js-note-text").val("").trigger "input"
###
Called when clicking the "Choose File" button.
Opens the file selection dialog.
###
chooseNoteAttachment: ->
form = $(this).closest("form")
form.find(".js-note-attachment-input").click()
###
Shows the main form and does some setup on it.
Sets some hidden fields in the form.
###
setupMainTargetNoteForm: ->
# find the form
form = $(".js-new-note-form")
# insert the form after the button
form.clone().replaceAll $(".js-main-target-form")
form = form.prev("form")
# show the form
@setupNoteForm(form)
# fix classes
form.removeClass "js-new-note-form"
form.addClass "js-main-target-form"
# remove unnecessary fields and buttons
form.find("#note_line_code").remove()
form.find(".js-close-discussion-note-form").remove()
###
General note form setup.
deactivates the submit button when text is empty
hides the preview button when text is empty
setup GFM auto complete
show the form
###
setupNoteForm: (form) ->
disableButtonIfEmptyField form.find(".js-note-text"), form.find(".js-comment-button")
form.removeClass "js-new-note-form"
# setup preview buttons
form.find(".js-note-write-button, .js-note-preview-button").tooltip placement: "left"
previewButton = form.find(".js-note-preview-button")
form.find(".js-note-text").on "input", ->
if $(this).val().trim() isnt ""
previewButton.removeClass("turn-off").addClass "turn-on"
else
previewButton.removeClass("turn-on").addClass "turn-off"
# remove notify commit author checkbox for non-commit notes
form.find(".js-notify-commit-author").remove() if form.find("#note_noteable_type").val() isnt "Commit"
GitLab.GfmAutoComplete.setup()
form.show()
###
Called in response to the new note form being submitted
Adds new note to list.
###
addNote: (xhr, note, status) =>
@renderNote(note)
@updateVotes()
###
Called in response to the new note form being submitted
Adds new note to list.
###
addDiscussionNote: (xhr, note, status) =>
@renderDiscussionNote(note)
###
Called in response to the edit note form being submitted
Updates the current note field.
###
updateNote: (xhr, note, status) =>
note_li = $("#note_" + note.id)
note_li.replaceWith(note.html)
code = "#note_" + note.id + " .highlight pre code"
$(code).each (i, e) ->
hljs.highlightBlock(e)
###
Called in response to clicking the edit note link
Replaces the note text with the note edit form
Adds a hidden div with the original content of the note to fill the edit note form with
if the user cancels
###
showEditForm: (e) ->
e.preventDefault()
note = $(this).closest(".note")
note.find(".note-text").hide()
# Show the attachment delete link
note.find(".js-note-attachment-delete").show()
GitLab.GfmAutoComplete.setup()
form = note.find(".note-edit-form")
form.show()
textarea = form.find("textarea")
textarea.focus()
disableButtonIfEmptyField textarea, form.find(".js-comment-button")
###
Called in response to clicking the edit note link
Hides edit form
###
cancelEdit: (e) ->
e.preventDefault()
note = $(this).closest(".note")
note.find(".note-text").show()
note.find(".js-note-attachment-delete").hide()
note.find(".note-edit-form").hide()
###
Called in response to deleting a note of any kind.
Removes the actual note from view.
Removes the whole discussion if the last note is being removed.
###
removeNote: ->
note = $(this).closest(".note")
notes = note.closest(".notes")
# check if this is the last note for this line
if notes.find(".note").length is 1
# for discussions
notes.closest(".discussion").remove()
# for diff lines
notes.closest("tr").remove()
note.remove()
###
Called in response to clicking the delete attachment link
Removes the attachment wrapper view, including image tag if it exists
Resets the note editing form
###
removeAttachment: ->
note = $(this).closest(".note")
note.find(".note-attachment").remove()
note.find(".note-text").show()
note.find(".js-note-attachment-delete").hide()
note.find(".note-edit-form").hide()
###
Called when clicking on the "reply" button for a diff line.
Shows the note form below the notes.
###
replyToDiscussionNote: (e) =>
form = $(".js-new-note-form")
replyLink = $(e.target).closest(".js-discussion-reply-button")
replyLink.hide()
# insert the form after the button
form.clone().insertAfter replyLink
# show the form
@setupDiscussionNoteForm(replyLink, replyLink.next("form"))
###
Shows the diff or discussion form and does some setup on it.
Sets some hidden fields in the form.
Note: dataHolder must have the "discussionId", "lineCode", "noteableType"
and "noteableId" data attributes set.
###
setupDiscussionNoteForm: (dataHolder, form) =>
# setup note target
form.attr "rel", dataHolder.data("discussionId")
form.find("#note_commit_id").val dataHolder.data("commitId")
form.find("#note_line_code").val dataHolder.data("lineCode")
form.find("#note_noteable_type").val dataHolder.data("noteableType")
form.find("#note_noteable_id").val dataHolder.data("noteableId")
@setupNoteForm form
form.find(".js-note-text").focus()
form.addClass "js-discussion-note-form"
###
General note form setup.
deactivates the submit button when text is empty
hides the preview button when text is empty
setup GFM auto complete
show the form
###
setupNoteForm: (form) =>
disableButtonIfEmptyField form.find(".js-note-text"), form.find(".js-comment-button")
form.removeClass "js-new-note-form"
form.removeClass "js-new-note-form"
GitLab.GfmAutoComplete.setup()
# setup preview buttons
previewButton = form.find(".js-note-preview-button")
form.find(".js-note-text").on "input", ->
if $(this).val().trim() isnt ""
previewButton.removeClass("turn-off").addClass "turn-on"
else
previewButton.removeClass("turn-on").addClass "turn-off"
form.show()
###
Called when clicking on the "add a comment" button on the side of a diff line.
Inserts a temporary row for the form below the line.
Sets up the form and shows it.
###
addDiffNote: (e) =>
e.preventDefault()
link = e.target
form = $(".js-new-note-form")
row = $(link).closest("tr")
nextRow = row.next()
# does it already have notes?
if nextRow.is(".notes_holder")
replyButton = nextRow.find(".js-discussion-reply-button")
if replyButton.length > 0
$.proxy(@replyToDiscussionNote, replyButton).call()
else
# add a notes row and insert the form
row.after "<tr class=\"notes_holder js-temp-notes-holder\"><td class=\"notes_line\" colspan=\"2\"></td><td class=\"notes_content\"></td></tr>"
form.clone().appendTo row.next().find(".notes_content")
# show the form
@setupDiscussionNoteForm $(link), row.next().find("form")
###
Called in response to "cancel" on a diff note form.
Shows the reply button again.
Removes the form and if necessary it's temporary row.
###
removeDiscussionNoteForm: (form)->
row = form.closest("tr")
# show the reply button (will only work for replies)
form.prev(".js-discussion-reply-button").show()
if row.is(".js-temp-notes-holder")
# remove temporary row for diff lines
row.remove()
else
# only remove the form
form.remove()
cancelDiscussionForm: (e) =>
e.preventDefault()
form = $(".js-new-note-form")
form = $(e.target).closest(".js-discussion-note-form")
@removeDiscussionNoteForm(form)
updateVotes: ->
(new NotesVotes).updateVotes()
###
Called after an attachment file has been selected.
Updates the file name for the selected attachment.
###
updateFormAttachment: ->
form = $(this).closest("form")
# get only the basename
filename = $(this).val().replace(/^.*[\\\/]/, "")
form.find(".js-attachment-filename").text filename
###
Called when the tab visibility changes
###
visibilityChange: =>
@refresh()
@Notes = Notes

View File

@ -0,0 +1,22 @@
class NotesVotes
updateVotes: ->
votes = $("#votes .votes")
notes = $("#notes-list .note .vote")
# only update if there is a vote display
if votes.size()
upvotes = notes.filter(".upvote").size()
downvotes = notes.filter(".downvote").size()
votesCount = upvotes + downvotes
upvotesPercent = (if votesCount then (100.0 / votesCount * upvotes) else 0)
downvotesPercent = (if votesCount then (100.0 - upvotesPercent) else 0)
# change vote bar lengths
votes.find(".bar-success").css "width", upvotesPercent + "%"
votes.find(".bar-danger").css "width", downvotesPercent + "%"
# replace vote numbers
votes.find(".upvotes").text votes.find(".upvotes").text().replace(/\d+/, upvotes)
votes.find(".downvotes").text votes.find(".downvotes").text().replace(/\d+/, downvotes)
@NotesVotes = NotesVotes

View File

@ -0,0 +1,42 @@
@Pager =
init: (@limit = 0, preload, @disable = false) ->
@loading = $(".loading")
if preload
@offset = 0
@getOld()
else
@offset = @limit
@initLoadMore()
getOld: ->
@loading.show()
$.ajax
type: "GET"
url: location.href
data: "limit=" + @limit + "&offset=" + @offset
complete: =>
@loading.hide()
success: (data) ->
Pager.append(data.count, data.html)
dataType: "json"
append: (count, html) ->
$(".content_list").append html
if count > 0
@offset += count
else
@disable = true
initLoadMore: ->
$(document).unbind('scroll')
$(document).endlessScroll
bottomPixels: 400
fireDelay: 1000
fireOnce: true
ceaseFire: ->
Pager.disable
callback: (i) =>
unless @loading.is(':visible')
@loading.show()
Pager.getOld()

View File

@ -0,0 +1,30 @@
$ ->
$('.edit_user .application-theme input, .edit_user .code-preview-theme input').click ->
# Submit the form
$('.edit_user').submit()
new Flash("Appearance settings saved", "notice")
$('.update-username form').on 'ajax:before', ->
$('.loading-gif').show()
$(this).find('.update-success').hide()
$(this).find('.update-failed').hide()
$('.update-username form').on 'ajax:complete', ->
$(this).find('.btn-save').enableButton()
$(this).find('.loading-gif').hide()
$('.update-notifications').on 'ajax:complete', ->
$(this).find('.btn-save').enableButton()
$('.js-choose-user-avatar-button').bind "click", ->
form = $(this).closest("form")
form.find(".js-user-avatar-input").click()
$('.js-user-avatar-input').bind "change", ->
form = $(this).closest("form")
filename = $(this).val().replace(/^.*[\\\/]/, '')
form.find(".js-avatar-filename").text(filename)
$('.profile-groups-avatars').tooltip("placement": "top")

View File

@ -0,0 +1,53 @@
class Project
constructor: ->
$('.project-edit-container').on 'ajax:before', =>
$('.project-edit-container').hide()
$('.save-project-loader').show()
@initEvents()
initEvents: ->
disableButtonIfEmptyField '#project_name', '.project-submit'
$('#project_issues_enabled').change ->
if ($(this).is(':checked') == true)
$('#project_issues_tracker').removeAttr('disabled')
else
$('#project_issues_tracker').attr('disabled', 'disabled')
$('#project_issues_tracker').change()
$('#project_issues_tracker').change ->
if ($(this).val() == gon.default_issues_tracker || $(this).is(':disabled'))
$('#project_issues_tracker_id').attr('disabled', 'disabled')
else
$('#project_issues_tracker_id').removeAttr('disabled')
@Project = Project
$ ->
# Git clone panel switcher
scope = $ '.git-clone-holder'
if scope.length > 0
$('a, button', scope).click ->
$('a, button', scope).removeClass 'active'
$(@).addClass 'active'
$('#project_clone', scope).val $(@).data 'clone'
$(".clone").text("").append $(@).data 'clone'
# Ref switcher
$('.project-refs-select').on 'change', ->
$(@).parents('form').submit()
$('.hide-no-ssh-message').on 'click', (e) ->
path = '/'
$.cookie('hide_no_ssh_message', 'false', { path: path })
$(@).parents('.no-ssh-key-message').hide()
e.preventDefault()
$('.project-side .star').on 'ajax:success', (e, data, status, xhr) ->
$(@).toggleClass('on').find('.count').html(data.star_count)
.on 'ajax:error', (e, xhr, status, error) ->
new Flash('Star toggle failed. Try again later.', 'alert')

View File

@ -0,0 +1,7 @@
class ProjectImport
constructor: ->
setTimeout ->
Turbolinks.visit(location.href)
, 5000
@ProjectImport = ProjectImport

View File

@ -0,0 +1,59 @@
@projectUsersSelect =
init: ->
$('.ajax-project-users-select').each (i, select) ->
project_id = $(select).data('project-id') || $('body').data('project-id')
$(select).select2
placeholder: $(select).data('placeholder') || "Search for a user"
multiple: $(select).hasClass('multiselect')
minimumInputLength: 0
query: (query) ->
Api.projectUsers project_id, query.term, (users) ->
data = { results: users }
nullUser = {
name: 'Unassigned',
avatar: null,
username: 'none',
id: ''
}
data.results.unshift(nullUser)
query.callback(data)
initSelection: (element, callback) ->
id = $(element).val()
if id isnt ""
Api.user(id, callback)
formatResult: projectUsersSelect.projectUserFormatResult
formatSelection: projectUsersSelect.projectUserFormatSelection
dropdownCssClass: "ajax-project-users-dropdown"
dropdownAutoWidth: true
escapeMarkup: (m) -> # we do not want to escape markup since we are displaying html in results
m
projectUserFormatResult: (user) ->
if user.avatar_url
avatar = user.avatar_url
else
avatar = gon.default_avatar_url
if user.id == ''
avatarMarkup = ''
else
avatarMarkup = "<div class='user-image'><img class='avatar s24' src='#{avatar}'></div>"
"<div class='user-result'>
#{avatarMarkup}
<div class='user-name'>#{user.name}</div>
<div class='user-username'>#{user.username}</div>
</div>"
projectUserFormatSelection: (user) ->
user.name
$ ->
projectUsersSelect.init()

View File

@ -0,0 +1,13 @@
class SearchAutocomplete
constructor: (search_autocomplete_path, project_id, project_ref) ->
project_id = '' unless project_id
project_ref = '' unless project_ref
query = "?project_id=" + project_id + "&project_ref=" + project_ref
$("#search").autocomplete
source: search_autocomplete_path + query
minLength: 1
select: (event, ui) ->
location.href = ui.item.url
@SearchAutocomplete = SearchAutocomplete

View File

@ -0,0 +1,11 @@
class Shortcuts
constructor: ->
if $('#modal-shortcuts').length > 0
$('#modal-shortcuts').modal('show')
else
$.ajax(
url: '/help/shortcuts',
dataType: "script"
)
@Shortcuts = Shortcuts

View File

@ -0,0 +1,26 @@
responsive_resize = ->
current_width = $(window).width()
if current_width < 985
$('.responsive-side').addClass("ui right wide sidebar")
else
$('.responsive-side').removeClass("ui right wide sidebar")
$ ->
# Depending on window size, set the sidebar offscreen.
responsive_resize()
$('.sidebar-expand-button').click ->
$('.ui.sidebar')
.sidebar({overlay: true})
.sidebar('toggle')
# Hide sidebar on click outside of sidebar
$(document).mouseup (e) ->
container = $(".ui.sidebar")
container.sidebar "hide" if not container.is(e.target) and container.has(e.target).length is 0
return
# On resize, check if sidebar should be offscreen.
$(window).resize ->
responsive_resize()
return

View File

@ -0,0 +1,6 @@
class window.StatGraph
@log: {}
@get_log: ->
@log
@set_log: (data) ->
@log = data

View File

@ -0,0 +1,84 @@
class window.ContributorsStatGraph
init: (log) ->
@parsed_log = ContributorsStatGraphUtil.parse_log(log)
@set_current_field("commits")
total_commits = ContributorsStatGraphUtil.get_total_data(@parsed_log, @field)
author_commits = ContributorsStatGraphUtil.get_author_data(@parsed_log, @field)
@add_master_graph(total_commits)
@add_authors_graph(author_commits)
@change_date_header()
add_master_graph: (total_data) ->
@master_graph = new ContributorsMasterGraph(total_data)
@master_graph.draw()
add_authors_graph: (author_data) ->
@authors = []
limited_author_data = author_data.slice(0, 100)
_.each(limited_author_data, (d) =>
author_header = @create_author_header(d)
$(".contributors-list").append(author_header)
@authors[d.author_name] = author_graph = new ContributorsAuthorGraph(d.dates)
author_graph.draw()
)
format_author_commit_info: (author) ->
commits = $('<span/>', {
class: 'graph-author-commits-count'
})
commits.text(author.commits + " commits")
additions = $('<span/>', {
class: 'graph-additions'
})
additions.text(author.additions + " ++")
deletions = $('<span/>', {
class: 'graph-deletions'
})
deletions.text(author.deletions + " --")
$('<span/>').append(commits)
.append(" / ")
.append(additions)
.append(" / ")
.append(deletions)
create_author_header: (author) ->
list_item = $('<li/>', {
class: 'person'
style: 'display: block;'
})
author_name = $('<h4>' + author.author_name + '</h4>')
author_email = $('<p class="graph-author-email">' + author.author_email + '</p>')
author_commit_info_span = $('<span/>', {
class: 'commits'
})
author_commit_info = @format_author_commit_info(author)
author_commit_info_span.html(author_commit_info)
list_item.append(author_name)
list_item.append(author_email)
list_item.append(author_commit_info_span)
list_item
redraw_master: ->
total_data = ContributorsStatGraphUtil.get_total_data(@parsed_log, @field)
@master_graph.set_data(total_data)
@master_graph.redraw()
redraw_authors: ->
$("ol").html("")
x_domain = ContributorsGraph.prototype.x_domain
author_commits = ContributorsStatGraphUtil.get_author_data(@parsed_log, @field, x_domain)
_.each(author_commits, (d) =>
@redraw_author_commit_info(d)
$(@authors[d.author_name].list_item).appendTo("ol")
@authors[d.author_name].set_data(d.dates)
@authors[d.author_name].redraw()
)
set_current_field: (field) ->
@field = field
change_date_header: ->
x_domain = ContributorsGraph.prototype.x_domain
print_date_format = d3.time.format("%B %e %Y")
print = print_date_format(x_domain[0]) + " - " + print_date_format(x_domain[1])
$("#date_header").text(print)
redraw_author_commit_info: (author) ->
author_list_item = $(@authors[author.author_name].list_item)
author_commit_info = @format_author_commit_info(author)
author_list_item.find("span").html(author_commit_info)

View File

@ -0,0 +1,167 @@
class window.ContributorsGraph
MARGIN:
top: 20
right: 20
bottom: 30
left: 50
x_domain: null
y_domain: null
dates: []
@set_x_domain: (data) =>
@prototype.x_domain = data
@set_y_domain: (data) =>
@prototype.y_domain = [0, d3.max(data, (d) ->
d.commits = d.commits ? d.additions ? d.deletions
)]
@init_x_domain: (data) =>
@prototype.x_domain = d3.extent(data, (d) ->
d.date
)
@init_y_domain: (data) =>
@prototype.y_domain = [0, d3.max(data, (d) ->
d.commits = d.commits ? d.additions ? d.deletions
)]
@init_domain: (data) =>
@init_x_domain(data)
@init_y_domain(data)
@set_dates: (data) =>
@prototype.dates = data
set_x_domain: ->
@x.domain(@x_domain)
set_y_domain: ->
@y.domain(@y_domain)
set_domain: ->
@set_x_domain()
@set_y_domain()
create_scale: (width, height) ->
@x = d3.time.scale().range([0, width]).clamp(true)
@y = d3.scale.linear().range([height, 0]).nice()
draw_x_axis: ->
@svg.append("g").attr("class", "x axis").attr("transform", "translate(0, #{@height})")
.call(@x_axis)
draw_y_axis: ->
@svg.append("g").attr("class", "y axis").call(@y_axis)
set_data: (data) ->
@data = data
class window.ContributorsMasterGraph extends ContributorsGraph
constructor: (@data) ->
@width = $('.container').width() - 70
@height = 200
@x = null
@y = null
@x_axis = null
@y_axis = null
@area = null
@svg = null
@brush = null
@x_max_domain = null
process_dates: (data) ->
dates = @get_dates(data)
@parse_dates(data)
ContributorsGraph.set_dates(dates)
get_dates: (data) ->
_.pluck(data, 'date')
parse_dates: (data) ->
parseDate = d3.time.format("%Y-%m-%d").parse
data.forEach((d) ->
d.date = parseDate(d.date)
)
create_scale: ->
super @width, @height
create_axes: ->
@x_axis = d3.svg.axis().scale(@x).orient("bottom")
@y_axis = d3.svg.axis().scale(@y).orient("left").ticks(5)
create_svg: ->
@svg = d3.select("#contributors-master").append("svg")
.attr("width", @width + @MARGIN.left + @MARGIN.right)
.attr("height", @height + @MARGIN.top + @MARGIN.bottom)
.attr("class", "tint-box")
.append("g")
.attr("transform", "translate(" + @MARGIN.left + "," + @MARGIN.top + ")")
create_area: (x, y) ->
@area = d3.svg.area().x((d) ->
x(d.date)
).y0(@height).y1((d) ->
xa = d.commits = d.commits ? d.additions ? d.deletions
y(xa)
).interpolate("basis")
create_brush: ->
@brush = d3.svg.brush().x(@x).on("brushend", @update_content)
draw_path: (data) ->
@svg.append("path").datum(data).attr("class", "area").attr("d", @area)
add_brush: ->
@svg.append("g").attr("class", "selection").call(@brush).selectAll("rect").attr("height", @height)
update_content: =>
ContributorsGraph.set_x_domain(if @brush.empty() then @x_max_domain else @brush.extent())
$("#brush_change").trigger('change')
draw: ->
@process_dates(@data)
@create_scale()
@create_axes()
ContributorsGraph.init_domain(@data)
@x_max_domain = @x_domain
@set_domain()
@create_area(@x, @y)
@create_svg()
@create_brush()
@draw_path(@data)
@draw_x_axis()
@draw_y_axis()
@add_brush()
redraw: ->
@process_dates(@data)
ContributorsGraph.set_y_domain(@data)
@set_y_domain()
@svg.select("path").datum(@data)
@svg.select("path").attr("d", @area)
@svg.select(".y.axis").call(@y_axis)
class window.ContributorsAuthorGraph extends ContributorsGraph
constructor: (@data) ->
@width = $('.container').width()/2 - 100
@height = 200
@x = null
@y = null
@x_axis = null
@y_axis = null
@area = null
@svg = null
@list_item = null
create_scale: ->
super @width, @height
create_axes: ->
@x_axis = d3.svg.axis().scale(@x).orient("bottom").ticks(8)
@y_axis = d3.svg.axis().scale(@y).orient("left").ticks(5)
create_area: (x, y) ->
@area = d3.svg.area().x((d) ->
parseDate = d3.time.format("%Y-%m-%d").parse
x(parseDate(d))
).y0(@height).y1((d) =>
if @data[d]? then y(@data[d]) else y(0)
).interpolate("basis")
create_svg: ->
@list_item = d3.selectAll(".person")[0].pop()
@svg = d3.select(@list_item).append("svg")
.attr("width", @width + @MARGIN.left + @MARGIN.right)
.attr("height", @height + @MARGIN.top + @MARGIN.bottom)
.attr("class", "spark")
.append("g")
.attr("transform", "translate(" + @MARGIN.left + "," + @MARGIN.top + ")")
draw_path: (data) ->
@svg.append("path").datum(data).attr("class", "area-contributor").attr("d", @area)
draw: ->
@create_scale()
@create_axes()
@set_domain()
@create_area(@x, @y)
@create_svg()
@draw_path(@dates)
@draw_x_axis()
@draw_y_axis()
redraw: ->
@set_domain()
@svg.select("path").datum(@dates)
@svg.select("path").attr("d", @area)
@svg.select(".x.axis").call(@x_axis)
@svg.select(".y.axis").call(@y_axis)

View File

@ -0,0 +1,93 @@
window.ContributorsStatGraphUtil =
parse_log: (log) ->
total = {}
by_author = {}
for entry in log
@add_date(entry.date, total) unless total[entry.date]?
@add_author(entry, by_author) unless by_author[entry.author_name]?
@add_date(entry.date, by_author[entry.author_name]) unless by_author[entry.author_name][entry.date]
@store_data(entry, total[entry.date], by_author[entry.author_name][entry.date])
total = _.toArray(total)
by_author = _.toArray(by_author)
total: total, by_author: by_author
add_date: (date, collection) ->
collection[date] = {}
collection[date].date = date
add_author: (author, by_author) ->
by_author[author.author_name] = {}
by_author[author.author_name].author_name = author.author_name
by_author[author.author_name].author_email = author.author_email
store_data: (entry, total, by_author) ->
@store_commits(total, by_author)
@store_additions(entry, total, by_author)
@store_deletions(entry, total, by_author)
store_commits: (total, by_author) ->
@add(total, "commits", 1)
@add(by_author, "commits", 1)
add: (collection, field, value) ->
collection[field] ?= 0
collection[field] += value
store_additions: (entry, total, by_author) ->
entry.additions ?= 0
@add(total, "additions", entry.additions)
@add(by_author, "additions", entry.additions)
store_deletions: (entry, total, by_author) ->
entry.deletions ?= 0
@add(total, "deletions", entry.deletions)
@add(by_author, "deletions", entry.deletions)
get_total_data: (parsed_log, field) ->
log = parsed_log.total
total_data = @pick_field(log, field)
_.sortBy(total_data, (d) ->
d.date
)
pick_field: (log, field) ->
total_data = []
_.each(log, (d) ->
total_data.push(_.pick(d, [field, 'date']))
)
total_data
get_author_data: (parsed_log, field, date_range = null) ->
log = parsed_log.by_author
author_data = []
_.each(log, (log_entry) =>
parsed_log_entry = @parse_log_entry(log_entry, field, date_range)
if not _.isEmpty(parsed_log_entry.dates)
author_data.push(parsed_log_entry)
)
_.sortBy(author_data, (d) ->
d[field]
).reverse()
parse_log_entry: (log_entry, field, date_range) ->
parsed_entry = {}
parsed_entry.author_name = log_entry.author_name
parsed_entry.author_email = log_entry.author_email
parsed_entry.dates = {}
parsed_entry.commits = parsed_entry.additions = parsed_entry.deletions = 0
_.each(_.omit(log_entry, 'author_name', 'author_email'), (value, key) =>
if @in_range(value.date, date_range)
parsed_entry.dates[value.date] = value[field]
parsed_entry.commits += value.commits
parsed_entry.additions += value.additions
parsed_entry.deletions += value.deletions
)
return parsed_entry
in_range: (date, date_range) ->
if date_range is null || date_range[0] <= new Date(date) <= date_range[1]
true
else
false

View File

@ -0,0 +1,6 @@
class TeamMembers
constructor: ->
$('.team-members .project-access-select').on "change", ->
$(this.form).submit()
@TeamMembers = TeamMembers

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