diff options
Diffstat (limited to 'animism-align')
16 files changed, 180 insertions, 40 deletions
diff --git a/animism-align/cli/app/controllers/upload_controller.py b/animism-align/cli/app/controllers/upload_controller.py index 1a324cb..e910f28 100644 --- a/animism-align/cli/app/controllers/upload_controller.py +++ b/animism-align/cli/app/controllers/upload_controller.py @@ -86,10 +86,18 @@ class UploadView(FlaskView): # except: # return jsonify({ 'status': 'error', 'error': 'Image parse error' }) + uploaded_im_fn = secure_filename(file.filename) + uploaded_im_abspath = os.path.join(app_cfg.DIR_UPLOADS, tag) + uploaded_im_fullpath = os.path.join(uploaded_im_abspath, uploaded_im_fn) + session = Session() upload = session.query(Upload).filter_by(sha256=sha256).first() if upload is not None: print("Already uploaded image") + if not os.path.exists(uploaded_im_fullpath): + # if we got in some weird state where the record wasnt deleted.... + os.makedirs(uploaded_im_abspath, exist_ok=True) + file.save(uploaded_im_fullpath) response = { 'status': 'ok', 'notes': 'Image already uploaded', @@ -98,16 +106,10 @@ class UploadView(FlaskView): session.close() return jsonify(response) - uploaded_im_fn = secure_filename(file.filename) - uploaded_im_abspath = os.path.join(app_cfg.DIR_UPLOADS, tag) - uploaded_im_fullpath = os.path.join(uploaded_im_abspath, uploaded_im_fn) - uploaded_im_stored_fn = uploaded_im_fn - os.makedirs(uploaded_im_abspath, exist_ok=True) - # nparr.tofile(uploaded_im_fullpath) file.save(uploaded_im_fullpath) - upload = Upload(username=username, tag=tag, fn=uploaded_im_stored_fn, sha256=sha256, ext=ext) + upload = Upload(username=username, tag=tag, fn=uploaded_im_fn, sha256=sha256, ext=ext) session.add(upload) session.commit() response = { @@ -135,11 +137,13 @@ class UploadView(FlaskView): tag = upload.tag # uploaded_im_fn = secure_filename(fn) - uploaded_im_abspath = os.path.join(app_cfg.DIR_UPLOADS, tag, fn) + uploaded_im_abspath = os.path.join(app_cfg.DIR_UPLOADS, tag) + uploaded_im_fullpath = os.path.join(uploaded_im_abspath, fn) + # uploaded_im_fullpath = os.path.join(uploaded_im_abspath, fn) - if os.path.exists(uploaded_im_abspath): - print("Removing " + uploaded_im_abspath) - os.remove(uploaded_im_abspath) + if os.path.exists(uploaded_im_fullpath): + print("Removing " + uploaded_im_fullpath) + os.remove(uploaded_im_fullpath) session.delete(upload) session.commit() diff --git a/animism-align/cli/app/sql/models/media.py b/animism-align/cli/app/sql/models/media.py index e3a395d..4628f4d 100644 --- a/animism-align/cli/app/sql/models/media.py +++ b/animism-align/cli/app/sql/models/media.py @@ -18,6 +18,7 @@ class Media(Base): title = Column(String(256, convert_unicode=True), nullable=True) author = Column(String(256, convert_unicode=True), nullable=True) pre_title = Column(String(256, convert_unicode=True), nullable=True) + post_title = Column(String(256, convert_unicode=True), nullable=True) translated_title = Column(String(256, convert_unicode=True), nullable=True) date = Column(String(256, convert_unicode=True), nullable=True) source = Column(String(256, convert_unicode=True), nullable=True) @@ -34,6 +35,7 @@ class Media(Base): 'url': self.url, 'title': self.title, 'pre_title': self.pre_title, + 'post_title': self.post_title, 'translated_title': self.translated_title, 'date': self.date, 'source': self.source, diff --git a/animism-align/cli/app/sql/versions/202007171640_add_post_title_to_media.py b/animism-align/cli/app/sql/versions/202007171640_add_post_title_to_media.py new file mode 100644 index 0000000..63dc00a --- /dev/null +++ b/animism-align/cli/app/sql/versions/202007171640_add_post_title_to_media.py @@ -0,0 +1,29 @@ +"""add post_title to media + +Revision ID: 1e3c915ef21f +Revises: 65b17598c815 +Create Date: 2020-07-17 16:40:08.620589 + +""" +from alembic import op +import sqlalchemy as sa +import sqlalchemy_utc + + +# revision identifiers, used by Alembic. +revision = '1e3c915ef21f' +down_revision = '65b17598c815' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('media', sa.Column('post_title', sa.String(length=256, _expect_unicode=True), nullable=True)) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('media', 'post_title') + # ### end Alembic commands ### diff --git a/animism-align/frontend/common/form.component.js b/animism-align/frontend/common/form.component.js index 9a94136..c38a299 100644 --- a/animism-align/frontend/common/form.component.js +++ b/animism-align/frontend/common/form.component.js @@ -66,6 +66,7 @@ export const TextArea = props => ( <textarea onChange={props.onChange} name={props.name} + placeholder={props.placeholder} value={props.data[props.name]} /> </label> diff --git a/animism-align/frontend/common/form.css b/animism-align/frontend/common/form.css index ca7c27b..bbee27e 100644 --- a/animism-align/frontend/common/form.css +++ b/animism-align/frontend/common/form.css @@ -113,19 +113,19 @@ input[type=checkbox] { input[type=checkbox] + span { font-size: 0.825rem; text-transform: uppercase; - color: #444; + color: #ddd; } input[type=checkbox]:hover + span { - color: #000; + color: #fff; } input[type=checkbox]:focus + span { - color: #84f; + color: #fff; } input[type="checkbox"]:checked + span { - color: #000; + color: #fff; } input[type="checkbox"]:focus:checked + span { - color: #84f; + color: #fff; } input[type="checkbox"]:after { diff --git a/animism-align/frontend/common/menubutton.component.js b/animism-align/frontend/common/menubutton.component.js index d4c2e31..a4eea39 100644 --- a/animism-align/frontend/common/menubutton.component.js +++ b/animism-align/frontend/common/menubutton.component.js @@ -84,6 +84,11 @@ const icons = { image: '/static/img/fastfood.svg', svg: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18.06 22.99h1.66c.84 0 1.53-.64 1.63-1.46L23 5.05h-5V1h-1.97v4.05h-4.97l.3 2.34c1.71.47 3.31 1.32 4.27 2.26 1.44 1.42 2.43 2.89 2.43 5.29v8.05zM1 21.99V21h15.03v.99c0 .55-.45 1-1.01 1H2.01c-.56 0-1.01-.45-1.01-1zm15.03-7c0-8-15.03-8-15.03 0h15.03zM1.02 17h15v2h-15z"/><path fill="none" d="M0 0h24v24H0z"/></svg> }, + copy: { + title: 'Copy', + image: '/static/img/copy.svg', + svg: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg>, + }, // export: { // title: 'Export', // image: '/static/img/export.svg', diff --git a/animism-align/frontend/views/align/components/annotations/annotation.form.js b/animism-align/frontend/views/align/components/annotations/annotation.form.js index 3348804..53f640f 100644 --- a/animism-align/frontend/views/align/components/annotations/annotation.form.js +++ b/animism-align/frontend/views/align/components/annotations/annotation.form.js @@ -43,14 +43,14 @@ class AnnotationForm extends Component { case 38: // up e.preventDefault() start_ts -= 0.1 - actions.align.updateAnnotationForm('start_ts', start_ts) + actions.align.updateAnnotationForm('start_ts', Math.max(0, start_ts)) actions.audio.seek(start_ts) actions.align.setCursor(start_ts) break case 40: // down e.preventDefault() start_ts += 0.1 - actions.align.updateAnnotationForm('start_ts', start_ts) + actions.align.updateAnnotationForm('start_ts', Math.max(0, start_ts)) actions.audio.seek(start_ts) actions.align.setCursor(start_ts) break diff --git a/animism-align/frontend/views/align/containers/timeline.container.js b/animism-align/frontend/views/align/containers/timeline.container.js index a0f4d3a..a924eaf 100644 --- a/animism-align/frontend/views/align/containers/timeline.container.js +++ b/animism-align/frontend/views/align/containers/timeline.container.js @@ -54,7 +54,7 @@ class Timeline extends Component { switch (e.keyCode) { case 38: // up e.preventDefault() - selectedAnnotation.start_ts -= e.shiftKey ? 1 : 0.1 + selectedAnnotation.start_ts = Math.max(selectedAnnotation.start_ts - (e.shiftKey ? 1 : 0.1), 0) actions.align.updateSelectedAnnotation(selectedAnnotation) actions.audio.seek(selectedAnnotation.start_ts) actions.align.setCursor(selectedAnnotation.start_ts) diff --git a/animism-align/frontend/views/media/components/media.form.js b/animism-align/frontend/views/media/components/media.form.js index 9b93788..1463041 100644 --- a/animism-align/frontend/views/media/components/media.form.js +++ b/animism-align/frontend/views/media/components/media.form.js @@ -16,6 +16,7 @@ const newMedia = () => ({ title: '', author: '', pre_title: '', + post_title: '', translated_title: '', date: '', source: '', @@ -38,9 +39,11 @@ export default class MediaForm extends Component { constructor(props) { super(props) + this.handleKeyDown = this.handleKeyDown.bind(this) this.handleSelect = this.handleSelect.bind(this) this.handleChange = this.handleChange.bind(this) this.handleSettingsChange = this.handleSettingsChange.bind(this) + this.handleSettingsChangeEvent = this.handleSettingsChangeEvent.bind(this) this.handleSubmit = this.handleSubmit.bind(this) } @@ -57,6 +60,21 @@ export default class MediaForm extends Component { ...data }, }) + window.addEventListener('keydown', this.handleKeyDown) + } + + componentWillUnmount() { + window.removeEventListener('keydown', this.handleKeyDown) + } + + handleKeyDown(e) { + // console.log(e, e.keyCode) + if ((e.ctrlKey || e.metaKey) && e.keyCode === 83) { + if (e) { + e.preventDefault() + } + this.handleSubmit() + } } handleChange(e) { @@ -78,7 +96,13 @@ export default class MediaForm extends Component { }) } + handleSettingsChangeEvent(e) { + const { name, value } = e.target + this.handleSettingsChange(name, value) + } + handleSettingsChange(name, value) { + console.log(name, value) if (name !== 'multiple') { value = { [name]: value } } @@ -94,11 +118,13 @@ export default class MediaForm extends Component { } handleSubmit(e) { - e.preventDefault() + if (e) { + e.preventDefault() + } const { isNew, onSubmit } = this.props const { data } = this.state const requiredKeys = "author title date".split(" ") - const validKeys = "type tag url title author pre_title translated_title date source medium start_ts settings".split(" ") + const validKeys = "type tag url title author pre_title post_title translated_title date source medium start_ts settings".split(" ") const validData = validKeys.reduce((a,b) => { a[b] = data[b]; return a }, {}) const errorFields = requiredKeys.filter(key => !validData[key]) if (errorFields.length) { @@ -167,7 +193,13 @@ export default class MediaForm extends Component { <TextInput title="Title Prefix" name="pre_title" - required + data={data} + onChange={this.handleChange} + autoComplete="off" + /> + <TextInput + title="Title Suffix" + name="post_title" data={data} onChange={this.handleChange} autoComplete="off" @@ -175,7 +207,6 @@ export default class MediaForm extends Component { <TextInput title="Translated Title" name="translated_title" - required data={data} onChange={this.handleChange} autoComplete="off" @@ -205,6 +236,20 @@ export default class MediaForm extends Component { autoComplete="off" /> <TextArea + title="Citation" + name="bibliography" + placeholder="Use if special HTML formatting needed" + data={data.settings} + onChange={this.handleSettingsChangeEvent} + /> + <Checkbox + label="Hide in list of works" + name="hide_in_bibliography" + checked={data.settings.hide_in_bibliography} + onChange={this.handleSettingsChange} + autoComplete="off" + /> + <TextArea title="Description" name="description" data={data} @@ -217,7 +262,7 @@ export default class MediaForm extends Component { {!!errorFields.size && <label> <span></span> - <span>Please complete the required fields =)</span> + <span>Please complete the required fields</span> </label> } </form> diff --git a/animism-align/frontend/views/media/components/media.formImage.js b/animism-align/frontend/views/media/components/media.formImage.js index abd0f94..4a97112 100644 --- a/animism-align/frontend/views/media/components/media.formImage.js +++ b/animism-align/frontend/views/media/components/media.formImage.js @@ -86,19 +86,23 @@ export default class MediaImageForm extends Component { // when we upload an image, if the image already exists in this "position" // on the record, we should also delete it if (this.props.data.settings[tag] && this.props.data.settings[tag].id) { - return actions.upload.destroy(this.props.data.settings[tag]) - .then(() => { - return this.uploadTaggedSize(image, tag, fn) - }) - .catch(() => { - console.log('error deleting the image') - return this.uploadTaggedSize(image, tag, fn) + return new Promise((resolve, reject) => { + actions.upload.destroy(this.props.data.settings[tag]) + .then(() => { + console.log('destroy successful') + this.uploadTaggedSize(image, tag, fn).then(data => resolve(data)) + }) + .catch(() => { + console.log('error deleting the image') + this.uploadTaggedSize(image, tag, fn).then(data => resolve(data)) + }) }) } return this.uploadTaggedSize(image, tag, fn) } uploadTaggedSize(image, tag, fn) { + console.log('uploading size', tag) const uploadData = { image, tag, diff --git a/animism-align/frontend/views/media/components/media.menu.js b/animism-align/frontend/views/media/components/media.menu.js index 3d7e86a..153a5c1 100644 --- a/animism-align/frontend/views/media/components/media.menu.js +++ b/animism-align/frontend/views/media/components/media.menu.js @@ -28,7 +28,7 @@ const MediaIndexMenu = () => ([ ]) const MediaShowMenu = connect(mapStateToProps)((props) => ([ - <MenuButton key='back' name="back" />, + <MenuButton key='back' name="back" href="/media/" />, <MenuButton key='edit' name="edit" href={"/media/" + props.match.params.id + "/edit/"} />, <MenuButton key='delete' name="delete" onClick={() => { const { res: media } = props.media.show @@ -41,9 +41,18 @@ const MediaShowMenu = connect(mapStateToProps)((props) => ([ ])) const MediaNewMenu = (props) => ([ - <MenuButton key='back' name="back" />, + <MenuButton key='back' name="back" href="/media/" />, ]) -const MediaEditMenu = (props) => ([ - <MenuButton key='back' name="back" />, -]) +const MediaEditMenu = connect(mapStateToProps)((props) => ([ + <MenuButton key='back' name="back" href="/media/" />, + <MenuButton key='copy' name="copy" href={"/media/" + props.match.params.id + '/copy/'} label="Make a copy" />, + <MenuButton key='delete' name="delete" onClick={() => { + const { res: media } = props.media.show + if (confirm("Really delete this media?")) { + actions.media.destroy(media).then(() => { + history.push('/media/') + }) + } + }} />, +])) diff --git a/animism-align/frontend/views/media/containers/media.edit.js b/animism-align/frontend/views/media/containers/media.edit.js index 8c353d9..143cdfe 100644 --- a/animism-align/frontend/views/media/containers/media.edit.js +++ b/animism-align/frontend/views/media/containers/media.edit.js @@ -36,7 +36,7 @@ class MediaEdit extends Component { } return ( <div className='row formContainer'> - <MediaMenu /> + <MediaMenu mediaActions={this.props.mediaActions} /> <MediaForm data={show.res} onSubmit={this.handleSubmit.bind(this)} @@ -51,7 +51,7 @@ const mapStateToProps = state => ({ }) const mapDispatchToProps = dispatch => ({ - // searchActions: bindActionCreators({ ...searchActions }, dispatch), + // mediaActions: bindActionCreators({ ...mediaActions }, dispatch), }) export default connect(mapStateToProps, mapDispatchToProps)(MediaEdit) diff --git a/animism-align/frontend/views/media/containers/media.index.js b/animism-align/frontend/views/media/containers/media.index.js index bff781e..a865522 100644 --- a/animism-align/frontend/views/media/containers/media.index.js +++ b/animism-align/frontend/views/media/containers/media.index.js @@ -72,7 +72,10 @@ class MediaIndex extends Component { <div className="row"> <MediaMenu /> <div className={'results ' + options.thumbnailSize}> - {order.map(id => <MediaItem key={id} data={lookup[id]} />)} + <h2>Images</h2> + {order.filter(id => lookup[id].type === 'image').map(id => <MediaItem key={id} data={lookup[id]} />)} + <h2>Video</h2> + {order.filter(id => lookup[id].type === 'video').map(id => <MediaItem key={id} data={lookup[id]} />)} </div> </div> {order.length >= 50 && <button className='loadMore' onClick={() => this.fetch(true)}>Load More</button>} diff --git a/animism-align/frontend/views/media/containers/media.new.js b/animism-align/frontend/views/media/containers/media.new.js index e740c0c..80879cb 100644 --- a/animism-align/frontend/views/media/containers/media.new.js +++ b/animism-align/frontend/views/media/containers/media.new.js @@ -9,6 +9,34 @@ import MediaForm from '../components/media.form' import MediaMenu from '../components/media.menu' class MediaNew extends Component { + state = { + loading: true, + initialData: {}, + } + + componentDidMount() { + // console.log(this.props.match.params.id) + if (this.props.match.params && this.props.match.params.id) { + actions.media.show(this.props.match.params.id) + .then(data => { + const { id, ...initialData } = data.res + delete initialData.settings.video + delete initialData.settings.crop + delete initialData.settings.display + delete initialData.settings.fullsize + delete initialData.settings.thumbnail + delete initialData.settings.bibliography + console.log("copying", id) + this.setState({ + loading: false, + initialData, + }) + }) + } else { + this.setState({ loading: false }) + } + } + handleSubmit(data) { console.log(data) actions.media.create(data) @@ -24,12 +52,17 @@ class MediaNew extends Component { } render() { + if (this.state.loading) { + return ( + <div className='row formContainer' /> + ) + } return ( <div className='row formContainer'> <MediaMenu /> <MediaForm isNew - data={{}} + data={this.state.initialData} onSubmit={this.handleSubmit.bind(this)} /> </div> diff --git a/animism-align/frontend/views/media/media.container.js b/animism-align/frontend/views/media/media.container.js index 41fed4b..97a5b08 100644 --- a/animism-align/frontend/views/media/media.container.js +++ b/animism-align/frontend/views/media/media.container.js @@ -16,6 +16,7 @@ class Container extends Component { render() { return ( <div className='media'> + <Route exact path='/media/:id/copy/' component={MediaNew} /> <Route exact path='/media/:id/edit/' component={MediaEdit} /> <Route exact path='/media/new/' component={MediaNew} /> <Route exact path='/media/' component={MediaIndex} /> diff --git a/animism-align/frontend/views/media/media.css b/animism-align/frontend/views/media/media.css index 701cf44..a2d95c8 100644 --- a/animism-align/frontend/views/media/media.css +++ b/animism-align/frontend/views/media/media.css @@ -8,6 +8,10 @@ margin-bottom: 1rem; margin-right: 1rem; } +.results h2 { + display: block; + width: 100%; +} .media .results .meta > div { max-width: 100%; } |
