Add "my files" using resources #3792
	
		
	
				
					
				
			
							parent
							
								
									b090544ba6
								
							
						
					
					
						commit
						fcabbcc1ef
					
				@ -0,0 +1,75 @@ | 
				
			||||
<template> | 
				
			||||
        <q-form> | 
				
			||||
          <q-input | 
				
			||||
              id="item_title" | 
				
			||||
              v-model="item.title" | 
				
			||||
              :placeholder="$t('Title')" | 
				
			||||
              :error="v$.item.title.$error" | 
				
			||||
              @input="v$.item.title.$touch()" | 
				
			||||
              @blur="v$.item.title.$touch()" | 
				
			||||
              :error-message="titleErrors" | 
				
			||||
          /> | 
				
			||||
          <slot></slot> | 
				
			||||
        </q-form> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script> | 
				
			||||
import has from 'lodash/has'; | 
				
			||||
import useVuelidate from '@vuelidate/core'; | 
				
			||||
import { required } from '@vuelidate/validators'; | 
				
			||||
 | 
				
			||||
export default { | 
				
			||||
  name: 'PersonalFileForm', | 
				
			||||
  setup () { | 
				
			||||
    return { v$: useVuelidate() } | 
				
			||||
  }, | 
				
			||||
  props: { | 
				
			||||
    values: { | 
				
			||||
      type: Object, | 
				
			||||
      required: true | 
				
			||||
    }, | 
				
			||||
    errors: { | 
				
			||||
      type: Object, | 
				
			||||
      default: () => {} | 
				
			||||
    }, | 
				
			||||
    initialValues: { | 
				
			||||
      type: Object, | 
				
			||||
      default: () => {} | 
				
			||||
    }, | 
				
			||||
  }, | 
				
			||||
  data() { | 
				
			||||
    return { | 
				
			||||
      title: null, | 
				
			||||
      parentResourceNodeId: null, | 
				
			||||
    }; | 
				
			||||
  }, | 
				
			||||
  computed: { | 
				
			||||
    item() { | 
				
			||||
      return this.initialValues || this.values; | 
				
			||||
    }, | 
				
			||||
    titleErrors() { | 
				
			||||
      const errors = []; | 
				
			||||
      if (!this.v$.item.title.$dirty) return errors; | 
				
			||||
      has(this.violations, 'title') && errors.push(this.violations.title); | 
				
			||||
 | 
				
			||||
      if (this.v$.item.title.required) { | 
				
			||||
        return this.$t('Field is required') | 
				
			||||
      } | 
				
			||||
 | 
				
			||||
      return errors; | 
				
			||||
    }, | 
				
			||||
    violations() { | 
				
			||||
      return this.errors || {}; | 
				
			||||
    } | 
				
			||||
  }, | 
				
			||||
  validations: { | 
				
			||||
    item: { | 
				
			||||
      title: { | 
				
			||||
        required, | 
				
			||||
      }, | 
				
			||||
      parentResourceNodeId: { | 
				
			||||
      }, | 
				
			||||
    } | 
				
			||||
  } | 
				
			||||
}; | 
				
			||||
</script> | 
				
			||||
@ -0,0 +1,126 @@ | 
				
			||||
<template> | 
				
			||||
  <q-form> | 
				
			||||
        <div class="input-group mb-3"> | 
				
			||||
          <div class="custom-file"> | 
				
			||||
            <input | 
				
			||||
                id="file_upload" | 
				
			||||
                type="file" | 
				
			||||
                class="custom-file-input" | 
				
			||||
                ref="fileList" | 
				
			||||
                multiple | 
				
			||||
                placeholder="File upload" | 
				
			||||
                @change="selectFile" | 
				
			||||
            /> | 
				
			||||
            <label | 
				
			||||
                class="custom-file-label" | 
				
			||||
                for="file_upload" | 
				
			||||
                aria-describedby="File upload"> | 
				
			||||
              Choose file | 
				
			||||
            </label> | 
				
			||||
          </div> | 
				
			||||
        </div> | 
				
			||||
 | 
				
			||||
        <div class="field"> | 
				
			||||
          <div | 
				
			||||
              v-for="(file, index) in files" | 
				
			||||
              :key="index" | 
				
			||||
              :class="{ error : file.invalidMessage}" | 
				
			||||
          > | 
				
			||||
            <div> | 
				
			||||
              {{ file.name }} | 
				
			||||
              <span v-if="file.invalidMessage"> | 
				
			||||
                - {{ file.invalidMessage }} | 
				
			||||
              </span> | 
				
			||||
              <span> | 
				
			||||
              <a @click.prevent="files.splice(index, 1)" | 
				
			||||
                 class="delete" | 
				
			||||
              > | 
				
			||||
                <FontAwesomeIcon icon="trash" /> | 
				
			||||
              </a> | 
				
			||||
            </span> | 
				
			||||
            </div> | 
				
			||||
          </div> | 
				
			||||
        </div> | 
				
			||||
  </q-form> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script> | 
				
			||||
import has from 'lodash/has'; | 
				
			||||
import map from 'lodash/map'; | 
				
			||||
import useVuelidate from '@vuelidate/core'; | 
				
			||||
 | 
				
			||||
export default { | 
				
			||||
  name: 'PersonalFileFormUpload', | 
				
			||||
  setup () { | 
				
			||||
    return { v$: useVuelidate() } | 
				
			||||
  }, | 
				
			||||
  props: { | 
				
			||||
    values: { | 
				
			||||
      type: Array, | 
				
			||||
      required: true | 
				
			||||
    }, | 
				
			||||
    parentResourceNodeId: { | 
				
			||||
      type: Number | 
				
			||||
    }, | 
				
			||||
    resourceLinkList: { | 
				
			||||
      type: String, | 
				
			||||
    }, | 
				
			||||
    errors: { | 
				
			||||
      type: Object, | 
				
			||||
      default: () => {} | 
				
			||||
    }, | 
				
			||||
    processFiles: { | 
				
			||||
      type: Function, | 
				
			||||
      required: false | 
				
			||||
    }, | 
				
			||||
  }, | 
				
			||||
  data() { | 
				
			||||
    return { | 
				
			||||
      fileList:[], | 
				
			||||
      files: [], | 
				
			||||
    }; | 
				
			||||
  }, | 
				
			||||
  computed: { | 
				
			||||
    titleErrors() { | 
				
			||||
      const errors = []; | 
				
			||||
      if (!this.$v.item.title.$dirty) return errors; | 
				
			||||
      has(this.violations, 'title') && errors.push(this.violations.title); | 
				
			||||
      !this.$v.item.title.required && errors.push(this.$t('Field is required')); | 
				
			||||
 | 
				
			||||
      return errors; | 
				
			||||
    }, | 
				
			||||
    violations() { | 
				
			||||
      return this.errors || {}; | 
				
			||||
    } | 
				
			||||
  }, | 
				
			||||
  methods: { | 
				
			||||
    selectFile() { | 
				
			||||
      const files = this.$refs.fileList.files; | 
				
			||||
 | 
				
			||||
      this.files = [ | 
				
			||||
        ...this.files, | 
				
			||||
        ...map(files, file => ({ | 
				
			||||
          name: file.name, | 
				
			||||
          size: file.size, | 
				
			||||
          type: file.type, | 
				
			||||
          filetype: 'file', | 
				
			||||
          parentResourceNodeId: this.parentResourceNodeId, | 
				
			||||
          resourceLinkList: this.resourceLinkList, | 
				
			||||
          uploadFile: file, | 
				
			||||
          invalidMessage: this.validate(file), | 
				
			||||
        })) | 
				
			||||
      ] | 
				
			||||
    }, | 
				
			||||
    validate(file) { | 
				
			||||
      if (file) { | 
				
			||||
        return ''; | 
				
			||||
      } | 
				
			||||
 | 
				
			||||
      return 'error'; | 
				
			||||
    } | 
				
			||||
  }, | 
				
			||||
  validations: { | 
				
			||||
    files: {} | 
				
			||||
  } | 
				
			||||
}; | 
				
			||||
</script> | 
				
			||||
@ -0,0 +1,9 @@ | 
				
			||||
<template> | 
				
			||||
  <router-view></router-view> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script> | 
				
			||||
  export default { | 
				
			||||
      name: 'PersonalFileLayout' | 
				
			||||
  } | 
				
			||||
</script> | 
				
			||||
@ -0,0 +1,68 @@ | 
				
			||||
<template> | 
				
			||||
  <div v-if="item && item['resourceLinkListFromEntity']"> | 
				
			||||
    <ul> | 
				
			||||
      <li | 
				
			||||
        v-for="link in item['resourceLinkListFromEntity']" | 
				
			||||
      > | 
				
			||||
        <div v-if="link['course']"> | 
				
			||||
          {{ $t('Course') }}:  {{ link.course.resourceNode.title }} | 
				
			||||
        </div> | 
				
			||||
 | 
				
			||||
        <div v-if="link['session']"> | 
				
			||||
          {{ $t('Session') }}:  {{ link.session.name }} | 
				
			||||
        </div> | 
				
			||||
 | 
				
			||||
        <div v-if="link['group']"> | 
				
			||||
          {{ $t('Group') }}: {{ link.session.resourceNode.title }} | 
				
			||||
        </div> | 
				
			||||
 | 
				
			||||
        <q-separator /> | 
				
			||||
 | 
				
			||||
        <q-select | 
				
			||||
          filled | 
				
			||||
          v-model="link.visibility" | 
				
			||||
          :options="visibilityList" | 
				
			||||
          label="Status" | 
				
			||||
          emit-value | 
				
			||||
          map-options | 
				
			||||
        /> | 
				
			||||
      </li> | 
				
			||||
    </ul> | 
				
			||||
  </div> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script> | 
				
			||||
import has from 'lodash/has'; | 
				
			||||
import useVuelidate from '@vuelidate/core'; | 
				
			||||
import { required } from '@vuelidate/validators'; | 
				
			||||
 | 
				
			||||
export default { | 
				
			||||
  name: 'ResourceLinkForm', | 
				
			||||
  setup () { | 
				
			||||
    const visibilityList = [ | 
				
			||||
      {value: 2, label: 'Published'}, | 
				
			||||
      {value: 0, label: 'Draft'}, | 
				
			||||
    ]; | 
				
			||||
    return {v$: useVuelidate(), visibilityList}; | 
				
			||||
  }, | 
				
			||||
  props: { | 
				
			||||
    values: { | 
				
			||||
      type: Object, | 
				
			||||
      required: true | 
				
			||||
    }, | 
				
			||||
    errors: { | 
				
			||||
      type: Object, | 
				
			||||
      default: () => {} | 
				
			||||
    }, | 
				
			||||
    initialValues: { | 
				
			||||
      type: Object, | 
				
			||||
      default: () => {} | 
				
			||||
    }, | 
				
			||||
  }, | 
				
			||||
  computed: { | 
				
			||||
    item() { | 
				
			||||
      return this.initialValues || this.values; | 
				
			||||
    }, | 
				
			||||
  }, | 
				
			||||
}; | 
				
			||||
</script> | 
				
			||||
@ -0,0 +1,42 @@ | 
				
			||||
export default { | 
				
			||||
  path: '/resources/personal_files', | 
				
			||||
  meta: { requiresAuth: true }, | 
				
			||||
  name: 'personal_files', | 
				
			||||
  component: () => import('../views/personalfile/Home.vue'), | 
				
			||||
  children: [ | 
				
			||||
    { | 
				
			||||
      name: 'personal_files', | 
				
			||||
      path: ':node/', | 
				
			||||
      component: () => import('../components/personalfile/Layout.vue'), | 
				
			||||
      redirect: { name: 'PersonalFileList' }, | 
				
			||||
      children: [ | 
				
			||||
        { | 
				
			||||
          name: 'PersonalFileList', | 
				
			||||
          path: '', | 
				
			||||
          component: () => import('../views/personalfile/List.vue') | 
				
			||||
        }, | 
				
			||||
        { | 
				
			||||
          name: 'PersonalFileCreate', | 
				
			||||
          path: 'new', | 
				
			||||
          component: () => import('../views/personalfile/Create.vue') | 
				
			||||
        }, | 
				
			||||
        { | 
				
			||||
          name: 'PersonalFileUploadFile', | 
				
			||||
          path: 'upload', | 
				
			||||
          component: () => import('../views/personalfile/Upload.vue') | 
				
			||||
        }, | 
				
			||||
        { | 
				
			||||
          name: 'PersonalFileUpdate', | 
				
			||||
          //path: ':id/edit',
 | 
				
			||||
          path: 'edit_file', | 
				
			||||
          component: () => import('../views/personalfile/Update.vue') | 
				
			||||
        }, | 
				
			||||
        { | 
				
			||||
          name: 'PersonalFileShow', | 
				
			||||
          path: 'show', | 
				
			||||
          component: () => import('../views/personalfile/Show.vue') | 
				
			||||
        } | 
				
			||||
      ] | 
				
			||||
    }, | 
				
			||||
  ] | 
				
			||||
}; | 
				
			||||
@ -0,0 +1,3 @@ | 
				
			||||
import makeService from './api'; | 
				
			||||
 | 
				
			||||
export default makeService('personal_files'); | 
				
			||||
@ -0,0 +1,63 @@ | 
				
			||||
<template> | 
				
			||||
  <div> | 
				
			||||
    <Toolbar | 
				
			||||
        :handle-submit="onSendForm" | 
				
			||||
        :handle-reset="resetForm" | 
				
			||||
    /> | 
				
			||||
 | 
				
			||||
    <DocumentsForm | 
				
			||||
      ref="createForm" | 
				
			||||
      :values="item" | 
				
			||||
      :errors="violations" | 
				
			||||
    /> | 
				
			||||
    <Loading :visible="isLoading" /> | 
				
			||||
  </div> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script> | 
				
			||||
import { mapActions } from 'vuex'; | 
				
			||||
import { createHelpers } from 'vuex-map-fields'; | 
				
			||||
import DocumentsForm from '../../components/personalfile/Form.vue'; | 
				
			||||
import Loading from '../../components/Loading.vue'; | 
				
			||||
import Toolbar from '../../components/Toolbar.vue'; | 
				
			||||
import CreateMixin from '../../mixins/CreateMixin'; | 
				
			||||
 | 
				
			||||
const servicePrefix = 'PersonalFile'; | 
				
			||||
 | 
				
			||||
const { mapFields } = createHelpers({ | 
				
			||||
  getterType: 'personal_file/getField', | 
				
			||||
  mutationType: 'personal_file/updateField' | 
				
			||||
}); | 
				
			||||
 | 
				
			||||
export default { | 
				
			||||
  name: 'PersonalFileCreate', | 
				
			||||
  servicePrefix, | 
				
			||||
  components: { | 
				
			||||
    Loading, | 
				
			||||
    Toolbar, | 
				
			||||
    DocumentsForm | 
				
			||||
  }, | 
				
			||||
  mixins: [CreateMixin], | 
				
			||||
  data() { | 
				
			||||
    return { | 
				
			||||
      item: {}, | 
				
			||||
      type: 'folder' | 
				
			||||
    }; | 
				
			||||
  }, | 
				
			||||
  computed: { | 
				
			||||
    ...mapFields(['error', 'isLoading', 'created', 'violations']) | 
				
			||||
  }, | 
				
			||||
  created() { | 
				
			||||
    this.item.parentResourceNodeId = this.$route.params.node; | 
				
			||||
    this.item.resourceLinkList = JSON.stringify([{ | 
				
			||||
      gid: this.$route.query.gid, | 
				
			||||
      sid: this.$route.query.sid, | 
				
			||||
      c_id: this.$route.query.cid, | 
				
			||||
      visibility: 2, // visible by default | 
				
			||||
    }]); | 
				
			||||
  }, | 
				
			||||
  methods: { | 
				
			||||
    ...mapActions('personalfile', ['create', 'reset']) | 
				
			||||
  } | 
				
			||||
}; | 
				
			||||
</script> | 
				
			||||
@ -0,0 +1,43 @@ | 
				
			||||
<template> | 
				
			||||
  <router-view></router-view> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script> | 
				
			||||
import { mapActions, mapGetters } from 'vuex'; | 
				
			||||
import Loading from '../../components/Loading.vue'; | 
				
			||||
import ShowMixin from '../../mixins/ShowMixin'; | 
				
			||||
import Toolbar from '../../components/Toolbar.vue'; | 
				
			||||
import isEmpty from "lodash/isEmpty"; | 
				
			||||
const servicePrefix = 'PersonalFile'; | 
				
			||||
 | 
				
			||||
export default { | 
				
			||||
  name: 'PersonalFileHome', | 
				
			||||
  servicePrefix, | 
				
			||||
  components: { | 
				
			||||
      Loading, | 
				
			||||
      Toolbar | 
				
			||||
  }, | 
				
			||||
  created() { | 
				
			||||
    console.log('CREATED HOME'); | 
				
			||||
    let resourceNodeId = this.currentUser.resourceNode['id']; | 
				
			||||
 | 
				
			||||
    console.log(resourceNodeId); | 
				
			||||
    this.$router | 
				
			||||
        .push({ name: `${this.$options.servicePrefix}List`,    params: { node: resourceNodeId },}) | 
				
			||||
        .catch(() => {}); | 
				
			||||
 | 
				
			||||
  }, | 
				
			||||
  computed: { | 
				
			||||
    // From crud.js list function | 
				
			||||
    ...mapGetters('resourcenode', { | 
				
			||||
      resourceNode: 'getResourceNode' | 
				
			||||
    }), | 
				
			||||
    ...mapGetters({ | 
				
			||||
      'isAuthenticated': 'security/isAuthenticated', | 
				
			||||
      'isAdmin': 'security/isAdmin', | 
				
			||||
      'currentUser': 'security/getUser', | 
				
			||||
    }), | 
				
			||||
  } | 
				
			||||
 | 
				
			||||
}; | 
				
			||||
</script> | 
				
			||||
@ -0,0 +1,363 @@ | 
				
			||||
<template> | 
				
			||||
  <div v-if="isAuthenticated"  class="q-card"> | 
				
			||||
    <div class="p-4 flex flex-row gap-1 mb-2"> | 
				
			||||
      <div class="flex flex-row gap-2" > | 
				
			||||
        <Button label="New folder" icon="fa fa-folder-plus" class="btn btn-primary" @click="openNew" /> | 
				
			||||
        <Button label="Upload" icon="fa fa-file-upload" class="btn btn-primary" @click="uploadDocumentHandler()" /> | 
				
			||||
        <Button label="Delete" icon="pi pi-trash" class="btn btn-danger " @click="confirmDeleteMultiple" :disabled="!selectedItems || !selectedItems.length" /> | 
				
			||||
      </div> | 
				
			||||
    </div> | 
				
			||||
  </div> | 
				
			||||
  <DataTable | 
				
			||||
      class="p-datatable-sm" | 
				
			||||
      :value="items" | 
				
			||||
      v-model:selection="selectedItems" | 
				
			||||
      dataKey="iid" | 
				
			||||
      v-model:filters="filters" | 
				
			||||
      filterDisplay="menu" | 
				
			||||
      :lazy="true" | 
				
			||||
      :paginator="true" | 
				
			||||
      :rows="10" | 
				
			||||
      :totalRecords="totalItems" | 
				
			||||
      :loading="isLoading" | 
				
			||||
      @page="onPage($event)" | 
				
			||||
      @sort="sortingChanged($event)" | 
				
			||||
      paginatorTemplate="CurrentPageReport FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink RowsPerPageDropdown" | 
				
			||||
      :rowsPerPageOptions="[5, 10, 20, 50]" | 
				
			||||
      responsiveLayout="scroll" | 
				
			||||
      currentPageReportTemplate="Showing {first} to {last} of {totalRecords}" | 
				
			||||
      :globalFilterFields="['resourceNode.title', 'resourceNode.updatedAt']"> | 
				
			||||
 | 
				
			||||
    <span v-if="isCurrentTeacher"> | 
				
			||||
      <Column selectionMode="multiple" style="width: 3rem" :exportable="false"></Column> | 
				
			||||
    </span> | 
				
			||||
 | 
				
			||||
    <Column field="resourceNode.title" :header="$t('Title')" :sortable="true"> | 
				
			||||
      <template #body="slotProps"> | 
				
			||||
        <div v-if="slotProps.data && slotProps.data.resourceNode && slotProps.data.resourceNode.resourceFile"> | 
				
			||||
          <ResourceFileLink :resource="slotProps.data" /> | 
				
			||||
        </div> | 
				
			||||
        <div v-else> | 
				
			||||
          <a | 
				
			||||
              v-if="slotProps.data" | 
				
			||||
              @click="handleClick(slotProps.data)" | 
				
			||||
              class="cursor-pointer " > | 
				
			||||
            <FontAwesomeIcon | 
				
			||||
                icon="folder" | 
				
			||||
                size="lg" | 
				
			||||
            /> | 
				
			||||
            {{ slotProps.data.resourceNode.title }} | 
				
			||||
          </a> | 
				
			||||
        </div> | 
				
			||||
      </template> | 
				
			||||
    </Column> | 
				
			||||
 | 
				
			||||
    <Column field="resourceNode.resourceFile.size" :header="$t('Size')" :sortable="true"> | 
				
			||||
      <template #body="slotProps"> | 
				
			||||
        {{ | 
				
			||||
          slotProps.data.resourceNode.resourceFile ? $filters.prettyBytes(slotProps.data.resourceNode.resourceFile.size) : '' | 
				
			||||
        }} | 
				
			||||
      </template> | 
				
			||||
    </Column> | 
				
			||||
 | 
				
			||||
    <Column field="resourceNode.updatedAt" :header="$t('Modified')" :sortable="true"> | 
				
			||||
      <template #body="slotProps"> | 
				
			||||
        {{$luxonDateTime.fromISO(slotProps.data.resourceNode.updatedAt).toRelative() }} | 
				
			||||
      </template> | 
				
			||||
    </Column> | 
				
			||||
 | 
				
			||||
    <Column :exportable="false"> | 
				
			||||
      <template #body="slotProps"> | 
				
			||||
        <div class="flex flex-row gap-2"> | 
				
			||||
          <Button icon="fa fa-info-circle"  class="btn btn-primary " @click="showHandler(slotProps.data)" /> | 
				
			||||
          <Button v-if="isAuthenticated" icon="pi pi-pencil" class="btn btn-primary p-mr-2" @click="editHandler(slotProps.data)" /> | 
				
			||||
          <Button v-if="isAuthenticated" icon="pi pi-trash" class="btn btn-danger" @click="confirmDeleteItem(slotProps.data)" /> | 
				
			||||
        </div> | 
				
			||||
      </template> | 
				
			||||
    </Column> | 
				
			||||
 | 
				
			||||
<!--    <template #paginatorLeft>--> | 
				
			||||
<!--      <Button type="button" icon="pi pi-refresh" class="p-button-text" />--> | 
				
			||||
<!--    </template>--> | 
				
			||||
<!--    <template #paginatorRight>--> | 
				
			||||
<!--      <Button type="button" icon="pi pi-cloud" class="p-button-text" />--> | 
				
			||||
<!--    </template>--> | 
				
			||||
  </DataTable> | 
				
			||||
 | 
				
			||||
  <Dialog v-model:visible="itemDialog" :style="{width: '450px'}" :header="$t('New folder')" :modal="true" class="p-fluid"> | 
				
			||||
    <div class="p-field"> | 
				
			||||
      <label for="name">{{ $t('Name') }}</label> | 
				
			||||
      <InputText | 
				
			||||
          autocomplete="off" | 
				
			||||
          id="title" | 
				
			||||
          v-model.trim="item.title" | 
				
			||||
          required="true" | 
				
			||||
          autofocus | 
				
			||||
          :class="{'p-invalid': submitted && !item.title}" | 
				
			||||
      /> | 
				
			||||
      <small class="p-error" v-if="submitted && !item.title">$t('Title is required')</small> | 
				
			||||
    </div> | 
				
			||||
 | 
				
			||||
    <template #footer> | 
				
			||||
      <Button label="Cancel" icon="pi pi-times" class="p-button-text" @click="hideDialog"/> | 
				
			||||
      <Button label="Save" icon="pi pi-check" class="p-button-text" @click="saveItem" /> | 
				
			||||
    </template> | 
				
			||||
  </Dialog> | 
				
			||||
 | 
				
			||||
  <Dialog v-model:visible="deleteItemDialog" :style="{width: '450px'}" header="Confirm" :modal="true"> | 
				
			||||
    <div class="confirmation-content"> | 
				
			||||
      <i class="pi pi-exclamation-triangle p-mr-3" style="font-size: 2rem" /> | 
				
			||||
      <span v-if="item">Are you sure you want to delete <b>{{item.title}}</b>?</span> | 
				
			||||
    </div> | 
				
			||||
    <template #footer> | 
				
			||||
      <Button label="No" icon="pi pi-times" class="p-button-text" @click="deleteItemDialog = false"/> | 
				
			||||
      <Button label="Yes" icon="pi pi-check" class="p-button-text" @click="deleteItemButton" /> | 
				
			||||
    </template> | 
				
			||||
  </Dialog> | 
				
			||||
 | 
				
			||||
  <Dialog v-model:visible="deleteMultipleDialog" :style="{width: '450px'}" header="Confirm" :modal="true"> | 
				
			||||
    <div class="confirmation-content"> | 
				
			||||
      <i class="pi pi-exclamation-triangle p-mr-3" style="font-size: 2rem" /> | 
				
			||||
      <span v-if="item">Are you sure you want to delete the selected items?</span> | 
				
			||||
    </div> | 
				
			||||
    <template #footer> | 
				
			||||
      <Button label="No" icon="pi pi-times" class="p-button-text" @click="deleteMultipleDialog = false"/> | 
				
			||||
      <Button label="Yes" icon="pi pi-check" class="p-button-text" @click="deleteMultipleItems" /> | 
				
			||||
    </template> | 
				
			||||
  </Dialog> | 
				
			||||
 | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script> | 
				
			||||
import { mapActions, mapGetters } from 'vuex'; | 
				
			||||
import { mapFields } from 'vuex-map-fields'; | 
				
			||||
import ListMixin from '../../mixins/ListMixin'; | 
				
			||||
import ActionCell from '../../components/ActionCell.vue'; | 
				
			||||
//import Toolbar from '../../components/Toolbar.vue'; | 
				
			||||
import ResourceFileIcon from '../../components/documents/ResourceFileIcon.vue'; | 
				
			||||
import ResourceFileLink from '../../components/documents/ResourceFileLink.vue'; | 
				
			||||
 | 
				
			||||
import { useRoute } from 'vue-router' | 
				
			||||
import DataFilter from '../../components/DataFilter'; | 
				
			||||
//import DocumentsFilterForm from '../../components/personalfile/Filter'; | 
				
			||||
import { ref, reactive, onMounted, computed } from 'vue'; | 
				
			||||
import { useStore } from 'vuex'; | 
				
			||||
import isEmpty from 'lodash/isEmpty'; | 
				
			||||
import moment from "moment"; | 
				
			||||
 | 
				
			||||
export default { | 
				
			||||
  name: 'PersonalFileList', | 
				
			||||
  servicePrefix: 'PersonalFile', | 
				
			||||
  components: { | 
				
			||||
    //8Toolbar, | 
				
			||||
    ActionCell, | 
				
			||||
    ResourceFileIcon, | 
				
			||||
    ResourceFileLink, | 
				
			||||
    //DocumentsFilterForm, | 
				
			||||
    DataFilter | 
				
			||||
  }, | 
				
			||||
  mixins: [ListMixin], | 
				
			||||
  data() { | 
				
			||||
    return { | 
				
			||||
      sortBy: 'title', | 
				
			||||
      sortDesc: false, | 
				
			||||
      columnsQua: [ | 
				
			||||
        {align: 'left', name: 'resourceNode.title', label: this.$i18n.t('Title'), field: 'resourceNode.title', sortable: true}, | 
				
			||||
        {align: 'left', name: 'resourceNode.updatedAt', label: this.$i18n.t('Modified'), field: 'resourceNode.updatedAt', sortable: true}, | 
				
			||||
        {name: 'resourceNode.resourceFile.size', label: this.$i18n.t('Size'), field: 'resourceNode.resourceFile.size', sortable: true}, | 
				
			||||
        {name: 'action', label: this.$i18n.t('Actions'), field: 'action', sortable: false} | 
				
			||||
      ], | 
				
			||||
      columns: [ | 
				
			||||
        { label: this.$i18n.t('Title'), field: 'title', name: 'title', sortable: true}, | 
				
			||||
        { label: this.$i18n.t('Modified'), field: 'resourceNode.updatedAt', name: 'updatedAt', sortable: true}, | 
				
			||||
        { label: this.$i18n.t('Size'), field: 'resourceNode.resourceFile.size', name: 'size', sortable: true}, | 
				
			||||
        { label: this.$i18n.t('Actions'), name: 'action', sortable: false} | 
				
			||||
      ], | 
				
			||||
      pageOptions: [10, 20, 50, this.$i18n.t('All')], | 
				
			||||
      selected: [], | 
				
			||||
      isBusy: false, | 
				
			||||
      options: [], | 
				
			||||
      selectedItems: [], | 
				
			||||
      // prime vue | 
				
			||||
      itemDialog: false, | 
				
			||||
      deleteItemDialog: false, | 
				
			||||
      deleteMultipleDialog: false, | 
				
			||||
      item: {}, | 
				
			||||
      filters: {}, | 
				
			||||
      submitted: false, | 
				
			||||
    }; | 
				
			||||
  }, | 
				
			||||
  created() { | 
				
			||||
    console.log('CREATED'); | 
				
			||||
    let resourceNodeId = this.currentUser.resourceNode['id']; | 
				
			||||
    if (isEmpty(this.$route.params.node)) { | 
				
			||||
      this.$route.params.node = resourceNodeId; | 
				
			||||
    } | 
				
			||||
    //this.item.parentResourceNodeId = this.$route.params.node; | 
				
			||||
    this.filters['resourceNode.parent'] = resourceNodeId; | 
				
			||||
  }, | 
				
			||||
  mounted() { | 
				
			||||
    const route = useRoute() | 
				
			||||
    /*let nodeId = route.params['node']; | 
				
			||||
    if (!isEmpty(nodeId)) { | 
				
			||||
      this.findResourceNode('/api/resource_nodes/' + nodeId); | 
				
			||||
    }*/ | 
				
			||||
    console.log(this.options); | 
				
			||||
    this.onUpdateOptions(this.options); | 
				
			||||
  }, | 
				
			||||
  computed: { | 
				
			||||
    // From crud.js list function | 
				
			||||
    ...mapGetters('resourcenode', { | 
				
			||||
      resourceNode: 'getResourceNode' | 
				
			||||
    }), | 
				
			||||
    ...mapGetters({ | 
				
			||||
      'isAuthenticated': 'security/isAuthenticated', | 
				
			||||
      'isAdmin': 'security/isAdmin', | 
				
			||||
      'currentUser': 'security/getUser', | 
				
			||||
    }), | 
				
			||||
 | 
				
			||||
    ...mapGetters('personalfile', { | 
				
			||||
      items: 'list', | 
				
			||||
    }), | 
				
			||||
 | 
				
			||||
    //...getters | 
				
			||||
 | 
				
			||||
    // From ListMixin | 
				
			||||
    ...mapFields('personalfile', { | 
				
			||||
      deletedItem: 'deleted', | 
				
			||||
      error: 'error', | 
				
			||||
      isLoading: 'isLoading', | 
				
			||||
      resetList: 'resetList', | 
				
			||||
      totalItems: 'totalItems', | 
				
			||||
      view: 'view' | 
				
			||||
    }), | 
				
			||||
  }, | 
				
			||||
  methods: { | 
				
			||||
    // prime | 
				
			||||
    onPage(event) { | 
				
			||||
      console.log(event); | 
				
			||||
      console.log(event.page); | 
				
			||||
      console.log(event.sortField); | 
				
			||||
      console.log(event.sortOrder); | 
				
			||||
 | 
				
			||||
      this.options.itemsPerPage = event.rows; | 
				
			||||
      this.options.page = event.page + 1; | 
				
			||||
      this.options.sortBy = event.sortField; | 
				
			||||
      this.options.sortDesc = event.sortOrder === -1; | 
				
			||||
 | 
				
			||||
      this.onUpdateOptions(this.options); | 
				
			||||
    }, | 
				
			||||
    sortingChanged(event) { | 
				
			||||
      console.log('sortingChanged'); | 
				
			||||
      console.log(event); | 
				
			||||
      this.options.sortBy = event.sortField; | 
				
			||||
      this.options.sortDesc = event.sortOrder === -1; | 
				
			||||
 | 
				
			||||
      this.onUpdateOptions(this.options); | 
				
			||||
      // ctx.sortBy   ==> Field key for sorting by (or null for no sorting) | 
				
			||||
      // ctx.sortDesc ==> true if sorting descending, false otherwise | 
				
			||||
    }, | 
				
			||||
 | 
				
			||||
    openNew() { | 
				
			||||
      this.item = {}; | 
				
			||||
      this.submitted = false; | 
				
			||||
      this.itemDialog = true; | 
				
			||||
    }, | 
				
			||||
    hideDialog() { | 
				
			||||
      this.itemDialog = false; | 
				
			||||
      this.submitted = false; | 
				
			||||
    }, | 
				
			||||
    saveItem() { | 
				
			||||
      this.submitted = true; | 
				
			||||
 | 
				
			||||
      if (this.item.title.trim()) { | 
				
			||||
        if (this.item.id) { | 
				
			||||
        } else { | 
				
			||||
          let resourceNodeId = this.currentUser.resourceNode['id']; | 
				
			||||
          if (!isEmpty(this.$route.params.node)) { | 
				
			||||
            resourceNodeId = this.$route.params.node; | 
				
			||||
          } | 
				
			||||
 | 
				
			||||
          this.item.parentResourceNodeId = resourceNodeId; | 
				
			||||
          this.item.resourceLinkList = JSON.stringify([{ | 
				
			||||
            gid: 0, | 
				
			||||
            sid: 0, | 
				
			||||
            c_id: 0, | 
				
			||||
            visibility: 2, // visible by default | 
				
			||||
          }]); | 
				
			||||
 | 
				
			||||
          this.create(this.item); | 
				
			||||
          this.showMessage('Saved'); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        this.itemDialog = false; | 
				
			||||
        this.item = {}; | 
				
			||||
      } | 
				
			||||
    }, | 
				
			||||
    editItem(item) { | 
				
			||||
      this.item = {...item}; | 
				
			||||
      this.itemDialog = true; | 
				
			||||
    }, | 
				
			||||
    confirmDeleteItem(item) { | 
				
			||||
      this.item = item; | 
				
			||||
      this.deleteItemDialog = true; | 
				
			||||
    }, | 
				
			||||
    confirmDeleteMultiple() { | 
				
			||||
      this.deleteMultipleDialog = true; | 
				
			||||
    }, | 
				
			||||
    deleteMultipleItems() { | 
				
			||||
      console.log('deleteMultipleItems'); | 
				
			||||
      console.log(this.selectedItems); | 
				
			||||
      this.deleteMultipleAction(this.selectedItems); | 
				
			||||
      this.onRequest({ | 
				
			||||
        pagination: this.pagination, | 
				
			||||
      }); | 
				
			||||
      this.deleteMultipleDialog = false; | 
				
			||||
      this.selectedItems = null; | 
				
			||||
      //this.$toast.add({severity:'success', summary: 'Successful', detail: 'Products Deleted', life: 3000});*/ | 
				
			||||
    }, | 
				
			||||
    deleteItemButton() { | 
				
			||||
      console.log('deleteItem'); | 
				
			||||
      this.deleteItem(this.item); | 
				
			||||
      //this.items = this.items.filter(val => val.iid !== this.item.iid); | 
				
			||||
      this.deleteItemDialog = false; | 
				
			||||
      this.item = {}; | 
				
			||||
      this.onUpdateOptions(this.options); | 
				
			||||
    }, | 
				
			||||
    onRowSelected(items) { | 
				
			||||
      this.selected = items | 
				
			||||
    }, | 
				
			||||
    selectAllRows() { | 
				
			||||
      this.$refs.selectableTable.selectAllRows() | 
				
			||||
    }, | 
				
			||||
    clearSelected() { | 
				
			||||
      this.$refs.selectableTable.clearSelected() | 
				
			||||
    }, | 
				
			||||
    async deleteSelected() { | 
				
			||||
      console.log('deleteSelected'); | 
				
			||||
      /*for (let i = 0; i < this.selected.length; i++) { | 
				
			||||
        let item = this.selected[i]; | 
				
			||||
        //this.deleteHandler(item); | 
				
			||||
        this.deleteItem(item); | 
				
			||||
      }*/ | 
				
			||||
 | 
				
			||||
      this.deleteMultipleAction(this.selected); | 
				
			||||
      this.onRequest({ | 
				
			||||
        pagination: this.pagination, | 
				
			||||
      }); | 
				
			||||
      console.log('end -- deleteSelected'); | 
				
			||||
    }, | 
				
			||||
    //...actions, | 
				
			||||
    // From ListMixin | 
				
			||||
    ...mapActions('personalfile', { | 
				
			||||
      getPage: 'fetchAll', | 
				
			||||
      create: 'create', | 
				
			||||
      deleteItem: 'del', | 
				
			||||
      deleteMultipleAction: 'delMultiple' | 
				
			||||
    }), | 
				
			||||
    ...mapActions('resourcenode', { | 
				
			||||
      findResourceNode: 'findResourceNode', | 
				
			||||
    }), | 
				
			||||
  } | 
				
			||||
}; | 
				
			||||
</script> | 
				
			||||
@ -0,0 +1,193 @@ | 
				
			||||
<template> | 
				
			||||
  <div> | 
				
			||||
    <Toolbar | 
				
			||||
      v-if="item && isCurrentTeacher" | 
				
			||||
      :handle-edit="editHandler" | 
				
			||||
      :handle-delete="del" | 
				
			||||
    > | 
				
			||||
      <template slot="left"> | 
				
			||||
<!--        <v-toolbar-title v-if="item">--> | 
				
			||||
<!--          {{--> | 
				
			||||
<!--            `${$options.servicePrefix} ${item['@id']}`--> | 
				
			||||
<!--          }}--> | 
				
			||||
<!--        </v-toolbar-title>--> | 
				
			||||
      </template> | 
				
			||||
    </Toolbar> | 
				
			||||
 | 
				
			||||
    <p class="text-lg" v-if="item"> | 
				
			||||
      {{ item['title'] }} | 
				
			||||
    </p> | 
				
			||||
 | 
				
			||||
    <div v-if="item" class="flex flex-row"> | 
				
			||||
      <div class="w-1/2"> | 
				
			||||
        <div class ="flex justify-center" v-if="item['resourceNode']['resourceFile']"> | 
				
			||||
          <div class="w-64"> | 
				
			||||
            <q-img | 
				
			||||
                spinner-color="primary" | 
				
			||||
                v-if="item['resourceNode']['resourceFile']['image']" | 
				
			||||
                :src="item['contentUrl'] + '&w=300'" | 
				
			||||
            /> | 
				
			||||
            <span v-else-if="item['resourceNode']['resourceFile']['video']"> | 
				
			||||
              <video controls> | 
				
			||||
                <source :src="item['contentUrl']" /> | 
				
			||||
              </video> | 
				
			||||
            </span> | 
				
			||||
            <span v-else> | 
				
			||||
                <q-btn | 
				
			||||
                    class="btn btn-primary" | 
				
			||||
                    :to="item['downloadUrl']" | 
				
			||||
                > | 
				
			||||
                  <FontAwesomeIcon icon="file-download" /> | 
				
			||||
                  {{ $t('Download file') }} | 
				
			||||
                </q-btn> | 
				
			||||
              </span> | 
				
			||||
          </div> | 
				
			||||
        </div> | 
				
			||||
        <div class ="flex justify-center" v-else> | 
				
			||||
              <FontAwesomeIcon | 
				
			||||
                  icon="folder" | 
				
			||||
                  size="7x" | 
				
			||||
              /> | 
				
			||||
        </div> | 
				
			||||
      </div> | 
				
			||||
 | 
				
			||||
      <span class="w-1/2"> | 
				
			||||
        <q-markup-table> | 
				
			||||
          <tbody> | 
				
			||||
          <tr> | 
				
			||||
            <td><strong>{{ $t('Author') }}</strong></td> | 
				
			||||
            <td> | 
				
			||||
              {{ item['resourceNode'].creator.username }} | 
				
			||||
            </td> | 
				
			||||
            <td></td> | 
				
			||||
            <td /> | 
				
			||||
          </tr> | 
				
			||||
          <tr> | 
				
			||||
            <td><strong>{{ $t('Comment') }}</strong></td> | 
				
			||||
            <td> | 
				
			||||
              {{ item['comment'] }} | 
				
			||||
            </td> | 
				
			||||
          </tr> | 
				
			||||
          <tr> | 
				
			||||
            <td><strong>{{ $t('Created at') }}</strong></td> | 
				
			||||
            <td> | 
				
			||||
              {{ item['resourceNode'] ? $luxonDateTime.fromISO(item['resourceNode'].createdAt).toRelative() : ''}} | 
				
			||||
            </td> | 
				
			||||
            <td /> | 
				
			||||
          </tr> | 
				
			||||
          <tr> | 
				
			||||
            <td><strong>{{ $t('Updated at') }}</strong></td> | 
				
			||||
            <td> | 
				
			||||
              {{ item['resourceNode'] ? $luxonDateTime.fromISO(item['resourceNode'].updatedAt).toRelative() : ''}} | 
				
			||||
            </td> | 
				
			||||
            <td /> | 
				
			||||
          </tr> | 
				
			||||
          <tr v-if="item['resourceNode']['resourceFile']"> | 
				
			||||
            <td><strong>{{ $t('File') }}</strong></td> | 
				
			||||
            <td> | 
				
			||||
              <div> | 
				
			||||
                <a | 
				
			||||
                    class="btn btn-primary" | 
				
			||||
                    :href="item['downloadUrl']" | 
				
			||||
                > | 
				
			||||
                  <FontAwesomeIcon icon="file-download" /> | 
				
			||||
                  {{ $t('Download file') }} | 
				
			||||
                </a> | 
				
			||||
              </div> | 
				
			||||
            </td> | 
				
			||||
            <td /> | 
				
			||||
          </tr> | 
				
			||||
          </tbody> | 
				
			||||
        </q-markup-table> | 
				
			||||
 | 
				
			||||
        <hr /> | 
				
			||||
 | 
				
			||||
        <span v-if="item['resourceLinkListFromEntity']"> | 
				
			||||
           <h2>{{ $t('Shared') }}</h2> | 
				
			||||
            <span | 
				
			||||
                v-for="link in item['resourceLinkListFromEntity']" | 
				
			||||
            > | 
				
			||||
             <q-markup-table> | 
				
			||||
              <tbody> | 
				
			||||
              <tr> | 
				
			||||
                 <td> | 
				
			||||
                   {{ $t('Status') }} | 
				
			||||
                 </td> | 
				
			||||
                 <td> | 
				
			||||
                   {{ link.visibilityName }} | 
				
			||||
                 </td> | 
				
			||||
              </tr> | 
				
			||||
 | 
				
			||||
              <tr v-if="link['course']"> | 
				
			||||
                 <td> | 
				
			||||
                   {{ $t('Course') }} | 
				
			||||
                 </td> | 
				
			||||
                <td> | 
				
			||||
                  {{ link.course.resourceNode.title }} | 
				
			||||
                 </td> | 
				
			||||
              </tr> | 
				
			||||
 | 
				
			||||
              <tr v-if="link['session']"> | 
				
			||||
                 <td> | 
				
			||||
                   {{ $t('Session') }} | 
				
			||||
                 </td> | 
				
			||||
                <td> | 
				
			||||
                 {{ link.session.name }} | 
				
			||||
                 </td> | 
				
			||||
              </tr> | 
				
			||||
 | 
				
			||||
              <tr v-if="link['group']"> | 
				
			||||
                 <td> | 
				
			||||
                   {{ $t('Group') }} | 
				
			||||
                 </td> | 
				
			||||
                <td> | 
				
			||||
                 {{ link.group.resourceNode.title }} | 
				
			||||
                 </td> | 
				
			||||
              </tr> | 
				
			||||
              </tbody> | 
				
			||||
            </q-markup-table> | 
				
			||||
            </span> | 
				
			||||
        </span> | 
				
			||||
      </span> | 
				
			||||
    </div> | 
				
			||||
 | 
				
			||||
    <Loading :visible="isLoading" /> | 
				
			||||
  </div> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script> | 
				
			||||
import { mapActions, mapGetters } from 'vuex'; | 
				
			||||
import { mapFields } from 'vuex-map-fields'; | 
				
			||||
import Loading from '../../components/Loading.vue'; | 
				
			||||
import ShowMixin from '../../mixins/ShowMixin'; | 
				
			||||
import Toolbar from '../../components/Toolbar.vue'; | 
				
			||||
const servicePrefix = 'PersonalFile'; | 
				
			||||
 | 
				
			||||
export default { | 
				
			||||
  name: 'PersonalFileShow', | 
				
			||||
  components: { | 
				
			||||
      Loading, | 
				
			||||
      Toolbar | 
				
			||||
  }, | 
				
			||||
  mixins: [ShowMixin], | 
				
			||||
  computed: { | 
				
			||||
    ...mapFields('personalfile', { | 
				
			||||
      isLoading: 'isLoading' | 
				
			||||
    }), | 
				
			||||
    ...mapGetters('personalfile', ['find']), | 
				
			||||
    ...mapGetters({ | 
				
			||||
      'isAuthenticated': 'security/isAuthenticated', | 
				
			||||
      'isAdmin': 'security/isAdmin', | 
				
			||||
      'isCurrentTeacher': 'security/isCurrentTeacher', | 
				
			||||
    }), | 
				
			||||
  }, | 
				
			||||
  methods: { | 
				
			||||
    ...mapActions('personalfile', { | 
				
			||||
      deleteItem: 'del', | 
				
			||||
      reset: 'resetShow', | 
				
			||||
      retrieve: 'loadWithQuery' | 
				
			||||
    }), | 
				
			||||
  }, | 
				
			||||
  servicePrefix | 
				
			||||
}; | 
				
			||||
</script> | 
				
			||||
@ -0,0 +1,68 @@ | 
				
			||||
<template> | 
				
			||||
  <div v-if="!isLoading && item && isCurrentTeacher"> | 
				
			||||
    <!--      :handle-delete="del"--> | 
				
			||||
    <Toolbar | 
				
			||||
        :handle-submit="onSendForm" | 
				
			||||
        :handle-reset="resetForm" | 
				
			||||
    /> | 
				
			||||
    <DocumentsForm | 
				
			||||
      ref="updateForm" | 
				
			||||
      :values="item" | 
				
			||||
      :errors="violations" | 
				
			||||
    > | 
				
			||||
      <ResourceLinkForm | 
				
			||||
          v-if="item && !isLoading" | 
				
			||||
          ref="resourceLinkForm" | 
				
			||||
          :values="item" | 
				
			||||
      /> | 
				
			||||
    </DocumentsForm> | 
				
			||||
    <Loading :visible="isLoading || deleteLoading" /> | 
				
			||||
  </div> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script> | 
				
			||||
import { mapActions, mapGetters } from 'vuex'; | 
				
			||||
import { mapFields } from 'vuex-map-fields'; | 
				
			||||
import DocumentsForm from '../../components/personalfile/Form.vue'; | 
				
			||||
import ResourceLinkForm from '../../components/personalfile/ResourceLinkForm.vue'; | 
				
			||||
import Loading from '../../components/Loading.vue'; | 
				
			||||
import Toolbar from '../../components/Toolbar.vue'; | 
				
			||||
import UpdateMixin from '../../mixins/UpdateMixin'; | 
				
			||||
 | 
				
			||||
const servicePrefix = 'PersonalFile'; | 
				
			||||
 | 
				
			||||
export default { | 
				
			||||
  name: 'PersonalFileUpdate', | 
				
			||||
  servicePrefix, | 
				
			||||
  components: { | 
				
			||||
    Loading, | 
				
			||||
    Toolbar, | 
				
			||||
    DocumentsForm, | 
				
			||||
    ResourceLinkForm | 
				
			||||
  }, | 
				
			||||
  mixins: [UpdateMixin], | 
				
			||||
  computed: { | 
				
			||||
    ...mapFields('personalfile', { | 
				
			||||
      deleteLoading: 'isLoading', | 
				
			||||
      isLoading: 'isLoading', | 
				
			||||
      error: 'error', | 
				
			||||
      updated: 'updated', | 
				
			||||
      violations: 'violations' | 
				
			||||
    }), | 
				
			||||
    ...mapGetters('personalfile', ['find']), | 
				
			||||
    ...mapGetters({ | 
				
			||||
      'isCurrentTeacher': 'security/isCurrentTeacher', | 
				
			||||
    }), | 
				
			||||
  }, | 
				
			||||
  methods: { | 
				
			||||
    ...mapActions('personalfile', { | 
				
			||||
      createReset: 'resetCreate', | 
				
			||||
      deleteItem: 'del', | 
				
			||||
      delReset: 'resetDelete', | 
				
			||||
      retrieve: 'load', | 
				
			||||
      update: 'update', | 
				
			||||
      updateReset: 'resetUpdate' | 
				
			||||
    }) | 
				
			||||
  } | 
				
			||||
}; | 
				
			||||
</script> | 
				
			||||
@ -0,0 +1,138 @@ | 
				
			||||
<template> | 
				
			||||
  <div> | 
				
			||||
    <DocumentsForm | 
				
			||||
      ref="createForm" | 
				
			||||
      :values="files" | 
				
			||||
      :parentResourceNodeId="parentResourceNodeId" | 
				
			||||
      :resourceLinkList="resourceLinkList" | 
				
			||||
      :errors="violations" | 
				
			||||
      :process-files="processFiles" | 
				
			||||
    /> | 
				
			||||
 | 
				
			||||
    <Toolbar | 
				
			||||
      :handle-submit="onUploadForm" | 
				
			||||
    /> | 
				
			||||
    <Loading :visible="isLoading" /> | 
				
			||||
  </div> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script> | 
				
			||||
import {mapActions, mapGetters} from 'vuex'; | 
				
			||||
import { createHelpers } from 'vuex-map-fields'; | 
				
			||||
import DocumentsForm from '../../components/personalfile/FormUpload.vue'; | 
				
			||||
import Loading from '../../components/Loading.vue'; | 
				
			||||
import Toolbar from '../../components/Toolbar.vue'; | 
				
			||||
import UploadMixin from '../../mixins/UploadMixin'; | 
				
			||||
import { ref, onMounted } from 'vue' | 
				
			||||
import isEmpty from 'lodash/isEmpty'; | 
				
			||||
 | 
				
			||||
const servicePrefix = 'PersonalFile'; | 
				
			||||
 | 
				
			||||
const { mapFields } = createHelpers({ | 
				
			||||
  getterType: 'personalfile/getField', | 
				
			||||
  mutationType: 'personalfile/updateField' | 
				
			||||
}); | 
				
			||||
 | 
				
			||||
export default { | 
				
			||||
  name: 'PersonalFileUploadFile', | 
				
			||||
  servicePrefix, | 
				
			||||
  components: { | 
				
			||||
    Loading, | 
				
			||||
    Toolbar, | 
				
			||||
    DocumentsForm | 
				
			||||
  }, | 
				
			||||
  setup() { | 
				
			||||
    const createForm = ref(null); | 
				
			||||
 | 
				
			||||
    return { | 
				
			||||
      createForm | 
				
			||||
    } | 
				
			||||
  }, | 
				
			||||
  mixins: [UploadMixin], | 
				
			||||
  data() { | 
				
			||||
    return { | 
				
			||||
      files : [], | 
				
			||||
      parentResourceNodeId: 0, | 
				
			||||
      resourceLinkList: '', | 
				
			||||
    }; | 
				
			||||
  }, | 
				
			||||
  computed: { | 
				
			||||
    ...mapFields(['error', 'isLoading', 'created', 'violations']), | 
				
			||||
    ...mapGetters({ | 
				
			||||
      'isAuthenticated': 'security/isAuthenticated', | 
				
			||||
      'isAdmin': 'security/isAdmin', | 
				
			||||
      'currentUser': 'security/getUser', | 
				
			||||
    }), | 
				
			||||
  }, | 
				
			||||
  created() { | 
				
			||||
    console.log('created'); | 
				
			||||
    let nodeId = this.$route.params.node; | 
				
			||||
    if (isEmpty(nodeId)) { | 
				
			||||
      nodeId = this.currentUser.resourceNode['id'] | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    console.log(nodeId) | 
				
			||||
    this.parentResourceNodeId = Number(nodeId); | 
				
			||||
    this.resourceLinkList = JSON.stringify([{ | 
				
			||||
      gid: this.$route.query.gid, | 
				
			||||
      sid: this.$route.query.sid, | 
				
			||||
      c_id: this.$route.query.cid, | 
				
			||||
      visibility: 2, | 
				
			||||
    }]); | 
				
			||||
    this.files = []; | 
				
			||||
  }, | 
				
			||||
  methods: { | 
				
			||||
    async processFiles(files) { | 
				
			||||
      /*this.files = [ | 
				
			||||
        ...this.files, | 
				
			||||
        ...map(files, file => ({ | 
				
			||||
          title: file.name, | 
				
			||||
          name: file.name, | 
				
			||||
          size: file.size, | 
				
			||||
          type: file.type, | 
				
			||||
          filetype: 'file', | 
				
			||||
          parentResourceNodeId: this.parentResourceNodeId, | 
				
			||||
          resourceLinkList: this.resourceLinkList, | 
				
			||||
          uploadFile: file, | 
				
			||||
          invalidMessage: this.validate(file), | 
				
			||||
        })) | 
				
			||||
      ];*/ | 
				
			||||
 | 
				
			||||
      return new Promise((resolve) => { | 
				
			||||
        for (let i = 0; i < files.length; i++) { | 
				
			||||
          files[i].title = files[i].name; | 
				
			||||
          files[i].parentResourceNodeId = this.parentResourceNodeId; | 
				
			||||
          files[i].resourceLinkList = this.resourceLinkList; | 
				
			||||
          files[i].uploadFile = files[i]; | 
				
			||||
          this.createFile(files[i]); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        resolve(files); | 
				
			||||
        /*console.log(file); | 
				
			||||
        file.title = file.name; | 
				
			||||
        file.parentResourceNodeId = this.parentResourceNodeId; | 
				
			||||
        file.resourceLinkList = this.resourceLinkList; | 
				
			||||
        file.uploadFile = file; | 
				
			||||
        this.create(file); | 
				
			||||
        resolve(file);*/ | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        /*for (let i = 0; i < this.files.length; i++) { | 
				
			||||
          this.create(this.files[i]); | 
				
			||||
        } | 
				
			||||
        resolve(true);*/ | 
				
			||||
      }).then(() => { | 
				
			||||
        this.files = []; | 
				
			||||
      }); | 
				
			||||
    }, | 
				
			||||
    validate(file) { | 
				
			||||
      if (file) { | 
				
			||||
        return ''; | 
				
			||||
      } | 
				
			||||
 | 
				
			||||
      return 'error'; | 
				
			||||
    }, | 
				
			||||
    ...mapActions('personalfile', ['uploadMany', 'create', 'createFile']) | 
				
			||||
  } | 
				
			||||
}; | 
				
			||||
</script> | 
				
			||||
@ -0,0 +1,23 @@ | 
				
			||||
<?php | 
				
			||||
 | 
				
			||||
declare(strict_types=1); | 
				
			||||
 | 
				
			||||
/* For licensing terms, see /license.txt */ | 
				
			||||
 | 
				
			||||
namespace Chamilo\CoreBundle\Controller\Api; | 
				
			||||
 | 
				
			||||
use Chamilo\CoreBundle\Entity\PersonalFile; | 
				
			||||
use Symfony\Component\HttpFoundation\Request; | 
				
			||||
 | 
				
			||||
class CreatePersonalFileAction extends BaseResourceFileAction | 
				
			||||
{ | 
				
			||||
    public function __invoke(Request $request): PersonalFile | 
				
			||||
    { | 
				
			||||
        error_log('CreatePersonalFileAction __invoke'); | 
				
			||||
 | 
				
			||||
        $document = new PersonalFile(); | 
				
			||||
        $this->handleCreateRequest($document, $request); | 
				
			||||
 | 
				
			||||
        return $document; | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
@ -0,0 +1,25 @@ | 
				
			||||
<?php | 
				
			||||
 | 
				
			||||
declare(strict_types=1); | 
				
			||||
 | 
				
			||||
/* For licensing terms, see /license.txt */ | 
				
			||||
 | 
				
			||||
namespace Chamilo\CoreBundle\Controller\Api; | 
				
			||||
 | 
				
			||||
use Chamilo\CoreBundle\Entity\PersonalFile; | 
				
			||||
use Chamilo\CourseBundle\Repository\CDocumentRepository; | 
				
			||||
use Symfony\Component\HttpFoundation\Request; | 
				
			||||
 | 
				
			||||
class UpdatePersonalFileAction extends BaseResourceFileAction | 
				
			||||
{ | 
				
			||||
    public function __invoke(PersonalFile $document, Request $request, CDocumentRepository $repo): PersonalFile | 
				
			||||
    { | 
				
			||||
        error_log('UpdatePersonalFileAction __invoke'); | 
				
			||||
 | 
				
			||||
        $this->handleUpdateRequest($document, $repo, $request); | 
				
			||||
 | 
				
			||||
        error_log('Finish update resource node file action'); | 
				
			||||
 | 
				
			||||
        return $document; | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
					Loading…
					
					
				
		Reference in new issue