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
0
Merge Requests
0
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
Léo-Paul Géneau
gitlab-ce
Commits
e6d87021
Commit
e6d87021
authored
Aug 15, 2017
by
Jacob Schatz
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'bpj-repo-editor-fixes' into 'master'
Repo Editor Fixes See merge request !13468
parents
f82d8a22
85519977
Changes
19
Hide whitespace changes
Inline
Side-by-side
Showing
19 changed files
with
141 additions
and
114 deletions
+141
-114
app/assets/javascripts/project.js
app/assets/javascripts/project.js
+1
-1
app/assets/javascripts/repo/components/repo_commit_section.vue
...ssets/javascripts/repo/components/repo_commit_section.vue
+1
-3
app/assets/javascripts/repo/components/repo_edit_button.vue
app/assets/javascripts/repo/components/repo_edit_button.vue
+10
-8
app/assets/javascripts/repo/components/repo_editor.vue
app/assets/javascripts/repo/components/repo_editor.vue
+6
-29
app/assets/javascripts/repo/components/repo_sidebar.vue
app/assets/javascripts/repo/components/repo_sidebar.vue
+2
-4
app/assets/javascripts/repo/components/repo_tab.vue
app/assets/javascripts/repo/components/repo_tab.vue
+3
-3
app/assets/javascripts/repo/components/repo_tabs.vue
app/assets/javascripts/repo/components/repo_tabs.vue
+10
-5
app/assets/javascripts/repo/helpers/repo_helper.js
app/assets/javascripts/repo/helpers/repo_helper.js
+2
-2
app/assets/javascripts/repo/index.js
app/assets/javascripts/repo/index.js
+3
-0
app/assets/javascripts/repo/services/repo_service.js
app/assets/javascripts/repo/services/repo_service.js
+6
-4
app/assets/javascripts/repo/stores/repo_store.js
app/assets/javascripts/repo/stores/repo_store.js
+1
-1
app/assets/javascripts/vue_shared/components/popup_dialog.vue
...assets/javascripts/vue_shared/components/popup_dialog.vue
+46
-23
app/assets/stylesheets/pages/repo.scss
app/assets/stylesheets/pages/repo.scss
+1
-7
app/views/shared/repo/_repo.html.haml
app/views/shared/repo/_repo.html.haml
+0
-1
spec/javascripts/repo/components/repo_edit_button_spec.js
spec/javascripts/repo/components/repo_edit_button_spec.js
+2
-0
spec/javascripts/repo/components/repo_editor_spec.js
spec/javascripts/repo/components/repo_editor_spec.js
+37
-14
spec/javascripts/repo/components/repo_file_buttons_spec.js
spec/javascripts/repo/components/repo_file_buttons_spec.js
+1
-0
spec/javascripts/repo/components/repo_tab_spec.js
spec/javascripts/repo/components/repo_tab_spec.js
+7
-7
spec/javascripts/repo/components/repo_tabs_spec.js
spec/javascripts/repo/components/repo_tabs_spec.js
+2
-2
No files found.
app/assets/javascripts/project.js
View file @
e6d87021
...
...
@@ -130,7 +130,7 @@ import Cookies from 'js-cookie';
var
action
=
$form
.
attr
(
'
action
'
);
var
divider
=
action
.
indexOf
(
'
?
'
)
===
-
1
?
'
?
'
:
'
&
'
;
if
(
shouldVisit
)
{
gl
.
utils
.
visitUrl
(
action
+
''
+
divider
+
''
+
$form
.
serialize
()
);
gl
.
utils
.
visitUrl
(
`
${
action
}${
divider
}${
$form
.
serialize
()}
`
);
}
}
}
...
...
app/assets/javascripts/repo/components/repo_commit_section.vue
View file @
e6d87021
...
...
@@ -5,7 +5,7 @@ import RepoMixin from '../mixins/repo_mixin';
import
Helper
from
'
../helpers/repo_helper
'
;
import
Service
from
'
../services/repo_service
'
;
const
RepoCommitSection
=
{
export
default
{
data
:
()
=>
Store
,
mixins
:
[
RepoMixin
],
...
...
@@ -54,8 +54,6 @@ const RepoCommitSection = {
},
},
};
export
default
RepoCommitSection
;
</
script
>
<
template
>
...
...
app/assets/javascripts/repo/components/repo_edit_button.vue
View file @
e6d87021
...
...
@@ -23,19 +23,21 @@ export default {
this
.
editMode
=
!
this
.
editMode
;
Store
.
toggleBlobView
();
},
},
watch
:
{
editMode
()
{
toggleProjectRefsForm
()
{
if
(
this
.
editMode
)
{
$
(
'
.project-refs-form
'
).
addClass
(
'
disabled
'
);
$
(
'
.
js-tree-ref-target-holder
'
).
show
();
$
(
'
.project-refs-form
'
).
addClass
(
'
disabled
-content
'
);
$
(
'
.
project-refs-target-form
'
).
show
();
}
else
{
$
(
'
.project-refs-form
'
).
removeClass
(
'
disabled
'
);
$
(
'
.
js-tree-ref-target-holder
'
).
hide
();
$
(
'
.project-refs-form
'
).
removeClass
(
'
disabled
-content
'
);
$
(
'
.
project-refs-target-form
'
).
hide
();
}
},
},
watch
:
{
editMode
()
{
this
.
toggleProjectRefsForm
();
},
},
};
</
script
>
...
...
app/assets/javascripts/repo/components/repo_editor.vue
View file @
e6d87021
...
...
@@ -32,7 +32,6 @@ const RepoEditor = {
const
languages
=
this
.
monaco
.
languages
.
getLanguages
();
const
languageID
=
Helper
.
getLanguageIDForFile
(
this
.
activeFile
,
languages
);
this
.
showHide
();
const
newModel
=
this
.
monaco
.
editor
.
createModel
(
this
.
blobRaw
,
languageID
);
this
.
monacoInstance
.
setModel
(
newModel
);
...
...
@@ -40,14 +39,6 @@ const RepoEditor = {
},
methods
:
{
showHide
()
{
if
(
!
this
.
openedFiles
.
length
||
(
this
.
binary
&&
!
this
.
activeFile
.
raw
))
{
this
.
$el
.
style
.
display
=
'
none
'
;
}
else
{
this
.
$el
.
style
.
display
=
'
inline-block
'
;
}
},
addMonacoEvents
()
{
this
.
monacoInstance
.
onMouseUp
(
this
.
onMonacoEditorMouseUp
);
this
.
monacoInstance
.
onKeyUp
(
this
.
onMonacoEditorKeysPressed
.
bind
(
this
));
...
...
@@ -73,11 +64,6 @@ const RepoEditor = {
column
:
1
,
});
},
activeFileLabel
()
{
this
.
showHide
();
},
dialog
:
{
handler
(
obj
)
{
const
newObj
=
obj
;
...
...
@@ -99,21 +85,7 @@ const RepoEditor = {
deep
:
true
,
},
isTree
()
{
this
.
showHide
();
},
openedFiles
()
{
this
.
showHide
();
},
binary
()
{
this
.
showHide
();
},
blobRaw
()
{
this
.
showHide
();
if
(
this
.
isTree
)
return
;
this
.
monacoInstance
.
setModel
(
null
);
...
...
@@ -125,11 +97,16 @@ const RepoEditor = {
this
.
monacoInstance
.
setModel
(
newModel
);
},
},
computed
:
{
shouldHideEditor
()
{
return
!
this
.
openedFiles
.
length
||
(
this
.
binary
&&
!
this
.
activeFile
.
raw
);
},
},
};
export
default
RepoEditor
;
</
script
>
<
template
>
<div
id=
"ide"
></div>
<div
id=
"ide"
v-if=
'!shouldHideEditor'
></div>
</
template
>
app/assets/javascripts/repo/components/repo_sidebar.vue
View file @
e6d87021
...
...
@@ -8,7 +8,7 @@ import RepoFile from './repo_file.vue';
import
RepoLoadingFile
from
'
./repo_loading_file.vue
'
;
import
RepoMixin
from
'
../mixins/repo_mixin
'
;
const
RepoSidebar
=
{
export
default
{
mixins
:
[
RepoMixin
],
components
:
{
'
repo-file-options
'
:
RepoFileOptions
,
...
...
@@ -59,12 +59,10 @@ const RepoSidebar = {
},
},
};
export
default
RepoSidebar
;
</
script
>
<
template
>
<div
id=
"sidebar"
:class=
"
{'sidebar-mini' : isMini}"
v-cloak
>
<div
id=
"sidebar"
:class=
"
{'sidebar-mini' : isMini}">
<table
class=
"table"
>
<thead
v-if=
"!isMini"
>
<tr>
...
...
app/assets/javascripts/repo/components/repo_tab.vue
View file @
e6d87021
...
...
@@ -28,9 +28,9 @@ const RepoTab = {
methods
:
{
tabClicked
:
Store
.
setActiveFiles
,
xClicked
(
file
)
{
closeTab
(
file
)
{
if
(
file
.
changed
)
return
;
this
.
$emit
(
'
xclick
ed
'
,
file
);
this
.
$emit
(
'
tabclos
ed
'
,
file
);
},
},
};
...
...
@@ -43,7 +43,7 @@ export default RepoTab;
<a
href=
"#0"
class=
"close"
@
click.prevent=
"
xClicked
(tab)"
@
click.prevent=
"
closeTab
(tab)"
:aria-label=
"closeLabel"
>
<i
class=
"fa"
...
...
app/assets/javascripts/repo/components/repo_tabs.vue
View file @
e6d87021
...
...
@@ -13,7 +13,7 @@ const RepoTabs = {
data
:
()
=>
Store
,
methods
:
{
xClick
ed
(
file
)
{
tabClos
ed
(
file
)
{
Store
.
removeFromOpenedFiles
(
file
);
},
},
...
...
@@ -23,10 +23,15 @@ export default RepoTabs;
</
script
>
<
template
>
<ul
v-if=
"isMini"
id=
"tabs"
>
<repo-tab
v-for=
"tab in openedFiles"
:key=
"tab.id"
:tab=
"tab"
:class=
"
{'active' : tab.active}" @xclicked="xClicked"/>
<ul
id=
"tabs"
v-if=
"isMini"
>
<repo-tab
v-for=
"tab in openedFiles"
:key=
"tab.id"
:tab=
"tab"
:class=
"
{'active' : tab.active}"
@tabclosed="tabClosed"
/>
<li
class=
"tabs-divider"
/>
</ul>
</
template
>
app/assets/javascripts/repo/helpers/repo_helper.js
View file @
e6d87021
...
...
@@ -62,7 +62,7 @@ const RepoHelper = {
file
.
opened
=
true
;
file
.
icon
=
'
fa-folder-open
'
;
RepoHelper
.
toURL
(
file
.
url
,
file
.
name
);
RepoHelper
.
updateHistoryEntry
(
file
.
url
,
file
.
name
);
return
file
;
},
...
...
@@ -276,7 +276,7 @@ const RepoHelper = {
RepoHelper
.
key
=
key
;
},
toURL
(
url
,
title
)
{
updateHistoryEntry
(
url
,
title
)
{
const
history
=
window
.
history
;
RepoHelper
.
key
=
RepoHelper
.
genKey
();
...
...
app/assets/javascripts/repo/index.js
View file @
e6d87021
...
...
@@ -43,6 +43,9 @@ function initRepo(el) {
components
:
{
repo
:
Repo
,
},
render
(
createElement
)
{
return
createElement
(
'
repo
'
);
},
});
}
...
...
app/assets/javascripts/repo/services/repo_service.js
View file @
e6d87021
...
...
@@ -15,10 +15,12 @@ const RepoService = {
checkCurrentBranchIsCommitable
()
{
const
url
=
Store
.
service
.
refsUrl
;
return
axios
.
get
(
url
,
{
params
:
{
ref
:
Store
.
currentBranch
,
search
:
Store
.
currentBranch
,
}
});
return
axios
.
get
(
url
,
{
params
:
{
ref
:
Store
.
currentBranch
,
search
:
Store
.
currentBranch
,
},
});
},
getRaw
(
url
)
{
...
...
app/assets/javascripts/repo/stores/repo_store.js
View file @
e6d87021
...
...
@@ -90,7 +90,7 @@ const RepoStore = {
}).
catch
(
Helper
.
loadingError
);
}
if
(
!
file
.
loading
)
Helper
.
toURL
(
file
.
url
,
file
.
name
);
if
(
!
file
.
loading
)
Helper
.
updateHistoryEntry
(
file
.
url
,
file
.
name
);
RepoStore
.
binary
=
file
.
binary
;
},
...
...
app/assets/javascripts/vue_shared/components/popup_dialog.vue
View file @
e6d87021
<
script
>
const
PopupDialog
=
{
export
default
{
name
:
'
popup-dialog
'
,
props
:
{
open
:
Boolean
,
title
:
String
,
body
:
String
,
open
:
{
type
:
Boolean
,
required
:
true
,
},
title
:
{
type
:
String
,
required
:
true
,
},
body
:
{
type
:
String
,
required
:
true
,
},
kind
:
{
type
:
String
,
required
:
false
,
default
:
'
primary
'
,
},
closeButtonLabel
:
{
type
:
String
,
required
:
false
,
default
:
'
Cancel
'
,
},
primaryButtonLabel
:
{
type
:
String
,
default
:
'
Save changes
'
,
required
:
true
,
},
},
computed
:
{
typeOfClass
()
{
const
className
=
`btn-
${
this
.
kind
}
`
;
const
returnObj
=
{};
returnObj
[
className
]
=
true
;
return
returnObj
;
btnKindClass
()
{
return
{
[
`btn-
${
this
.
kind
}
`
]:
true
,
};
},
},
...
...
@@ -33,33 +43,46 @@ const PopupDialog = {
close
()
{
this
.
$emit
(
'
toggle
'
,
false
);
},
yesClick
()
{
this
.
$emit
(
'
submit
'
,
true
);
},
noClick
()
{
this
.
$emit
(
'
submit
'
,
false
);
emitSubmit
(
status
)
{
this
.
$emit
(
'
submit
'
,
status
);
},
},
};
export
default
PopupDialog
;
</
script
>
<
template
>
<div
class=
"modal popup-dialog"
tabindex=
"-1"
v-show=
"open"
role=
"dialog"
>
<div
class=
"modal popup-dialog"
v-if=
"open"
role=
"dialog"
tabindex=
"-1"
>
<div
class=
"modal-dialog"
role=
"document"
>
<div
class=
"modal-content"
>
<div
class=
"modal-header"
>
<button
type=
"button"
class=
"close"
@
click=
"close"
data-dismiss=
"modal"
aria-label=
"Close"
><span
aria-hidden=
"true"
>
×
</span></button>
<button
type=
"button"
class=
"close"
@
click=
"close"
aria-label=
"Close"
>
<span
aria-hidden=
"true"
>
×
</span>
</button>
<h4
class=
"modal-title"
>
{{
this
.
title
}}
</h4>
</div>
<div
class=
"modal-body"
>
<p>
{{
this
.
body
}}
</p>
</div>
<div
class=
"modal-footer"
>
<button
type=
"button"
class=
"btn btn-default"
data-dismiss=
"modal"
@
click=
"noClick"
>
{{
closeButtonLabel
}}
</button>
<button
type=
"button"
class=
"btn"
:class=
"typeOfClass"
@
click=
"yesClick"
>
{{
primaryButtonLabel
}}
</button>
<button
type=
"button"
class=
"btn btn-default"
@
click=
"emitSubmit(false)"
>
{{
closeButtonLabel
}}
</button>
<button
type=
"button"
class=
"btn"
:class=
"btnKindClass"
@
click=
"emitSubmit(true)"
>
{{
primaryButtonLabel
}}
</button>
</div>
</div>
</div>
...
...
app/assets/stylesheets/pages/repo.scss
View file @
e6d87021
...
...
@@ -28,11 +28,6 @@
.project-refs-form
,
.project-refs-target-form
{
display
:
inline-block
;
&
.disabled
{
opacity
:
0
.5
;
pointer-events
:
none
;
}
}
.fade-enter
,
...
...
@@ -133,7 +128,6 @@
a
{
@include
str-truncated
(
100px
);
color
:
$black
;
display
:
inline-block
;
width
:
100px
;
text-align
:
center
;
vertical-align
:
middle
;
...
...
@@ -298,7 +292,7 @@
}
.fa
{
font-size
:
$code_font_size
;
font-size
:
12px
;
margin-right
:
5px
;
}
}
...
...
app/views/shared/repo/_repo.html.haml
View file @
e6d87021
#repo
{
data:
{
url:
content_url
,
project_name:
project
.
name
,
refs_url:
refs_project_path
(
project
,
format: :json
),
project_url:
project_path
(
project
),
project_id:
project
.
id
,
can_commit:
(
!!
can_push_branch?
(
project
,
@ref
)).
to_s
}
}
%repo
spec/javascripts/repo/components/repo_edit_button_spec.js
View file @
e6d87021
...
...
@@ -19,11 +19,13 @@ describe('RepoEditButton', () => {
expect
(
vm
.
$el
.
textContent
).
toMatch
(
'
Edit
'
);
spyOn
(
vm
,
'
editClicked
'
).
and
.
callThrough
();
spyOn
(
vm
,
'
toggleProjectRefsForm
'
);
vm
.
$el
.
click
();
Vue
.
nextTick
(()
=>
{
expect
(
vm
.
editClicked
).
toHaveBeenCalled
();
expect
(
vm
.
toggleProjectRefsForm
).
toHaveBeenCalled
();
expect
(
vm
.
$el
.
textContent
).
toMatch
(
'
Cancel edit
'
);
done
();
});
...
...
spec/javascripts/repo/components/repo_editor_spec.js
View file @
e6d87021
import
Vue
from
'
vue
'
;
import
repoEditor
from
'
~/repo/components/repo_editor.vue
'
;
import
RepoStore
from
'
~/repo/stores/repo_store
'
;
describe
(
'
RepoEditor
'
,
()
=>
{
function
createComponent
()
{
beforeEach
(()
=>
{
const
RepoEditor
=
Vue
.
extend
(
repoEditor
);
return
new
RepoEditor
().
$mount
();
}
this
.
vm
=
new
RepoEditor
().
$mount
();
});
it
(
'
renders an ide container
'
,
(
done
)
=>
{
this
.
vm
.
openedFiles
=
[
'
idiidid
'
];
this
.
vm
.
binary
=
false
;
it
(
'
renders an ide container
'
,
()
=>
{
const
monacoInstance
=
jasmine
.
createSpyObj
(
'
monacoInstance
'
,
[
'
onMouseUp
'
,
'
onKeyUp
'
,
'
setModel
'
,
'
updateOptions
'
]);
const
monaco
=
{
editor
:
jasmine
.
createSpyObj
(
'
editor
'
,
[
'
create
'
]),
};
RepoStore
.
monaco
=
monaco
;
Vue
.
nextTick
(()
=>
{
expect
(
this
.
vm
.
shouldHideEditor
).
toBe
(
false
);
expect
(
this
.
vm
.
$el
.
id
).
toEqual
(
'
ide
'
);
expect
(
this
.
vm
.
$el
.
tagName
).
toBe
(
'
DIV
'
);
done
();
});
});
monaco
.
editor
.
create
.
and
.
returnValue
(
monacoInstance
);
spyOn
(
repoEditor
.
watch
,
'
blobRaw
'
);
describe
(
'
when there are no open files
'
,
()
=>
{
it
(
'
does not render the ide
'
,
(
done
)
=>
{
this
.
vm
.
openedFiles
=
[];
Vue
.
nextTick
(()
=>
{
expect
(
this
.
vm
.
shouldHideEditor
).
toBe
(
true
);
expect
(
this
.
vm
.
$el
.
tagName
).
not
.
toBeDefined
();
done
();
});
});
});
const
vm
=
createComponent
();
describe
(
'
when open file is binary and not raw
'
,
()
=>
{
it
(
'
does not render the IDE
'
,
(
done
)
=>
{
this
.
vm
.
binary
=
true
;
this
.
vm
.
activeFile
=
{
raw
:
false
,
};
expect
(
vm
.
$el
.
id
).
toEqual
(
'
ide
'
);
Vue
.
nextTick
(()
=>
{
expect
(
this
.
vm
.
shouldHideEditor
).
toBe
(
true
);
expect
(
this
.
vm
.
$el
.
tagName
).
not
.
toBeDefined
();
done
();
});
});
});
});
spec/javascripts/repo/components/repo_file_buttons_spec.js
View file @
e6d87021
...
...
@@ -23,6 +23,7 @@ describe('RepoFileButtons', () => {
RepoStore
.
activeFile
=
activeFile
;
RepoStore
.
activeFileLabel
=
activeFileLabel
;
RepoStore
.
editMode
=
true
;
RepoStore
.
binary
=
false
;
const
vm
=
createComponent
();
const
raw
=
vm
.
$el
.
querySelector
(
'
.raw
'
);
...
...
spec/javascripts/repo/components/repo_tab_spec.js
View file @
e6d87021
...
...
@@ -21,7 +21,7 @@ describe('RepoTab', () => {
const
close
=
vm
.
$el
.
querySelector
(
'
.close
'
);
const
name
=
vm
.
$el
.
querySelector
(
`a[title="
${
tab
.
url
}
"]`
);
spyOn
(
vm
,
'
xClicked
'
);
spyOn
(
vm
,
'
closeTab
'
);
spyOn
(
vm
,
'
tabClicked
'
);
expect
(
close
.
querySelector
(
'
.fa-times
'
)).
toBeTruthy
();
...
...
@@ -30,7 +30,7 @@ describe('RepoTab', () => {
close
.
click
();
name
.
click
();
expect
(
vm
.
xClicked
).
toHaveBeenCalledWith
(
tab
);
expect
(
vm
.
closeTab
).
toHaveBeenCalledWith
(
tab
);
expect
(
vm
.
tabClicked
).
toHaveBeenCalledWith
(
tab
);
});
...
...
@@ -48,22 +48,22 @@ describe('RepoTab', () => {
});
describe
(
'
methods
'
,
()
=>
{
describe
(
'
xClicked
'
,
()
=>
{
describe
(
'
closeTab
'
,
()
=>
{
const
vm
=
jasmine
.
createSpyObj
(
'
vm
'
,
[
'
$emit
'
]);
it
(
'
returns undefined and does not $emit if file is changed
'
,
()
=>
{
const
file
=
{
changed
:
true
};
const
returnVal
=
repoTab
.
methods
.
xClicked
.
call
(
vm
,
file
);
const
returnVal
=
repoTab
.
methods
.
closeTab
.
call
(
vm
,
file
);
expect
(
returnVal
).
toBeUndefined
();
expect
(
vm
.
$emit
).
not
.
toHaveBeenCalled
();
});
it
(
'
$emits
xclick
ed event with file obj
'
,
()
=>
{
it
(
'
$emits
tabclos
ed event with file obj
'
,
()
=>
{
const
file
=
{
changed
:
false
};
repoTab
.
methods
.
xClicked
.
call
(
vm
,
file
);
repoTab
.
methods
.
closeTab
.
call
(
vm
,
file
);
expect
(
vm
.
$emit
).
toHaveBeenCalledWith
(
'
xclick
ed
'
,
file
);
expect
(
vm
.
$emit
).
toHaveBeenCalledWith
(
'
tabclos
ed
'
,
file
);
});
});
});
...
...
spec/javascripts/repo/components/repo_tabs_spec.js
View file @
e6d87021
...
...
@@ -38,13 +38,13 @@ describe('RepoTabs', () => {
});
describe
(
'
methods
'
,
()
=>
{
describe
(
'
xClick
ed
'
,
()
=>
{
describe
(
'
tabClos
ed
'
,
()
=>
{
it
(
'
calls removeFromOpenedFiles with file obj
'
,
()
=>
{
const
file
=
{};
spyOn
(
RepoStore
,
'
removeFromOpenedFiles
'
);
repoTabs
.
methods
.
xClick
ed
(
file
);
repoTabs
.
methods
.
tabClos
ed
(
file
);
expect
(
RepoStore
.
removeFromOpenedFiles
).
toHaveBeenCalledWith
(
file
);
});
...
...
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