Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
O
origin-web-common
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Java-于龙
origin-web-common
Commits
1cd15ddb
Commit
1cd15ddb
authored
Aug 22, 2017
by
Sam Padgett
Committed by
GitHub
Aug 22, 2017
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #150 from spadgett/project-list
Cache project list
parents
eb1c5e84
32eadb7c
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
219 additions
and
58 deletions
+219
-58
origin-web-common-services.js
dist/origin-web-common-services.js
+87
-3
origin-web-common-ui.js
dist/origin-web-common-ui.js
+21
-24
origin-web-common.js
dist/origin-web-common.js
+0
-0
origin-web-common.min.js
dist/origin-web-common.min.js
+0
-0
templates.js
dist/scripts/templates.js
+4
-5
delete-project-modal.html
src/components/delete-project/delete-project-modal.html
+4
-5
deleteProject.js
src/components/delete-project/deleteProject.js
+6
-12
editProject.js
src/components/edit-project/editProject.js
+10
-6
projects.js
src/services/projects.js
+87
-3
No files found.
dist/origin-web-common-services.js
View file @
1cd15ddb
...
...
@@ -2921,8 +2921,28 @@ angular.module('openshiftCommonServices')
angular
.
module
(
'openshiftCommonServices'
)
.
factory
(
'ProjectsService'
,
function
(
$location
,
$q
,
AuthService
,
DataService
,
annotationNameFilter
,
AuthorizationService
,
RecentlyViewedProjectsService
)
{
function
(
$location
,
$q
,
$rootScope
,
AuthService
,
AuthorizationService
,
DataService
,
Logger
,
RecentlyViewedProjectsService
,
annotationNameFilter
)
{
// Cache project data when we can so we don't request it on every page load.
var
cachedProjectData
;
var
cachedProjectDataIncomplete
=
false
;
var
clearCachedProjectData
=
function
()
{
Logger
.
debug
(
'ProjectsService: clearing project cache'
);
cachedProjectData
=
null
;
cachedProjectDataIncomplete
=
false
;
};
AuthService
.
onUserChanged
(
clearCachedProjectData
);
AuthService
.
onLogout
(
clearCachedProjectData
);
var
cleanEditableAnnotations
=
function
(
resource
)
{
var
paths
=
[
...
...
@@ -2957,6 +2977,10 @@ angular.module('openshiftCommonServices')
context
.
project
=
project
;
context
.
projectPromise
.
resolve
(
project
);
RecentlyViewedProjectsService
.
addProjectUID
(
project
.
metadata
.
uid
);
if
(
cachedProjectData
)
{
cachedProjectData
.
update
(
project
,
'MODIFIED'
);
}
// TODO: fix need to return context & projectPromise
return
[
project
,
context
];
});
...
...
@@ -2983,10 +3007,56 @@ angular.module('openshiftCommonServices')
});
});
},
// List the projects the user has access to. This method returns
// cached data if the projects had previously been fetched to avoid
// requesting them again and again, which is a problem for admins who
// might have hundreds or more.
list
:
function
(
forceRefresh
)
{
if
(
cachedProjectData
&&
!
forceRefresh
)
{
Logger
.
debug
(
'ProjectsService: returning cached project data'
);
return
$q
.
when
(
cachedProjectData
);
}
Logger
.
debug
(
'ProjectsService: listing projects, force refresh'
,
forceRefresh
);
return
DataService
.
list
(
'projects'
,
{}).
then
(
function
(
projectData
)
{
cachedProjectData
=
projectData
;
return
projectData
;
},
function
(
error
)
{
// If the request fails, don't try to list projects again without `forceRefresh`.
cachedProjectData
=
{};
cachedProjectDataIncomplete
=
true
;
});
},
isProjectListIncomplete
:
function
()
{
return
cachedProjectDataIncomplete
;
},
watch
:
function
(
context
,
callback
)
{
// Wrap `DataService.watch` so we can update the cached projects
// list on changes. TODO: We might want to disable watches entirely
// if we know the project list is large.
return
DataService
.
watch
(
'projects'
,
context
,
function
(
projectData
)
{
cachedProjectData
=
projectData
;
callback
(
projectData
);
});
},
update
:
function
(
projectName
,
data
)
{
return
DataService
.
update
(
'projects'
,
projectName
,
cleanEditableAnnotations
(
data
),
{
projectName
:
projectName
},
{
errorNotification
:
false
});
return
DataService
.
update
(
'projects'
,
projectName
,
cleanEditableAnnotations
(
data
),
{
projectName
:
projectName
},
{
errorNotification
:
false
}).
then
(
function
(
updatedProject
)
{
if
(
cachedProjectData
)
{
cachedProjectData
.
update
(
updatedProject
,
'MODIFIED'
);
}
return
updatedProject
;
});
},
create
:
function
(
name
,
displayName
,
description
)
{
var
projectRequest
=
{
apiVersion
:
"v1"
,
...
...
@@ -3001,11 +3071,25 @@ angular.module('openshiftCommonServices')
.
create
(
'projectrequests'
,
null
,
projectRequest
,
{})
.
then
(
function
(
project
)
{
RecentlyViewedProjectsService
.
addProjectUID
(
project
.
metadata
.
uid
);
if
(
cachedProjectData
)
{
cachedProjectData
.
update
(
project
,
'ADDED'
);
}
return
project
;
});
},
canCreate
:
function
()
{
return
DataService
.
get
(
"projectrequests"
,
null
,
{},
{
errorNotification
:
false
});
},
delete
:
function
(
project
)
{
return
DataService
.
delete
(
'projects'
,
project
.
metadata
.
name
,
{}).
then
(
function
(
deletedProject
)
{
if
(
cachedProjectData
)
{
cachedProjectData
.
update
(
project
,
'DELETED'
);
}
return
deletedProject
;
});
}
};
});
...
...
dist/origin-web-common-ui.js
View file @
1cd15ddb
...
...
@@ -310,11 +310,10 @@ hawtioPluginLoader.addModule('openshiftCommonUI');
" <!-- Use a form so that the enter key submits when typing a project name to confirm. -->
\
n"
+
" <form>
\
n"
+
" <div class=
\"
modal-body
\"
>
\
n"
+
" <h1>Are you sure you want to delete the project
\
n"
+
" '<strong>{{displayName ? displayName : projectName}}</strong>'?</h1>
\
n"
+
" <h1>Are you sure you want to delete the project '<strong>{{project | displayName}}</strong>'?</h1>
\
n"
+
" <p>
\
n"
+
" This will <strong>delete all resources</strong> associated with
\
n"
+
" the project {{
displayName ? displayName : project
Name}} and <strong>cannot be
\
n"
+
" the project {{
project | display
Name}} and <strong>cannot be
\
n"
+
" undone</strong>. Make sure this is something you really want to do!
\
n"
+
" </p>
\
n"
+
" <div ng-show=
\"
typeNameToConfirm
\"
>
\
n"
+
...
...
@@ -334,8 +333,8 @@ hawtioPluginLoader.addModule('openshiftCommonUI');
" </div>
\
n"
+
" </div>
\
n"
+
" <div class=
\"
modal-footer
\"
>
\
n"
+
" <button ng-disabled=
\"
typeNameToConfirm && confirmName !== project
Name && confirmName !== displayName
\"
class=
\"
btn btn-lg btn-danger
\"
type=
\"
submit
\"
ng-click=
\"
delete();
\"
>Delete</button>
\
n"
+
" <button class=
\"
btn btn-lg btn-default
\"
type=
\"
button
\"
ng-click=
\"
cancel()
;
\"
>Cancel</button>
\
n"
+
" <button ng-disabled=
\"
typeNameToConfirm && confirmName !== project
.metadata.name && confirmName !== (project | displayName : false)
\"
class=
\"
btn btn-lg btn-danger
\"
type=
\"
submit
\"
ng-click=
\"
delete()
\"
>Delete</button>
\
n"
+
" <button class=
\"
btn btn-lg btn-default
\"
type=
\"
button
\"
ng-click=
\"
cancel()
\"
>Cancel</button>
\
n"
+
" </div>
\
n"
+
" </form>
\
n"
+
"</div>
\
n"
...
...
@@ -625,14 +624,12 @@ angular.module("openshiftCommonUI")
;
'use strict'
;
angular
.
module
(
"openshiftCommonUI"
)
.
directive
(
"deleteProject"
,
function
(
$uibModal
,
$location
,
$filter
,
$q
,
hashSizeFilter
,
APIService
,
DataService
,
Notification
sService
,
Logger
)
{
.
directive
(
"deleteProject"
,
function
(
$uibModal
,
$location
,
$filter
,
$q
,
hashSizeFilter
,
APIService
,
NotificationsService
,
Project
sService
,
Logger
)
{
return
{
restrict
:
"E"
,
scope
:
{
// The name of project to delete
projectName
:
"@"
,
// Optional display name of the project to delete.
displayName
:
"@"
,
// The project to delete
project
:
"="
,
// Set to true to disable the delete button.
disableDelete
:
"=?"
,
// Force the user to enter the name before we'll delete the project.
...
...
@@ -658,6 +655,7 @@ angular.module("openshiftCommonUI")
// Replace so ".dropdown-menu > li > a" styles are applied.
replace
:
true
,
link
:
function
(
scope
,
element
,
attrs
)
{
var
displayName
=
$filter
(
'displayName'
);
var
navigateToList
=
function
()
{
if
(
scope
.
stayOnCurrentPage
)
{
return
;
...
...
@@ -692,14 +690,9 @@ angular.module("openshiftCommonUI")
modalInstance
.
result
.
then
(
function
()
{
// upon clicking delete button, delete resource and send alert
var
projectName
=
scope
.
projectName
;
var
formattedResource
=
"Project
\
'"
+
(
scope
.
displayName
||
projectName
)
+
"
\
'"
;
var
context
=
{};
DataService
.
delete
({
resource
:
APIService
.
kindToResource
(
"Project"
)
},
projectName
,
context
)
.
then
(
function
()
{
var
formattedResource
=
"Project
\
'"
+
displayName
(
scope
.
project
)
+
"
\
'"
;
ProjectsService
.
delete
(
scope
.
project
).
then
(
function
()
{
NotificationsService
.
addNotification
({
type
:
"success"
,
message
:
formattedResource
+
" was marked for deletion."
...
...
@@ -758,7 +751,14 @@ angular.module("openshiftCommonUI")
isDialog
:
'@'
},
templateUrl
:
'src/components/edit-project/editProject.html'
,
controller
:
function
(
$scope
,
$filter
,
$location
,
DataService
,
NotificationsService
,
annotationNameFilter
,
displayNameFilter
,
Logger
)
{
controller
:
function
(
$scope
,
$filter
,
$location
,
Logger
,
NotificationsService
,
ProjectsService
,
annotationNameFilter
,
displayNameFilter
)
{
if
(
!
(
$scope
.
submitButtonLabel
))
{
$scope
.
submitButtonLabel
=
'Save'
;
}
...
...
@@ -800,13 +800,10 @@ angular.module("openshiftCommonUI")
$scope
.
update
=
function
()
{
$scope
.
disableInputs
=
true
;
if
(
$scope
.
editProjectForm
.
$valid
)
{
Data
Service
Projects
Service
.
update
(
'projects'
,
$scope
.
project
.
metadata
.
name
,
cleanEditableAnnotations
(
mergeEditable
(
$scope
.
project
,
$scope
.
editableFields
)),
{
projectName
:
$scope
.
project
.
name
},
{
errorNotification
:
false
})
cleanEditableAnnotations
(
mergeEditable
(
$scope
.
project
,
$scope
.
editableFields
)))
.
then
(
function
(
project
)
{
// angular is actually wrapping the redirect action :/
var
cb
=
$scope
.
redirectAction
();
...
...
dist/origin-web-common.js
View file @
1cd15ddb
This diff is collapsed.
Click to expand it.
dist/origin-web-common.min.js
View file @
1cd15ddb
This diff is collapsed.
Click to expand it.
dist/scripts/templates.js
View file @
1cd15ddb
...
...
@@ -281,11 +281,10 @@ angular.module('openshiftCommonUI').run(['$templateCache', function($templateCac
" <!-- Use a form so that the enter key submits when typing a project name to confirm. -->
\
n"
+
" <form>
\
n"
+
" <div class=
\"
modal-body
\"
>
\
n"
+
" <h1>Are you sure you want to delete the project
\
n"
+
" '<strong>{{displayName ? displayName : projectName}}</strong>'?</h1>
\
n"
+
" <h1>Are you sure you want to delete the project '<strong>{{project | displayName}}</strong>'?</h1>
\
n"
+
" <p>
\
n"
+
" This will <strong>delete all resources</strong> associated with
\
n"
+
" the project {{
displayName ? displayName : project
Name}} and <strong>cannot be
\
n"
+
" the project {{
project | display
Name}} and <strong>cannot be
\
n"
+
" undone</strong>. Make sure this is something you really want to do!
\
n"
+
" </p>
\
n"
+
" <div ng-show=
\"
typeNameToConfirm
\"
>
\
n"
+
...
...
@@ -305,8 +304,8 @@ angular.module('openshiftCommonUI').run(['$templateCache', function($templateCac
" </div>
\
n"
+
" </div>
\
n"
+
" <div class=
\"
modal-footer
\"
>
\
n"
+
" <button ng-disabled=
\"
typeNameToConfirm && confirmName !== project
Name && confirmName !== displayName
\"
class=
\"
btn btn-lg btn-danger
\"
type=
\"
submit
\"
ng-click=
\"
delete();
\"
>Delete</button>
\
n"
+
" <button class=
\"
btn btn-lg btn-default
\"
type=
\"
button
\"
ng-click=
\"
cancel()
;
\"
>Cancel</button>
\
n"
+
" <button ng-disabled=
\"
typeNameToConfirm && confirmName !== project
.metadata.name && confirmName !== (project | displayName : false)
\"
class=
\"
btn btn-lg btn-danger
\"
type=
\"
submit
\"
ng-click=
\"
delete()
\"
>Delete</button>
\
n"
+
" <button class=
\"
btn btn-lg btn-default
\"
type=
\"
button
\"
ng-click=
\"
cancel()
\"
>Cancel</button>
\
n"
+
" </div>
\
n"
+
" </form>
\
n"
+
"</div>
\
n"
...
...
src/components/delete-project/delete-project-modal.html
View file @
1cd15ddb
...
...
@@ -2,11 +2,10 @@
<!-- Use a form so that the enter key submits when typing a project name to confirm. -->
<form>
<div
class=
"modal-body"
>
<h1>
Are you sure you want to delete the project
'
<strong>
{{displayName ? displayName : projectName}}
</strong>
'?
</h1>
<h1>
Are you sure you want to delete the project '
<strong>
{{project | displayName}}
</strong>
'?
</h1>
<p>
This will
<strong>
delete all resources
</strong>
associated with
the project {{
displayName ? displayName : project
Name}} and
<strong>
cannot be
the project {{
project | display
Name}} and
<strong>
cannot be
undone
</strong>
. Make sure this is something you really want to do!
</p>
<div
ng-show=
"typeNameToConfirm"
>
...
...
@@ -26,8 +25,8 @@
</div>
</div>
<div
class=
"modal-footer"
>
<button
ng-disabled=
"typeNameToConfirm && confirmName !== project
Name && confirmName !== displayName"
class=
"btn btn-lg btn-danger"
type=
"submit"
ng-click=
"delete();
"
>
Delete
</button>
<button
class=
"btn btn-lg btn-default"
type=
"button"
ng-click=
"cancel()
;
"
>
Cancel
</button>
<button
ng-disabled=
"typeNameToConfirm && confirmName !== project
.metadata.name && confirmName !== (project | displayName : false)"
class=
"btn btn-lg btn-danger"
type=
"submit"
ng-click=
"delete()
"
>
Delete
</button>
<button
class=
"btn btn-lg btn-default"
type=
"button"
ng-click=
"cancel()"
>
Cancel
</button>
</div>
</form>
</div>
src/components/delete-project/deleteProject.js
View file @
1cd15ddb
'use strict'
;
angular
.
module
(
"openshiftCommonUI"
)
.
directive
(
"deleteProject"
,
function
(
$uibModal
,
$location
,
$filter
,
$q
,
hashSizeFilter
,
APIService
,
DataService
,
Notification
sService
,
Logger
)
{
.
directive
(
"deleteProject"
,
function
(
$uibModal
,
$location
,
$filter
,
$q
,
hashSizeFilter
,
APIService
,
NotificationsService
,
Project
sService
,
Logger
)
{
return
{
restrict
:
"E"
,
scope
:
{
// The name of project to delete
projectName
:
"@"
,
// Optional display name of the project to delete.
displayName
:
"@"
,
// The project to delete
project
:
"="
,
// Set to true to disable the delete button.
disableDelete
:
"=?"
,
// Force the user to enter the name before we'll delete the project.
...
...
@@ -34,6 +32,7 @@ angular.module("openshiftCommonUI")
// Replace so ".dropdown-menu > li > a" styles are applied.
replace
:
true
,
link
:
function
(
scope
,
element
,
attrs
)
{
var
displayName
=
$filter
(
'displayName'
);
var
navigateToList
=
function
()
{
if
(
scope
.
stayOnCurrentPage
)
{
return
;
...
...
@@ -68,14 +67,9 @@ angular.module("openshiftCommonUI")
modalInstance
.
result
.
then
(
function
()
{
// upon clicking delete button, delete resource and send alert
var
projectName
=
scope
.
projectName
;
var
formattedResource
=
"Project
\
'"
+
(
scope
.
displayName
||
projectName
)
+
"
\
'"
;
var
context
=
{};
var
formattedResource
=
"Project
\
'"
+
displayName
(
scope
.
project
)
+
"
\
'"
;
DataService
.
delete
({
resource
:
APIService
.
kindToResource
(
"Project"
)
},
projectName
,
context
)
.
then
(
function
()
{
ProjectsService
.
delete
(
scope
.
project
).
then
(
function
()
{
NotificationsService
.
addNotification
({
type
:
"success"
,
message
:
formattedResource
+
" was marked for deletion."
...
...
src/components/edit-project/editProject.js
View file @
1cd15ddb
...
...
@@ -13,7 +13,14 @@ angular.module("openshiftCommonUI")
isDialog
:
'@'
},
templateUrl
:
'src/components/edit-project/editProject.html'
,
controller
:
function
(
$scope
,
$filter
,
$location
,
DataService
,
NotificationsService
,
annotationNameFilter
,
displayNameFilter
,
Logger
)
{
controller
:
function
(
$scope
,
$filter
,
$location
,
Logger
,
NotificationsService
,
ProjectsService
,
annotationNameFilter
,
displayNameFilter
)
{
if
(
!
(
$scope
.
submitButtonLabel
))
{
$scope
.
submitButtonLabel
=
'Save'
;
}
...
...
@@ -55,13 +62,10 @@ angular.module("openshiftCommonUI")
$scope
.
update
=
function
()
{
$scope
.
disableInputs
=
true
;
if
(
$scope
.
editProjectForm
.
$valid
)
{
Data
Service
Projects
Service
.
update
(
'projects'
,
$scope
.
project
.
metadata
.
name
,
cleanEditableAnnotations
(
mergeEditable
(
$scope
.
project
,
$scope
.
editableFields
)),
{
projectName
:
$scope
.
project
.
name
},
{
errorNotification
:
false
})
cleanEditableAnnotations
(
mergeEditable
(
$scope
.
project
,
$scope
.
editableFields
)))
.
then
(
function
(
project
)
{
// angular is actually wrapping the redirect action :/
var
cb
=
$scope
.
redirectAction
();
...
...
src/services/projects.js
View file @
1cd15ddb
...
...
@@ -2,8 +2,28 @@
angular
.
module
(
'openshiftCommonServices'
)
.
factory
(
'ProjectsService'
,
function
(
$location
,
$q
,
AuthService
,
DataService
,
annotationNameFilter
,
AuthorizationService
,
RecentlyViewedProjectsService
)
{
function
(
$location
,
$q
,
$rootScope
,
AuthService
,
AuthorizationService
,
DataService
,
Logger
,
RecentlyViewedProjectsService
,
annotationNameFilter
)
{
// Cache project data when we can so we don't request it on every page load.
var
cachedProjectData
;
var
cachedProjectDataIncomplete
=
false
;
var
clearCachedProjectData
=
function
()
{
Logger
.
debug
(
'ProjectsService: clearing project cache'
);
cachedProjectData
=
null
;
cachedProjectDataIncomplete
=
false
;
};
AuthService
.
onUserChanged
(
clearCachedProjectData
);
AuthService
.
onLogout
(
clearCachedProjectData
);
var
cleanEditableAnnotations
=
function
(
resource
)
{
var
paths
=
[
...
...
@@ -38,6 +58,10 @@ angular.module('openshiftCommonServices')
context
.
project
=
project
;
context
.
projectPromise
.
resolve
(
project
);
RecentlyViewedProjectsService
.
addProjectUID
(
project
.
metadata
.
uid
);
if
(
cachedProjectData
)
{
cachedProjectData
.
update
(
project
,
'MODIFIED'
);
}
// TODO: fix need to return context & projectPromise
return
[
project
,
context
];
});
...
...
@@ -64,10 +88,56 @@ angular.module('openshiftCommonServices')
});
});
},
// List the projects the user has access to. This method returns
// cached data if the projects had previously been fetched to avoid
// requesting them again and again, which is a problem for admins who
// might have hundreds or more.
list
:
function
(
forceRefresh
)
{
if
(
cachedProjectData
&&
!
forceRefresh
)
{
Logger
.
debug
(
'ProjectsService: returning cached project data'
);
return
$q
.
when
(
cachedProjectData
);
}
Logger
.
debug
(
'ProjectsService: listing projects, force refresh'
,
forceRefresh
);
return
DataService
.
list
(
'projects'
,
{}).
then
(
function
(
projectData
)
{
cachedProjectData
=
projectData
;
return
projectData
;
},
function
(
error
)
{
// If the request fails, don't try to list projects again without `forceRefresh`.
cachedProjectData
=
{};
cachedProjectDataIncomplete
=
true
;
});
},
isProjectListIncomplete
:
function
()
{
return
cachedProjectDataIncomplete
;
},
watch
:
function
(
context
,
callback
)
{
// Wrap `DataService.watch` so we can update the cached projects
// list on changes. TODO: We might want to disable watches entirely
// if we know the project list is large.
return
DataService
.
watch
(
'projects'
,
context
,
function
(
projectData
)
{
cachedProjectData
=
projectData
;
callback
(
projectData
);
});
},
update
:
function
(
projectName
,
data
)
{
return
DataService
.
update
(
'projects'
,
projectName
,
cleanEditableAnnotations
(
data
),
{
projectName
:
projectName
},
{
errorNotification
:
false
});
return
DataService
.
update
(
'projects'
,
projectName
,
cleanEditableAnnotations
(
data
),
{
projectName
:
projectName
},
{
errorNotification
:
false
}).
then
(
function
(
updatedProject
)
{
if
(
cachedProjectData
)
{
cachedProjectData
.
update
(
updatedProject
,
'MODIFIED'
);
}
return
updatedProject
;
});
},
create
:
function
(
name
,
displayName
,
description
)
{
var
projectRequest
=
{
apiVersion
:
"v1"
,
...
...
@@ -82,11 +152,25 @@ angular.module('openshiftCommonServices')
.
create
(
'projectrequests'
,
null
,
projectRequest
,
{})
.
then
(
function
(
project
)
{
RecentlyViewedProjectsService
.
addProjectUID
(
project
.
metadata
.
uid
);
if
(
cachedProjectData
)
{
cachedProjectData
.
update
(
project
,
'ADDED'
);
}
return
project
;
});
},
canCreate
:
function
()
{
return
DataService
.
get
(
"projectrequests"
,
null
,
{},
{
errorNotification
:
false
});
},
delete
:
function
(
project
)
{
return
DataService
.
delete
(
'projects'
,
project
.
metadata
.
name
,
{}).
then
(
function
(
deletedProject
)
{
if
(
cachedProjectData
)
{
cachedProjectData
.
update
(
project
,
'DELETED'
);
}
return
deletedProject
;
});
}
};
});
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