Commit cfc4a2db authored by Franz-Robert van Vugt's avatar Franz-Robert van Vugt

Merge branch 'master' of https://github.com/gitlabhq/gitlabhq into patch-1

parents f03820eb fc5ac145
--color --drb
--color
......@@ -19,10 +19,10 @@ rvm:
services:
- redis-server
before_script:
- "bundle exec rake db:setup"
- "bundle exec rake db:seed_fu"
- "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
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)
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)
......@@ -9,6 +20,7 @@ v 6.7.0
- 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)
......@@ -24,6 +36,12 @@ v 6.7.0
- 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)
......
......@@ -24,9 +24,12 @@ Issues and merge requests should be in English and contain appropriate language
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 bugs or misbehavior in the latest [stable or development release of GitLab](MAINTENANCE.md). 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.
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.
Do not use the issue tracker for feature requests. We have a specific [feedback and suggestions forum](http://feedback.gitlab.com) for this purpose.
Do not use the issue tracker for feature requests. We have a specific [feature request forum](http://feedback.gitlab.com) for this purpose.
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.
......@@ -48,7 +51,7 @@ Please send a merge request with a tested solution or a merge request with a fai
## 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 feedback 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.
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 request guidelines
......@@ -64,7 +67,8 @@ If you can, please submit a merge request with the fix or improvements including
1. The MR title should describes 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. Link relevant [issues](https://gitlab.com/gitlab-org/gitlab-ce/issues) and/or [feedback items](http://feedback.gitlab.com/) from the merge request description and leave a comment on them with a link back to the MR
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 submittion
1. If your MR touches code that executes shell commands, make sure it adheres to the [shell command guidelines]( doc/development/shell_commands.md).
......@@ -74,6 +78,14 @@ Please keep the change in a single MR **as small as possible**. If you want to c
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). 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?
2. Are there points in the code the reviewer needs to double check?
3. Why was this MR needed?
4. What are the relevant issue numbers / [Feature requests](http://feedback.gitlab.com/)?
5. Screenshots (If appropiate)
## Contribution acceptance criteria
1. The change is as small as possible (see the above paragraph for details)
......
......@@ -132,6 +132,9 @@ gem "gitlab-flowdock-git-hook", "~> 0.4.2"
# Gemnasium integration
gem "gemnasium-gitlab-service", "~> 0.2"
# Slack integration
gem "slack-notifier", "~> 0.2.0"
# d3
gem "d3_rails", "~> 3.1.4"
......@@ -162,8 +165,9 @@ gem "modernizr", "2.6.2"
gem "raphael-rails", "~> 2.1.2"
gem 'bootstrap-sass', '~> 3.0'
gem "font-awesome-rails", '~> 3.2'
gem "gemoji", "~> 1.3.0"
gem "gitlab_emoji", "~> 0.0.1.1"
gem "gon", '~> 5.0.0'
gem 'nprogress-rails'
group :development do
gem "annotate", "~> 2.6.0.beta2"
......@@ -214,7 +218,6 @@ group :development, :test do
# PhantomJS driver for Capybara
gem 'poltergeist', '~> 1.4.1'
gem 'spork', '~> 1.0rc'
gem 'jasmine', '2.0.0.rc5'
gem "spring", '1.1.1'
......
......@@ -128,6 +128,8 @@ GEM
mail (~> 2.2)
email_validator (1.4.0)
activemodel
emoji (1.0.1)
json
enumerize (0.7.0)
activesupport (>= 3.2)
equalizer (0.0.8)
......@@ -165,7 +167,6 @@ GEM
formatador (0.2.4)
gemnasium-gitlab-service (0.2.1)
rugged (~> 0.19)
gemoji (1.3.1)
gherkin-ruby (0.3.1)
racc
github-markdown (0.5.5)
......@@ -190,6 +191,8 @@ GEM
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 (5.7.1)
activesupport (~> 4.0.0)
charlock_holmes (~> 0.6.9)
......@@ -297,6 +300,7 @@ GEM
net-ssh (>= 1.99.1)
net-ssh (2.7.0)
nokogiri (1.5.10)
nprogress-rails (0.1.2.3)
oauth (0.4.7)
oauth2 (0.8.1)
faraday (~> 0.8)
......@@ -468,6 +472,7 @@ GEM
rack-protection (~> 1.4)
tilt (~> 1.3, >= 1.3.4)
six (0.2.0)
slack-notifier (0.2.0)
slim (2.0.2)
temple (~> 0.6.6)
tilt (>= 1.3.3, < 2.1)
......@@ -479,7 +484,6 @@ GEM
capybara (>= 2.0.0)
railties (>= 3)
spinach (>= 0.4)
spork (1.0.0rc4)
spring (1.1.1)
spring-commands-rspec (1.0.1)
spring (>= 0.9.1)
......@@ -591,12 +595,12 @@ DEPENDENCIES
font-awesome-rails (~> 3.2)
foreman
gemnasium-gitlab-service (~> 0.2)
gemoji (~> 1.3.0)
github-markup (~> 0.7.4)!
gitlab-flowdock-git-hook (~> 0.4.2)
gitlab-gollum-lib (~> 1.1.0)
gitlab-grack (~> 2.0.0.pre)
gitlab-linguist (~> 3.0.0)
gitlab_emoji (~> 0.0.1.1)
gitlab_git (~> 5.7.1)
gitlab_meta (= 6.0)
gitlab_omniauth-ldap (= 1.0.4)
......@@ -620,6 +624,7 @@ DEPENDENCIES
minitest (~> 4.7.0)
modernizr (= 2.6.2)
mysql2
nprogress-rails
omniauth (~> 1.1.3)
omniauth-github
omniauth-google-oauth2
......@@ -652,9 +657,9 @@ DEPENDENCIES
simplecov
sinatra
six
slack-notifier (~> 0.2.0)
slim
spinach-rails
spork (~> 1.0rc)
spring (= 1.1.1)
spring-commands-rspec (= 1.0.1)
spring-commands-spinach (= 1.0.0)
......
......@@ -24,8 +24,6 @@ Below we describe the contributing process to GitLab for two reasons. So that co
- Monitors for new merge requests (at least once a week)
- Manages their work queue by looking at issues and merge requests assigned to them
- Close fixed issues (via commit messages or manually)
- Codes [new features](http://feedback.gitlab.com/forums/176466-general/filters/top)!
- Response guidelines
- Be kind to people trying to contribute. Be aware that people can be a non-native or a native English speaker, they might not understand thing or they might be very sensitive to how your 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).
## Priorities of the issue team
......@@ -73,7 +71,7 @@ Thanks for the issue report. Please reformat your issue to conform to the issue
### Feature requests
Thanks for your interest in GitLab. We don't use the issue tracker for feature requests. Please use 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.
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 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
......
web: bundle exec unicorn_rails -p $PORT -E development
web: bundle exec unicorn_rails -p $PORT -E development -c config/unicorn_development.rb
worker: bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,common,default,gitlab_shell
......@@ -71,7 +71,7 @@ Since 2011 GitLab is released on the 22nd of every month. Every new release incl
It is recommended to follow a monthly upgrade schedule. Security releases come out when needed. For more information about the release process see the documentation for [monthly](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/release/monthly.md) and [security](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/release/security.md) releases.
* Features that will be in the next releases are listed on [the feedback and suggestions 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).
* Features that will be in the next releases are listed 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).
### Run in production mode
......@@ -96,14 +96,9 @@ or start each component separately
### Run the tests
* Seed the database
bundle exec rake db:setup RAILS_ENV=test
bundle exec rake db:seed_fu RAILS_ENV=test
* Run all tests
bundle exec rake gitlab:test RAILS_ENV=test
bundle exec rake test
* [RSpec](http://rspec.info/) unit and functional tests
......@@ -134,22 +129,4 @@ or start each component separately
### Getting help
* [Maintenance policy](MAINTENANCE.md) specifies what versions are supported.
* [Troubleshooting guide](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Trouble-Shooting-Guide) contains solutions to common problems.
* [Mailing list](https://groups.google.com/forum/#!forum/gitlabhq) and [Stack Overflow](http://stackoverflow.com/questions/tagged/gitlab) are the best places to ask questions. For example you can use it if you have questions about: permission denied errors, invisible repos, can't clone/pull/push or with web hooks that don't fire. Please search for similar issues before posting your own, there's a good chance somebody else had the same issue you have now and has resolved it. 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 to a fix.
* [Feedback and suggestions forum](http://feedback.gitlab.com) is the place to propose and discuss new features for GitLab.
* [Contributing guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md) describes how to submit merge requests and issues. Pull requests and issues not in line with the guidelines in this document will be closed.
* [Support subscription](http://www.gitlab.com/subscription/) connects you to the knowledge of GitLab experts that will resolve your issues and answer your questions.
* [Consultancy](http://www.gitlab.com/consultancy/) from the GitLab experts for installations, upgrades and customizations.
* [#gitlab IRC channel](http://www.freenode.net/) on Freenode to get in touch with other GitLab users and get help, it's managed by James Newton (newton), Drew Blessing (dblessing), and Sam Gleske (sag47).
* [Book](http://www.packtpub.com/gitlab-repository-management/book) written by GitLab enthusiast Jonathan M. Hethey is unofficial but it offers a good overview.
* [Gitter chat room](https://gitter.im/gitlabhq/gitlabhq#) here you can ask questions when you need help.
Please see [Getting help for GitLab](https://www.gitlab.com/getting-help/) on our website for the many options to get help.
6.7.0.pre
6.8.0.pre
// 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 modernizr
//= require select2
//= require raphael
//= require g.raphael-min
//= require g.bar-min
//= require branch-graph
//= require highlightjs.min
//= require ace/ace
//= require_tree .
//= require d3
//= require underscore
# 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 modernizr
#= require select2
#= require raphael
#= require g.raphael-min
#= require g.bar-min
#= require branch-graph
#= require highlightjs.min
#= require ace/ace
#= require d3
#= require underscore
#= require nprogress
#= require nprogress-turbolinks
#= require_tree .
window.slugify = (text) ->
text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase()
......@@ -41,19 +75,11 @@ window.linkify = (str) ->
window.simpleFormat = (str) ->
linkify(sanitize(str).replace(/\n/g, '<br />'))
window.startSpinner = ->
$('.turbolink-spinner').fadeIn()
window.stopSpinner = ->
$('.turbolink-spinner').fadeOut()
window.unbindEvents = ->
$(document).unbind('scroll')
$(document).off('scroll')
document.addEventListener("page:fetch", startSpinner)
document.addEventListener("page:fetch", unbindEvents)
document.addEventListener("page:change", stopSpinner)
$ ->
# Click a .one_click_select field, select the contents
......
......@@ -6,7 +6,6 @@ GitLab.GfmAutoComplete =
dataSource: ''
# Emoji
Emoji:
assetBase: ''
template: '<li data-value="${insert}">${name} <img alt="${name}" height="20" src="${image}" width="20" /></li>'
# Team Members
......@@ -27,7 +26,7 @@ GitLab.GfmAutoComplete =
tpl: @Emoji.template
callbacks:
before_save: (emojis) =>
$.map emojis, (em) => name: em, insert: em+ ':', image: "#{@Emoji.assetBase}/#{em}.png"
$.map emojis, (em) => name: em.name, insert: em.name+ ':', image: em.path
# Team Members
input.atwho
......
......@@ -7,6 +7,8 @@
*= require select2
*= require highlightjs.min
*= require_self
*= require nprogress
*= require nprogress-bootstrap
*/
@import "main/variables.scss";
......
......@@ -355,3 +355,7 @@ table {
@media (max-width: $screen-xs-max) {
.container .content { margin-top: 20px; }
}
.wiki .highlight, .note-body .highlight {
margin-bottom: 9px;
}
......@@ -88,9 +88,6 @@ a:focus {
.wiki {
@include md-typography;
font-size: 14px;
line-height: 1.6;
/* Link to current header. */
h1, h2, h3, h4, h5, h6 {
position: relative;
......@@ -120,3 +117,11 @@ a:focus {
.md {
@include md-typography;
}
/**
* Textareas intended for GFM
*
*/
textarea.js-gfm-input {
font-family: $monospace_font;
}
......@@ -84,6 +84,9 @@
}
@mixin md-typography {
font-size: 14px;
line-height: 1.6;
img {
max-width: 100%;
}
......
/* Generic print styles */
header, nav, nav.main-nav, nav.navbar-collapse, nav.navbar-collapse.collapse {display: none!important;}
.profiler-results {display: none;}
/* Styles targeted specifically at printing files */
.tree-ref-holder, .tree-holder .breadcrumb, .blob-commit-info {display: none;}
.file-title {display: none;}
.file-holder {border: none;}
.wiki h1, .wiki h2, .wiki h3, .wiki h4, .wiki h5, .wiki h6 {margin-top: 17px; }
.wiki h1 {font-size: 30px;}
.wiki h2 {font-size: 22px;}
.wiki h3 {font-size: 18px; font-weight: bold; }
......@@ -61,7 +61,7 @@
}
.project-row, .group-row {
padding: 10px 12px !important;
padding: 8px 12px !important;
font-size: 14px;
line-height: 24px;
......
......@@ -62,6 +62,29 @@
font-size: 12px;
}
}
.text-file-parallel div {
display: inline-block;
padding-bottom: 16px;
}
.diff-side {
overflow-x: scroll;
width: 508px;
height: 700px;
}
.diff-side.diff-side-left{
overflow-y:hidden;
}
.diff-side table, td.diff-middle table {
height: 700px;
}
.diff-middle {
width: 114px;
vertical-align: top;
height: 700px;
overflow: hidden
}
.old_line, .new_line, .diff_line {
margin: 0px;
padding: 0px;
......@@ -125,8 +148,6 @@
}
&.parallel {
display: table-cell;
overflow: hidden;
width: 50%;
}
}
}
......
......@@ -42,7 +42,7 @@
}
}
padding: 14px 0px;
padding: 12px 0px;
border-bottom: 1px solid #eee;
.event-title {
color: #333;
......@@ -63,6 +63,10 @@
color: #666;
margin-top: 5px;
.md {
font-size: 13px;
}
pre {
border: none;
background: #f9f9f9;
......
......@@ -273,3 +273,9 @@ header {
}
}
}
@media (max-width: $screen-xs-max) {
#nprogress .spinner {
right: 35px !important;
}
}
......@@ -27,11 +27,15 @@ ul.notes {
.discussion-last-update,
.note-last-update {
font-style: italic;
&:before {
content: "\00b7";
}
font-size: 13px;
}
.author {
color: $style_color;
color: #555;
font-weight: bold;
font-size: 14px;
&:hover {
color: $primary_color;
}
......
class PasswordsController < Devise::PasswordsController
def create
email = resource_params[:email]
resource_found = resource_class.find_by_email(email)
if resource_found && resource_found.ldap_user?
flash[:alert] = "Cannot reset password for LDAP user."
respond_with({}, :location => after_sending_reset_password_instructions_path_for(resource_name)) and return
end
self.resource = resource_class.send_reset_password_instructions(resource_params)
if successfully_sent?(resource)
respond_with({}, :location => after_sending_reset_password_instructions_path_for(resource_name))
else
respond_with(resource)
end
end
end
......@@ -41,7 +41,7 @@ class Profiles::KeysController < ApplicationController
begin
user = User.find_by_username(params[:username])
if user.present?
render text: user.all_ssh_keys.join("\n")
render text: user.all_ssh_keys.join("\n"), content_type: "text/plain"
else
render_404 and return
end
......
......@@ -76,7 +76,7 @@ class Projects::IssuesController < Projects::ApplicationController
end
def update
@issue.update_attributes(params[:issue].merge(author_id_of_changes: current_user.id))
@issue.update_attributes(params[:issue])
@issue.reset_events_cache
respond_to do |format|
......
......@@ -109,7 +109,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
params[:merge_request].delete(:source_project_id)
params[:merge_request].delete(:target_project_id)
if @merge_request.update_attributes(params[:merge_request].merge(author_id_of_changes: current_user.id))
if @merge_request.update_attributes(params[:merge_request])
@merge_request.reset_events_cache
respond_to do |format|
......
......@@ -38,7 +38,6 @@ class Projects::MilestonesController < Projects::ApplicationController
def create
@milestone = @project.milestones.new(params[:milestone])
@milestone.author_id_of_changes = current_user.id
if @milestone.save
redirect_to project_milestone_path(@project, @milestone)
......@@ -48,7 +47,7 @@ class Projects::MilestonesController < Projects::ApplicationController
end
def update
@milestone.update_attributes(params[:milestone].merge(author_id_of_changes: current_user.id))
@milestone.update_attributes(params[:milestone])
respond_to do |format|
format.js
......
......@@ -123,11 +123,20 @@ class ProjectsController < ApplicationController
end
def autocomplete_sources
note_type = params['type']
note_id = params['type_id']
participating = if note_type && note_id
participants_in(note_type, note_id)
else
[]
end
team_members = sorted(@project.team.members)
participants = team_members + participating
@suggestions = {
emojis: Emoji.names,
emojis: Emoji.names.map { |e| { name: e, path: view_context.image_url("emoji/#{e}.png") } },
issues: @project.issues.select([:iid, :title, :description]),
mergerequests: @project.merge_requests.select([:iid, :title, :description]),
members: @project.team.members.sort_by(&:username).map { |user| { username: user.username, name: user.name } }
members: participants.uniq
}
respond_to do |format|
......@@ -162,4 +171,25 @@ class ProjectsController < ApplicationController
def user_layout
current_user ? "projects" : "public_projects"
end
def participants_in(type, id)
users = case type
when "Issue"
issue = @project.issues.find_by_iid(id)
issue ? issue.participants : []
when "MergeRequest"
merge_request = @project.merge_requests.find_by_iid(id)
merge_request ? merge_request.participants : []
when "Commit"
author_ids = Note.for_commit_id(id).pluck(:author_id).uniq
User.where(id: author_ids)
else
[]
end
sorted(users)
end
def sorted(users)
users.uniq.sort_by(&:username).map { |user| { username: user.username, name: user.name } }
end
end
......@@ -47,9 +47,9 @@ class BaseFinder
[]
end
elsif current_user && params[:authorized_only].presence
klass.of_projects(current_user.authorized_projects)
klass.of_projects(current_user.authorized_projects).references(:project)
else
klass.of_projects(Project.accessible_to(current_user))
klass.of_projects(Project.accessible_to(current_user)).references(:project)
end
end
......
......@@ -105,8 +105,80 @@ module CommitsHelper
branches.sort.map { |branch| link_to(branch, project_tree_path(project, branch)) }.join(", ").html_safe
end
def get_old_file(project, commit, diff)
project.repository.blob_at(commit.parent_id, diff.old_path) if commit.parent_id
def parallel_diff_lines(project, commit, diff, file)
old_file = project.repository.blob_at(commit.parent_id, diff.old_path) if commit.parent_id
deleted_lines = {}
added_lines = {}
each_diff_line(diff, 0) do |line, type, line_code, line_new, line_old|
if type == "old"
deleted_lines[line_old] = { line_code: line_code, type: type, line: line }
elsif type == "new"
added_lines[line_new] = { line_code: line_code, type: type, line: line }
end
end
max_length = old_file ? old_file.sloc + added_lines.length : file.sloc
offset1 = 0
offset2 = 0
old_lines = []
new_lines = []
max_length.times do |line_index|
line_index1 = line_index - offset1
line_index2 = line_index - offset2
deleted_line = deleted_lines[line_index1 + 1]
added_line = added_lines[line_index2 + 1]
old_line = old_file.lines[line_index1] if old_file
new_line = file.lines[line_index2]
if deleted_line && added_line
elsif deleted_line
new_line = nil
offset2 += 1
elsif added_line
old_line = nil
offset1 += 1
end
old_lines[line_index] = DiffLine.new
new_lines[line_index] = DiffLine.new
# old
if line_index == 0 && diff.new_file
old_lines[line_index].type = :file_created
old_lines[line_index].content = 'File was created'
elsif deleted_line
old_lines[line_index].type = :deleted
old_lines[line_index].content = old_line
old_lines[line_index].num = line_index1 + 1
old_lines[line_index].code = deleted_line[:line_code]
elsif old_line
old_lines[line_index].type = :no_change
old_lines[line_index].content = old_line
old_lines[line_index].num = line_index1 + 1
else
old_lines[line_index].type = :added
end
# new
if line_index == 0 && diff.deleted_file
new_lines[line_index].type = :file_deleted
new_lines[line_index].content = "File was deleted"
elsif added_line
new_lines[line_index].type = :added
new_lines[line_index].num = line_index2 + 1
new_lines[line_index].content = new_line
new_lines[line_index].code = added_line[:line_code]
elsif new_line
new_lines[line_index].type = :no_change
new_lines[line_index].num = line_index2 + 1
new_lines[line_index].content = new_line
else
new_lines[line_index].type = :deleted
end
end
return old_lines, new_lines
end
protected
......
......@@ -20,7 +20,7 @@ module MergeRequestsHelper
target_project_id: target_project.id,
source_branch: event.branch_name,
target_branch: target_project.repository.root_ref,
title: event.branch_name.titleize
title: event.branch_name.humanize
}
end
......
......@@ -40,7 +40,7 @@ module TreeHelper
# Returns boolean
def markup?(filename)
filename.downcase.end_with?(*%w(.textile .rdoc .org .creole
.mediawiki .rst .asciidoc .pod))
.mediawiki .rst .adoc .asciidoc .pod))
end
def gitlab_markdown?(filename)
......
......@@ -29,11 +29,11 @@ module Emails
subject: subject("#{@merge_request.title} (!#{@merge_request.iid})"))
end
def merged_merge_request_email(recipient_id, merge_request_id)
def merged_merge_request_email(recipient_id, merge_request_id, updated_by_user_id)
@merge_request = MergeRequest.find(merge_request_id)
@project = @merge_request.project
@target_url = project_merge_request_url(@project, @merge_request)
mail(from: sender(@merge_request.author_id_of_changes),
mail(from: sender(updated_by_user_id),
to: recipient(recipient_id),
subject: subject("#{@merge_request.title} (!#{@merge_request.iid})"))
end
......
......@@ -37,8 +37,6 @@ module Issuable
allow_nil: true,
prefix: true
attr_accessor :author_id_of_changes
attr_mentionable :title, :description
end
......
class DiffLine
attr_accessor :type, :content, :num, :code
end
......@@ -47,14 +47,6 @@ class Event < ActiveRecord::Base
scope :in_projects, ->(project_ids) { where(project_id: project_ids).recent }
class << self
def determine_action(record)
if [Issue, MergeRequest].include? record.class
Event::CREATED
elsif record.kind_of? Note
Event::COMMENTED
end
end
def create_ref_event(project, user, ref, action = 'add', prefix = 'refs/heads')
commit = project.repository.commit(ref.target)
......
......@@ -30,8 +30,7 @@ class Issue < ActiveRecord::Base
scope :of_user_team, ->(team) { where(project_id: team.project_ids, assignee_id: team.member_ids) }
attr_accessible :title, :assignee_id, :position, :description,
:milestone_id, :label_list, :author_id_of_changes,
:state_event
:milestone_id, :label_list, :state_event
acts_as_taggable_on :labels
......
......@@ -38,7 +38,7 @@ class MergeRequest < ActiveRecord::Base
delegate :commits, :diffs, :last_commit, :last_commit_short_sha, to: :merge_request_diff, prefix: nil
attr_accessible :title, :assignee_id, :source_project_id, :source_branch, :target_project_id, :target_branch, :milestone_id, :author_id_of_changes, :state_event, :description
attr_accessible :title, :assignee_id, :source_project_id, :source_branch, :target_project_id, :target_branch, :milestone_id, :state_event, :description
attr_accessor :should_remove_source_branch
......
......@@ -16,8 +16,7 @@
class Milestone < ActiveRecord::Base
include InternalId
attr_accessible :title, :description, :due_date, :state_event, :author_id_of_changes
attr_accessor :author_id_of_changes
attr_accessible :title, :description, :due_date, :state_event
belongs_to :project
has_many :issues
......@@ -89,6 +88,6 @@ class Milestone < ActiveRecord::Base
end
def author_id
author_id_of_changes
nil
end
end
......@@ -199,7 +199,8 @@ class Note < ActiveRecord::Base
def downvote?
votable? && (note.start_with?('-1') ||
note.start_with?(':-1:') ||
note.start_with?(':thumbsdown:')
note.start_with?(':thumbsdown:') ||
note.start_with?(':thumbs_down_sign:')
)
end
......@@ -249,7 +250,8 @@ class Note < ActiveRecord::Base
def upvote?
votable? && (note.start_with?('+1') ||
note.start_with?(':+1:') ||
note.start_with?(':thumbsup:')
note.start_with?(':thumbsup:') ||
note.start_with?(':thumbs_up_sign:')
)
end
......
......@@ -56,6 +56,7 @@ class Project < ActiveRecord::Base
has_one :flowdock_service, dependent: :destroy
has_one :assembla_service, dependent: :destroy
has_one :gemnasium_service, dependent: :destroy
has_one :slack_service, dependent: :destroy
has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id"
has_one :forked_from_project, through: :forked_project_link
# Merge Requests for target project should be removed with it
......@@ -304,7 +305,7 @@ class Project < ActiveRecord::Base
end
def available_services_names
%w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla emails_on_push gemnasium)
%w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla emails_on_push gemnasium slack)
end
def gitlab_ci?
......
require 'slack-notifier'
class SlackMessage
def initialize(params)
@after = params.fetch(:after)
@before = params.fetch(:before)
@commits = params.fetch(:commits, [])
@project_name = params.fetch(:project_name)
@project_url = params.fetch(:project_url)
@ref = params.fetch(:ref).gsub('refs/heads/', '')
@username = params.fetch(:user_name)
end
def compose
format(message)
end
private
attr_reader :after
attr_reader :before
attr_reader :commits
attr_reader :project_name
attr_reader :project_url
attr_reader :ref
attr_reader :username
def message
if new_branch?
new_branch_message
elsif removed_branch?
removed_branch_message
else
push_message << commit_messages
end
end
def format(string)
Slack::Notifier::LinkFormatter.format(string)
end
def new_branch_message
"#{username} pushed new branch #{branch_link} to #{project_link}"
end
def removed_branch_message
"#{username} removed branch #{ref} from #{project_link}"
end
def push_message
"#{username} pushed to branch #{branch_link} of #{project_link} (#{compare_link})"
end
def commit_messages
commits.each_with_object('') do |commit, str|
str << compose_commit_message(commit)
end
end
def compose_commit_message(commit)
id = commit.fetch(:id)[0..5]
message = commit.fetch(:message)
url = commit.fetch(:url)
"\n - #{message} ([#{id}](#{url}))"
end
def new_branch?
before =~ /000000/
end
def removed_branch?
after =~ /000000/
end
def branch_url
"#{project_url}/commits/#{ref}"
end
def compare_url
"#{project_url}/compare/#{before}...#{after}"
end
def branch_link
"[#{ref}](#{branch_url})"
end
def project_link
"[#{project_name}](#{project_url})"
end
def compare_link
"[Compare changes](#{compare_url})"
end
end
# == Schema Information
#
# Table name: services
#
# id :integer not null, primary key
# type :string(255)
# title :string(255)
# token :string(255)
# project_id :integer not null
# created_at :datetime not null
# updated_at :datetime not null
# active :boolean default(FALSE), not null
# project_url :string(255)
# subdomain :string(255)
# room :string(255)
# api_key :string(255)
#
class SlackService < Service
attr_accessible :room
attr_accessible :subdomain
validates :room, presence: true, if: :activated?
validates :subdomain, presence: true, if: :activated?
validates :token, presence: true, if: :activated?
def title
'Slack'
end
def description
'A team communication tool for the 21st century'
end
def to_param
'slack'
end
def fields
[
{ type: 'text', name: 'subdomain', placeholder: '' },
{ type: 'text', name: 'token', placeholder: '' },
{ type: 'text', name: 'room', placeholder: 'Ex. #general' },
]
end
def execute(push_data)
message = SlackMessage.new(push_data.merge(
project_url: project_url,
project_name: project_name
))
notifier = Slack::Notifier.new(subdomain, token)
notifier.channel = room
notifier.username = 'GitLab'
notifier.ping(message.compose)
end
private
def project_name
project.name_with_namespace.gsub(/\s/, '')
end
def project_url
project.web_url
end
end
......@@ -204,7 +204,7 @@ class User < ActiveRecord::Base
end
def search query
where("name LIKE :query OR email LIKE :query OR username LIKE :query", query: "%#{query}%")
where("lower(name) LIKE :query OR lower(email) LIKE :query OR lower(username) LIKE :query", query: "%#{query.downcase}%")
end
def by_username_or_id(name_or_id)
......@@ -249,7 +249,7 @@ class User < ActiveRecord::Base
def namespace_uniq
namespace_name = self.username
if Namespace.find_by(path: namespace_name)
self.errors.add :username, "already exist"
self.errors.add :username, "already exists"
end
end
......
class ActivityObserver < BaseObserver
observe :issue, :note, :milestone
def after_create(record)
event_author_id = record.author_id
if record.kind_of?(Note)
# Skip system notes, like status changes and cross-references.
return true if record.system?
# Skip wall notes to prevent spamming of dashboard
return true if record.noteable_type.blank?
end
if event_author_id
create_event(record, Event.determine_action(record))
end
end
def after_close(record, transition)
create_event(record, Event::CLOSED)
end
def after_reopen(record, transition)
create_event(record, Event::REOPENED)
end
protected
def create_event(record, status)
Event.create(
project: record.project,
target_id: record.id,
target_type: record.class.name,
action: status,
author_id: current_user.id
)
end
end
......@@ -3,6 +3,10 @@ class BaseObserver < ActiveRecord::Observer
NotificationService.new
end
def event_service
EventCreateService.new
end
def log_info message
Gitlab::AppLogger.info message
end
......
class IssueObserver < BaseObserver
def after_create(issue)
notification.new_issue(issue, current_user)
event_service.open_issue(issue, current_user)
issue.create_cross_references!(issue.project, current_user)
execute_hooks(issue)
end
def after_close(issue, transition)
notification.close_issue(issue, current_user)
event_service.close_issue(issue, current_user)
create_note(issue)
execute_hooks(issue)
end
def after_reopen(issue, transition)
event_service.reopen_issue(issue, current_user)
create_note(issue)
execute_hooks(issue)
end
......
class MergeRequestObserver < ActivityObserver
observe :merge_request
class MergeRequestObserver < BaseObserver
def after_create(merge_request)
if merge_request.author_id
create_event(merge_request, Event.determine_action(merge_request))
end
event_service.open_mr(merge_request, current_user)
notification.new_merge_request(merge_request, current_user)
merge_request.create_cross_references!(merge_request.project, current_user)
execute_hooks(merge_request)
end
def after_close(merge_request, transition)
create_event(merge_request, Event::CLOSED)
event_service.close_mr(merge_request, current_user)
notification.close_mr(merge_request, current_user)
create_note(merge_request)
execute_hooks(merge_request)
end
def after_reopen(merge_request, transition)
create_event(merge_request, Event::REOPENED)
event_service.reopen_mr(merge_request, current_user)
create_note(merge_request)
execute_hooks(merge_request)
merge_request.reload_code
......@@ -33,16 +28,6 @@ class MergeRequestObserver < ActivityObserver
execute_hooks(merge_request)
end
def create_event(record, status)
Event.create(
project: record.target_project,
target_id: record.id,
target_type: record.class.name,
action: status,
author_id: current_user.id
)
end
private
# Create merge request note with service comment like 'Status changed to closed'
......
class MilestoneObserver < BaseObserver
def after_create(milestone)
event_service.open_milestone(milestone, current_user)
end
def after_close(milestone, transition)
event_service.close_milestone(milestone, current_user)
end
def after_reopen(milestone, transition)
event_service.reopen_milestone(milestone, current_user)
end
end
......@@ -2,6 +2,12 @@ class NoteObserver < BaseObserver
def after_create(note)
notification.new_note(note)
# Skip system notes, like status changes and cross-references.
# Skip wall notes to prevent spamming of dashboard
if note.noteable_type.present? && !note.system
event_service.leave_note(note, current_user)
end
unless note.system?
# Create a cross-reference note if this Note contains GFM that names an
# issue, merge request, or commit.
......
# EventCreateService class
#
# Used for creating events feed on dashboard after certain user action
#
# Ex.
# EventCreateService.new.new_issue(issue, current_user)
#
class EventCreateService
def open_issue(issue, current_user)
create_event(issue, current_user, Event::CREATED)
end
def close_issue(issue, current_user)
create_event(issue, current_user, Event::CLOSED)
end
def reopen_issue(issue, current_user)
create_event(issue, current_user, Event::REOPENED)
end
def open_mr(merge_request, current_user)
create_event(merge_request, current_user, Event::CREATED)
end
def close_mr(merge_request, current_user)
create_event(merge_request, current_user, Event::CLOSED)
end
def reopen_mr(merge_request, current_user)
create_event(merge_request, current_user, Event::REOPENED)
end
def merge_mr(merge_request, current_user)
create_event(merge_request, current_user, Event::MERGED)
end
def open_milestone(milestone, current_user)
create_event(milestone, current_user, Event::CREATED)
end
def close_milestone(milestone, current_user)
create_event(milestone, current_user, Event::CLOSED)
end
def reopen_milestone(milestone, current_user)
create_event(milestone, current_user, Event::REOPENED)
end
def leave_note(note, current_user)
create_event(note, current_user, Event::COMMENTED)
end
private
def create_event(record, current_user, status)
Event.create(
project: record.project,
target_id: record.id,
target_type: record.class.name,
action: status,
author_id: current_user.id
)
end
end
......@@ -9,11 +9,10 @@ module MergeRequests
merge_request.lock
if Gitlab::Satellite::MergeAction.new(current_user, merge_request).merge!(commit_message)
merge_request.author_id_of_changes = current_user.id
merge_request.merge
notification.merge_mr(merge_request)
create_merge_event(merge_request)
notification.merge_mr(merge_request, current_user)
create_merge_event(merge_request, current_user)
execute_project_hooks(merge_request)
true
......
......@@ -7,14 +7,8 @@ module MergeRequests
NotificationService.new
end
def create_merge_event(merge_request)
Event.create(
project: merge_request.target_project,
target_id: merge_request.id,
target_type: merge_request.class.name,
action: Event::MERGED,
author_id: merge_request.author_id_of_changes
)
def create_merge_event(merge_request, current_user)
EventCreateService.new.merge_mr(merge_request, current_user)
end
def execute_project_hooks(merge_request)
......
......@@ -7,11 +7,10 @@ module MergeRequests
# to target branch
class MergeService < BaseMergeService
def execute(merge_request, current_user, commit_message)
merge_request.author_id_of_changes = current_user.id
merge_request.merge
notification.merge_mr(merge_request)
create_merge_event(merge_request)
notification.merge_mr(merge_request, current_user)
create_merge_event(merge_request, current_user)
execute_project_hooks(merge_request)
true
......
......@@ -86,12 +86,12 @@ class NotificationService
# * merge_request assignee if their notification level is not Disabled
# * project team members with notification level higher then Participating
#
def merge_mr(merge_request)
def merge_mr(merge_request, current_user)
recipients = reject_muted_users([merge_request.author, merge_request.assignee], merge_request.target_project)
recipients = recipients.concat(project_watchers(merge_request.target_project)).uniq
recipients.each do |recipient|
mailer.merged_merge_request_email(recipient.id, merge_request.id)
mailer.merged_merge_request_email(recipient.id, merge_request.id, current_user.id)
end
end
......@@ -111,6 +111,7 @@ class NotificationService
# ignore gitlab service messages
return true if note.note =~ /\A_Status changed to closed_/
return true if note.note =~ /\A_mentioned in / && note.system == true
opts = { noteable_type: note.noteable_type, project_id: note.project_id }
......
......@@ -2,10 +2,12 @@
- if providers.present?
%hr
%div{:'data-no-turbolink' => 'data-no-turbolink'}
%span Sign in with: &nbsp;
%span Sign in with*: &nbsp;
- providers.each do |provider|
%span
- if default_providers.include?(provider)
= link_to authbutton(provider, 32), omniauth_authorize_path(resource_name, provider)
- else
= link_to provider.to_s.titleize, omniauth_authorize_path(resource_name, provider), class: "btn"
%br
%small * Make sure your email address is public
......@@ -4,7 +4,8 @@
= "#{title} | " if defined?(title)
GitLab
= favicon_link_tag 'favicon.ico'
= stylesheet_link_tag "application"
= stylesheet_link_tag "application", :media => "all"
= stylesheet_link_tag "print", :media => "print"
= javascript_include_tag "application"
= csrf_meta_tags
= include_gon
......
......@@ -14,10 +14,6 @@
.navbar-collapse.collapse
%ul.nav.navbar-nav
%li.hidden-sm.hidden-xs
%a
%div.hide.turbolink-spinner
%i.icon-refresh.icon-spin
%li.hidden-sm.hidden-xs
= render "layouts/search"
%li.visible-sm.visible-xs
......
:javascript
GitLab.GfmAutoComplete.dataSource = "#{autocomplete_sources_project_path(@project)}"
GitLab.GfmAutoComplete.Emoji.assetBase = "#{Gitlab.config.gitlab.relative_url_root + '/assets/emoji'}"
GitLab.GfmAutoComplete.dataSource = "#{autocomplete_sources_project_path(@project, type: @noteable.class, type_id: params[:id])}"
GitLab.GfmAutoComplete.setup();
......@@ -8,11 +8,15 @@
%span.separator
%h1.title= title
.pull-right
%button.navbar-toggle{"data-target" => ".navbar-collapse", "data-toggle" => "collapse", type: "button"}
%span.sr-only Toggle navigation
%i.icon-reorder
.pull-right.hidden-xs
= link_to "Sign in", new_session_path(:user), class: 'btn btn-sign-in btn-new'
%ul.nav.navbar-nav
%li
%a
%div.hide.turbolink-spinner
%i.icon-refresh.icon-spin
.navbar-collapse.collapse
%ul.nav.navbar-nav
%li.visible-xs
= link_to "Sign in", new_session_path(:user)
......@@ -4,7 +4,7 @@
%body{class: "#{app_theme} application", :'data-page' => body_data_page}
= render "layouts/broadcast"
= render "layouts/public_head_panel", title: project_title(@project)
%nav.main-nav
%nav.main-nav.navbar-collapse.collapse
.container= render 'layouts/nav/project'
.container
.content= yield
......@@ -7,7 +7,7 @@
%li
#{commit.short_id} - #{commit.title}
%h4 Diff:
%h4 Changes:
- @diffs.each do |diff|
%li
%strong
......@@ -23,6 +23,6 @@
%br
- if @compare.timeout
%h5 Huge diff. To prevent performance issues it was hidden
%h5 To prevent performance issues changes are hidden
- elsif @compare.commits_over_limit?
%h5 Diff for big amount of commits is disabled
%h5 Changes are not shown due to large amount of commits
......@@ -6,7 +6,7 @@ Commits:
#{commit.short_id} - #{truncate(commit.title, length: 40)}
\
\
Diff:
Changes:
- @diffs.each do |diff|
\
\=====================================
......@@ -22,4 +22,4 @@ Diff:
- if @compare.timeout
Huge diff. To prevent performance issues it was hidden
- elsif @compare.commits_over_limit?
Diff for big amount of commits is disabled
Changes are not shown due to large amount of commits
/ Side-by-side diff view
- old_file = get_old_file(project, @commit, diff)
- deleted_lines = {}
- added_lines = {}
- each_diff_line(diff, index) do |line, type, line_code, line_new, line_old, raw_line|
- if type == "old"
- deleted_lines[line_old] = { line_code: line_code, type: type, line: line }
- elsif type == "new"
- added_lines[line_new] = { line_code: line_code, type: type, line: line }
- max_length = old_file.sloc + added_lines.length if old_file
- max_length ||= file.sloc
- offset1 = 0
- offset2 = 0
- old_lines, new_lines = parallel_diff_lines(project, @commit, diff, file)
- num_lines = old_lines.length
%div.text-file-parallel
%table{ style: "table-layout: fixed;" }
- max_length.times do |line_index|
- line_index1 = line_index - offset1
- line_index2 = line_index - offset2
- deleted_line = deleted_lines[line_index1 + 1]
- added_line = added_lines[line_index2 + 1]
- old_line = old_file.lines[line_index1] if old_file
- new_line = file.lines[line_index2]
%div.diff-side.diff-side-left
%table
- old_lines.each do |line|
%tr.line_holder.parallel
- if line.type == :file_created
%td.line_content.parallel= "File was created"
- elsif line.type == :deleted
%td.line_content{class: "parallel noteable_line old #{line.code}", "line_code" => line.code }= line.content
- else line.type == :no_change
%td.line_content.parallel= line.content
%div.diff-middle
%table
- num_lines.times do |index|
%tr
- if old_lines[index].type == :deleted
%td.old_line.old= old_lines[index].num
- else
%td.old_line= old_lines[index].num
%td.diff_line=""
- if deleted_line && added_line
- elsif deleted_line
- new_line = nil
- offset2 += 1
- elsif added_line
- old_line = nil
- offset1 += 1
- if new_lines[index].type == :added
%td.new_line.new= new_lines[index].num
- else
%td.new_line= new_lines[index].num
%tr.line_holder.parallel
- if line_index == 0 && diff.new_file
%td.line_content.parallel= "File was created"
%td.old_line= ""
- elsif deleted_line
%td.line_content{class: "parallel noteable_line old #{deleted_line[:line_code]}", "line_code" => deleted_line[:line_code] }= old_line
%td.old_line.old
= line_index1 + 1
- if @comments_allowed
=# render "projects/notes/diff_note_link", line_code: deleted_line[:line_code]
- elsif old_line
%td.line_content.parallel= old_line
%td.old_line= line_index1 + 1
- else
%td.line_content.parallel= ""
%td.old_line= ""
%div.diff-side.diff-side-right
%table
- new_lines.each do |line|
%td.diff_line= ""
%tr.line_holder.parallel
- if line.type == :file_deleted
%td.line_content.parallel= "File was deleted"
- elsif line.type == :added
%td.line_content{class: "parallel noteable_line new #{line.code}", "line_code" => line.code }= line.content
- else line.type == :no_change
%td.line_content.parallel= line.content
- if diff.deleted_file && line_index == 0
%td.new_line= ""
%td.line_content.parallel= "File was deleted"
- elsif added_line
%td.new_line.new
= line_index2 + 1
- if @comments_allowed
=# render "projects/notes/diff_note_link", line_code: added_line[:line_code]
%td.line_content{class: "parallel noteable_line new #{added_line[:line_code]}", "line_code" => added_line[:line_code] }= new_line
- elsif new_line
%td.new_line= line_index2 + 1
%td.line_content.parallel= new_line
- else
%td.new_line= ""
%td.line_content.parallel= ""
:javascript
$('.diff-side-right').on('scroll', function(){
$('.diff-side-left, .diff-middle').scrollTop($(this).scrollTop());
$('.diff-side-left').scrollLeft($(this).scrollLeft());
});
- if @reply_allowed
- comments1 = []
- comments2 = []
- comments1 = @line_notes.select { |n| n.line_code == deleted_line[:line_code] }.sort_by(&:created_at) if deleted_line
- comments2 = @line_notes.select { |n| n.line_code == added_line[:line_code] }.sort_by(&:created_at) if added_line
- unless comments1.empty? && comments2.empty?
= render "projects/notes/diff_notes_with_reply_parallel", notes1: comments1, notes2: comments2, line1: deleted_line, line2: added_line
\ No newline at end of file
$('.diff-side-left').on('scroll', function(){
$('.diff-side-right, .diff-middle').scrollTop($(this).scrollTop()); // might never be relevant
$('.diff-side-right').scrollLeft($(this).scrollLeft());
});
- too_big = diff.diff.lines.count > 1000
- if too_big
%a.supp_diff_link Diff suppressed. Click to show
%a.supp_diff_link Changes suppressed. Click to show
%table.text-file{class: "#{'hide' if too_big}"}
- each_diff_line(diff, index) do |line, type, line_code, line_new, line_old, raw_line|
......
......@@ -18,17 +18,17 @@
- else
%ul.well-list= render Commit.decorate(@commits), project: @project
%h4 Diff
%h4 Changes
- if @diffs.present?
= render "projects/commits/diffs", diffs: @diffs, project: @project
- elsif @commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE
.bs-callout.bs-callout-danger
%h4 This comparison includes more than #{MergeRequestDiff::COMMITS_SAFE_SIZE} commits.
%p To preserve performance the line diff is not shown.
%p To preserve performance the line changes are not shown.
- elsif @timeout
.bs-callout.bs-callout-danger
%h4 Diff for this comparison is extremely large.
%p Use command line to browse diff for this comparison.
%h4 Number of changed files for this comparison is extremely large.
%p Use command line to browse through changes for this comparison.
- else
......
......@@ -2,7 +2,7 @@
Deploy key:
= @key.title
%small
created at
created on
= @key.created_at.stamp("Aug 21, 2011")
.back-link
= link_to project_deploy_keys_path(@project) do
......
%div.issue-form-holder
%h3.page-title= @issue.new_record? ? "New Issue" : "Edit Issue ##{@issue.iid}"
%hr
- if @repository.contribution_guide && !@issue.persisted?
- if !@repository.empty? && @repository.contribution_guide && !@issue.persisted?
- contribution_guide_url = project_blob_path(@project, tree_join(@repository.root_ref, @repository.contribution_guide.name))
.alert.alert-info.col-sm-10.col-sm-offset-2
="Please review the <strong>#{link_to "guidelines for contribution", contribution_guide_url}</strong> to this repository.".html_safe
......
- if @repository.contribution_guide && !@merge_request.persisted?
- contribution_guide_url = project_blob_path(@project, tree_join(@repository.root_ref, @repository.contribution_guide.name))
.alert.alert-info.col-sm-10.col-sm-offset-2
="Please review the <strong>#{link_to "guidelines for contribution", contribution_guide_url}</strong> to this repository.".html_safe
= form_for [@project, @merge_request], html: { class: "merge-request-form form-horizontal" } do |f|
-if @merge_request.errors.any?
.alert.alert-danger
%ul
- @merge_request.errors.full_messages.each do |msg|
%li= msg
.row
.col-sm-2
.col-sm-10
- if @repository.contribution_guide && !@merge_request.persisted?
- contribution_guide_url = project_blob_path(@project, tree_join(@repository.root_ref, @repository.contribution_guide.name))
.alert.alert-info
Please review the
%strong #{link_to "guidelines for contribution", contribution_guide_url}
to this repository.
-if @merge_request.errors.any?
.alert.alert-danger
- @merge_request.errors.full_messages.each do |msg|
%div= msg
.merge-request-branches
.form-group
......@@ -47,24 +52,6 @@
= f.text_area :description, class: "form-control js-gfm-input", rows: 14
%p.hint Description is parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
%hr
.form-group
.merge-request-assignee
= f.label :assignee_id, class: 'control-label' do
%i.icon-user
Assign to
.col-sm-10
= project_users_select_tag('merge_request[assignee_id]', placeholder: 'Select a user', class: 'custom-form-control', selected: @merge_request.assignee_id)
&nbsp;
= link_to 'Assign to me', '#', class: 'btn btn-small assign-to-me-link'
.form-group
.merge-request-milestone
= f.label :milestone_id, class: 'control-label' do
%i.icon-time
Milestone
.col-sm-10= f.select(:milestone_id, milestone_options(@merge_request), { include_blank: "Select milestone" }, {class: 'select2'})
.form-actions
- if @merge_request.new_record?
= f.submit 'Submit merge request', class: "btn btn-create"
......@@ -96,7 +83,3 @@
target_branch.on("change", function() {
$.get("#{branch_to_project_merge_requests_path(@source_project)}", {target_project_id: target_project.val(),ref: $(this).val() });
});
$('.assign-to-me-link').on('click', function(e){
$('#merge_request_assignee_id').val("#{current_user.id}").trigger("change");
e.preventDefault();
});
......@@ -20,7 +20,7 @@
%li.diffs-tab{data: {action: 'diffs'}}
= link_to diffs_project_merge_request_path(@project, @merge_request) do
%i.icon-list-alt
Diff
Changes
- content_for :note_actions do
- if can?(current_user, :modify_merge_request, @merge_request)
......
......@@ -3,5 +3,5 @@
var mrTitle = $('#merge_request_title');
if(mrTitle.val().length == 0) {
mrTitle.val("#{params[:ref].titleize}");
mrTitle.val("#{params[:ref].humanize}");
}
......@@ -5,7 +5,7 @@
- else
.bs-callout.bs-callout-warning
%h4
Diff for this comparison is extremely large.
Changes view for this comparison is extremely large.
%p
You can
= link_to "download it", project_merge_request_path(@merge_request.source_project, @merge_request, format: :diff), class: "vlink"
......
- unless @allowed_to_merge
.bs-callout
%strong You don't have permission to merge this MR
- if @project.archived?
.bs-callout.bs-callout-warning
%strong Archived projects cannot be committed to!
- else
.bs-callout
.automerge_widget.cannot_be_merged.hide
%strong This can't be merged automatically, even if it could be merged you don't have the permission to do so.
.automerge_widget.can_be_merged.hide
%strong This can be merged automatically but you don't have the permission to do so.
- if @show_merge_controls
......
......@@ -29,13 +29,13 @@
%span
%i.icon-remove
Closed by #{link_to_member(@project, @merge_request.closed_event.author)}
#{time_ago_with_tooltip(@merge_request.closed_event.created_at)}.
#{time_ago_with_tooltip(@merge_request.closed_event.created_at)}
- if @merge_request.merged?
.alert.alert-info
%span
%i.icon-ok
Merged by #{link_to_member(@project, @merge_request.merge_event.author)}
#{time_ago_with_tooltip(@merge_request.merge_event.created_at)}.
#{time_ago_with_tooltip(@merge_request.merge_event.created_at)}
- if !@closes_issues.empty? && @merge_request.open?
.alert.alert-info.alert-info
%span
......
......@@ -23,7 +23,7 @@
%i.icon-thumbs-up
\+1
- if note.downvote?
%span.vote.downvote.label.label-error
%span.vote.downvote.label.label-danger
%i.icon-thumbs-down
\-1
......
......@@ -9,8 +9,8 @@
.col-md-3.project-side.hidden-sm
.clearfix
- if @project.archived?
.alert
%h5
.alert.alert-warning
%h4
%i.icon-warning-sign
Archived project!
%p Repository is read-only
......
......@@ -19,7 +19,7 @@ module Gitlab
# config.plugins = [ :exception_notification, :ssl_requirement, :all ]
# Activate observers that should always be running.
config.active_record.observers = :activity_observer,
config.active_record.observers = :milestone_observer,
:project_activity_cache_observer,
:issue_observer,
:key_observer,
......
......@@ -33,6 +33,12 @@ Gitlab::Application.configure do
# See everything in the log (default is :info)
# config.log_level = :debug
# Suppress 'Rendered template ...' messages in the log
# source: http://stackoverflow.com/a/16369363
%w{render_template render_partial render_collection}.each do |event|
ActiveSupport::Notifications.unsubscribe "#{event}.action_view"
end
# Prepend all log lines with the following tags
# config.log_tags = [ :subdomain, :uuid ]
......
# Workaround for https://github.com/github/gemoji/pull/18
require 'gemoji'
Gitlab::Application.config.assets.paths << Emoji.images_path
......@@ -167,7 +167,7 @@ Gitlab::Application.routes.draw do
resources :projects, constraints: { id: /[^\/]+/ }, only: [:new, :create]
devise_for :users, controllers: { omniauth_callbacks: :omniauth_callbacks, registrations: :registrations }
devise_for :users, controllers: { omniauth_callbacks: :omniauth_callbacks, registrations: :registrations , passwords: :passwords}
#
# Project Area
......
worker_processes 2
timeout 30
......@@ -2,7 +2,7 @@ Gitlab::Seeder.quiet do
User.first(30).each_with_index do |user, i|
Key.seed(:id, [
{
id: i,
id: i + 1,
title: "Sample key #{i}",
key: "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt#{i + 100}6k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
user_id: user.id,
......
## The GitLab Documentation covers the following subjects
+ [API](api/README.md)
+ [Development](development/README.md)
+ [Install](install/README.md)
+ [Integration](external-issue-tracker/README.md)
+ [Legal](legal/README.md)
+ [Markdown](markdown/markdown.md)
+ [Permissions](permissions/permissions.md)
+ [Public access](public_access/public_access.md)
+ [Raketasks](raketasks/README.md)
+ [Release](release/README.md)
+ [Security](security/README.md)
+ [System hooks](system_hooks/system_hooks.md)
+ [Update](update/README.md)
+ [Web hooks](web_hooks/web_hooks.md)
+ [Workflow](workflow/workflow.md)
# GitLab API
## End-points
+ [Users](users.md)
+ [Session](session.md)
+ [Projects](projects.md)
+ [Project Snippets](project_snippets.md)
+ [Repositories](repositories.md)
+ [Repository Files](repository_files.md)
+ [Commits](commits.md)
+ [Merge Requests](merge_requests.md)
+ [Issues](issues.md)
+ [Milestones](milestones.md)
+ [Notes](notes.md)
+ [Deploy Keys](deploy_keys.md)
+ [System Hooks](system_hooks.md)
+ [Groups](groups.md)
## Clients
+ [php-gitlab-api](https://github.com/m4tthumphrey/php-gitlab-api) - PHP
+ [Ruby Wrapper](https://github.com/NARKOZ/gitlab) - Ruby
+ [python-gitlab](https://github.com/Itxaka/python-gitlab) - Python
+ [java-gitlab-api](https://github.com/timols/java-gitlab-api) - Java
## Introduction
All API requests require authentication. You need to pass a `private_token` parameter by url or header. If passed as header, the header name must be "PRIVATE-TOKEN" (capital and with dash instead of underscore). You can find or reset your private token in your profile.
If no, or an invalid, `private_token` is provided then an error message will be returned with status code 401:
......@@ -117,30 +143,3 @@ Issue
So if you want to get issue with api you use `http://host/api/v3/.../issues/:id.json`
But when you want to create a link to web page - use `http:://host/project/issues/:iid.json`
## Contents
+ [Users](users.md)
+ [Session](session.md)
+ [Projects](projects.md)
+ [Project Snippets](project_snippets.md)
+ [Repositories](repositories.md)
+ [Repository Files](repository_files.md)
+ [Commits](commits.md)
+ [Merge Requests](merge_requests.md)
+ [Issues](issues.md)
+ [Milestones](milestones.md)
+ [Notes](notes.md)
+ [Deploy Keys](deploy_keys.md)
+ [System Hooks](system_hooks.md)
+ [Groups](groups.md)
## Clients
+ [php-gitlab-api](https://github.com/m4tthumphrey/php-gitlab-api) - PHP
+ [Ruby Wrapper](https://github.com/NARKOZ/gitlab) - Ruby
+ [python-gitlab](https://github.com/Itxaka/python-gitlab) - Python
+ [java-gitlab-api](https://github.com/timols/java-gitlab-api) - Java
......@@ -150,6 +150,7 @@ Parameters:
+ `target_branch` - The target branch
+ `assignee_id` - Assignee user ID
+ `title` - Title of MR
+ `state_event` - New state (close|reopen|merge)
```json
......@@ -210,3 +211,44 @@ Parameters:
"note":"text1"
}
```
## Get the comments on a MR
Gets all the comments associated with a merge request.
```
GET /projects/:id/merge_request/:merge_request_id/comments
```
Parameters:
+ `id` (required) - The ID of a project
+ `merge_request_id` (required) - ID of merge request
```json
[
{
"note":"this is the 1st comment on the 2merge merge request",
"author":{
"id":11,
"username":"admin",
"email":"admin@local.host",
"name":"Administrator",
"state":"active",
"created_at":"2014-03-06T08:17:35.000Z"
}
},
{
"note":"_Status changed to closed_",
"author":{
"id":11,
"username":"admin",
"email":"admin@local.host",
"name":"Administrator",
"state":"active",
"created_at":"2014-03-06T08:17:35.000Z"
}
}
]
```
......@@ -621,3 +621,29 @@ Parameters:
+ query (required) - A string contained in the project name
+ per_page (optional) - number of projects to return per page
+ page (optional) - the page to retrieve
## Labels
### List project labels
Get a list of project labels.
```
GET /projects/:id/labels
```
Parameters:
+ `id` (required) - The ID or NAMESPACE/PROJECT_NAME of a project
```json
[
{
"name":"featute"
},
{
"name": "bug"
}
]
```
All methods require admin authorization.
The url endpoint of the system hooks can be configured in [the admin area under hooks](/admin/hooks).
## List system hooks
Get list of system hooks
```
GET /hooks
```
Parameters:
+ **none**
```json
[
{
"id":3,
"url":"http://example.com/hook",
"created_at":"2013-10-02T10:15:31Z"
}
]
```
## Add new system hook hook
```
POST /hooks
```
Parameters:
+ `url` (required) - The hook URL
## Test system hook
```
GET /hooks/:id
```
Parameters:
+ `id` (required) - The ID of hook
```json
{
"event_name":"project_create",
"name":"Ruby",
"path":"ruby",
"project_id":1,
"owner_name":"Someone",
"owner_email":"example@gitlabhq.com"
}
```
## Delete system hook
Deletes a system hook. This is an idempotent API function and returns `200 Ok` even if the hook
is not available. If the hook is deleted it is also returned as JSON.
```
DELETE /hooks/:id
```
Parameters:
+ `id` (required) - The ID of hook
All methods require admin authorization.
The url endpoint of the system hooks can be configured in [the admin area under hooks](/admin/hooks).
## List system hooks
Get list of system hooks
```
GET /hooks
```
Parameters:
+ **none**
```json
[
{
"id":3,
"url":"http://example.com/hook",
"created_at":"2013-10-02T10:15:31Z"
}
]
```
## Add new system hook hook
```
POST /hooks
```
Parameters:
+ `url` (required) - The hook URL
## Test system hook
```
GET /hooks/:id
```
Parameters:
+ `id` (required) - The ID of hook
```json
{
"event_name":"project_create",
"name":"Ruby",
"path":"ruby",
"project_id":1,
"owner_name":"Someone",
"owner_email":"example@gitlabhq.com"
}
```
## Delete system hook
Deletes a system hook. This is an idempotent API function and returns `200 Ok` even if the hook
is not available. If the hook is deleted it is also returned as JSON.
```
DELETE /hooks/:id
```
Parameters:
+ `id` (required) - The ID of hook
+ [Architecture](architecture.md)
+ [Shell commands](shell_commands.md)
......@@ -18,7 +18,7 @@ New releases are generally around the same time as GitLab CE releases with excep
# System Layout
When referring to ~git in the picures it means the home directory of the git user which is typically /home/git.
When referring to ~git in the pictures it means the home directory of the git user which is typically /home/git.
GitLab is primarily installed within the `/home/git` user home directory as `git` user.
Within the home directory is where the gitlabhq server software resides as well as the repositories (though the repository location is configurable).
......@@ -28,7 +28,7 @@ To serve repositories over SSH there's an add-on application called gitlab-shell
## Components
![GitLab Diagram Overview](resources/gitlab_diagram_overview.png "GitLab Diagram Overview")
![GitLab Diagram Overview](resources/gitlab_diagram_overview.png)
A typical install of GitLab will be on Ubuntu Linux or RHEL/CentOS.
It uses Nginx or Apache as a web front end to proxypass the Unicorn web server.
......@@ -180,4 +180,4 @@ bundle exec rake gitlab:check RAILS_ENV=production
```
Note: It is recommended to log into the `git` user using `sudo -i -u git` or `sudo su - git`.
While the sudo commands provided by gitlabhq work in Ubuntu they do not always work in RHEL.
While the sudo commands provided by gitlabhq work in Ubuntu they do not always work in RHEL.
\ No newline at end of file
# Guidelines for shell commands in the GitLab codebase
## References
- [Google Ruby Security Reviewer's Guide](https://code.google.com/p/ruby-security/wiki/Guide)
- [OWASP Command Injection](https://www.owasp.org/index.php/Command_Injection)
- [Ruby on Rails Security Guide Command Line Injection](http://guides.rubyonrails.org/security.html#command-line-injection)
## Use File and FileUtils instead of shell commands
Sometimes we invoke basic Unix commands via the shell when there is also a Ruby API for doing it.
......
+ [Installation](installation.md)
+ [Requirements](requirements.md)
+ [Structure](structure.md)
+ [Database MySQL](database_mysql.md)
......@@ -6,6 +6,9 @@ We do not recommend using MySQL due to various issues. For example, case [(in)se
# Install the database packages
sudo apt-get install -y mysql-server mysql-client libmysqlclient-dev
# Ensure you have MySQL version 5.5.14 or later
mysql --version
# Pick a database root password (can be anything), type it and press enter
# Retype the database root password and press enter
......@@ -23,6 +26,10 @@ We do not recommend using MySQL due to various issues. For example, case [(in)se
# change $password in the command below to a real password you pick
mysql> CREATE USER 'git'@'localhost' IDENTIFIED BY '$password';
# Ensure you can use the InnoDB engine which is necessary to support long indexes.
# If this fails, check your MySQL config files (e.g. `/etc/mysql/*.cnf`, `/etc/mysql/conf.d/*`) for the setting "innodb = off"
mysql> SET storage_engine=INNODB;
# Create the GitLab production database
mysql> CREATE DATABASE IF NOT EXISTS `gitlabhq_production` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_unicode_ci`;
......
......@@ -128,7 +128,7 @@ GitLab Shell is an ssh access and repository management software developed speci
cd /home/git
# Clone gitlab shell
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-shell.git -b v1.8.0
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-shell.git -b v1.9.1
cd gitlab-shell
......@@ -173,7 +173,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da
## Clone the Source
# Clone GitLab repository
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 6-6-stable gitlab
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 6-7-stable gitlab
# Go to gitlab dir
cd /home/git/gitlab
......
......@@ -5,3 +5,5 @@ GitLab has a great issue tracker but you can also use an external issue tracker
- textual references to PROJECT-1234 in comments, commit messages get turned into HTML links to the corresponding JIRA issue.
![jira screenshot](jira-intergration-points.png)
You can configure the integration in the gitlab.yml configuration file.
+ [Corporate contributor license agreement](corporate_contributor_license_agreement.md)
+ [Individual contributor license agreement](individual_contributor_license_agreement.md)
......@@ -38,7 +38,7 @@ If a user is a GitLab administrator they receive all permissions.
|------|-----|--------|---------|------|-----|
|Browse group|✓|✓|✓|✓|✓|
|Edit group|||||✓|
|create project in group|||||✓|
|Create project in group|||||✓|
|Manage group members|||||✓|
|Remove group|||||✓|
......
+ [Backup restore](backup_restore.md)
+ [Cleanup](cleanup.md)
+ [Features](features.md)
+ [Maintenance](maintenance.md)
+ [User management](user_management.md)
+ [Web hooks](web_hooks.md)
+ [Monthly](monthly.md)
+ [Security](security.md)
......@@ -58,19 +58,17 @@ Check if changed since last release (~22nd of last month depending on when last
After making the release branch new commits are cherry-picked from master. When the release gets closer we get more selective what is cherry-picked. The days of the month are approximately as follows:
* 17th: feature freeze (stop merging new features in master)
* 18th: UI freeze (stop merging changes to the user interface)
* 19th: code freeze (stop merging non-essential code improvements)
* 20th: release candidate 1 (VERSION x.x.0.rc1, tag and tweet about x.x.0.rc1)
* 21st: optional release candidate 2 (x.x.0.rc2, only if rc1 had problems)
* 1-7th: official merge window (see contributing guide)
* 8-14th: work on bugfixes, sponsored features and GitLab EE
* 15th: code freeze (stop merging into master except essential bugfixes)
* 18th: release candidate 1 (VERSION x.x.0.rc1, tag and tweet about x.x.0.rc1, release on GitLab Cloud)
* 20st: optional release candidate 2 (x.x.0.rc2, only if rc1 had problems)
* 22nd: release (VERSION x.x.0, create x-x-stable branch, tag, blog and tweet)
* 23nd: optional patch releases (x.x.1, x.x.2, etc., only if there are serious problems)
* 24-end of month: release Enterprise Edition and upgrade GitLab Cloud
* 1-7th: official merge window (see contributing guide)
* 8-16th: bugfixes and sponsored features
* 24-end of month: release GitLab EE and GitLab CI
# Write a blog post
* Mention what GitLab is on the second line: GitLab is open source software to collaborate on code.
* Select and thank the the Most Valuable Person (MVP) of this release.
* Add a note if there are security fixes: This release fixes an important security issue and we advise everyone to upgrade as soon as possible.
\ No newline at end of file
* Add a note if there are security fixes: This release fixes an important security issue and we advise everyone to upgrade as soon as possible.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment