Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
7e0ef892
Commit
7e0ef892
authored
Apr 20, 2016
by
Alfredo Sumaran
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'origin/master' into label-dropdown-fix
parents
a5290ac2
7e6d5906
Changes
58
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
58 changed files
with
3448 additions
and
191 deletions
+3448
-191
CHANGELOG
CHANGELOG
+15
-2
CONTRIBUTING.md
CONTRIBUTING.md
+1
-0
app/assets/javascripts/application.js.coffee
app/assets/javascripts/application.js.coffee
+1
-0
app/assets/javascripts/dispatcher.js.coffee
app/assets/javascripts/dispatcher.js.coffee
+2
-1
app/assets/javascripts/due_date_select.js.coffee
app/assets/javascripts/due_date_select.js.coffee
+64
-0
app/assets/javascripts/gl_dropdown.js.coffee
app/assets/javascripts/gl_dropdown.js.coffee
+4
-4
app/assets/javascripts/issuable.js.coffee
app/assets/javascripts/issuable.js.coffee
+84
-0
app/assets/javascripts/issuable_context.js.coffee
app/assets/javascripts/issuable_context.js.coffee
+0
-1
app/assets/javascripts/issues.js.coffee
app/assets/javascripts/issues.js.coffee
+1
-50
app/assets/javascripts/labels_select.js.coffee
app/assets/javascripts/labels_select.js.coffee
+44
-19
app/assets/javascripts/lib/animate.js.coffee
app/assets/javascripts/lib/animate.js.coffee
+30
-4
app/assets/javascripts/lib/url_utility.js.coffee
app/assets/javascripts/lib/url_utility.js.coffee
+14
-2
app/assets/javascripts/merge_requests.js.coffee
app/assets/javascripts/merge_requests.js.coffee
+0
-35
app/assets/javascripts/milestone_select.js.coffee
app/assets/javascripts/milestone_select.js.coffee
+1
-1
app/assets/javascripts/raven_config.js.coffee
app/assets/javascripts/raven_config.js.coffee
+44
-0
app/assets/javascripts/users_select.js.coffee
app/assets/javascripts/users_select.js.coffee
+1
-1
app/assets/stylesheets/framework/dropdowns.scss
app/assets/stylesheets/framework/dropdowns.scss
+128
-2
app/assets/stylesheets/framework/markdown_area.scss
app/assets/stylesheets/framework/markdown_area.scss
+9
-0
app/assets/stylesheets/framework/variables.scss
app/assets/stylesheets/framework/variables.scss
+5
-0
app/assets/stylesheets/pages/detail_page.scss
app/assets/stylesheets/pages/detail_page.scss
+6
-0
app/assets/stylesheets/pages/diff.scss
app/assets/stylesheets/pages/diff.scss
+3
-2
app/assets/stylesheets/pages/help.scss
app/assets/stylesheets/pages/help.scss
+2
-0
app/assets/stylesheets/pages/issuable.scss
app/assets/stylesheets/pages/issuable.scss
+3
-2
app/assets/stylesheets/pages/note_form.scss
app/assets/stylesheets/pages/note_form.scss
+0
-12
app/assets/stylesheets/pages/notes.scss
app/assets/stylesheets/pages/notes.scss
+4
-17
app/controllers/projects/issues_controller.rb
app/controllers/projects/issues_controller.rb
+4
-3
app/controllers/projects/merge_requests_controller.rb
app/controllers/projects/merge_requests_controller.rb
+3
-2
app/finders/issuable_finder.rb
app/finders/issuable_finder.rb
+40
-1
app/helpers/application_helper.rb
app/helpers/application_helper.rb
+8
-1
app/helpers/issuables_helper.rb
app/helpers/issuables_helper.rb
+19
-0
app/helpers/issues_helper.rb
app/helpers/issues_helper.rb
+13
-1
app/helpers/sorting_helper.rb
app/helpers/sorting_helper.rb
+18
-0
app/models/issue.rb
app/models/issue.rb
+27
-0
app/models/label.rb
app/models/label.rb
+4
-0
app/views/projects/_activity.html.haml
app/views/projects/_activity.html.haml
+4
-1
app/views/projects/issues/_issue.html.haml
app/views/projects/issues/_issue.html.haml
+5
-0
app/views/projects/merge_requests/show/_mr_title.html.haml
app/views/projects/merge_requests/show/_mr_title.html.haml
+2
-2
app/views/shared/_label_row.html.haml
app/views/shared/_label_row.html.haml
+1
-1
app/views/shared/_labels_row.html.haml
app/views/shared/_labels_row.html.haml
+3
-0
app/views/shared/_sort_dropdown.html.haml
app/views/shared/_sort_dropdown.html.haml
+5
-0
app/views/shared/issuable/_filter.html.haml
app/views/shared/issuable/_filter.html.haml
+5
-3
app/views/shared/issuable/_label_dropdown.html.haml
app/views/shared/issuable/_label_dropdown.html.haml
+5
-3
app/views/shared/issuable/_nav.html.haml
app/views/shared/issuable/_nav.html.haml
+5
-5
app/views/shared/issuable/_sidebar.html.haml
app/views/shared/issuable/_sidebar.html.haml
+34
-5
db/migrate/20160310124959_add_due_date_to_issues.rb
db/migrate/20160310124959_add_due_date_to_issues.rb
+6
-0
db/schema.rb
db/schema.rb
+2
-0
features/project/issues/filter_labels.feature
features/project/issues/filter_labels.feature
+1
-0
features/steps/project/issues/filter_labels.rb
features/steps/project/issues/filter_labels.rb
+4
-0
lib/api/internal.rb
lib/api/internal.rb
+1
-1
lib/gitlab/gon_helper.rb
lib/gitlab/gon_helper.rb
+1
-0
lib/gitlab/metrics.rb
lib/gitlab/metrics.rb
+9
-0
spec/features/issues/filter_by_labels_spec.rb
spec/features/issues/filter_by_labels_spec.rb
+172
-0
spec/features/issues/filter_issues_spec.rb
spec/features/issues/filter_issues_spec.rb
+8
-1
spec/features/issues_spec.rb
spec/features/issues_spec.rb
+92
-6
spec/features/merge_requests/filter_by_milestone_spec.rb
spec/features/merge_requests/filter_by_milestone_spec.rb
+6
-0
spec/finders/issues_finder_spec.rb
spec/finders/issues_finder_spec.rb
+16
-0
spec/lib/gitlab/metrics_spec.rb
spec/lib/gitlab/metrics_spec.rb
+24
-0
vendor/assets/javascripts/raven.js
vendor/assets/javascripts/raven.js
+2435
-0
No files found.
CHANGELOG
View file @
7e0ef892
...
...
@@ -19,6 +19,7 @@ v 8.7.0 (unreleased)
- Load award emoji images separately unless opening the full picker. Saves several hundred KBs of data for most pages. (Connor Shea)
- Do not include award_emojis in issue and merge_request comment_count !3610 (Lucas Charles)
- Restrict user profiles when public visibility level is restricted.
- Add ability set due date to issues, sort and filter issues by due date (Mehmet Beydogan)
- All images in discussions and wikis now link to their source files !3464 (Connor Shea).
- Return status code 303 after a branch DELETE operation to avoid project deletion (Stan Hu)
- Add setting for customizing the list of trusted proxies !3524
...
...
@@ -86,6 +87,10 @@ v 8.7.0 (unreleased)
- Fix repository cache invalidation issue when project is recreated with an empty repo (Stan Hu)
- Fix: Allow empty recipients list for builds emails service when pushed is added (Frank Groeneveld)
- Improved markdown forms
- Show JavaScript errors in sentry
- Diff design updates (colors, button styles, etc)
- Copying and pasting a diff no longer pastes the line numbers or +/-
- Add null check to formData when updating profile content to fix Firefox bug
- Delete tags using Rugged for performance reasons (Robert Schilling)
- Add Slack notifications when Wiki is edited (Sebastian Klier)
- Diffs load at the correct point when linking from from number
...
...
@@ -107,7 +112,7 @@ v 8.7.0 (unreleased)
- Add RAW build trace output and button on build page
- Add incremental build trace update into CI API
v 8.6.7
(unreleased)
v 8.6.7
- Fix persistent XSS vulnerability in `commit_person_link` helper
- Fix persistent XSS vulnerability in Label and Milestone dropdowns
- Fix vulnerability that made it possible to enumerate private projects belonging to group
...
...
@@ -117,7 +122,6 @@ v 8.6.6
- Fix error on language detection when repository has no HEAD (e.g., master branch) (Jeroen Bobbeldijk). !3654
- Fix revoking of authorized OAuth applications (Connor Shea). !3690
- Fix error on language detection when repository has no HEAD (e.g., master branch). !3654 (Jeroen Bobbeldijk)
- Project switcher uses new dropdown styling
- Issuable header is consistent between issues and merge requests
- Improved spacing in issuable header on mobile
...
...
@@ -249,6 +253,9 @@ v 8.6.0
- Trigger a todo for mentions on commits page
- Let project owners and admins soft delete issues and merge requests
v 8.5.11
- Fix persistent XSS vulnerability in `commit_person_link` helper
v 8.5.10
- Fix a 2FA authentication spoofing vulnerability.
...
...
@@ -396,6 +403,9 @@ v 8.5.0
- Show label row when filtering issues or merge requests by label (Nuttanart Pornprasitsakul)
- Add Todos
v 8.4.9
- Fix persistent XSS vulnerability in `commit_person_link` helper
v 8.4.8
- Fix a 2FA authentication spoofing vulnerability.
...
...
@@ -518,6 +528,9 @@ v 8.4.0
- Add IP check against DNSBLs at account sign-up
- Added cache:key to .gitlab-ci.yml allowing to fine tune the caching
v 8.3.8
- Fix persistent XSS vulnerability in `commit_person_link` helper
v 8.3.7
- Fix a 2FA authentication spoofing vulnerability.
...
...
CONTRIBUTING.md
View file @
7e0ef892
...
...
@@ -323,6 +323,7 @@ request is as follows:
[
shell command guidelines
](
doc/development/shell_commands.md
)
1.
If your code creates new files on disk please read the
[
shared files guidelines
](
doc/development/shared_files.md
)
.
1.
When writing commit messages please follow
[
these
](
http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
)
[
guidelines
]
(http://chris.beams.io/posts/git-commit/).
The
**official merge window**
is in the beginning of the month from the 1st to
the 7th day of the month. This is the best time to submit an MR and get
...
...
app/assets/javascripts/application.js.coffee
View file @
7e0ef892
...
...
@@ -55,6 +55,7 @@
#= require_tree .
#= require fuzzaldrin-plus
#= require cropper
#= require raven
window
.
slugify
=
(
text
)
->
text
.
replace
(
/[^-a-zA-Z0-9]+/g
,
'_'
).
toLowerCase
()
...
...
app/assets/javascripts/dispatcher.js.coffee
View file @
7e0ef892
...
...
@@ -17,6 +17,7 @@ class Dispatcher
switch
page
when
'projects:issues:index'
Issues
.
init
()
Issuable
.
init
()
shortcut_handler
=
new
ShortcutsNavigation
()
when
'projects:issues:show'
new
Issue
()
...
...
@@ -57,7 +58,7 @@ class Dispatcher
new
ZenMode
()
when
'projects:merge_requests:index'
shortcut_handler
=
new
ShortcutsNavigation
()
MergeRequests
.
init
()
Issuable
.
init
()
when
'dashboard:activity'
new
Activities
()
when
'dashboard:projects:starred'
...
...
app/assets/javascripts/due_date_select.js.coffee
0 → 100644
View file @
7e0ef892
class
@
DueDateSelect
constructor
:
->
$loading
=
$
(
'.js-issuable-update .due_date'
)
.
find
(
'.block-loading'
)
.
hide
()
$
(
'.js-due-date-select'
).
each
(
i
,
dropdown
)
->
$dropdown
=
$
(
dropdown
)
$dropdownParent
=
$dropdown
.
closest
(
'.dropdown'
)
$datePicker
=
$dropdownParent
.
find
(
'.js-due-date-calendar'
)
$block
=
$dropdown
.
closest
(
'.block'
)
$selectbox
=
$dropdown
.
closest
(
'.selectbox'
)
$value
=
$block
.
find
(
'.value'
)
$sidebarValue
=
$
(
'.js-due-date-sidebar-value'
,
$block
)
fieldName
=
$dropdown
.
data
(
'field-name'
)
abilityName
=
$dropdown
.
data
(
'ability-name'
)
issueUpdateURL
=
$dropdown
.
data
(
'issue-update'
)
$dropdown
.
glDropdown
(
hidden
:
->
$selectbox
.
hide
()
$value
.
removeAttr
(
'style'
)
)
addDueDate
=
->
# Create the post date
value
=
$
(
"input[name='
#{
fieldName
}
']"
).
val
()
date
=
new
Date
value
.
replace
(
new
RegExp
(
'-'
,
'g'
),
','
)
mediumDate
=
$
.
datepicker
.
formatDate
'M d, yy'
,
date
data
=
{}
data
[
abilityName
]
=
{}
data
[
abilityName
].
due_date
=
value
$
.
ajax
(
type
:
'PUT'
url
:
issueUpdateURL
data
:
data
beforeSend
:
->
$loading
.
fadeIn
()
$dropdown
.
trigger
(
'loading.gl.dropdown'
)
$selectbox
.
hide
()
$value
.
removeAttr
(
'style'
)
$value
.
html
(
mediumDate
)
$sidebarValue
.
html
(
mediumDate
)
).
done
(
data
)
->
$dropdown
.
trigger
(
'loaded.gl.dropdown'
)
$dropdown
.
dropdown
(
'toggle'
)
$loading
.
fadeOut
()
$datePicker
.
datepicker
(
dateFormat
:
'yy-mm-dd'
,
defaultDate
:
$
(
"input[name='
#{
fieldName
}
']"
).
val
()
altField
:
"input[name='
#{
fieldName
}
']"
onSelect
:
->
addDueDate
()
)
$
(
document
)
.
off
'click'
,
'.ui-datepicker-header a'
.
on
'click'
,
'.ui-datepicker-header a'
,
(
e
)
->
e
.
stopImmediatePropagation
()
app/assets/javascripts/gl_dropdown.js.coffee
View file @
7e0ef892
...
...
@@ -389,13 +389,13 @@ class GitLabDropdown
else
selectedObject
else
if
!
value
?
field
.
remove
()
if
not
@
options
.
multiSelect
if
not
@
options
.
multiSelect
or
el
.
hasClass
(
'dropdown-clear-active'
)
@
dropdown
.
find
(
".
#{
ACTIVE_CLASS
}
"
).
removeClass
ACTIVE_CLASS
@
dropdown
.
parent
().
find
(
"input[name='
#{
fieldName
}
']"
).
remove
()
if
!
value
?
field
.
remove
()
# Toggle active class for the tick mark
el
.
addClass
ACTIVE_CLASS
...
...
app/assets/javascripts/issuable.js.coffee
0 → 100644
View file @
7e0ef892
@
Issuable
=
init
:
->
Issuable
.
initTemplates
()
Issuable
.
initSearch
()
initTemplates
:
->
Issuable
.
labelRow
=
_
.
template
(
'<% _.each(labels, function(label){ %>
<span class="label-row">
<a href="#"><span class="label color-label has-tooltip" style="background-color: <%= label.color %>; color: <%= label.text_color %>" title="<%= _.escape(label.description) %>" data-container="body"><%= _.escape(label.title) %></span></a>
</span>
<% }); %>'
)
initSearch
:
->
@
timer
=
null
$
(
'#issue_search'
)
.
off
'keyup'
.
on
'keyup'
,
->
clearTimeout
(
@
timer
)
@
timer
=
setTimeout
(
->
Issuable
.
filterResults
$
(
'#issue_search_form'
)
,
500
)
toggleLabelFilters
:
->
$filteredLabels
=
$
(
'.filtered-labels'
)
if
$filteredLabels
.
find
(
'.label-row'
).
length
>
0
$filteredLabels
.
removeClass
(
'hidden'
)
else
$filteredLabels
.
addClass
(
'hidden'
)
filterResults
:
(
form
)
=>
formData
=
form
.
serialize
()
$
(
'.issues-holder, .merge-requests-holder'
).
css
(
'opacity'
,
'0.5'
)
formAction
=
form
.
attr
(
'action'
)
issuesUrl
=
formAction
issuesUrl
+=
(
"
#{
if
formAction
.
indexOf
(
'?'
)
<
0
then
'?'
else
'&'
}
"
)
issuesUrl
+=
formData
$
.
ajax
type
:
'GET'
url
:
formAction
data
:
formData
complete
:
->
$
(
'.issues-holder, .merge-requests-holder'
).
css
(
'opacity'
,
'1.0'
)
success
:
(
data
)
->
$
(
'.issues-holder, .merge-requests-holder'
).
html
(
data
.
html
)
# Change url so if user reload a page - search results are saved
history
.
replaceState
{
page
:
issuesUrl
},
document
.
title
,
issuesUrl
Issuable
.
reload
()
Issuable
.
updateStateFilters
()
$filteredLabels
=
$
(
'.filtered-labels'
)
if
typeof
Issuable
.
labelRow
is
'function'
$filteredLabels
.
html
(
Issuable
.
labelRow
(
data
))
Issuable
.
toggleLabelFilters
()
dataType
:
"json"
reload
:
->
if
Issues
.
created
Issues
.
initChecks
()
$
(
'#filter_issue_search'
).
val
(
$
(
'#issue_search'
).
val
())
updateStateFilters
:
->
stateFilters
=
$
(
'.issues-state-filters'
)
newParams
=
{}
paramKeys
=
[
'author_id'
,
'milestone_title'
,
'assignee_id'
,
'issue_search'
]
for
paramKey
in
paramKeys
newParams
[
paramKey
]
=
gl
.
utils
.
getParameterValues
(
paramKey
)[
0
]
or
''
if
stateFilters
.
length
stateFilters
.
find
(
'a'
).
each
->
initialUrl
=
gl
.
utils
.
removeParamQueryString
(
$
(
this
).
attr
(
'href'
),
'label_name[]'
)
labelNameValues
=
gl
.
utils
.
getParameterValues
(
'label_name[]'
)
if
labelNameValues
labelNameQueryString
=
(
"label_name[]=
#{
value
}
"
for
value
in
labelNameValues
).
join
(
'&'
)
newUrl
=
"
#{
gl
.
utils
.
mergeUrlParams
(
newParams
,
initialUrl
)
}
&
#{
labelNameQueryString
}
"
else
newUrl
=
gl
.
utils
.
mergeUrlParams
(
newParams
,
initialUrl
)
$
(
this
).
attr
'href'
,
newUrl
app/assets/javascripts/issuable_context.js.coffee
View file @
7e0ef892
...
...
@@ -33,7 +33,6 @@ class @IssuableContext
$block
.
find
(
'.dropdown-menu-toggle'
).
trigger
'click'
,
0
$
(
".right-sidebar"
).
niceScroll
()
initParticipants
:
->
...
...
app/assets/javascripts/issues.js.coffee
View file @
7e0ef892
@
Issues
=
init
:
->
Issues
.
initSearch
()
Issues
.
created
=
true
Issues
.
initChecks
()
$
(
"body"
).
on
"ajax:success"
,
".close_issue, .reopen_issue"
,
->
...
...
@@ -15,10 +15,6 @@
else
$
(
this
).
html
totalIssues
-
1
reload
:
->
Issues
.
initChecks
()
$
(
'#filter_issue_search'
).
val
(
$
(
'#issue_search'
).
val
())
initChecks
:
->
$
(
".check_all_issues"
).
click
->
$
(
".selected_issue"
).
prop
(
"checked"
,
@
checked
)
...
...
@@ -26,51 +22,6 @@
$
(
".selected_issue"
).
bind
"change"
,
Issues
.
checkChanged
# Update state filters if present in page
updateStateFilters
:
->
stateFilters
=
$
(
'.issues-state-filters'
)
newParams
=
{}
paramKeys
=
[
'author_id'
,
'label_name'
,
'milestone_title'
,
'assignee_id'
,
'issue_search'
]
for
paramKey
in
paramKeys
newParams
[
paramKey
]
=
gl
.
utils
.
getUrlParameter
(
paramKey
)
or
''
if
stateFilters
.
length
stateFilters
.
find
(
'a'
).
each
->
initialUrl
=
$
(
this
).
attr
'href'
$
(
this
).
attr
'href'
,
gl
.
utils
.
mergeUrlParams
(
newParams
,
initialUrl
)
# Make sure we trigger ajax request only after user stop typing
initSearch
:
->
@
timer
=
null
$
(
"#issue_search"
).
keyup
->
clearTimeout
(
@
timer
)
@
timer
=
setTimeout
(
->
Issues
.
filterResults
$
(
"#issue_search_form"
)
,
500
)
filterResults
:
(
form
)
=>
$
(
'.issues-holder, .merge-requests-holder'
).
css
(
"opacity"
,
'0.5'
)
formAction
=
form
.
attr
(
'action'
)
formData
=
form
.
serialize
()
issuesUrl
=
formAction
issuesUrl
+=
(
"
#{
if
formAction
.
indexOf
(
"?"
)
<
0
then
'?'
else
'&'
}
"
)
issuesUrl
+=
formData
$
.
ajax
type
:
"GET"
url
:
formAction
data
:
formData
complete
:
->
$
(
'.issues-holder, .merge-requests-holder'
).
css
(
"opacity"
,
'1.0'
)
success
:
(
data
)
->
$
(
'.issues-holder, .merge-requests-holder'
).
html
(
data
.
html
)
# Change url so if user reload a page - search results are saved
history
.
replaceState
{
page
:
issuesUrl
},
document
.
title
,
issuesUrl
Issues
.
reload
()
Issues
.
updateStateFilters
()
dataType
:
"json"
checkChanged
:
->
checked_issues
=
$
(
".selected_issue:checked"
)
if
checked_issues
.
length
>
0
...
...
app/assets/javascripts/labels_select.js.coffee
View file @
7e0ef892
...
...
@@ -6,7 +6,7 @@ class @LabelsSelect
labelUrl
=
$dropdown
.
data
(
'labels'
)
issueUpdateURL
=
$dropdown
.
data
(
'issueUpdate'
)
selectedLabel
=
$dropdown
.
data
(
'selected'
)
if
selectedLabel
?
if
selectedLabel
?
and
not
$dropdown
.
hasClass
'js-multiselect'
selectedLabel
=
selectedLabel
.
split
(
','
)
newLabelField
=
$
(
'#new_label_name'
)
newColorField
=
$
(
'#new_label_color'
)
...
...
@@ -16,6 +16,7 @@ class @LabelsSelect
abilityName
=
$dropdown
.
data
(
'ability-name'
)
$selectbox
=
$dropdown
.
closest
(
'.selectbox'
)
$block
=
$selectbox
.
closest
(
'.block'
)
$form
=
$dropdown
.
closest
(
'form'
)
$sidebarCollapsedValue
=
$block
.
find
(
'.sidebar-collapsed-icon span'
)
$value
=
$block
.
find
(
'.value'
)
$newLabelError
=
$
(
'.js-label-error'
)
...
...
@@ -151,7 +152,7 @@ class @LabelsSelect
.
find
(
'a'
)
.
each
((
i
)
->
setTimeout
(
=>
gl
A
nimate
(
$
(
@
),
'pulse'
)
gl
.
animate
.
a
nimate
(
$
(
@
),
'pulse'
)
,
200
*
i
)
)
...
...
@@ -180,16 +181,21 @@ class @LabelsSelect
callback
data
renderRow
:
(
label
)
->
selectedClass
=
''
if
$selectbox
.
find
(
"input[type='hidden']
\
[name='
#{
$dropdown
.
data
(
'field-name'
)
}
']
\
[value='
#{
label
.
id
}
']"
).
length
selectedClass
=
'is-active'
removesAll
=
label
.
id
is
0
or
not
label
.
id
?
selectedClass
=
[]
if
$form
.
find
(
"input[type='hidden']
\
[name='
#{
$dropdown
.
data
(
'fieldName'
)
}
']
\
[value='
#{
this
.
id
(
label
)
}
']"
).
length
selectedClass
.
push
'is-active'
if
$dropdown
.
hasClass
(
'js-multiselect'
)
and
removesAll
selectedClass
.
push
'dropdown-clear-active'
color
=
if
label
.
color
?
then
"<span class='dropdown-label-box' style='background-color:
#{
label
.
color
}
'></span>"
else
""
"<li>
<a href='#' class='
#{
selectedClass
}
'>
<a href='#' class='
#{
selectedClass
.
join
(
' '
)
}
'>
#{
color
}
#{
_
.
escape
(
label
.
title
)
}
</a>
...
...
@@ -199,37 +205,56 @@ class @LabelsSelect
fields
:
[
'title'
]
selectable
:
true
toggleLabel
:
(
selected
)
->
toggleLabel
:
(
selected
,
el
)
->
selected_labels
=
$
(
'.js-label-select'
).
siblings
(
'.dropdown-menu-labels'
).
find
(
'.is-active'
)
if
selected
and
selected
.
title
?
if
selected_labels
.
length
>
1
"
#{
selected
.
title
}
+
#{
selected_labels
.
length
-
1
}
more"
else
selected
.
title
else
if
not
selected
and
selected_labels
.
length
isnt
0
if
selected_labels
.
length
>
1
"
#{
$
(
selected_labels
[
0
]).
text
()
}
+
#{
selected_labels
.
length
-
1
}
more"
else
if
selected_labels
.
length
is
1
$
(
selected_labels
).
text
()
else
defaultLabel
fieldName
:
$dropdown
.
data
(
'field-name'
)
id
:
(
label
)
->
if
label
.
isAny
?
''
else
if
$dropdown
.
hasClass
"js-filter-submit"
if
$dropdown
.
hasClass
(
"js-filter-submit"
)
and
not
label
.
isAny
?
label
.
title
else
label
.
id
hidden
:
->
page
=
$
(
'body'
).
data
'page'
isIssueIndex
=
page
is
'projects:issues:index'
isMRIndex
=
page
is
'projects:merge_requests:index'
$selectbox
.
hide
()
# display:block overrides the hide-collapse rule
$value
.
removeAttr
(
'style'
)
if
$dropdown
.
hasClass
'js-multiselect'
if
$dropdown
.
hasClass
(
'js-filter-submit'
)
and
(
isIssueIndex
or
isMRIndex
)
selectedLabels
=
$dropdown
.
closest
(
'form'
)
.
find
(
"input:hidden[name='
#{
$dropdown
.
data
(
'fieldName'
)
}
']"
)
Issuable
.
filterResults
$dropdown
.
closest
(
'form'
)
else
if
$dropdown
.
hasClass
(
'js-filter-submit'
)
$dropdown
.
closest
(
'form'
).
submit
()
else
saveLabelData
()
multiSelect
:
$dropdown
.
hasClass
'js-multiselect'
clicked
:
(
label
)
->
page
=
$
(
'body'
).
data
'page'
isIssueIndex
=
page
is
'projects:issues:index'
isMRIndex
=
page
is
page
is
'projects:merge_requests:index'
isMRIndex
=
page
is
'projects:merge_requests:index'
if
$dropdown
.
hasClass
(
'js-filter-submit'
)
and
(
isIssueIndex
or
isMRIndex
)
if
not
$dropdown
.
hasClass
'js-multiselect'
selectedLabel
=
label
.
title
Issues
.
filterResults
$dropdown
.
closest
(
'form'
)
Issuable
.
filterResults
$dropdown
.
closest
(
'form'
)
else
if
$dropdown
.
hasClass
'js-filter-submit'
$dropdown
.
closest
(
'form'
).
submit
()
else
...
...
app/assets/javascripts/lib/animate.js.coffee
View file @
7e0ef892
((
w
)
->
if
not
w
.
gl
?
then
w
.
gl
=
{}
if
not
gl
.
animate
?
then
gl
.
animate
=
{}
w
.
glAnimate
=
(
$el
,
animation
,
done
)
->
gl
.
animate
.
animate
=
(
$el
,
animation
,
options
,
done
)
->
if
options
?
.
cssStart
?
$el
.
css
(
options
.
cssStart
)
$el
.
removeClass
()
.
removeClass
(
animation
+
' animated'
)
.
addClass
(
animation
+
' animated'
)
.
one
'webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend'
,
->
$
(
this
).
removeClass
()
return
$
(
this
).
removeClass
(
animation
+
' animated'
)
if
done
?
done
()
if
options
?
.
cssEnd
?
$el
.
css
(
options
.
cssEnd
)
return
return
gl
.
animate
.
animateEach
=
(
$els
,
animation
,
time
,
options
,
done
)
->
dfd
=
$
.
Deferred
()
if
not
$els
.
length
dfd
.
resolve
()
$els
.
each
((
i
)
->
setTimeout
(
=>
$this
=
$
(
@
)
gl
.
animate
.
animate
(
$this
,
animation
,
options
,
=>
if
i
is
$els
.
length
-
1
dfd
.
resolve
()
if
done
?
done
()
)
,
time
*
i
)
return
)
return
dfd
.
promise
()
return
)
window
\ No newline at end of file
app/assets/javascripts/lib/url_utility.js.coffee
View file @
7e0ef892
...
...
@@ -3,16 +3,20 @@
w
.
gl
?=
{}
w
.
gl
.
utils
?=
{}
w
.
gl
.
utils
.
getUrlParameter
=
(
sParam
)
->
# Returns an array containing the value(s) of the
# of the key passed as an argument
w
.
gl
.
utils
.
getParameterValues
=
(
sParam
)
->
sPageURL
=
decodeURIComponent
(
window
.
location
.
search
.
substring
(
1
))
sURLVariables
=
sPageURL
.
split
(
'&'
)
sParameterName
=
undefined
values
=
[]
i
=
0
while
i
<
sURLVariables
.
length
sParameterName
=
sURLVariables
[
i
].
split
(
'='
)
if
sParameterName
[
0
]
is
sParam
return
if
sParameterName
[
1
]
is
undefined
then
true
else
sParameterName
[
1
]
values
.
push
(
sParameterName
[
1
])
i
++
values
# #
# @param {Object} params - url keys and value to merge
...
...
@@ -28,4 +32,12 @@
newUrl
=
"
#{
newUrl
}#{
(
if
newUrl
.
indexOf
(
'?'
)
>
0
then
'&'
else
'?'
)
}#{
paramName
}
=
#{
paramValue
}
"
newUrl
# removes parameter query string from url. returns the modified url
w
.
gl
.
utils
.
removeParamQueryString
=
(
url
,
param
)
->
url
=
decodeURIComponent
(
url
)
urlVariables
=
url
.
split
(
'&'
)
(
variables
for
variables
in
urlVariables
when
variables
.
indexOf
(
param
)
is
-
1
).
join
(
'&'
)
)
window
app/assets/javascripts/merge_requests.js.coffee
deleted
100644 → 0
View file @
a5290ac2
#
# * Filter merge requests
#
@
MergeRequests
=
init
:
->
MergeRequests
.
initSearch
()
# Make sure we trigger ajax request only after user stop typing
initSearch
:
->
@
timer
=
null
$
(
"#issue_search"
).
keyup
->
clearTimeout
(
@
timer
)
@
timer
=
setTimeout
(
MergeRequests
.
filterResults
,
500
)
filterResults
:
=>
form
=
$
(
"#issue_search_form"
)
search
=
$
(
"#issue_search"
).
val
()
$
(
'.merge-requests-holder'
).
css
(
"opacity"
,
'0.5'
)
issues_url
=
form
.
attr
(
'action'
)
+
'?'
+
form
.
serialize
()
$
.
ajax
type
:
"GET"
url
:
form
.
attr
(
'action'
)
data
:
form
.
serialize
()
complete
:
->
$
(
'.merge-requests-holder'
).
css
(
"opacity"
,
'1.0'
)
success
:
(
data
)
->
$
(
'.merge-requests-holder'
).
html
(
data
.
html
)
# Change url so if user reload a page - search results are saved
history
.
replaceState
{
page
:
issues_url
},
document
.
title
,
issues_url
MergeRequests
.
reload
()
dataType
:
"json"
reload
:
->
$
(
'#filter_issue_search'
).
val
(
$
(
'#issue_search'
).
val
())
app/assets/javascripts/milestone_select.js.coffee
View file @
7e0ef892
...
...
@@ -97,7 +97,7 @@ class @MilestoneSelect
selectedMilestone
=
selected
.
name
else
selectedMilestone
=
''
Issu
es
.
filterResults
$dropdown
.
closest
(
'form'
)
Issu
able
.
filterResults
$dropdown
.
closest
(
'form'
)
else
if
$dropdown
.
hasClass
(
'js-filter-submit'
)
$dropdown
.
closest
(
'form'
).
submit
()
else
...
...
app/assets/javascripts/raven_config.js.coffee
0 → 100644
View file @
7e0ef892
@
raven
=
init
:
->
if
gon
.
sentry_dsn
?
Raven
.
config
(
gon
.
sentry_dsn
,
{
includePaths
:
[
/gon.relative_url_root/
]
ignoreErrors
:
[
# Random plugins/extensions
'top.GLOBALS'
,
# See: http://blog.errorception.com/2012/03/tale-of-unfindable-js-error. html
'originalCreateNotification'
,
'canvas.contentDocument'
,
'MyApp_RemoveAllHighlights'
,
'http://tt.epicplay.com'
,
'Can
\'
t find variable: ZiteReader'
,
'jigsaw is not defined'
,
'ComboSearch is not defined'
,
'http://loading.retry.widdit.com/'
,
'atomicFindClose'
,
# ISP "optimizing" proxy - `Cache-Control: no-transform` seems to
# reduce this. (thanks @acdha)
# See http://stackoverflow.com/questions/4113268
'bmi_SafeAddOnload'
,
'EBCallBackMessageReceived'
,
# See http://toolbar.conduit.com/Developer/HtmlAndGadget/Methods/JSInjection.aspx
'conduitPage'
],
ignoreUrls
:
[
# Chrome extensions
/extensions\//i
,
/^chrome:\/\//i
,
# Other plugins
/127\.0\.0\.1:4001\/isrunning/i
,
# Cacaoweb
/webappstoolbarba\.texthelp\.com\//i
,
/metrics\.itunes\.apple\.com\.edgesuite\.net\//i
]
}).
install
()
if
gon
.
current_user_id
Raven
.
setUserContext
({
id
:
gon
.
current_user_id
})
$
->
raven
.
init
()
app/assets/javascripts/users_select.js.coffee
View file @
7e0ef892
...
...
@@ -158,7 +158,7 @@ class @UsersSelect
if
$dropdown
.
hasClass
(
'js-filter-submit'
)
and
(
isIssueIndex
or
isMRIndex
)
selectedId
=
user
.
id
Issu
es
.
filterResults
$dropdown
.
closest
(
'form'
)
Issu
able
.
filterResults
$dropdown
.
closest
(
'form'
)
else
if
$dropdown
.
hasClass
'js-filter-submit'
$dropdown
.
closest
(
'form'
).
submit
()
else
...
...
app/assets/stylesheets/framework/dropdowns.scss
View file @
7e0ef892
...
...
@@ -248,7 +248,7 @@
.dropdown-title
{
position
:
relative
;
padding
:
0
25px
1
5
px
;
padding
:
0
25px
1
0
px
;
margin
:
0
10px
10px
;
font-weight
:
600
;
line-height
:
1
;
...
...
@@ -278,7 +278,7 @@
right
:
5px
;
width
:
20px
;
height
:
20px
;
top
:
-
1
px
;
top
:
-
3
px
;
}
.dropdown-menu-back
{
...
...
@@ -358,6 +358,13 @@
border-top
:
1px
solid
$dropdown-divider-color
;
}
.dropdown-due-date-footer
{
padding-top
:
0
;
margin-left
:
10px
;
margin-right
:
10px
;
border-top
:
0
;
}
.dropdown-footer-list
{
font-size
:
14px
;
...
...
@@ -395,3 +402,122 @@
height
:
15px
;
border-radius
:
$border-radius-base
;
}
.dropdown-menu-due-date
{
.dropdown-content
{
max-height
:
230px
;
}
.ui-widget
{
table
{
margin
:
0
;
}
&
.ui-datepicker-inline
{
padding
:
0
10px
;
border
:
0
;
width
:
100%
;
}
.ui-datepicker-header
{
padding
:
0
8px
10px
;
border
:
0
;
.ui-icon
{
background
:
none
;
font-size
:
20px
;
text-indent
:
0
;
&
:before
{
display
:
block
;
position
:
relative
;
top
:
-2px
;
color
:
$dropdown-title-btn-color
;
font
:
normal
normal
normal
14px
/
1
FontAwesome
;
font-size
:
inherit
;
text-rendering
:
auto
;
-webkit-font-smoothing
:
antialiased
;
-moz-osx-font-smoothing
:
grayscale
;
}
}
}
.ui-state-active
,
.ui-state-hover
{
color
:
$md-link-color
;
background-color
:
$calendar-hover-bg
;
}
.ui-datepicker-prev
,
.ui-datepicker-next
{
top
:
0
;
height
:
15px
;
cursor
:
pointer
;
&
:hover
{
background-color
:
transparent
;
border
:
0
;
.ui-icon
:before
{
color
:
$md-link-color
;
}
}
}
.ui-datepicker-prev
{
left
:
0
;
.ui-icon
:before
{
content
:
'\f104'
;
text-align
:
left
;
}
}
.ui-datepicker-next
{
right
:
0
;
.ui-icon
:before
{
content
:
'\f105'
;
text-align
:
right
;
}
}
td
{
padding
:
0
;
border
:
1px
solid
$calendar-border-color
;
&
:first-child
{
border-left
:
0
;
}
&
:last-child
{
border-right
:
0
;
}
a
{
line-height
:
17px
;
border
:
0
;
border-radius
:
0
;
}
}
.ui-datepicker-title
{
color
:
$gl-gray
;
font-size
:
15px
;
line-height
:
1
;
font-weight
:
normal
;
}
}
th
{
padding
:
2px
0
;
color
:
$calendar-header-color
;
font-weight
:
normal
;
text-transform
:
lowercase
;
border-top
:
1px
solid
$calendar-border-color
;
}
.ui-datepicker-unselectable
{
background-color
:
$calendar-unselectable-bg
;
}
}
app/assets/stylesheets/framework/markdown_area.scss
View file @
7e0ef892
...
...
@@ -90,3 +90,12 @@
box-shadow
:
none
;
width
:
100%
;
}
.md
{
&
.md-preview-holder
{
code
{
white-space
:
pre-wrap
;
word-break
:
break-all
;
}
}
}
app/assets/stylesheets/framework/variables.scss
View file @
7e0ef892
...
...
@@ -241,3 +241,8 @@ $note-form-border-color: #e5e5e5;
$note-toolbar-color
:
#959494
;
$zen-control-hover-color
:
#111
;
$calendar-header-color
:
#b8b8b8
;
$calendar-hover-bg
:
#ecf3fe
;
$calendar-border-color
:
rgba
(
#000
,
.1
);
$calendar-unselectable-bg
:
#faf9f9
;
app/assets/stylesheets/pages/detail_page.scss
View file @
7e0ef892
...
...
@@ -36,4 +36,10 @@
}
}
}
.wiki
{
code
{
white-space
:
pre-wrap
;
}
}
}
app/assets/stylesheets/pages/diff.scss
View file @
7e0ef892
...
...
@@ -34,6 +34,7 @@
background
:
#fff
;
color
:
#333
;
border-radius
:
0
0
3px
3px
;
-webkit-overflow-scrolling
:
auto
;
.unfold
{
cursor
:
pointer
;
...
...
@@ -86,7 +87,7 @@
}
span
{
white-space
:
pre
;
white-space
:
pre
-wrap
;
}
}
}
...
...
@@ -335,7 +336,7 @@
}
.diff-file
.line_content
{
white-space
:
pre
;
white-space
:
pre
-wrap
;
}
.diff-wrap-lines
.line_content
{
...
...
app/assets/stylesheets/pages/help.scss
View file @
7e0ef892
...
...
@@ -59,8 +59,10 @@
position
:
relative
;
overflow-y
:
auto
;
padding
:
15px
;
.form-actions
{
margin
:
-
$gl-padding
+
1
;
margin-top
:
15px
;
}
}
...
...
app/assets/stylesheets/pages/issuable.scss
View file @
7e0ef892
...
...
@@ -128,6 +128,7 @@
top
:
58px
;
bottom
:
0
;
right
:
0
;
z-index
:
10
;
transition
:
width
.3s
;
background
:
$gray-light
;
padding
:
10px
20px
;
...
...
@@ -241,7 +242,7 @@
}
}
.
btn
{
.
issuable-pager
{
background
:
$gray-normal
;
border
:
1px
solid
$border-gray-normal
;
&
:hover
{
...
...
@@ -254,7 +255,7 @@
}
}
a
:not
(
.
btn
)
{
a
:not
(
.
issuable-pager
)
{
&
:hover
{
color
:
$md-link-color
;
text-decoration
:
none
;
...
...
app/assets/stylesheets/pages/note_form.scss
View file @
7e0ef892
...
...
@@ -84,18 +84,6 @@
border-color
:
$gl-success
;
}
}
p
{
code
{
white-space
:
normal
;
}
pre
{
code
{
white-space
:
pre
;
}
}
}
}
}
...
...
app/assets/stylesheets/pages/notes.scss
View file @
7e0ef892
...
...
@@ -81,16 +81,8 @@ ul.notes {
@include
md-typography
;
// On diffs code should wrap nicely and not overflow
p
{
code
{
white-space
:
normal
;
}
pre
{
code
{
white-space
:
pre
;
}
}
white-space
:
pre-wrap
;
}
// Reset ul style types since we're nested inside a ul already
...
...
@@ -137,7 +129,7 @@ ul.notes {
margin-right
:
10px
;
}
.line_content
{
white-space
:
pre
;
white-space
:
pre
-wrap
;
}
}
...
...
@@ -171,11 +163,6 @@ ul.notes {
&
.parallel
{
border-width
:
1px
;
.code
,
code
{
white-space
:
pre-wrap
;
}
}
.notes
{
...
...
@@ -308,7 +295,7 @@ ul.notes {
padding
:
4px
;
font-size
:
16px
;
color
:
$gl-link-color
;
margin-left
:
-
60
px
;
margin-left
:
-
56
px
;
position
:
absolute
;
z-index
:
10
;
width
:
32px
;
...
...
app/controllers/projects/issues_controller.rb
View file @
7e0ef892
...
...
@@ -33,14 +33,15 @@ class Projects::IssuesController < Projects::ApplicationController
end
@issues
=
@issues
.
page
(
params
[
:page
])
@label
=
@project
.
labels
.
find_by
(
title:
params
[
:label_name
])
@label
s
=
@project
.
labels
.
where
(
title:
params
[
:label_name
])
respond_to
do
|
format
|
format
.
html
format
.
atom
{
render
layout:
false
}
format
.
json
do
render
json:
{
html:
view_to_html_string
(
"projects/issues/_issues"
)
html:
view_to_html_string
(
"projects/issues/_issues"
),
labels:
@labels
.
as_json
(
methods: :text_color
)
}
end
end
...
...
@@ -191,7 +192,7 @@ class Projects::IssuesController < Projects::ApplicationController
def
issue_params
params
.
require
(
:issue
).
permit
(
:title
,
:assignee_id
,
:position
,
:description
,
:confidential
,
:milestone_id
,
:state_event
,
:task_num
,
label_ids:
[]
:milestone_id
,
:
due_date
,
:
state_event
,
:task_num
,
label_ids:
[]
)
end
...
...
app/controllers/projects/merge_requests_controller.rb
View file @
7e0ef892
...
...
@@ -38,13 +38,14 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_requests
=
@merge_requests
.
page
(
params
[
:page
])
@merge_requests
=
@merge_requests
.
preload
(
:target_project
)
@label
=
@project
.
labels
.
find_by
(
title:
params
[
:label_name
])
@label
s
=
@project
.
labels
.
where
(
title:
params
[
:label_name
])
respond_to
do
|
format
|
format
.
html
format
.
json
do
render
json:
{
html:
view_to_html_string
(
"projects/merge_requests/_merge_requests"
)
html:
view_to_html_string
(
"projects/merge_requests/_merge_requests"
),
labels:
@labels
.
as_json
(
methods: :text_color
)
}
end
end
...
...
app/finders/issuable_finder.rb
View file @
7e0ef892
...
...
@@ -39,6 +39,7 @@ class IssuableFinder
items
=
by_assignee
(
items
)
items
=
by_author
(
items
)
items
=
by_label
(
items
)
items
=
by_due_date
(
items
)
sort
(
items
)
end
...
...
@@ -117,7 +118,7 @@ class IssuableFinder
end
def
filter_by_no_label?
labels?
&&
params
[
:label_name
]
==
Label
::
None
.
title
labels?
&&
params
[
:label_name
]
.
include?
(
Label
::
None
.
title
)
end
def
labels
...
...
@@ -278,9 +279,47 @@ class IssuableFinder
end
end
# When filtering by multiple labels we may end up duplicating issues (if one
# has multiple labels). This ensures we only return unique issues.
items
.
distinct
end
def
by_due_date
(
items
)
if
due_date?
if
filter_by_no_due_date?
items
=
items
.
without_due_date
elsif
filter_by_overdue?
items
=
items
.
due_before
(
Date
.
today
)
elsif
filter_by_due_this_week?
items
=
items
.
due_between
(
Date
.
today
.
beginning_of_week
,
Date
.
today
.
end_of_week
)
elsif
filter_by_due_this_month?
items
=
items
.
due_between
(
Date
.
today
.
beginning_of_month
,
Date
.
today
.
end_of_month
)
end
end
items
end
def
filter_by_no_due_date?
due_date?
&&
params
[
:due_date
]
==
Issue
::
NoDueDate
.
name
end
def
filter_by_overdue?
due_date?
&&
params
[
:due_date
]
==
Issue
::
Overdue
.
name
end
def
filter_by_due_this_week?
due_date?
&&
params
[
:due_date
]
==
Issue
::
DueThisWeek
.
name
end
def
filter_by_due_this_month?
due_date?
&&
params
[
:due_date
]
==
Issue
::
DueThisMonth
.
name
end
def
due_date?
params
[
:due_date
].
present?
&&
klass
.
column_names
.
include?
(
'due_date'
)
end
def
label_names
params
[
:label_name
].
split
(
','
)
end
...
...
app/helpers/application_helper.rb
View file @
7e0ef892
...
...
@@ -254,11 +254,11 @@ module ApplicationHelper
def
page_filter_path
(
options
=
{})
without
=
options
.
delete
(
:without
)
add_label
=
options
.
delete
(
:label
)
exist_opts
=
{
state:
params
[
:state
],
scope:
params
[
:scope
],
label_name:
params
[
:label_name
],
milestone_title:
params
[
:milestone_title
],
assignee_id:
params
[
:assignee_id
],
author_id:
params
[
:author_id
],
...
...
@@ -275,6 +275,13 @@ module ApplicationHelper
path
=
request
.
path
path
<<
"?
#{
options
.
to_param
}
"
if
add_label
if
params
[
:label_name
].
present?
and
params
[
:label_name
].
respond_to?
(
'any?'
)
params
[
:label_name
].
each
do
|
label
|
path
<<
"&label_name[]=
#{
label
}
"
end
end
end
path
end
...
...
app/helpers/issuables_helper.rb
View file @
7e0ef892
...
...
@@ -16,6 +16,25 @@ module IssuablesHelper
base_issuable_scope
(
issuable
).
where
(
'iid > ?'
,
issuable
.
iid
).
last
end
def
multi_label_name
(
current_labels
,
default_label
)
# current_labels may be a string from before
if
current_labels
.
is_a?
(
Array
)
if
current_labels
.
count
>
1
"
#{
current_labels
[
0
]
}
+
#{
current_labels
.
count
-
1
}
more"
else
current_labels
[
0
]
end
elsif
current_labels
.
is_a?
(
String
)
if
current_labels
.
nil?
||
current_labels
.
empty?
default_label
else
current_labels
end
else
default_label
end
end
def
issuable_json_path
(
issuable
)
project
=
issuable
.
project
...
...
app/helpers/issues_helper.rb
View file @
7e0ef892
...
...
@@ -172,6 +172,18 @@ module IssuesHelper
end
.
to_h
end
def
due_date_options
options
=
[
Issue
::
AnyDueDate
,
Issue
::
NoDueDate
,
Issue
::
DueThisWeek
,
Issue
::
DueThisMonth
,
Issue
::
Overdue
]
options_from_collection_for_select
(
options
,
'name'
,
'title'
,
params
[
:due_date
])
end
# Required for Banzai::Filter::IssueReferenceFilter
module_function
:url_for_issue
end
app/helpers/sorting_helper.rb
View file @
7e0ef892
...
...
@@ -8,6 +8,8 @@ module SortingHelper
sort_value_oldest_created
=>
sort_title_oldest_created
,
sort_value_milestone_soon
=>
sort_title_milestone_soon
,
sort_value_milestone_later
=>
sort_title_milestone_later
,
sort_value_due_date_soon
=>
sort_title_due_date_soon
,
sort_value_due_date_later
=>
sort_title_due_date_later
,
sort_value_largest_repo
=>
sort_title_largest_repo
,
sort_value_recently_signin
=>
sort_title_recently_signin
,
sort_value_oldest_signin
=>
sort_title_oldest_signin
,
...
...
@@ -50,6 +52,14 @@ module SortingHelper
'Milestone due later'
end
def
sort_title_due_date_soon
'Due soon'
end
def
sort_title_due_date_later
'Due later'
end
def
sort_title_name
'Name'
end
...
...
@@ -98,6 +108,14 @@ module SortingHelper
'milestone_due_desc'
end
def
sort_value_due_date_soon
'due_date_asc'
end
def
sort_value_due_date_later
'due_date_desc'
end
def
sort_value_name
'name_asc'
end
...
...
app/models/issue.rb
View file @
7e0ef892
...
...
@@ -28,6 +28,13 @@ class Issue < ActiveRecord::Base
include
Sortable
include
Taskable
DueDateStruct
=
Struct
.
new
(
:title
,
:name
).
freeze
NoDueDate
=
DueDateStruct
.
new
(
'No Due Date'
,
'0'
).
freeze
AnyDueDate
=
DueDateStruct
.
new
(
'Any Due Date'
,
''
).
freeze
Overdue
=
DueDateStruct
.
new
(
'Overdue'
,
'overdue'
).
freeze
DueThisWeek
=
DueDateStruct
.
new
(
'Due This Week'
,
'week'
).
freeze
DueThisMonth
=
DueDateStruct
.
new
(
'Due This Month'
,
'month'
).
freeze
ActsAsTaggableOn
.
strict_case_match
=
true
belongs_to
:project
...
...
@@ -39,6 +46,13 @@ class Issue < ActiveRecord::Base
scope
:open_for
,
->
(
user
)
{
opened
.
assigned_to
(
user
)
}
scope
:in_projects
,
->
(
project_ids
)
{
where
(
project_id:
project_ids
)
}
scope
:without_due_date
,
->
{
where
(
due_date:
nil
)
}
scope
:due_before
,
->
(
date
)
{
where
(
'issues.due_date < ?'
,
date
)
}
scope
:due_between
,
->
(
from_date
,
to_date
)
{
where
(
'issues.due_date >= ?'
,
from_date
).
where
(
'issues.due_date <= ?'
,
to_date
)
}
scope
:order_due_date_asc
,
->
{
reorder
(
'issues.due_date IS NULL, issues.due_date ASC'
)
}
scope
:order_due_date_desc
,
->
{
reorder
(
'issues.due_date IS NULL, issues.due_date DESC'
)
}
state_machine
:state
,
initial: :opened
do
event
:close
do
transition
[
:reopened
,
:opened
]
=>
:closed
...
...
@@ -82,6 +96,15 @@ class Issue < ActiveRecord::Base
@link_reference_pattern
||=
super
(
"issues"
,
/(?<issue>\d+)/
)
end
def
self
.
sort
(
method
)
case
method
.
to_s
when
'due_date_asc'
then
order_due_date_asc
when
'due_date_desc'
then
order_due_date_desc
else
super
end
end
def
to_reference
(
from_project
=
nil
)
reference
=
"
#{
self
.
class
.
reference_prefix
}#{
iid
}
"
...
...
@@ -169,4 +192,8 @@ class Issue < ActiveRecord::Base
self
.
related_branches
(
current_user
).
empty?
&&
self
.
closed_by_merge_requests
(
current_user
).
empty?
end
def
overdue?
due_date
.
try
(
:past?
)
||
false
end
end
app/models/label.rb
View file @
7e0ef892
...
...
@@ -113,6 +113,10 @@ class Label < ActiveRecord::Base
template
end
def
text_color
LabelsHelper
::
text_color_for_bg
(
self
.
color
)
end
private
def
label_format_reference
(
format
=
:id
)
...
...
app/views/projects/_activity.html.haml
View file @
7e0ef892
...
...
@@ -9,4 +9,7 @@
=
spinner
:javascript
new
Activities
();
var
activity
=
new
Activities
();
$
(
document
).
on
(
'
page:restore
'
,
function
(
event
)
{
activity
.
reloadActivities
()
})
app/views/projects/issues/_issue.html.haml
View file @
7e0ef892
...
...
@@ -48,6 +48,11 @@
=
link_to
namespace_project_issues_path
(
issue
.
project
.
namespace
,
issue
.
project
,
milestone_title:
issue
.
milestone
.
title
)
do
=
icon
(
'clock-o'
)
=
issue
.
milestone
.
title
-
if
issue
.
due_date
%span
{
class:
"#{'cred' if issue.overdue?}"
}
=
icon
(
'calendar'
)
=
issue
.
due_date
.
to_s
(
:medium
)
-
if
issue
.
labels
.
any?
-
issue
.
labels
.
each
do
|
label
|
...
...
app/views/projects/merge_requests/show/_mr_title.html.haml
View file @
7e0ef892
...
...
@@ -24,9 +24,9 @@
%li
{
class:
issue_button_visibility
(
@merge_request
,
false
)
}
=
link_to
'Reopen'
,
merge_request_path
(
@merge_request
,
merge_request:
{
state_event: :reopen
}),
method: :put
,
class:
'reopen-mr-link'
,
title:
'Reopen merge request'
%li
=
link_to
'Edit'
,
edit_namespace_project_merge_request_path
(
@project
.
namespace
,
@project
,
@merge_request
),
class:
'issuable-edit'
,
id:
'edit_merge_request'
=
link_to
'Edit'
,
edit_namespace_project_merge_request_path
(
@project
.
namespace
,
@project
,
@merge_request
),
class:
'issuable-edit'
=
link_to
'Close'
,
merge_request_path
(
@merge_request
,
merge_request:
{
state_event: :close
}),
method: :put
,
class:
"hidden-xs hidden-sm btn btn-nr btn-grouped btn-close
#{
issue_button_visibility
(
@merge_request
,
true
)
}
"
,
title:
'Close merge request'
=
link_to
'Reopen'
,
merge_request_path
(
@merge_request
,
merge_request:
{
state_event: :reopen
}),
method: :put
,
class:
"hidden-xs hidden-sm btn btn-nr btn-grouped btn-reopen reopen-mr-link
#{
issue_button_visibility
(
@merge_request
,
false
)
}
"
,
title:
'Reopen merge request'
=
link_to
edit_namespace_project_merge_request_path
(
@project
.
namespace
,
@project
,
@merge_request
),
class:
"hidden-xs hidden-sm btn btn-nr btn-grouped issuable-edit"
,
id:
'edit_merge_request'
do
=
link_to
edit_namespace_project_merge_request_path
(
@project
.
namespace
,
@project
,
@merge_request
),
class:
"hidden-xs hidden-sm btn btn-nr btn-grouped issuable-edit"
do
=
icon
(
'pencil-square-o'
)
Edit
app/views/shared/_label_row.html.haml
View file @
7e0ef892
app/views/shared/_labels_row.html.haml
0 → 100644
View file @
7e0ef892
-
labels
.
each
do
|
label
|
%span
.label-row
=
link_to_label
(
label
,
tooltip:
false
)
app/views/shared/_sort_dropdown.html.haml
View file @
7e0ef892
...
...
@@ -20,6 +20,11 @@
=
sort_title_milestone_soon
=
link_to
page_filter_path
(
sort:
sort_value_milestone_later
)
do
=
sort_title_milestone_later
-
if
controller
.
controller_name
==
'issues'
||
controller
.
action_name
==
'issues'
=
link_to
page_filter_path
(
sort:
sort_value_due_date_soon
)
do
=
sort_title_due_date_soon
=
link_to
page_filter_path
(
sort:
sort_value_due_date_later
)
do
=
sort_title_due_date_later
=
link_to
page_filter_path
(
sort:
sort_value_upvotes
)
do
=
sort_title_upvotes
=
link_to
page_filter_path
(
sort:
sort_value_downvotes
)
do
...
...
app/views/shared/issuable/_filter.html.haml
View file @
7e0ef892
...
...
@@ -23,6 +23,7 @@
.filter-item.inline.labels-filter
=
render
"shared/issuable/label_dropdown"
.pull-right
=
render
'shared/sort_dropdown'
...
...
@@ -46,9 +47,10 @@
.filter-item.inline
=
button_tag
"Update issues"
,
class:
"btn update_selected_issues btn-save"
-
if
@label
.gray-content-block.second-block
=
render
"shared/label_row"
,
label:
@label
-
if
!
@labels
.
nil?
.gray-content-block.second-block.filtered-labels
{
class:
(
"hidden"
if
!
@labels
.
any?
)
}
-
if
@labels
.
any?
=
render
"shared/labels_row"
,
labels:
@labels
:javascript
new
UsersSelect
();
...
...
app/views/shared/issuable/_label_dropdown.html.haml
View file @
7e0ef892
-
if
params
[
:label_name
].
present?
=
hidden_field_tag
(
:label_name
,
params
[
:label_name
])
-
if
params
[
:label_name
].
respond_to?
(
'any?'
)
-
params
[
:label_name
].
each
do
|
label
|
=
hidden_field_tag
"label_name[]"
,
label
,
id:
nil
.dropdown
%button
.dropdown-menu-toggle.js-label-select.js-filter-submit.js-
extra-options
{
type:
"button"
,
data:
{
toggle:
"dropdown"
,
field_name:
"label_name
"
,
show_no:
"true"
,
show_any:
"true"
,
selected:
params
[
:label_name
],
project_id:
@project
.
try
(
:id
),
labels:
labels_filter_path
,
default_label:
"Label"
}}
%button
.dropdown-menu-toggle.js-label-select.js-filter-submit.js-
multiselect.js-extra-options
{
type:
"button"
,
data:
{
toggle:
"dropdown"
,
field_name:
"label_name[]
"
,
show_no:
"true"
,
show_any:
"true"
,
selected:
params
[
:label_name
],
project_id:
@project
.
try
(
:id
),
labels:
labels_filter_path
,
default_label:
"Label"
}}
%span
.dropdown-toggle-text
=
h
(
params
[
:label_name
].
presence
||
"Label"
)
=
h
(
multi_label_name
(
params
[
:label_name
],
"Label"
)
)
=
icon
(
'chevron-down'
)
.dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable
=
render
partial:
"shared/issuable/label_page_default"
,
locals:
{
title:
"Filter by label"
}
...
...
app/views/shared/issuable/_nav.html.haml
View file @
7e0ef892
...
...
@@ -4,22 +4,22 @@
-
else
-
page_context_word
=
'issues'
%li
{
class:
(
"active"
if
params
[
:state
]
==
'opened'
)}
=
link_to
page_filter_path
(
state:
'opened'
),
title:
"Filter by
#{
page_context_word
}
that are currently opened."
do
=
link_to
page_filter_path
(
state:
'opened'
,
label:
true
),
title:
"Filter by
#{
page_context_word
}
that are currently opened."
do
#{
state_filters_text_for
(
:opened
,
@project
)
}
-
if
defined?
(
type
)
&&
type
==
:merge_requests
%li
{
class:
(
"active"
if
params
[
:state
]
==
'merged'
)}
=
link_to
page_filter_path
(
state:
'merged'
),
title:
'Filter by merge requests that are currently merged.'
do
=
link_to
page_filter_path
(
state:
'merged'
,
label:
true
),
title:
'Filter by merge requests that are currently merged.'
do
#{
state_filters_text_for
(
:merged
,
@project
)
}
%li
{
class:
(
"active"
if
params
[
:state
]
==
'closed'
)}
=
link_to
page_filter_path
(
state:
'closed'
),
title:
'Filter by merge requests that are currently closed and unmerged.'
do
=
link_to
page_filter_path
(
state:
'closed'
,
label:
true
),
title:
'Filter by merge requests that are currently closed and unmerged.'
do
#{
state_filters_text_for
(
:closed
,
@project
)
}
-
else
%li
{
class:
(
"active"
if
params
[
:state
]
==
'closed'
)}
=
link_to
page_filter_path
(
state:
'closed'
),
title:
'Filter by issues that are currently closed.'
do
=
link_to
page_filter_path
(
state:
'closed'
,
label:
true
),
title:
'Filter by issues that are currently closed.'
do
#{
state_filters_text_for
(
:closed
,
@project
)
}
%li
{
class:
(
"active"
if
params
[
:state
]
==
'all'
)}
=
link_to
page_filter_path
(
state:
'all'
),
title:
"Show all
#{
page_context_word
}
."
do
=
link_to
page_filter_path
(
state:
'all'
,
label:
true
),
title:
"Show all
#{
page_context_word
}
."
do
#{
state_filters_text_for
(
:all
,
@project
)
}
app/views/shared/issuable/_sidebar.html.haml
View file @
7e0ef892
...
...
@@ -10,14 +10,14 @@
=
sidebar_gutter_toggle_icon
.issuable-nav.hide-collapsed.pull-right.btn-group
{
role:
'group'
,
"aria-label"
=>
'...'
}
-
if
prev_issuable
=
prev_issuable_for
(
issuable
)
=
link_to
'Prev'
,
[
@project
.
namespace
.
becomes
(
Namespace
),
@project
,
prev_issuable
],
class:
'btn btn-default prev-btn'
=
link_to
'Prev'
,
[
@project
.
namespace
.
becomes
(
Namespace
),
@project
,
prev_issuable
],
class:
'btn btn-default prev-btn
issuable-pager
'
-
else
%a
.btn.btn-default.disabled
{
href:
'#'
}
%a
.btn.btn-default.
issuable-pager.
disabled
{
href:
'#'
}
Prev
-
if
next_issuable
=
next_issuable_for
(
issuable
)
=
link_to
'Next'
,
[
@project
.
namespace
.
becomes
(
Namespace
),
@project
,
next_issuable
],
class:
'btn btn-default next-btn'
=
link_to
'Next'
,
[
@project
.
namespace
.
becomes
(
Namespace
),
@project
,
next_issuable
],
class:
'btn btn-default next-btn
issuable-pager
'
-
else
%a
.btn.btn-default.disabled
{
href:
'#'
}
%a
.btn.btn-default.
issuable-pager.
disabled
{
href:
'#'
}
Next
=
form_for
[
@project
.
namespace
.
becomes
(
Namespace
),
@project
,
issuable
],
remote:
true
,
html:
{
class:
'issuable-context-form inline-update js-issuable-update'
}
do
|
f
|
...
...
@@ -58,7 +58,7 @@
-
if
issuable
.
milestone
=
issuable
.
milestone
.
title
-
else
No
No
ne
.title.hide-collapsed
Milestone
=
icon
(
'spinner spin'
,
class:
'block-loading'
)
...
...
@@ -75,6 +75,34 @@
=
f
.
hidden_field
'milestone_id'
,
value:
issuable
.
milestone_id
,
id:
nil
=
dropdown_tag
(
'Milestone'
,
options:
{
title:
'Assign milestone'
,
toggle_class:
'js-milestone-select js-extra-options'
,
filter:
true
,
dropdown_class:
'dropdown-menu-selectable'
,
placeholder:
'Search milestones'
,
data:
{
show_no:
true
,
field_name:
"
#{
issuable
.
to_ability_name
}
[milestone_id]"
,
project_id:
@project
.
id
,
issuable_id:
issuable
.
id
,
milestones:
namespace_project_milestones_path
(
@project
.
namespace
,
@project
,
:json
),
ability_name:
issuable
.
to_ability_name
,
issue_update:
issuable_json_path
(
issuable
),
use_id:
true
}})
-
if
issuable
.
has_attribute?
(
:due_date
)
.block.due_date
.sidebar-collapsed-icon
=
icon
(
'calendar'
)
%span
.js-due-date-sidebar-value
=
issuable
.
due_date
.
try
(
:to_s
,
:medium
)
||
'None'
.title.hide-collapsed
Due date
=
icon
(
'spinner spin'
,
class:
'block-loading'
)
-
if
can?
(
current_user
,
:"admin_
#{
issuable
.
to_ability_name
}
"
,
@project
)
=
link_to
'Edit'
,
'#'
,
class:
'edit-link pull-right'
.value.bold.hide-collapsed
-
if
issuable
.
due_date
=
issuable
.
due_date
.
to_s
(
:medium
)
-
else
.light
None
-
if
can?
(
current_user
,
:"admin_
#{
issuable
.
to_ability_name
}
"
,
@project
)
.selectbox.hide-collapsed
=
f
.
hidden_field
:due_date
,
value:
issuable
.
due_date
.dropdown
%button
.dropdown-menu-toggle.js-due-date-select
{
type:
'button'
,
data:
{
toggle:
'dropdown'
,
field_name:
"#{issuable.to_ability_name}[due_date]"
,
ability_name:
issuable
.
to_ability_name
,
issue_update:
issuable_json_path
(
issuable
)
}
}
%span
.dropdown-toggle-text
Due date
=
icon
(
'chevron-down'
)
.dropdown-menu.dropdown-menu-due-date
=
dropdown_title
(
'Due date'
)
=
dropdown_content
do
.js-due-date-calendar
-
if
issuable
.
project
.
labels
.
any?
.block.labels
.sidebar-collapsed-icon
...
...
@@ -139,3 +167,4 @@
new
IssuableContext
(
'
#{
escape_javascript
(
current_user
.
to_json
(
only:
[
:username
,
:id
,
:name
]))
}
'
);
new
Subscription
(
'
.subscription
'
)
new
Sidebar
();
new
DueDateSelect
();
db/migrate/20160310124959_add_due_date_to_issues.rb
0 → 100644
View file @
7e0ef892
class
AddDueDateToIssues
<
ActiveRecord
::
Migration
def
change
add_column
:issues
,
:due_date
,
:date
add_index
:issues
,
:due_date
end
end
db/schema.rb
View file @
7e0ef892
...
...
@@ -422,6 +422,7 @@ ActiveRecord::Schema.define(version: 20160419120017) do
t
.
integer
"moved_to_id"
t
.
boolean
"confidential"
,
default:
false
t
.
datetime
"deleted_at"
t
.
date
"due_date"
end
add_index
"issues"
,
[
"assignee_id"
],
name:
"index_issues_on_assignee_id"
,
using: :btree
...
...
@@ -431,6 +432,7 @@ ActiveRecord::Schema.define(version: 20160419120017) do
add_index
"issues"
,
[
"created_at"
],
name:
"index_issues_on_created_at"
,
using: :btree
add_index
"issues"
,
[
"deleted_at"
],
name:
"index_issues_on_deleted_at"
,
using: :btree
add_index
"issues"
,
[
"description"
],
name:
"index_issues_on_description_trigram"
,
using: :gin
,
opclasses:
{
"description"
=>
"gin_trgm_ops"
}
add_index
"issues"
,
[
"due_date"
],
name:
"index_issues_on_due_date"
,
using: :btree
add_index
"issues"
,
[
"milestone_id"
],
name:
"index_issues_on_milestone_id"
,
using: :btree
add_index
"issues"
,
[
"project_id"
,
"iid"
],
name:
"index_issues_on_project_id_and_iid"
,
unique:
true
,
using: :btree
add_index
"issues"
,
[
"project_id"
],
name:
"index_issues_on_project_id"
,
using: :btree
...
...
features/project/issues/filter_labels.feature
View file @
7e0ef892
...
...
@@ -12,6 +12,7 @@ Feature: Project Issues Filter Labels
@javascript
Scenario
:
I
filter by one label
Given
I click link
"bug"
And
I click
"dropdown close button"
Then
I should see
"Bugfix1"
in issues list
And
I should see
"Bugfix2"
in issues list
And
I should not see
"Feature1"
in issues list
...
...
features/steps/project/issues/filter_labels.rb
View file @
7e0ef892
...
...
@@ -32,6 +32,10 @@ class Spinach::Features::ProjectIssuesFilterLabels < Spinach::FeatureSteps
page
.
find
(
'.js-label-select'
).
click
sleep
0.5
execute_script
(
"$('.dropdown-menu-labels li:contains(
\"
bug
\"
) a').click()"
)
end
step
'I click "dropdown close button"'
do
page
.
first
(
'.labels-filter .dropdown-title .dropdown-menu-close-icon'
).
click
sleep
2
end
...
...
lib/api/internal.rb
View file @
7e0ef892
...
...
@@ -23,7 +23,7 @@ module API
end
post
"/allowed"
do
Gitlab
::
Metrics
.
tag_transaction
(
'action'
,
'Grape#/internal/allowed'
)
Gitlab
::
Metrics
.
action
=
'Grape#/internal/allowed'
status
200
...
...
lib/gitlab/gon_helper.rb
View file @
7e0ef892
...
...
@@ -7,6 +7,7 @@ module Gitlab
gon
.
max_file_size
=
current_application_settings
.
max_attachment_size
gon
.
relative_url_root
=
Gitlab
.
config
.
gitlab
.
relative_url_root
gon
.
user_color_scheme
=
Gitlab
::
ColorSchemes
.
for_user
(
current_user
).
css_class
gon
.
sentry_dsn
=
ApplicationSetting
.
current
.
sentry_dsn
if
Rails
.
env
.
production?
if
current_user
gon
.
current_user_id
=
current_user
.
id
...
...
lib/gitlab/metrics.rb
View file @
7e0ef892
...
...
@@ -115,6 +115,15 @@ module Gitlab
trans
.
add_tag
(
name
,
value
)
if
trans
end
# Sets the action of the current transaction (if any)
#
# action - The name of the action.
def
self
.
action
=
(
action
)
trans
=
current_transaction
trans
.
action
=
action
if
trans
end
# When enabled this should be set before being used as the usual pattern
# "@foo ||= bar" is _not_ thread-safe.
if
enabled?
...
...
spec/features/issues/filter_by_labels_spec.rb
0 → 100644
View file @
7e0ef892
require
'rails_helper'
feature
'Issue filtering by Labels'
,
feature:
true
do
let
(
:project
)
{
create
(
:project
,
:public
)
}
let!
(
:user
)
{
create
(
:user
)}
let!
(
:label
)
{
create
(
:label
,
project:
project
)
}
before
do
[
'bug'
,
'feature'
,
'enhancement'
].
each
do
|
title
|
create
(
:label
,
project:
project
,
title:
title
)
end
issue1
=
create
(
:issue
,
title:
"Bugfix1"
,
project:
project
)
issue1
.
labels
<<
project
.
labels
.
find_by
(
title:
'bug'
)
issue2
=
create
(
:issue
,
title:
"Bugfix2"
,
project:
project
)
issue2
.
labels
<<
project
.
labels
.
find_by
(
title:
'bug'
)
issue2
.
labels
<<
project
.
labels
.
find_by
(
title:
'enhancement'
)
issue3
=
create
(
:issue
,
title:
"Feature1"
,
project:
project
)
issue3
.
labels
<<
project
.
labels
.
find_by
(
title:
'feature'
)
project
.
team
<<
[
user
,
:master
]
login_as
(
user
)
visit
namespace_project_issues_path
(
project
.
namespace
,
project
)
end
context
'filter by label bug'
,
js:
true
do
before
do
page
.
find
(
'.js-label-select'
).
click
sleep
0.5
execute_script
(
"$('.dropdown-menu-labels li:contains(
\"
bug
\"
) a').click()"
)
page
.
first
(
'.labels-filter .dropdown-title .dropdown-menu-close-icon'
).
click
sleep
2
end
it
'should show issue "Bugfix1" and "Bugfix2" in issues list'
do
expect
(
page
).
to
have_content
"Bugfix1"
expect
(
page
).
to
have_content
"Bugfix2"
end
it
'should not show "Feature1" in issues list'
do
expect
(
page
).
not_to
have_content
"Feature1"
end
it
'should show label "bug" in filtered-labels'
do
expect
(
find
(
'.filtered-labels'
)).
to
have_content
"bug"
end
it
'should not show label "feature" and "enhancement" in filtered-labels'
do
expect
(
find
(
'.filtered-labels'
)).
not_to
have_content
"feature"
expect
(
find
(
'.filtered-labels'
)).
not_to
have_content
"enhancement"
end
end
context
'filter by label feature'
,
js:
true
do
before
do
page
.
find
(
'.js-label-select'
).
click
sleep
0.5
execute_script
(
"$('.dropdown-menu-labels li:contains(
\"
feature
\"
) a').click()"
)
page
.
first
(
'.labels-filter .dropdown-title .dropdown-menu-close-icon'
).
click
sleep
2
end
it
'should show issue "Feature1" in issues list'
do
expect
(
page
).
to
have_content
"Feature1"
end
it
'should not show "Bugfix1" and "Bugfix2" in issues list'
do
expect
(
page
).
not_to
have_content
"Bugfix2"
expect
(
page
).
not_to
have_content
"Bugfix1"
end
it
'should show label "feature" in filtered-labels'
do
expect
(
find
(
'.filtered-labels'
)).
to
have_content
"feature"
end
it
'should not show label "bug" and "enhancement" in filtered-labels'
do
expect
(
find
(
'.filtered-labels'
)).
not_to
have_content
"bug"
expect
(
find
(
'.filtered-labels'
)).
not_to
have_content
"enhancement"
end
end
context
'filter by label enhancement'
,
js:
true
do
before
do
page
.
find
(
'.js-label-select'
).
click
sleep
0.5
execute_script
(
"$('.dropdown-menu-labels li:contains(
\"
enhancement
\"
) a').click()"
)
page
.
first
(
'.labels-filter .dropdown-title .dropdown-menu-close-icon'
).
click
sleep
2
end
it
'should show issue "Bugfix2" in issues list'
do
expect
(
page
).
to
have_content
"Bugfix2"
end
it
'should not show "Feature1" and "Bugfix1" in issues list'
do
expect
(
page
).
not_to
have_content
"Feature1"
expect
(
page
).
not_to
have_content
"Bugfix1"
end
it
'should show label "enhancement" in filtered-labels'
do
expect
(
find
(
'.filtered-labels'
)).
to
have_content
"enhancement"
end
it
'should not show label "feature" and "bug" in filtered-labels'
do
expect
(
find
(
'.filtered-labels'
)).
not_to
have_content
"bug"
expect
(
find
(
'.filtered-labels'
)).
not_to
have_content
"feature"
end
end
context
'filter by label enhancement or feature'
,
js:
true
do
before
do
page
.
find
(
'.js-label-select'
).
click
sleep
0.5
execute_script
(
"$('.dropdown-menu-labels li:contains(
\"
enhancement
\"
) a').click()"
)
execute_script
(
"$('.dropdown-menu-labels li:contains(
\"
feature
\"
) a').click()"
)
page
.
first
(
'.labels-filter .dropdown-title .dropdown-menu-close-icon'
).
click
sleep
2
end
it
'should show issue "Bugfix2" or "Feature1" in issues list'
do
expect
(
page
).
to
have_content
"Bugfix2"
expect
(
page
).
to
have_content
"Feature1"
end
it
'should not show "Bugfix1" in issues list'
do
expect
(
page
).
not_to
have_content
"Bugfix1"
end
it
'should show label "enhancement" and "feature" in filtered-labels'
do
expect
(
find
(
'.filtered-labels'
)).
to
have_content
"enhancement"
expect
(
find
(
'.filtered-labels'
)).
to
have_content
"feature"
end
it
'should not show label "bug" in filtered-labels'
do
expect
(
find
(
'.filtered-labels'
)).
not_to
have_content
"bug"
end
end
context
'filter by label enhancement or bug in issues list'
,
js:
true
do
before
do
page
.
find
(
'.js-label-select'
).
click
sleep
0.5
execute_script
(
"$('.dropdown-menu-labels li:contains(
\"
enhancement
\"
) a').click()"
)
execute_script
(
"$('.dropdown-menu-labels li:contains(
\"
bug
\"
) a').click()"
)
page
.
first
(
'.labels-filter .dropdown-title .dropdown-menu-close-icon'
).
click
sleep
2
end
it
'should show issue "Bugfix2" or "Bugfix1" in issues list'
do
expect
(
page
).
to
have_content
"Bugfix2"
expect
(
page
).
to
have_content
"Bugfix1"
end
it
'should not show "Feature1"'
do
expect
(
page
).
not_to
have_content
"Feature1"
end
it
'should show label "bug" and "enhancement" in filtered-labels'
do
expect
(
find
(
'.filtered-labels'
)).
to
have_content
"bug"
expect
(
find
(
'.filtered-labels'
)).
to
have_content
"enhancement"
end
it
'should not show label "feature" in filtered-labels'
do
expect
(
find
(
'.filtered-labels'
)).
not_to
have_content
"feature"
end
end
end
spec/features/issues/filter_issues_spec.rb
View file @
7e0ef892
...
...
@@ -84,14 +84,20 @@ describe 'Filter issues', feature: true do
it
'should filter by any label'
do
find
(
'.dropdown-menu-labels a'
,
text:
'Any Label'
).
click
page
.
first
(
'.labels-filter .dropdown-title .dropdown-menu-close-icon'
).
click
sleep
2
page
.
within
'.labels-filter'
do
expect
(
page
).
to
have_content
'Any Label'
end
expect
(
find
(
'.js-label-select .dropdown-toggle-text'
)).
to
have_content
(
'Label'
)
expect
(
find
(
'.js-label-select .dropdown-toggle-text'
)).
to
have_content
(
'
Any
Label'
)
end
it
'should filter by no label'
do
find
(
'.dropdown-menu-labels a'
,
text:
'No Label'
).
click
page
.
first
(
'.labels-filter .dropdown-title .dropdown-menu-close-icon'
).
click
sleep
2
page
.
within
'.labels-filter'
do
expect
(
page
).
to
have_content
'No Label'
end
...
...
@@ -121,6 +127,7 @@ describe 'Filter issues', feature: true do
find
(
'.js-label-select'
).
click
find
(
'.dropdown-menu-labels .dropdown-content a'
,
text:
label
.
title
).
click
page
.
first
(
'.labels-filter .dropdown-title .dropdown-menu-close-icon'
).
click
sleep
2
end
...
...
spec/features/issues_spec.rb
View file @
7e0ef892
...
...
@@ -112,7 +112,7 @@ describe 'Issues', feature: true do
end
describe
'filter issue'
do
titles
=
[
'foo'
,
'bar'
,
'baz'
]
titles
=
%w[foo bar baz
]
titles
.
each_with_index
do
|
title
,
index
|
let!
(
title
.
to_sym
)
do
create
(
:issue
,
title:
title
,
...
...
@@ -153,8 +153,94 @@ describe 'Issues', feature: true do
expect
(
first_issue
).
to
include
(
'baz'
)
end
describe
'sorting by due date'
do
before
do
foo
.
update
(
due_date:
1
.
day
.
from_now
)
bar
.
update
(
due_date:
6
.
days
.
from_now
)
end
it
'sorts by recently due date'
do
visit
namespace_project_issues_path
(
project
.
namespace
,
project
,
sort:
sort_value_due_date_soon
)
expect
(
first_issue
).
to
include
(
'foo'
)
end
it
'sorts by least recently due date'
do
visit
namespace_project_issues_path
(
project
.
namespace
,
project
,
sort:
sort_value_due_date_later
)
expect
(
first_issue
).
to
include
(
'bar'
)
end
it
'sorts by least recently due date by excluding nil due dates'
do
bar
.
update
(
due_date:
nil
)
visit
namespace_project_issues_path
(
project
.
namespace
,
project
,
sort:
sort_value_due_date_later
)
expect
(
first_issue
).
to
include
(
'foo'
)
end
end
describe
'filtering by due date'
do
before
do
foo
.
update
(
due_date:
1
.
day
.
from_now
)
bar
.
update
(
due_date:
6
.
days
.
from_now
)
end
it
'filters by none'
do
visit
namespace_project_issues_path
(
project
.
namespace
,
project
,
due_date:
Issue
::
NoDueDate
.
name
)
expect
(
page
).
not_to
have_content
(
'foo'
)
expect
(
page
).
not_to
have_content
(
'bar'
)
expect
(
page
).
to
have_content
(
'baz'
)
end
it
'filters by any'
do
visit
namespace_project_issues_path
(
project
.
namespace
,
project
,
due_date:
Issue
::
AnyDueDate
.
name
)
expect
(
page
).
to
have_content
(
'foo'
)
expect
(
page
).
to
have_content
(
'bar'
)
expect
(
page
).
to
have_content
(
'baz'
)
end
it
'filters by due this week'
do
foo
.
update
(
due_date:
Date
.
today
.
beginning_of_week
+
2
.
days
)
bar
.
update
(
due_date:
Date
.
today
.
end_of_week
)
baz
.
update
(
due_date:
Date
.
today
-
8
.
days
)
visit
namespace_project_issues_path
(
project
.
namespace
,
project
,
due_date:
Issue
::
DueThisWeek
.
name
)
expect
(
page
).
to
have_content
(
'foo'
)
expect
(
page
).
to
have_content
(
'bar'
)
expect
(
page
).
not_to
have_content
(
'baz'
)
end
it
'filters by due this month'
do
foo
.
update
(
due_date:
Date
.
today
.
beginning_of_month
+
2
.
days
)
bar
.
update
(
due_date:
Date
.
today
.
end_of_month
)
baz
.
update
(
due_date:
Date
.
today
-
50
.
days
)
visit
namespace_project_issues_path
(
project
.
namespace
,
project
,
due_date:
Issue
::
DueThisMonth
.
name
)
expect
(
page
).
to
have_content
(
'foo'
)
expect
(
page
).
to
have_content
(
'bar'
)
expect
(
page
).
not_to
have_content
(
'baz'
)
end
it
'filters by overdue'
do
foo
.
update
(
due_date:
Date
.
today
+
2
.
days
)
bar
.
update
(
due_date:
Date
.
today
+
20
.
days
)
baz
.
update
(
due_date:
Date
.
yesterday
)
visit
namespace_project_issues_path
(
project
.
namespace
,
project
,
due_date:
Issue
::
Overdue
.
name
)
expect
(
page
).
not_to
have_content
(
'foo'
)
expect
(
page
).
not_to
have_content
(
'bar'
)
expect
(
page
).
to
have_content
(
'baz'
)
end
end
describe
'sorting by milestone'
do
before
:each
do
before
do
foo
.
milestone
=
newer_due_milestone
foo
.
save
bar
.
milestone
=
later_due_milestone
...
...
@@ -177,7 +263,7 @@ describe 'Issues', feature: true do
describe
'combine filter and sort'
do
let
(
:user2
)
{
create
(
:user
)
}
before
:each
do
before
do
foo
.
assignee
=
user2
foo
.
save
bar
.
assignee
=
user2
...
...
@@ -224,7 +310,7 @@ describe 'Issues', feature: true do
let
(
:guest
)
{
create
(
:user
)
}
before
:each
do
before
do
project
.
team
<<
[[
guest
],
:guest
]
end
...
...
@@ -267,7 +353,7 @@ describe 'Issues', feature: true do
context
'by unauthorized user'
do
let
(
:guest
)
{
create
(
:user
)
}
before
:each
do
before
do
project
.
team
<<
[
guest
,
:guest
]
issue
.
milestone
=
milestone
issue
.
save
...
...
@@ -285,7 +371,7 @@ describe 'Issues', feature: true do
describe
'removing assignee'
do
let
(
:user2
)
{
create
(
:user
)
}
before
:each
do
before
do
issue
.
assignee
=
user2
issue
.
save
end
...
...
spec/features/merge_requests/filter_by_milestone_spec.rb
View file @
7e0ef892
...
...
@@ -2,8 +2,14 @@ require 'rails_helper'
feature
'Merge Request filtering by Milestone'
,
feature:
true
do
let
(
:project
)
{
create
(
:project
,
:public
)
}
let!
(
:user
)
{
create
(
:user
)}
let
(
:milestone
)
{
create
(
:milestone
,
project:
project
)
}
before
do
project
.
team
<<
[
user
,
:master
]
login_as
(
user
)
end
scenario
'filters by no Milestone'
,
js:
true
do
create
(
:merge_request
,
:with_diffs
,
source_project:
project
)
create
(
:merge_request
,
:simple
,
source_project:
project
,
milestone:
milestone
)
...
...
spec/finders/issues_finder_spec.rb
View file @
7e0ef892
...
...
@@ -62,6 +62,22 @@ describe IssuesFinder do
expect
(
issues
).
to
eq
([
issue2
])
end
it
'returns unique issues when filtering by multiple labels'
do
label2
=
create
(
:label
,
project:
project2
)
create
(
:label_link
,
label:
label2
,
target:
issue2
)
params
=
{
scope:
'all'
,
label_name:
[
label
.
title
,
label2
.
title
].
join
(
','
),
state:
'opened'
}
issues
=
IssuesFinder
.
new
(
user
,
params
).
execute
expect
(
issues
).
to
eq
([
issue2
])
end
it
'should filter by no label name'
do
params
=
{
scope:
"all"
,
label_name:
Label
::
None
.
title
,
state:
'opened'
}
issues
=
IssuesFinder
.
new
(
user
,
params
).
execute
...
...
spec/lib/gitlab/metrics_spec.rb
View file @
7e0ef892
...
...
@@ -123,4 +123,28 @@ describe Gitlab::Metrics do
end
end
end
describe
'.action='
do
context
'without a transaction'
do
it
'does nothing'
do
expect_any_instance_of
(
Gitlab
::
Metrics
::
Transaction
).
not_to
receive
(
:action
=
)
Gitlab
::
Metrics
.
action
=
'foo'
end
end
context
'with a transaction'
do
it
'sets the action of a transaction'
do
trans
=
Gitlab
::
Metrics
::
Transaction
.
new
expect
(
Gitlab
::
Metrics
).
to
receive
(
:current_transaction
).
and_return
(
trans
)
expect
(
trans
).
to
receive
(
:action
=
).
with
(
'foo'
)
Gitlab
::
Metrics
.
action
=
'foo'
end
end
end
end
vendor/assets/javascripts/raven.js
0 → 100644
View file @
7e0ef892
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment