Commit c6008ef6 authored by anton's avatar anton
Browse files

Merge branch 'release_2_4' into staging

# Conflicts:
#	CHANGELOG.md
#	package-lock.json
#	package.json
#	src/pages/piecemaker/media/annotate.vue
parents cd782579 861cd3fb
Pipeline #79592 passed with stage
in 4 minutes and 2 seconds
......@@ -12,17 +12,21 @@ and this project adheres to
### Added
- [BVH](https://research.cs.wisc.edu/graphics/Courses/cs-838-1999/Jeff/BVH.html) file support
- `media/create` allows uploading asset files
### Updated
- Updated [mbjs-media](https://gitlab.rlp.net/motionbank/mbjs/media)
to 0.6.1
## [2.3.1] - 2020-09-30
### Fixed
- `media/edit` page breaks when single ISO timestamp
is returned for selectorValue
### Updated
- [mbjs-media](https://gitlab.rlp.net/motionbank/mbjs/media) to 0.5.0
## [2.3.0] - 2020-08-17
......@@ -545,7 +549,8 @@ of a video cannot be retrieved
- Start proper versioning at 1.0.0
[Unreleased]: https://gitlab.rlp.net/motionbank/applications/systems-frontend/compare/v2.3.0...release_2_3
[Unreleased]: https://gitlab.rlp.net/motionbank/applications/systems-frontend/compare/v2.3.1...release_2_3
[2.3.1]: https://gitlab.rlp.net/motionbank/applications/systems-frontend/compare/v2.3.0...v2.3.1
[2.3.0]: https://gitlab.rlp.net/motionbank/applications/systems-frontend/compare/v2.2.1...v2.3.0
[2.2.1]: https://gitlab.rlp.net/motionbank/applications/systems-frontend/compare/v2.2.0...v2.2.1
[2.2.0]: https://gitlab.rlp.net/motionbank/applications/systems-frontend/compare/v2.1.2...v2.2.0
......
{
"name": "systems-frontend",
"version": "2.3.0",
"version": "2.4.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
......@@ -2356,9 +2356,9 @@
"dev": true
},
"@types/node": {
"version": "14.10.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.10.2.tgz",
"integrity": "sha512-IzMhbDYCpv26pC2wboJ4MMOa9GKtjplXfcAqrMeNJpUUwpM/2ATt2w1JPUXwS6spu856TvKZL2AOmeU2rAxskw=="
"version": "14.11.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.11.5.tgz",
"integrity": "sha512-jVFzDV6NTbrLMxm4xDSIW/gKnk8rQLF9wAzLWIOg+5nU6ACrIMndeBdXci0FGtqJbP9tQvm6V39eshc96TO2wQ=="
},
"@types/q": {
"version": "1.5.2",
......@@ -11096,9 +11096,9 @@
}
},
"mbjs-media": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/mbjs-media/-/mbjs-media-0.5.0.tgz",
"integrity": "sha512-7W8NN7Q4EIBaKc2RVCFJHtyzeuoC8i6MMUzsgPbcrV/GNFHeJxSDKmexPKUE811v/uZvF12zktNUzy/begqekg==",
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/mbjs-media/-/mbjs-media-0.6.1.tgz",
"integrity": "sha512-Y+cwkAw49E1Ni75DwFr8Aep46mblXEii1VfH6fVitiJpso6Nzxp6HHlEIO0gjwrco7EuyHc0wnJaGKwgbP0cAQ==",
"requires": {
"axios": "^0.18.0",
"bluebird": "^3.5.1",
......@@ -11110,7 +11110,7 @@
"mbjs-data-models": "0.0.8",
"mbjs-utils": "0.0.6",
"md5-file": "^4.0.0",
"mime-types": "^2.1.19",
"mime-types": "^2.1.27",
"mkdirp": "^0.5.1",
"mz": "^2.7.0",
"open-graph-scraper": "^3.5.0",
......@@ -11127,11 +11127,11 @@
}
},
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz",
"integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==",
"requires": {
"ms": "^2.1.1"
"ms": "2.1.2"
}
},
"is-buffer": {
......@@ -11179,6 +11179,24 @@
}
}
}
},
"mime-db": {
"version": "1.44.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
"integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg=="
},
"mime-types": {
"version": "2.1.27",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
"integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
"requires": {
"mime-db": "1.44.0"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}
}
},
......@@ -13495,10 +13513,9 @@
"dev": true
},
"pretty-bytes": {
"version": "4.0.2",
"resolved": "http://registry.npmjs.org/pretty-bytes/-/pretty-bytes-4.0.2.tgz",
"integrity": "sha1-sr+C5zUNZcbDOqlaqlpPYyf2HNk=",
"dev": true
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.4.1.tgz",
"integrity": "sha512-s1Iam6Gwz3JI5Hweaz4GoCD1WUNUIyzePFy5+Js2hjwGVt2Z79wNN+ZKOZ2vB6C+Xs6njyB84Z1IthQg8d9LxA=="
},
"pretty-error": {
"version": "2.1.1",
......@@ -16482,11 +16499,6 @@
"thenify": ">= 3.1.0 < 4"
}
},
"three": {
"version": "0.120.1",
"resolved": "https://registry.npmjs.org/three/-/three-0.120.1.tgz",
"integrity": "sha512-ktaCRFUR7JUZcKec+cBRz+oBex5pOVaJhrtxvFF2T7on53o9UkEux+/Nh1g/4zeb4t/pbxIFcADbn/ACu3LC1g=="
},
"throttleit": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/throttleit/-/throttleit-0.0.2.tgz",
......@@ -18244,6 +18256,12 @@
"jsonfile": "^4.0.0",
"universalify": "^0.1.0"
}
},
"pretty-bytes": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-4.0.2.tgz",
"integrity": "sha1-sr+C5zUNZcbDOqlaqlpPYyf2HNk=",
"dev": true
}
}
},
......
{
"name": "systems-frontend",
"version": "2.3.0",
"version": "2.4.0",
"description": "Combined frontend project for Motion Bank web services",
"productName": "Motion Bank Systems Frontend",
"cordovaId": "org.motionbank.frontend",
......@@ -60,15 +60,15 @@
"mbjs-auth-service": "^1.0.8",
"mbjs-data-models": "^2.0.18",
"mbjs-filesystem": "^1.0.0",
"mbjs-media": "0.5.0",
"mbjs-media": "0.6.1",
"mbjs-quasar": "^2.3.3",
"mbjs-utils": "0.0.6",
"nedb": "^1.8.0",
"pretty-bytes": "^5.4.1",
"qs": "^6.6.0",
"sanitize-html": "1.19.2",
"sift": "^13.1.10",
"socket.io-client": "^2.2.0",
"three": "^0.120.1",
"tiny-emitter": "2.0.2",
"uuid-validate": "0.0.2",
"video.js": "^7.7.5",
......
......@@ -160,6 +160,7 @@ module.exports = function (ctx) {
UI_HIDE_MOSYS: JSON.stringify(process.env.UI_HIDE_MOSYS || false),
UI_HIDE_DOCUMENTS: JSON.stringify(process.env.UI_HIDE_DOCUMENTS || false),
UI_HIDE_GROUPS: JSON.stringify(process.env.UI_HIDE_GROUPS || false),
UI_SHOW_ASSETS: JSON.stringify(process.env.UI_SHOW_ASSETS || false),
MODULE_PROVIDER: JSON.stringify(process.env.MODULE_PROVIDER || null)
}
},
......
Subproject commit eab6a2726b623649f306b47588c884e029ce7898
Subproject commit fa1a09dab1f4066aef8229a494f3937d505878e0
Subproject commit f98b109d26fd5d5758ddf04d8f6723c70afe2235
Subproject commit 2cfa4f24b0e38d70ce2035b89869630cf4928011
......@@ -16,7 +16,6 @@ export default {
back: 'Back',
create_account: 'Create Account',
close_account: 'Close Account',
create_document: 'Add Document',
create_timeline: 'Create Timeline',
create_grid: 'Create Grid',
create_package: 'Create Package',
......@@ -95,7 +94,6 @@ export default {
errors: {
no_invitations: 'You didn\'t invite anyone to this group yet.',
no_members: 'This group has no members yet.',
document_delete_failed: 'Failed to delete Document',
item_exists: 'This item already exists.',
unknown: 'Unknown Error',
invalid_email: 'Please enter a valid email',
......@@ -114,6 +112,7 @@ export default {
export_archive_failed: 'Failed to export timeline archive: {error}',
packaging_failed: 'Packaging failed: {error}',
update_acl_failed: 'Failed to update ACL: {error}',
upload_assets_errors: 'Upload assets completed with errors',
unauthorized: 'Unauthorized',
forbidden: 'Forbidden',
not_found: 'Not found',
......@@ -192,6 +191,7 @@ export default {
pending_invitations: 'Pending Invitations',
my_groups: 'My Groups',
my_vocabularies: 'My vocabularies',
my_assets: 'My Assets',
name: 'Name',
new_tag: 'New tag',
new_term: 'New term',
......@@ -216,11 +216,13 @@ export default {
size: 'Size',
tag: 'Tag',
tags: 'Tags',
target_path_optional: 'Target path (optional, e.g.: mycoolproject/videos)',
textual_body: 'Annotations',
title: 'Title',
title_unknown: 'Unknown Title',
timeline: 'Timeline',
type: 'Type',
upload_asset: 'Upload an asset file',
url: 'URL',
use_custom_date: 'Use custom date and time',
media_duration: 'Media duration',
......@@ -236,13 +238,10 @@ export default {
},
descriptions: {
access_control: 'Allow or disallow members of a group see your timeline and, optionally, the attached annotations.',
access_control_documents: 'Allow or disallow members of a group and/or the public to see your Document',
css_stylesheet: 'Either link an external stylesheet or set an inline value'
},
messages: {
acl_updated: 'Access settings updated',
document_created: 'Document created',
document_deleted: 'Document deleted',
login_success: 'Login successful',
logout_notice: 'You have been logged out',
registration_success: 'Account was successfully created. You can log in now!',
......@@ -258,7 +257,9 @@ export default {
confirm_delete: 'Delete this item?',
confirm_remove_member: 'Remove this member?',
confirm_delete_group: 'Delete this group?',
confirm_remove_asset: 'Delete this asset? Any existing references to it will be broken.',
updated_annotation: 'Updated annotation',
upload_assets_success: 'Assets uploaded successfully',
url_copied: 'URL copied to clipboard',
caution_media_time_override: 'Caution: Changing media reference time does not update associated annotations!',
browser_unsupported_warning: '<strong>Unsupported browser:</strong> For optimal performance please use ' +
......@@ -270,7 +271,9 @@ export default {
group_invite_request: 'You have been invited by {name} to join "{group}".',
invite_accepted: 'Invitation accepted',
invite_rejected: 'Invitation rejected',
confirm_remove_invitation: 'Remove invitation?'
confirm_remove_invitation: 'Remove invitation?',
delete_asset_success: 'Asset deleted successfully',
no_assets_uploaded: 'No assets have been uploaded.'
},
help: {
acl: {
......@@ -314,15 +317,9 @@ export default {
mosys_grids_edit: 'Edit',
mosys_grids_create: 'Create Grid'
},
documents: {
label: 'Documents',
documents_list: 'All Documents',
documents_copy_url: 'Copy URL',
documents_delete: 'Delete',
documents_download: 'Download',
documents_edit: 'Edit'
},
users: {
assets: 'Assets',
assets_upload: 'Upload asset files',
label: 'Account settings',
users_manage: 'Account settings',
groups: 'Groups'
......@@ -333,17 +330,6 @@ export default {
}
},
routes: {
documents: {
edit: {
title: 'Edit Document'
},
create: {
title: 'Add Document'
},
list: {
title: 'Documents'
}
},
annotate: {
media: {
title: 'Annotate Media',
......@@ -383,6 +369,13 @@ export default {
}
},
users: {
assets: {
title: 'Manage Assets',
caption: 'Add or delete asset files.'
},
assets_upload: {
title: 'Upload asset files'
},
create: {
title: 'Create Account',
caption: 'Fill out the form to create a new account.'
......
<template lang="pug">
full-screen
q-btn(v-if="!isMobile", slot="backButton", @click="$router.push({ name: 'documents.list' })",
icon="keyboard_backspace", round, small)
content-block(:position="'first'")
headline(:content="$t('routes.documents.create.title')")
content-paragraph(:position="'first'")
uploader(v-if="user", dark, :url="url", @finish="onFinish", :headers="headers", :fields="uploadFields")
</template>
<script>
import { mapGetters } from 'vuex'
import Headline from '../../components/shared/elements/Headline'
import ContentBlock from '../../components/shared/elements/ContentBlock'
import ContentParagraph from '../../components/shared/elements/ContentParagraph'
export default {
components: {
Headline,
ContentBlock,
ContentParagraph
},
data () {
return {
uploadFields: [],
headers: {
Authorization: `Bearer ${this.$auth.token}`
}
}
},
computed: {
...mapGetters({
user: 'auth/getUserState',
isMobile: 'globalSettings/getIsMobile'
}),
url () {
return this.user ? `${process.env.STORAGE_HOST}/files/user-${this.user.uuid}` : undefined
}
},
mounted () {
this.$root.$emit('setBackButton', 'list')
},
methods: {
onFinish (responses) {
for (let key of Object.keys(responses)) {
if (responses[key] && responses[key].message) {
this.$store.commit('notifications/addMessage', {
body: responses[key].message,
mode: 'alert',
type: 'error'
})
}
else {
this.$store.commit('notifications/addMessage', {
body: 'messages.document_created',
mode: 'alert',
type: 'success'
})
this.$router.push({ name: 'documents.list' })
}
}
}
}
}
</script>
<template lang="pug">
full-screen
//q-btn(v-if="!isMobile", slot="backButton", @click="$router.push({ name: 'documents.list' })", icon="keyboard_backspace", round, small)
content-block(v-if="availableRoles.length", :position="'last'")
headline(:content="$t('routes.documents.edit.title')")
| {{ $t('descriptions.access_control_documents') }}
content-paragraph(:position="'first'")
q-checkbox(v-model="acl.public", :label="$t('labels.public')", dark)
content-paragraph
q-select(v-model="acl.group", :clearable="true", :clear-value="undefined",
:float-label="$t('labels.access_control_add_group')", :options="availableRoles", dark)
content-paragraph
q-select(v-model="acl.group_remove", :clearable="true", :clear-value="undefined",
:float-label="$t('labels.access_control_remove_group')", :options="availableRoles", dark)
<!--q-field(dark)-->
<!--q-checkbox(v-model="acl.recursive", :label="$t('labels.recursive')", dark)-->
content-paragraph(:position="'last'")
.full-width.text-right.q-mt-sm
q-btn(:label="$t('buttons.update_access_control')", @click="updateACL", color="primary")
</template>
<script>
import { mapGetters } from 'vuex'
import { ObjectUtil } from 'mbjs-utils'
import Headline from '../../components/shared/elements/Headline'
import ContentBlock from '../../components/shared/elements/ContentBlock'
import ContentParagraph from '../../components/shared/elements/ContentParagraph'
export default {
components: {
Headline,
ContentBlock,
ContentParagraph
},
data () {
return {
acl: {
public: false,
group: undefined,
group_remove: undefined,
recursive: false
}
}
},
computed: {
...mapGetters({
user: 'auth/getUserState',
isMobile: 'globalSettings/getIsMobile'
}),
availableRoles () {
try {
const roles = this.user[`${process.env.AUTH0_APP_METADATA_PREFIX}roles`]
.filter(role => role !== 'user')
return roles.sort().map(role => { return { label: role, value: role } })
}
catch (e) {
return []
}
},
resourceName () {
return ObjectUtil.uuid5(`${this.$route.params.bucket}/${this.$route.params.asset}`)
}
},
async mounted () {
const aclQuery = {role: 'public', uuid: this.resourceName, permission: 'get'}
const permissions = await this.$store.dispatch('acl/isRoleAllowed', aclQuery)
this.acl.public = permissions.get === true
this.$root.$emit('setBackButton', 'list')
},
methods: {
async setACL (action, payload, recursive = false) {
await this.$store.dispatch(action, payload)
if (recursive) {
// TODO: add recursion to asset ACL
throw new Error('Recursion not implemented')
}
},
async updateACL () {
this.$q.loading.show()
if (this.acl.public) {
await this.setACL('acl/set', { role: 'public', id: this.resourceName, permissions: ['get'] }, this.acl.recursive)
}
else {
await this.setACL('acl/remove', { role: 'public', id: this.resourceName, permission: 'get' }, this.acl.recursive)
}
if (this.acl.group) {
await this.setACL('acl/set', { role: this.acl.group, id: this.resourceName, permissions: ['get'] }, this.acl.recursive)
}
if (this.acl.group_remove) {
await this.setACL('acl/remove', { role: this.acl.group_remove, id: this.resourceName, permission: 'get' }, this.acl.recursive)
}
this.$q.loading.hide()
this.$store.commit('notifications/addMessage', {
body: 'messages.acl_updated',
type: 'success'
})
}
}
}
</script>
<template lang="pug">
full-screen
confirm-modal(ref="confirmModal", @confirm="handleConfirmModal")
content-block(:position="'first'")
headline(:content="$t('routes.documents.list.title')")
content-paragraph(:position="'first'")
data-table(v-if="user", :config="config", :title="'routes.documents.list.title'", ref="listTable")
</template>
<script>
import { mapGetters } from 'vuex'
import { openURL } from 'quasar'
import Headline from '../../components/shared/elements/Headline'
import ContentBlock from '../../components/shared/elements/ContentBlock'
import ContentParagraph from '../../components/shared/elements/ContentParagraph'
export default {
components: {
Headline,
ContentBlock,
ContentParagraph
},
data () {
const _this = this
return {
assets: [],
config: {
columns: [
{
name: 'name',
label: this.$t('labels.name'),
field: 'name',
filter: true,
sortable: true
},
{
name: 'type',
label: this.$t('labels.type'),
field: row => row.metaData && row.metaData['content-type'] ? row.metaData['content-type'] : 'unknown'
},
{
name: 'size',
label: this.$t('labels.size'),
field: 'size',
format: val => `${(val / Math.pow(1024, 2)).toFixed(2)} MB`,
sortable: true
}
],
actions: [
{
type: 'url',
title: 'buttons.copy_url',
click: item => _this.copyToClipboard(_this.getAssetURL(item.name))
},
{
type: 'download',
title: 'buttons.download',
click: item => openURL(_this.getAssetURL(item.name, true))
},
{
type: 'edit',
title: 'buttons.edit',
click: item => _this.$router.push({ name: 'documents.edit', params: { asset: item.name, bucket: _this.bucketName } })
},
{
type: 'delete',
title: 'buttons.delete',
click: item => _this.$refs.confirmModal.show('buttons.delete', item, 'buttons.delete')
}
],
async request () {
return _this.$store.dispatch('files/list', _this.bucketName)
}
}
}
},
computed: {
...mapGetters({
user: 'auth/getUserState',
isMobile: 'globalSettings/getIsMobile'
}),
bucketName () {
return `user-${this.user.uuid}`
}
},
mounted () {
this.$root.$emit('setBackButton')
},
methods: {
getAssetURL (asset, download = false) {
const url = `${process.env.STORAGE_HOST}/files/user-${this.user.uuid}/${asset}`
if (download) return `${url}?dl=1`
return url
},
async copyToClipboard (text) {
try {
await navigator.clipboard.writeText(text)
this.$store.commit('notifications/addMessage', {
body: 'messages.url_copied',
type: 'success'
})
}
catch (err) {
this.$store.commit('notifications/addMessage', {
body: err.message,
type: 'error'
})
}
},