mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-08 02:10:24 +00:00
repo-tags-show (#2494)
This commit is contained in:
committed by
Daniel Pan
parent
973ca9cf35
commit
a63e8a4009
14
frontend/package-lock.json
generated
14
frontend/package-lock.json
generated
@@ -526,7 +526,7 @@
|
||||
},
|
||||
"axios": {
|
||||
"version": "0.18.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.18.0.tgz",
|
||||
"resolved": "http://registry.npmjs.org/axios/-/axios-0.18.0.tgz",
|
||||
"integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=",
|
||||
"requires": {
|
||||
"follow-redirects": "^1.3.0",
|
||||
@@ -1756,7 +1756,7 @@
|
||||
},
|
||||
"blob": {
|
||||
"version": "0.0.4",
|
||||
"resolved": "http://registry.npmjs.org/blob/-/blob-0.0.4.tgz",
|
||||
"resolved": "https://registry.npmjs.org/blob/-/blob-0.0.4.tgz",
|
||||
"integrity": "sha1-vPEwUspURj8w+fx+lbmkdjCpSSE="
|
||||
},
|
||||
"bluebird": {
|
||||
@@ -10226,9 +10226,9 @@
|
||||
}
|
||||
},
|
||||
"seafile-js": {
|
||||
"version": "0.2.29",
|
||||
"resolved": "https://registry.npmjs.org/seafile-js/-/seafile-js-0.2.29.tgz",
|
||||
"integrity": "sha512-fsWHcTWZk2iV/Ah3lRmnO5vyB0AFoxVyV3Z7FQ7k8aAYQtWcf07hxbFIhFpGU6cpmaq2mtnJQBUNU9zQdKtGRA==",
|
||||
"version": "0.2.31",
|
||||
"resolved": "https://registry.npmjs.org/seafile-js/-/seafile-js-0.2.31.tgz",
|
||||
"integrity": "sha512-l9IXpXUaQD/MOUSyYSPR1w+Nq2fcIWiif0e3YDCWvAb1sLp2lNlDdiISkGYPoEADUJtDqQrJfKiVHgKIaj1CXQ==",
|
||||
"requires": {
|
||||
"axios": "^0.18.0",
|
||||
"form-data": "^2.3.2"
|
||||
@@ -11852,13 +11852,13 @@
|
||||
"dependencies": {
|
||||
"ansi-regex": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-1.1.1.tgz",
|
||||
"resolved": "http://registry.npmjs.org/ansi-regex/-/ansi-regex-1.1.1.tgz",
|
||||
"integrity": "sha1-QchHGUZGN15qGl0Qw8oFTvn8mA0=",
|
||||
"dev": true
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-2.0.1.tgz",
|
||||
"resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-2.0.1.tgz",
|
||||
"integrity": "sha1-32LBqpTtLxFOHQ8h/R1QSCt5pg4=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
@@ -25,7 +25,7 @@
|
||||
"react-dom": "^16.5.2",
|
||||
"react-moment": "^0.7.9",
|
||||
"reactstrap": "^6.4.0",
|
||||
"seafile-js": "^0.2.29",
|
||||
"seafile-js": "^0.2.31",
|
||||
"seafile-ui": "^0.1.10",
|
||||
"sw-precache-webpack-plugin": "0.11.4",
|
||||
"unified": "^7.0.0",
|
||||
|
102
frontend/src/components/dialog/create-tag-dialog.js
Normal file
102
frontend/src/components/dialog/create-tag-dialog.js
Normal file
@@ -0,0 +1,102 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Button, Modal, ModalHeader, ModalBody, ModalFooter, Input } from 'reactstrap';
|
||||
import { gettext, repoID } from '../../utils/constants';
|
||||
import { seafileAPI } from '../../utils/seafile-api';
|
||||
|
||||
const propTypes = {
|
||||
toggleCancel: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
class CreateTagDialog extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
tagName: '',
|
||||
tagColor: '',
|
||||
newTag: {},
|
||||
colorList: ['lime', 'teal', 'azure', 'green', 'blue', 'purple', 'pink', 'indigo'],
|
||||
};
|
||||
this.newInput = React.createRef();
|
||||
}
|
||||
|
||||
inputNewName = (e) => {
|
||||
this.setState({
|
||||
tagName: e.target.value,
|
||||
});
|
||||
}
|
||||
|
||||
selectTagcolor = (e) => {
|
||||
this.setState({
|
||||
tagColor: e.target.value,
|
||||
});
|
||||
}
|
||||
|
||||
createTag = () => {
|
||||
let name = this.state.tagName;
|
||||
let color = this.state.tagColor;
|
||||
seafileAPI.createRepoTag(repoID, name, color).then(() =>{
|
||||
this.props.toggleCancel();
|
||||
});
|
||||
}
|
||||
|
||||
handleKeyPress = (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
this.createTag();
|
||||
}
|
||||
}
|
||||
|
||||
toggle = () => {
|
||||
this.props.toggleCancel();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.setState({
|
||||
tagColor: this.state.colorList[0]
|
||||
});
|
||||
this.newInput.focus();
|
||||
this.newInput.setSelectionRange(0, 0);
|
||||
}
|
||||
|
||||
render() {
|
||||
let colorList = this.state.colorList;
|
||||
return (
|
||||
<Modal isOpen={true} toggle={this.toggle}>
|
||||
<ModalHeader toggle={this.toggle}>{gettext('New Tag')}</ModalHeader>
|
||||
<ModalBody>
|
||||
<div className="tag-create">
|
||||
<p>{gettext('Name')}</p>
|
||||
<Input onKeyPress={this.handleKeyPress} innerRef={input => {this.newInput = input;}} placeholder={gettext('name')} value={this.state.tagName} onChange={this.inputNewName}/>
|
||||
<div className="form-group color-chooser">
|
||||
<label className="form-label">{gettext('Select a color')}</label>
|
||||
<div className="row gutters-xs">
|
||||
{colorList.map((item, index)=>{
|
||||
var className = 'colorinput-color bg-' + item;
|
||||
return (
|
||||
<div key={index} className="col-auto" onChange={this.selectTagcolor}>
|
||||
<label className="colorinput">
|
||||
{index===0 ?
|
||||
<input name="color" type="radio" value={item} className="colorinput-input" defaultChecked onClick={this.selectTagcolor}></input> :
|
||||
<input name="color" type="radio" value={item} className="colorinput-input" onClick={this.selectTagcolor}></input>}
|
||||
<span className={className}></span>
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button color="primary" onClick={this.createTag}>{gettext('Save')}</Button>
|
||||
<Button color="secondary" onClick={this.toggle}>{gettext('Cancel')}</Button>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
CreateTagDialog.propTypes = propTypes;
|
||||
|
||||
export default CreateTagDialog;
|
@@ -19,7 +19,7 @@ class Delete extends React.Component {
|
||||
let name = this.props.currentNode.name;
|
||||
return (
|
||||
<Modal isOpen={true} toggle={this.toggle}>
|
||||
<ModalHeader toggle={this.toggle}>{gettext('Delete')}</ModalHeader>
|
||||
<ModalHeader toggle={this.toggle}>{gettext('Delete Tag')}</ModalHeader>
|
||||
<ModalBody>
|
||||
<p>{gettext('Are you sure to delete')}{' '}<b>{name}</b> ?</p>
|
||||
</ModalBody>
|
||||
|
97
frontend/src/components/dialog/list-tag-dialog.js
Normal file
97
frontend/src/components/dialog/list-tag-dialog.js
Normal file
@@ -0,0 +1,97 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
|
||||
import { gettext, repoID } from '../../utils/constants';
|
||||
import { seafileAPI } from '../../utils/seafile-api';
|
||||
import RepoTag from '../../models/repo-tag';
|
||||
import '../../css/repo-tag.css';
|
||||
|
||||
const tagListItemPropTypes = {
|
||||
item: PropTypes.object.isRequired,
|
||||
onTagUpdate: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
class TagListItem extends React.Component {
|
||||
|
||||
onTagUpdate = () => {
|
||||
this.props.onTagUpdate(this.props.item);
|
||||
}
|
||||
|
||||
render() {
|
||||
return(
|
||||
<li className="tag-list-item">
|
||||
<span className="tag-demo" style={{background: this.props.item.color}}>{this.props.item.name}</span>
|
||||
<i className="tag-edit fa fa-pencil" onClick={this.onTagUpdate}></i>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TagListItem.propTypes = tagListItemPropTypes;
|
||||
|
||||
const listTagPropTypes = {
|
||||
onListTagCancel: PropTypes.func.isRequired,
|
||||
onCreateRepoTag: PropTypes.func.isRequired,
|
||||
onUpdateRepoTag: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
class ListTagDialog extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
repotagList: [],
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
seafileAPI.listRepoTags(repoID).then(res => {
|
||||
let repotagList = [];
|
||||
res.data.repo_tags.forEach(item => {
|
||||
let repo_tag = new RepoTag(item);
|
||||
repotagList.push(repo_tag);
|
||||
});
|
||||
this.setState({
|
||||
repotagList: repotagList,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
toggle = () => {
|
||||
this.props.onListTagCancel();
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Fragment>
|
||||
<Modal isOpen={true} toggle={this.toggle}>
|
||||
<ModalHeader toggle={this.toggle}>{gettext('Tag List')}</ModalHeader>
|
||||
<ModalBody>
|
||||
{
|
||||
this.state.repotagList.length === 0 &&
|
||||
<div className="tag-list tag-list-container">
|
||||
{gettext('Click new tag button to create tags.')}
|
||||
</div>
|
||||
}
|
||||
{ this.state.repotagList.length > 0 &&
|
||||
<ul className="tag-list tag-list-container">
|
||||
{this.state.repotagList.map((repoTag, index) => {
|
||||
return (
|
||||
<TagListItem key={index} item={repoTag} onTagUpdate={this.props.onUpdateRepoTag}/>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
}
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button color="primary" onClick={this.props.onCreateRepoTag}>{gettext('New Tag')}</Button>
|
||||
<Button color="secondary" onClick={this.toggle}>{gettext('Close')}</Button>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ListTagDialog.propTypes = listTagPropTypes;
|
||||
|
||||
export default ListTagDialog;
|
115
frontend/src/components/dialog/update-tag-dialog.js
Normal file
115
frontend/src/components/dialog/update-tag-dialog.js
Normal file
@@ -0,0 +1,115 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Button, Modal, ModalHeader, ModalBody, ModalFooter, Input } from 'reactstrap';
|
||||
import { gettext, repoID } from '../../utils/constants';
|
||||
import { seafileAPI } from '../../utils/seafile-api';
|
||||
|
||||
const propTypes = {
|
||||
currentTag: PropTypes.object,
|
||||
toggleCancel: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
class UpdateTagDialog extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
deleteRepoTag: false,
|
||||
newName: this.props.currentTag.name,
|
||||
newColor: this.props.currentTag.color,
|
||||
colorList: ['lime', 'teal', 'azure', 'green', 'blue', 'purple', 'pink', 'indigo'],
|
||||
};
|
||||
this.newInput = React.createRef();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.newInput.focus();
|
||||
this.newInput.setSelectionRange(0, -1);
|
||||
}
|
||||
|
||||
inputNewName = (e) => {
|
||||
this.setState({
|
||||
newName: e.target.value,
|
||||
});
|
||||
}
|
||||
|
||||
selectNewcolor = (e) => {
|
||||
this.setState({
|
||||
newColor: e.target.value,
|
||||
});
|
||||
}
|
||||
|
||||
updateTag = () => {
|
||||
let tag_id = this.props.currentTag.id;
|
||||
let name = this.state.newName;
|
||||
let color = this.state.newColor;
|
||||
seafileAPI.updateRepoTag(repoID, tag_id, name, color).then(() => {
|
||||
this.props.toggleCancel();
|
||||
});
|
||||
}
|
||||
|
||||
handleKeyPress = (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
this.updateTag();
|
||||
}
|
||||
}
|
||||
|
||||
toggle = () => {
|
||||
this.props.toggleCancel();
|
||||
}
|
||||
|
||||
deleteTagClick = (item) => {
|
||||
this.setState({
|
||||
deleteRepoTag: !this.state.deleteRepoTag,
|
||||
});
|
||||
}
|
||||
|
||||
onDeleteTag = () => {
|
||||
let tag = this.props.currentTag;
|
||||
seafileAPI.deleteRepoTag(repoID, tag.id).then(() => {
|
||||
this.props.toggleCancel();
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
let colorList = this.state.colorList;
|
||||
return (
|
||||
<Fragment>
|
||||
<Modal isOpen={true} toggle={this.toggle}>
|
||||
<ModalHeader toggle={this.toggle}>{gettext('Edit Tag')}</ModalHeader>
|
||||
<ModalBody>
|
||||
<div className="tag-edit">
|
||||
<p>{gettext('Name:')}</p>
|
||||
<Input onKeyPress={this.handleKeyPress} innerRef={input => {this.newInput = input;}} placeholder="newName" value={this.state.newName} onChange={this.inputNewName}/>
|
||||
<div className="form-group color-chooser">
|
||||
<label className="form-label">{gettext('Select a color')}</label>
|
||||
<div className="row gutters-xs">
|
||||
{colorList.map((item, index)=>{
|
||||
var className = 'colorinput-color bg-' + item;
|
||||
return (
|
||||
<div key={index} className="col-auto" onChange={this.selectNewcolor}>
|
||||
<label className="colorinput">
|
||||
{item===this.props.currentTag.color ?
|
||||
<input name="color" type="radio" value={item} className="colorinput-input" defaultChecked onChange={this.selectNewcolor}></input> :
|
||||
<input name="color" type="radio" value={item} className="colorinput-input" onChange={this.selectNewcolor}></input>}
|
||||
<span className={className}></span>
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button color="primary" onClick={this.updateTag}>{gettext('Save')}</Button>
|
||||
<Button color="danger" onClick={this.onDeleteTag}>{gettext('Delete')}</Button>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
UpdateTagDialog.propTypes = propTypes;
|
||||
|
||||
export default UpdateTagDialog;
|
@@ -1,7 +1,10 @@
|
||||
import React from 'react';
|
||||
import React, { Fragment } from 'react';
|
||||
import { gettext, repoID, slug, permission, siteRoot } from '../../utils/constants';
|
||||
import { Utils } from '../../utils/utils';
|
||||
import PropTypes from 'prop-types';
|
||||
import ListTagDialog from '../dialog/list-tag-dialog';
|
||||
import CreateTagDialog from '../dialog/create-tag-dialog';
|
||||
import UpdateTagDialog from '../dialog/update-tag-dialog';
|
||||
|
||||
const propTypes = {
|
||||
filePath: PropTypes.string.isRequired
|
||||
@@ -9,6 +12,35 @@ const propTypes = {
|
||||
|
||||
class PathToolbar extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
currentTag: null,
|
||||
isListRepoTagShow: false,
|
||||
isUpdateRepoTagShow: false,
|
||||
isCreateRepoTagShow: false,
|
||||
};
|
||||
}
|
||||
|
||||
onListRepoTagToggle = () => {
|
||||
this.setState({isListRepoTagShow: !this.state.isListRepoTagShow});
|
||||
}
|
||||
|
||||
onCreateRepoTagToggle = () => {
|
||||
this.setState({
|
||||
isCreateRepoTagShow: !this.state.isCreateRepoTagShow,
|
||||
isListRepoTagShow: !this.state.isListRepoTagShow,
|
||||
});
|
||||
}
|
||||
|
||||
onUpdateRepoTagToggle = (currentTag) => {
|
||||
this.setState({
|
||||
currentTag: currentTag,
|
||||
isListRepoTagShow: !this.state.isListRepoTagShow,
|
||||
isUpdateRepoTagShow: !this.state.isUpdateRepoTagShow,
|
||||
});
|
||||
}
|
||||
|
||||
isMarkdownFile(filePath) {
|
||||
let lastIndex = filePath.lastIndexOf('/');
|
||||
let name = filePath.slice(lastIndex + 1);
|
||||
@@ -23,10 +55,34 @@ class PathToolbar extends React.Component {
|
||||
let historyUrl = siteRoot + 'repo/history/' + repoID + '/?referer=' + encodeURIComponent(location.href);
|
||||
if ( (name === slug || name === '') && !isFile && permission) {
|
||||
return (
|
||||
<Fragment>
|
||||
<ul className="path-toolbar">
|
||||
<li className="toolbar-item"><a className="op-link sf2-icon-tag-manager" onClick={this.onListRepoTagToggle} title={gettext('Tags')} aria-label={gettext('Tags')}></a></li>
|
||||
<li className="toolbar-item"><a className="op-link sf2-icon-trash" href={trashUrl} title={gettext('Trash')} aria-label={gettext('Trash')}></a></li>
|
||||
<li className="toolbar-item"><a className="op-link sf2-icon-history" href={historyUrl} title={gettext('History')} aria-label={gettext('History')}></a></li>
|
||||
</ul>
|
||||
{
|
||||
this.state.isListRepoTagShow &&
|
||||
<ListTagDialog
|
||||
onListTagCancel={this.onListRepoTagToggle}
|
||||
onCreateRepoTag={this.onCreateRepoTagToggle}
|
||||
onUpdateRepoTag={this.onUpdateRepoTagToggle}
|
||||
/>
|
||||
}
|
||||
{
|
||||
this.state.isCreateRepoTagShow &&
|
||||
<CreateTagDialog
|
||||
toggleCancel={this.onCreateRepoTagToggle}
|
||||
/>
|
||||
}
|
||||
{
|
||||
this.state.isUpdateRepoTagShow &&
|
||||
<UpdateTagDialog
|
||||
currentTag={this.state.currentTag}
|
||||
toggleCancel={this.onUpdateRepoTagToggle}
|
||||
/>
|
||||
}
|
||||
</Fragment>
|
||||
);
|
||||
} else if ( !isFile && permission) {
|
||||
return (
|
||||
|
48
frontend/src/css/repo-tag.css
Normal file
48
frontend/src/css/repo-tag.css
Normal file
@@ -0,0 +1,48 @@
|
||||
.tag-list-container {
|
||||
padding: 0.5rem;
|
||||
padding-bottom: 0;
|
||||
max-height: 15rem;
|
||||
list-style: none;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.tag-list-item {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.tag-list-item .tag-demo {
|
||||
flex: 1;
|
||||
border-radius: 0.25rem;
|
||||
padding-left: 0.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.tag-list-item .tag-demo:hover {
|
||||
border-left: 0.75rem solid #eb8205;
|
||||
|
||||
}
|
||||
|
||||
.tag-list-item .tag-edit {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-left: 0.5rem;
|
||||
width: 2.25rem;
|
||||
height: 2.25rem;
|
||||
border-radius: 0.25rem;
|
||||
color: #798d99;
|
||||
}
|
||||
|
||||
.tag-list-item .tag-edit:hover {
|
||||
color: #092d42;
|
||||
background-color: rgba(9,45,66,.13);
|
||||
}
|
||||
|
||||
.tag-create .color-chooser,
|
||||
.tag-edit .color-chooser {
|
||||
margin-top: 0.5rem;
|
||||
}
|
10
frontend/src/models/repo-tag.js
Normal file
10
frontend/src/models/repo-tag.js
Normal file
@@ -0,0 +1,10 @@
|
||||
class RepoTag {
|
||||
constructor(object) {
|
||||
this.id = object.repo_tag_id;
|
||||
this.repo_id = object.repo_id;
|
||||
this.name = object.tag_name;
|
||||
this.color = object.tag_color;
|
||||
}
|
||||
}
|
||||
|
||||
export default RepoTag;
|
@@ -76,6 +76,7 @@
|
||||
.sf2-icon-two-columns:before { content:"\e036"; }
|
||||
.sf2-icon-confirm:before {content:"\e01e"}
|
||||
.sf2-icon-cancel:before {content:"\e01f"}
|
||||
.sf2-icon-tag-manager:before {content:"\e015"}
|
||||
|
||||
/* common class and element style*/
|
||||
a { color:#eb8205; }
|
||||
|
@@ -49,7 +49,7 @@ class RepoTagsView(APIView):
|
||||
for tag in tag_list:
|
||||
tags.append(tag.to_dict())
|
||||
|
||||
return Response({"tags": tags}, status=status.HTTP_200_OK)
|
||||
return Response({"repo_tags": tags}, status=status.HTTP_200_OK)
|
||||
|
||||
def post(self, request, repo_id):
|
||||
"""add one repo_tag.
|
||||
@@ -87,7 +87,7 @@ class RepoTagsView(APIView):
|
||||
error_msg = 'Internal Server Error.'
|
||||
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
|
||||
|
||||
return Response({"tag": repo_tag.to_dict()}, status=status.HTTP_201_CREATED)
|
||||
return Response({"repo_tag": repo_tag.to_dict()}, status=status.HTTP_201_CREATED)
|
||||
|
||||
|
||||
class RepoTagView(APIView):
|
||||
@@ -95,15 +95,10 @@ class RepoTagView(APIView):
|
||||
permission_classes = (IsAuthenticated,)
|
||||
throttle_classes = (UserRateThrottle,)
|
||||
|
||||
def put(self, request, repo_id):
|
||||
def put(self, request, repo_id, repo_tag_id):
|
||||
"""update one repo_tag
|
||||
"""
|
||||
# argument check
|
||||
tag_id = request.data.get('tag_id')
|
||||
if not tag_id:
|
||||
error_msg = 'tag_id invalid.'
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
|
||||
tag_name = request.data.get('name')
|
||||
if not tag_name:
|
||||
error_msg = 'name invalid.'
|
||||
@@ -115,7 +110,7 @@ class RepoTagView(APIView):
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
|
||||
# resource check
|
||||
repo_tag = RepoTags.objects.get_repo_tag_by_id(tag_id=tag_id)
|
||||
repo_tag = RepoTags.objects.get_repo_tag_by_id(repo_tag_id)
|
||||
if not repo_tag:
|
||||
error_msg = 'repo_tag not found.'
|
||||
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||
@@ -134,19 +129,13 @@ class RepoTagView(APIView):
|
||||
error_msg = 'Internal Server Error.'
|
||||
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
|
||||
|
||||
return Response({"tag": repo_tag.to_dict()}, status=status.HTTP_200_OK)
|
||||
return Response({"repo_tag": repo_tag.to_dict()}, status=status.HTTP_200_OK)
|
||||
|
||||
def delete(self, request, repo_id):
|
||||
def delete(self, request, repo_id, repo_tag_id):
|
||||
"""delete one repo_tag
|
||||
"""
|
||||
# argument check
|
||||
tag_id = request.data.get('tag_id')
|
||||
if not tag_id:
|
||||
error_msg = 'tag_id invalid.'
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
|
||||
# resource check
|
||||
repo_tag = RepoTags.objects.get_repo_tag_by_id(tag_id=tag_id)
|
||||
repo_tag = RepoTags.objects.get_repo_tag_by_id(repo_tag_id)
|
||||
if not repo_tag:
|
||||
error_msg = 'repo_tag not found.'
|
||||
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||
@@ -157,7 +146,7 @@ class RepoTagView(APIView):
|
||||
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
||||
|
||||
try:
|
||||
RepoTags.objects.delete_repo_tag(tag_id)
|
||||
RepoTags.objects.delete_repo_tag(repo_tag_id)
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
error_msg = 'Internal Server Error.'
|
||||
|
@@ -14,9 +14,9 @@ class RepoTagsManager(models.Manager):
|
||||
except self.model.DoesNotExist:
|
||||
return None
|
||||
|
||||
def get_repo_tag_by_id(self, tag_id):
|
||||
def get_repo_tag_by_id(self, repo_tag_id):
|
||||
try:
|
||||
return super(RepoTagsManager, self).get(pk=tag_id)
|
||||
return super(RepoTagsManager, self).get(pk=repo_tag_id)
|
||||
except self.model.DoesNotExist:
|
||||
return None
|
||||
|
||||
@@ -28,9 +28,9 @@ class RepoTagsManager(models.Manager):
|
||||
repo_tag.save()
|
||||
return repo_tag
|
||||
|
||||
def delete_repo_tag(self, tag_id):
|
||||
def delete_repo_tag(self, repo_tag_id):
|
||||
try:
|
||||
repo_tag = super(RepoTagsManager, self).get(pk=tag_id)
|
||||
repo_tag = super(RepoTagsManager, self).get(pk=repo_tag_id)
|
||||
repo_tag.delete()
|
||||
return True
|
||||
except self.model.DoesNotExist:
|
||||
@@ -47,7 +47,7 @@ class RepoTags(models.Model):
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
"tag_id": self.pk,
|
||||
"repo_tag_id": self.pk,
|
||||
"repo_id": self.repo_id,
|
||||
"tag_name": self.name,
|
||||
"tag_color": self.color,
|
||||
|
@@ -294,7 +294,7 @@ urlpatterns = [
|
||||
url(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/history/$', RepoHistory.as_view(), name='api-v2.1-repo-history'),
|
||||
url(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/set-password/$', RepoSetPassword.as_view(), name="api-v2.1-repo-set-password"),
|
||||
url(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/repo-tags/$', RepoTagsView.as_view(), name='api-v2.1-repo-tags'),
|
||||
url(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/repo-tag/$', RepoTagView.as_view(), name='api-v2.1-repo-tag'),
|
||||
url(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/repo-tags/(?P<repo_tag_id>\d+)/$', RepoTagView.as_view(), name='api-v2.1-repo-tag'),
|
||||
|
||||
## user::download-dir-zip-task
|
||||
url(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/zip-task/$', ZipTaskView.as_view(), name='api-v2.1-zip-task'),
|
||||
|
Reference in New Issue
Block a user