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
ca6f3abb
Commit
ca6f3abb
authored
Nov 29, 2016
by
Bryce Johnson
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Break out services by component.
parent
c3293aaf
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
152 additions
and
116 deletions
+152
-116
app/assets/javascripts/merge_request_widget/approvals/approvals_bundle.js.es6
...ts/merge_request_widget/approvals/approvals_bundle.js.es6
+2
-0
app/assets/javascripts/merge_request_widget/approvals/components/approvals_body.js.es6
...request_widget/approvals/components/approvals_body.js.es6
+11
-3
app/assets/javascripts/merge_request_widget/approvals/components/approvals_footer.js.es6
...quest_widget/approvals/components/approvals_footer.js.es6
+20
-36
app/assets/javascripts/merge_request_widget/approvals/services/approvals_service.js.es6
...equest_widget/approvals/services/approvals_service.js.es6
+24
-0
app/assets/javascripts/merge_request_widget/approvals/stores/approvals_store.js.es6
...ge_request_widget/approvals/stores/approvals_store.js.es6
+3
-0
app/assets/javascripts/merge_request_widget/services/widget_service.js.es6
...ripts/merge_request_widget/services/widget_service.js.es6
+4
-0
app/assets/javascripts/merge_request_widget/stores/widget_store.js.es6
...vascripts/merge_request_widget/stores/widget_store.js.es6
+7
-12
app/assets/javascripts/merge_request_widget/widget_bundle.js.es6
...ets/javascripts/merge_request_widget/widget_bundle.js.es6
+26
-16
app/assets/javascripts/vue_common_component/link_to_member_avatar.js.es6
...scripts/vue_common_component/link_to_member_avatar.js.es6
+44
-44
app/models/concerns/approvable.rb
app/models/concerns/approvable.rb
+6
-1
app/views/projects/merge_requests/widget/_open.html.haml
app/views/projects/merge_requests/widget/_open.html.haml
+5
-4
No files found.
app/assets/javascripts/merge_request_widget/approvals/approvals_bundle.js.es6
View file @
ca6f3abb
//= require_directory ./stores
//= require_directory ./services
//= require_directory ./components
app/assets/javascripts/merge_request_widget/approvals/components/approvals_body.js.es6
View file @
ca6f3abb
(() => {
// can the user edit this MR
// has this user approved this MR
// What is the info about users who can approve this MR but have not yet?
// How many approvals are needed?
// How many approvals are had?
gl.MergeRequestWidget.approvalsBody = {
props: ['approverNames', 'approvalsLeft', 'canApprove'],
computed: {
...
...
@@ -15,11 +20,14 @@
},
methods: {
approveMergeRequest() {
this.$emit('user-gives-approval'
);
approvalsService.approveMergeRequest(
);
},
},
beforeCreate() {
approvalsService.fetchApprovals();
},
template: `
<div>
Hello approval body
<div>
<h4> Requires {{ approvalsRequiredStringified }} (from {{ approverNamesStringified }})</h4>
<div class="append-bottom-10">
<button v-if='canApprove' @click='approveMergeRequest' class="btn btn-primary approve-btn">Approve Merge Request</button>
...
...
@@ -28,4 +36,4 @@
`,
};
})();
\ No newline at end of file
})();
app/assets/javascripts/merge_request_widget/approvals/components/approvals_footer.js.es6
View file @
ca6f3abb
(() => {
const PendingApprover = {
data() {
return {
dottedCircleUrl: '/assets/no_avatar.png'
};
},
template: `
<span>
<img width="24" alt="Required Approver" :src="dottedCircleUrl" class="avatar avatar-inline s24">
</span>
`
};
//= require vue_common_component/link_to_member_avatar
(() => {
// does the user have permission to edit this thing
// has the user already approved this or not
// what are the users who have approved this already, with all their info
gl.MergeRequestWidget.approvalsFooter = {
props: ['
approverNames', 'canApprove
', 'approvedByUsers'],
props: ['
canUpdateMergeRequest', 'hasApprovedMergeRequest
', 'approvedByUsers'],
methods: {
removeApproval() {
this.$emit('remove-approval'
);
approvalsService.unapproveMergeRequest(
);
}
},
components: {
'pending-approver': PendingApprover,
},
methods: {
beforeCreate() {
approvalsService.fetchApprovals();
},
template: `
<div>
Hello Approvals Footer
<div>
<div v-for='approver in approvedByUsers'>
<link-to-member-avatar
:avatar-url='approver.avatar.url'
:display-name='approver.name'
:username='approver.username'>
</link-to-member-avatar>
</div>
<span v-if='canApprove'>
<i class='fa fa-close'></i>
<button @click='removeApproval'>Remove your approval</button>
{{ approvedByUsers[0].name }}
</span>
<div v-for='approver in approvedByUsers'>
<link-to-member-avatar
:avatar-url='approver.avatar.url'
:display-name='approver.name'
:username='approver.username'>
</link-to-member-avatar>
</div>
<span>
<i class='fa fa-close'></i>
<button @click='removeApproval'>Remove your approval</button>
{{ approvedByUsers[0].name }}
</span>
</div>
`
};
})();
app/assets/javascripts/merge_request_widget/approvals/services/approvals_service.js.es6
0 → 100644
View file @
ca6f3abb
//= require ../stores/approvals_store
(() => {
class ApprovalsService {
constructor() {
this.isFetching = false;
this.hasBeenFetched = true;
}
fetchApprovals() {
return makeRequest().then((data) => {
this.isFetching = false;
this.hasBeenFetched = true;
return data;
});
}
approveMergeRequest(payload) {
return payload;
}
unapproveMergeRequest(payload) {
return payload;
}
}
gl.mergeRequestWidget.ApprovalsService = ApprovalsService;
})();
\ No newline at end of file
app/assets/javascripts/merge_request_widget/approvals/stores/approvals_store.js.es6
0 → 100644
View file @
ca6f3abb
(()=>{
})()
\ No newline at end of file
app/assets/javascripts/merge_request_widget/services/widget_service.js.es6
0 → 100644
View file @
ca6f3abb
(() => {
})();
\ No newline at end of file
app/assets/javascripts/merge_request_widget/stores/widget_store.js.es6
View file @
ca6f3abb
...
...
@@ -3,32 +3,28 @@
constructor(el) {
this.dataset = el.dataset;
this.data = {};
// TODO: Break each into their own store
this.initResource();
this.initPermissions();
this.initApprovals();
}
initResource() {
Object.assign(this.data, {
resource: {
canEdi
t: this.dataset.endpoint
resource: {
endpoin
t: this.dataset.endpoint
}
});
}
initPermissions() {
Object.assign(this.data, {
permissions: {
can
Edit: Boolean(this.dataset.canEdit
)
permissions: {
can
Approve: Boolean(this.dataset.canApprove
)
}
});
}
initApprovals() {
const dataset = this.dataset;
Object.assign(
this.data, {
Object.assign(
this.data, {
approvals: {
approvedByUsers: JSON.parse(dataset.approvedByUsers),
approverNames: JSON.parse(dataset.approverNames),
...
...
@@ -38,6 +34,5 @@
});
}
}
gl.MergeRequestWidgetStore = MergeRequestWidgetStore;
})()
gl.MergeRequestWidgetStore = MergeRequestWidgetStore;
})()
\ No newline at end of file
app/assets/javascripts/merge_request_widget/widget_bundle.js.es6
View file @
ca6f3abb
//= require ./stores/widget_store
//= require ./services/widget_service
//= require ./approvals/approvals_bundle
/**
*
* 'data-approved-by-users' => @merge_request.approved_by_users.to_json, 'data-approver-names' => @merge_request.approvers.to_json,'data-approvals-left' => @merge_request.approvals_left, 'data-more-approvals' => (@merge_request.approvals_left - @merge_request.approvers_left.count), 'data-can-approve' => @merge_request.user_is_approver(current_user), 'data-endpoint'=> '/myendpoint/tho' }
*
*/
$(() => {
const el = document.getElementById('merge-request-widget-app');
const Store = new gl.MergeRequestWidgetStore(el);
// Move initialization to somewhere else -- all can be conducted before DOM ready
new gl.MergeRequestWidgetStore();
new gl.MergeRequestWidgetService();
const app = gl.MergeRequestWidget;
new Vue({
el,
el
: '#merge-request-widget-app'
,
data: Store.data,
components: {
'approvals-body' : app.approvalsBody,
'approvals-footer' : app.approvalsFooter
},
methods: {
unapproveMergeRequest() {
console.log("Parent instance Unapprove MR");
},
approveMergeRequest() {
console.log("Parent instance Approve MR");
}
},
'approvals-body': app.approvalsBody,
'approvals-footer': app.approvalsFooter
}
});
});
/**
*
* gl.MergeRequestWidgetApp
* Store
* ApiService
*
*
* TODO: Add documentation for registering new widget components
*
*/
// Need list of approved by, list of those who can approve, and the number required
\ No newline at end of file
app/assets/javascripts/vue_common_component/link_to_member_avatar.js.es6
View file @
ca6f3abb
/* Analogue of link_to_member_avatar in app/helpers/projects_helper.rb
TODO: Support gravatar link generation, adding name text, username text
TODO: 1:1 configuration compared to link_to_member_avatar
TODO: Backport to CE
*/
Vue.component('link-to-member-avatar', {
props: {
avatarUrl: {
type: String,
required: false
(() => {
Vue.component('link-to-member-avatar', {
props: {
avatarUrl: {
type: String,
required: false
},
username: {
type: String,
required: true
},
displayName: {
type: String,
required: true,
},
avatarClass: {
type: String,
default: 'avatar avatar-inline s48',
required: false,
},
linkClass: {
type: String,
default: 'author_link has-tooltip',
required: false,
},
size: {
type: Number,
default: 48,
required: false
}
},
username: {
type: String,
required: true
data() {
return {
noAvatarUrl: '/assets/no_avatar.png'
};
},
displayName: {
type: String,
required: true,
},
avatarClass: {
type: String,
default: 'avatar avatar-inline s48',
required: false,
computed: {
userProfileUrl() {
return `/${this.username}`;
},
preppedAvatarUrl() {
return this.avatarUrl || this.noAvatarUrl;
}
},
linkClass: {
type: String,
default: 'author_link has-tooltip',
required: false,
},
size: {
type: Number,
default: 48,
required: false
}
},
data() {
return {
noAvatarUrl: '/assets/no_avatar.png'
};
},
computed: {
userProfileUrl() {
return `/${this.username}`;
},
preppedAvatarUrl() {
return this.avatarUrl || this.noAvatarUrl;
}
},
template: `
template: `
<a :href='userProfileUrl' :class='linkClass' :data-original-title='displayName' data-container='body'>
<img :class='avatarClass' :src='preppedAvatarUrl' :width='size' :height='size' :alt='displayName'/>
</a>
`
});
});
})();
\ No newline at end of file
app/models/concerns/approvable.rb
View file @
ca6f3abb
...
...
@@ -113,7 +113,12 @@ module Approvable
def
approvers_overwritten?
approvers
.
any?
||
approver_groups
.
any?
end
def
user_is_approver
(
user
)
return
false
unless
approvers
.
any?
approvers
.
include?
(
user
)
end
def
can_approve?
(
user
)
return
false
unless
user
return
true
if
approvers_left
.
include?
(
user
)
...
...
app/views/projects/merge_requests/widget/_open.html.haml
View file @
ca6f3abb
#merge-request-widget-app
.mr-state-widget
{
'data-approved-by-users'
=>
@merge_request
.
approved_by_users
.
to_json
,
'data-approver-names'
=>
@merge_request
.
approvers_left
.
map
(
&
:name
),
'data-approvals-left'
=>
@merge_request
.
approvals_left
,
'data-more-approvals'
=>
(
@merge_request
.
approvals_left
-
@merge_request
.
approvers_left
.
count
),
'data-can-edit'
=>
@merge_request
.
can_approve?
(
current_user
),
'data-endpoint'
=>
'/myendpoint/tho'
}
#merge-request-widget-app
.mr-state-widget
=
render
'projects/merge_requests/widget/heading'
.mr-widget-body
-# After conflicts are resolved, the user is redirected back to the MR page.
...
...
@@ -25,7 +24,8 @@
-
elsif
@merge_request
.
work_in_progress?
=
render
'projects/merge_requests/widget/open/wip'
-
elsif
@merge_request
.
requires_approve?
&&
!
@merge_request
.
approved?
%approvals-body
{
'@give-approval'
=>
'approveMergeRequest'
,
':approver-names'
=>
'approvals.approverNames'
,
':approvals-left'
=>
'approvals.approvalsLeft'
,
':more-approvals'
=>
'approvals.moreApprovals'
,
':can-approve'
=>
'permissions.canEdit'
}
%approvals-body
{
'@give-approval'
=>
'approveMergeRequest'
,
'@fetch-approvals'
=>
'fetchApprovals'
,
:approver
-
names
' => '
approvals
.
approverNames
', '
:approvals
-
left
' => '
approvals
.
approvalsLeft
', '
:more
-
approvals
' => '
approvals
.
moreApprovals
', '
:can
-
approve
' => '
permissions
.
canApprove
', '
:needs
-
approval
'
=>
(
@merge_request
.
requires_approve?
&&
!
@merge_request
.
approved?
)
}
=
icon
(
'spinner spin'
,
class:
'loading-icon'
)
-
elsif
@merge_request
.
merge_when_build_succeeds?
=
render
'projects/merge_requests/widget/open/merge_when_build_succeeds'
-
elsif
!
@merge_request
.
can_be_merged_by?
(
current_user
)
...
...
@@ -50,5 +50,6 @@
-
if
@merge_request
.
approvals
.
any?
.mr-widget-footer.approved-by-users
%approvals-footer
{
'@remove-approval'
=>
'unapproveMergeRequest'
,
':approved-by-users'
=>
'approvals.approvedByUsers'
,
':approver-names'
=>
'approvals.approverNames'
,
':approvals-left'
=>
'approvals.approvalsLeft'
,
':more-approvals'
=>
'approvals.moreApprovals'
,
':can-approve'
=>
'permissions.canEdit'
}
%approvals-footer
{
'@remove-approval'
=>
'unapproveMergeRequest'
,
'@fetch-approvals'
=>
'fetchApprovals'
,
':approved-by-users'
=>
'approvals.approvedByUsers'
,
':approver-names'
=>
'approvals.approverNames'
,
':approvals-left'
=>
'approvals.approvalsLeft'
,
':more-approvals'
=>
'approvals.moreApprovals'
,
':can-approve'
=>
'permissions.canApprove'
,
'has-approvals'
=>
@merge_request
.
approvals
.
any?
}
=
icon
(
'spinner spin'
,
class:
'loading-icon'
'
)
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