mirror of
https://github.com/typemill/typemill.git
synced 2025-03-14 17:19:39 +01:00
Version 1.5.1: File Restrictions, Download Controller and Shared File-Component
This commit is contained in:
parent
43d31a61cd
commit
1a09c7767b
@ -1,8 +1,10 @@
|
||||
# Content Elements
|
||||
|
||||
# Content Elements
|
||||
|
||||
Cyanine provides a lot of other settings for your content area. For example:
|
||||
|
||||
[ebook (EPUB, 496.65 KB)](media/files/ebook.epub){.tm-download file-epub}
|
||||
|
||||
The Cyanine theme supports all content elements like tables, images, notices or downloads. It also supports anchor-links next to headlines, so you can deep link to certain content sections of your page. You can activate the anchors in the system settings of Typemill.
|
||||
* Add an edit-button for github, gitlab or other plattforms.
|
||||
* Show the author.
|
||||
* Show the publish date.
|
||||
* Show the chapter numbers in the navigation.
|
||||
|
||||
The Cyanine theme supports all content elements like tables, images, notices or downloads. It also supports anchor-links next to headlines, so you can deep link to certain content sections of your page. You can activate the anchors in the system settings of Typemill.
|
2
media/files/filerestrictions.yaml
Normal file
2
media/files/filerestrictions.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
media/files/preview.pdf: member
|
||||
media/files/ebook.epub: author
|
@ -267,8 +267,15 @@ class ControllerAuthorMediaApi extends ControllerAuthor
|
||||
}
|
||||
|
||||
$fileinfo = $fileProcessor->storeFile($this->params['file'], $this->params['name']);
|
||||
|
||||
if($fileinfo)
|
||||
{
|
||||
# publish file directly, used for example by file field for meta-tabs
|
||||
if(isset($this->params['publish']) && $this->params['publish'])
|
||||
{
|
||||
$fileProcessor->publishFile();
|
||||
}
|
||||
|
||||
return $response->withJson(['errors' => false, 'info' => $fileinfo]);
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,8 @@ class ControllerDownload extends ControllerShared
|
||||
|
||||
if(!isset($_SESSION['role']))
|
||||
{
|
||||
die("You have to be an authenticated $allowedrole to download this file.");
|
||||
$this->c->flash->addMessage('error', "You have to be an authenticated $allowedrole to download this file.");
|
||||
return $response->withRedirect($this->c->router->pathFor('auth.show'));
|
||||
}
|
||||
elseif(
|
||||
$_SESSION['role'] != 'administrator'
|
||||
@ -43,7 +44,8 @@ class ControllerDownload extends ControllerShared
|
||||
AND !$this->c->acl->inheritsRole($_SESSION['role'], $allowedrole)
|
||||
)
|
||||
{
|
||||
die("You have to be a $allowedrole to download this file.");
|
||||
$this->c->flash->addMessage('error', "You have to be a $allowedrole to download this file.");
|
||||
return $response->withRedirect($this->c->router->pathFor('auth.show'));
|
||||
}
|
||||
}
|
||||
|
||||
@ -129,10 +131,11 @@ class ControllerDownload extends ControllerShared
|
||||
|
||||
header('Pragma: public');
|
||||
header('Content-Encoding: none');
|
||||
header('Expires: 0');
|
||||
header('Accept-Ranges: bytes'); # Allow support for download resume
|
||||
header('Expires: 0');
|
||||
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', filemtime($file)) . ' GMT');
|
||||
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
|
||||
header_remove("Last-Modified");
|
||||
header('Cache-Control: max-age=0, no-cache, no-store, must-revalidate');
|
||||
header('Cache-Control: private', false); # required for some browsers
|
||||
header('Content-Type: application/zip');
|
||||
header('Content-Disposition: attachment; filename="'.basename($file).'";'); # Make the browser display the Save As dialog
|
||||
|
@ -124,7 +124,7 @@ class ProcessFile extends ProcessAssets
|
||||
|
||||
foreach ($files as $key => $name)
|
||||
{
|
||||
if (!in_array($name, array(".","..")) && file_exists($this->fileFolder . $name))
|
||||
if (!in_array($name, array(".","..","filerestrictions.yaml")) && file_exists($this->fileFolder . $name))
|
||||
{
|
||||
$filelist[] = [
|
||||
'name' => $name,
|
||||
|
@ -98,7 +98,7 @@ const contentComponent = Vue.component('content-block', {
|
||||
},
|
||||
setComponentSize: function()
|
||||
{
|
||||
if(this.componentType == 'image-component')
|
||||
if(this.componentType == 'image-component' || this.componentType == 'file-component')
|
||||
{
|
||||
myself = this;
|
||||
setTimeout(function(){
|
||||
@ -1822,26 +1822,7 @@ const fileComponent = Vue.component('file-component', {
|
||||
}
|
||||
}
|
||||
|
||||
self = this;
|
||||
|
||||
myaxios.get('/api/v1/filerestrictions',{
|
||||
params: {
|
||||
'url': document.getElementById("path").value,
|
||||
'csrf_name': document.getElementById("csrf_name").value,
|
||||
'csrf_value': document.getElementById("csrf_value").value,
|
||||
'filename': this.fileurl,
|
||||
}
|
||||
})
|
||||
.then(function (response) {
|
||||
self.userroles = self.userroles.concat(response.data.userroles);
|
||||
self.selectedrole = response.data.restriction;
|
||||
})
|
||||
.catch(function (error)
|
||||
{
|
||||
if(error.response)
|
||||
{
|
||||
}
|
||||
});
|
||||
this.getrestriction();
|
||||
},
|
||||
methods: {
|
||||
openmedialib: function()
|
||||
@ -1855,30 +1836,10 @@ const fileComponent = Vue.component('file-component', {
|
||||
return ' checked';
|
||||
}
|
||||
},
|
||||
updatemarkdown: function(event)
|
||||
updatemarkdown: function(event, url)
|
||||
{
|
||||
this.$emit('updatedMarkdown', event.target.value);
|
||||
},
|
||||
updaterestriction: function()
|
||||
{
|
||||
myaxios.post('/api/v1/filerestrictions',{
|
||||
'url': document.getElementById("path").value,
|
||||
'csrf_name': document.getElementById("csrf_name").value,
|
||||
'csrf_value': document.getElementById("csrf_value").value,
|
||||
'filename': this.fileurl,
|
||||
'role': this.selectedrole,
|
||||
})
|
||||
.then(function (response) {
|
||||
|
||||
})
|
||||
.catch(function (error)
|
||||
{
|
||||
if(error.response)
|
||||
{
|
||||
}
|
||||
});
|
||||
|
||||
console.info(this.selectedrole);
|
||||
this.getrestriction(url);
|
||||
},
|
||||
createmarkdown: function()
|
||||
{
|
||||
@ -1920,6 +1881,55 @@ const fileComponent = Vue.component('file-component', {
|
||||
this.$emit('updatedMarkdown', filemarkdown);
|
||||
}
|
||||
},
|
||||
getrestriction: function(url)
|
||||
{
|
||||
var fileurl = this.fileurl;
|
||||
if(url)
|
||||
{
|
||||
fileurl = url;
|
||||
}
|
||||
|
||||
var myself = this;
|
||||
|
||||
myaxios.get('/api/v1/filerestrictions',{
|
||||
params: {
|
||||
'url': document.getElementById("path").value,
|
||||
'csrf_name': document.getElementById("csrf_name").value,
|
||||
'csrf_value': document.getElementById("csrf_value").value,
|
||||
'filename': fileurl,
|
||||
}
|
||||
})
|
||||
.then(function (response) {
|
||||
myself.userroles = ['all'];
|
||||
myself.userroles = myself.userroles.concat(response.data.userroles);
|
||||
myself.selectedrole = response.data.restriction;
|
||||
})
|
||||
.catch(function (error)
|
||||
{
|
||||
if(error.response)
|
||||
{
|
||||
}
|
||||
});
|
||||
},
|
||||
updaterestriction: function()
|
||||
{
|
||||
myaxios.post('/api/v1/filerestrictions',{
|
||||
'url': document.getElementById("path").value,
|
||||
'csrf_name': document.getElementById("csrf_name").value,
|
||||
'csrf_value': document.getElementById("csrf_value").value,
|
||||
'filename': this.fileurl,
|
||||
'role': this.selectedrole,
|
||||
})
|
||||
.then(function (response) {
|
||||
|
||||
})
|
||||
.catch(function (error)
|
||||
{
|
||||
if(error.response)
|
||||
{
|
||||
}
|
||||
});
|
||||
},
|
||||
onFileChange: function( e )
|
||||
{
|
||||
if(e.target.files.length > 0)
|
||||
@ -1954,10 +1964,12 @@ const fileComponent = Vue.component('file-component', {
|
||||
self.load = false;
|
||||
self.$parent.activatePage();
|
||||
|
||||
self.filemeta = true;
|
||||
self.filetitle = response.data.info.title;
|
||||
self.fileextension = response.data.info.extension;
|
||||
self.fileurl = response.data.info.url;
|
||||
self.filemeta = true;
|
||||
self.filetitle = response.data.info.title;
|
||||
self.fileextension = response.data.info.extension;
|
||||
self.fileurl = response.data.info.url;
|
||||
self.selectedrole = '';
|
||||
|
||||
self.createmarkdown();
|
||||
})
|
||||
.catch(function (error)
|
||||
|
@ -627,37 +627,26 @@ Vue.component('component-image', {
|
||||
reader.onload = function(e)
|
||||
{
|
||||
sharedself.imgpreview = e.target.result;
|
||||
|
||||
/* load image to server */
|
||||
var url = sharedself.$root.$data.root + '/api/v1/image';
|
||||
|
||||
var params = {
|
||||
|
||||
myaxios.post('/api/v1/image',{
|
||||
'url': document.getElementById("path").value,
|
||||
'image': e.target.result,
|
||||
'name': imageFile.name,
|
||||
'publish': true,
|
||||
'csrf_name': document.getElementById("csrf_name").value,
|
||||
'csrf_value': document.getElementById("csrf_value").value,
|
||||
};
|
||||
|
||||
var method = 'POST';
|
||||
|
||||
sendJson(function(response, httpStatus)
|
||||
{
|
||||
if(response)
|
||||
{
|
||||
var result = JSON.parse(response);
|
||||
|
||||
if(result.errors)
|
||||
{
|
||||
publishController.errors.message = result.errors.message;
|
||||
}
|
||||
else
|
||||
{
|
||||
sharedself.update(result.name);
|
||||
}
|
||||
}
|
||||
}, method, url, params);
|
||||
})
|
||||
.then(function (response) {
|
||||
sharedself.update(response.data.name);
|
||||
})
|
||||
.catch(function (error)
|
||||
{
|
||||
sharedself.load = false;
|
||||
if(error.response)
|
||||
{
|
||||
publishController.errors.message = error.response.data.errors;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -665,6 +654,188 @@ Vue.component('component-image', {
|
||||
},
|
||||
})
|
||||
|
||||
Vue.component('component-file', {
|
||||
props: ['id', 'description', 'maxlength', 'hidden', 'readonly', 'required', 'disabled', 'placeholder', 'label', 'name', 'type', 'value', 'errors'],
|
||||
template: '<div class="large">' +
|
||||
'<transition name="fade-editor">' +
|
||||
'<div v-if="showmedialib" class="modalWindow">' +
|
||||
'<medialib parentcomponent="files"></medialib>' +
|
||||
'</div>' +
|
||||
'</transition>' +
|
||||
'<label>{{ label|translate }}</label>' +
|
||||
'<div v-if="load" class="loadwrapper"><span class="load"></span></div>' +
|
||||
'<div class="ba b--moon-gray">' +
|
||||
'<div class="medium">' +
|
||||
'<label>{{ \'File URL (read only)\'|translate }}</label>' +
|
||||
'<div class="flex">' +
|
||||
'<button @click.prevent="deleteFile()" class="w-10 bn hover-bg-tm-red hover-white">x</button>' +
|
||||
'<input class="w-90" type="text"' +
|
||||
' :id="id"' +
|
||||
' :maxlength="maxlength"' +
|
||||
' readonly="readonly"' +
|
||||
' :hidden="hidden"' +
|
||||
' :required="required"' +
|
||||
' :disabled="disabled"' +
|
||||
' :name="name"' +
|
||||
' :placeholder="placeholder"' +
|
||||
' :value="value"' +
|
||||
'@input="update($event, name)">' +
|
||||
'</div>' +
|
||||
'<div class="flex">' +
|
||||
'<div class="relative dib w-100 br b--white bg-tm-green dim">' +
|
||||
'<input type="file" accept="*" name="file" @change="onFileChange( $event )" class="absolute o-0 w-100 top-0 z-1 pointer h2" />' +
|
||||
'<p class="relative w-100 bn br1 white pa1 h2 ma0 tc"><svg class="icon icon-upload baseline"><use xlink:href="#icon-upload"></use></svg> {{ \'upload\'|translate }}</p>' +
|
||||
'</div>' +
|
||||
'<div class="dib w-100 bl b--white">' +
|
||||
'<button @click.prevent="openmedialib()" class="w-100 pointer bn bg-tm-green white pa0 h2 ma0 tc dim"><svg class="icon icon-paperclip baseline"><use xlink:href="#icon-paperclip"></use></svg> {{ \'medialib\'|translate }}</button>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="medium">' +
|
||||
'<input title="fileid" type="hidden" placeholder="id" v-model="fileid" @input="createmarkdown" max="140" />' +
|
||||
'<label for="filerestriction">{{ \'Access for\'|translate }}: </label>' +
|
||||
'<select name="filerestriction" v-model="selectedrole" @change="updaterestriction">' +
|
||||
'<option disabled value="">{{ \'Please select\'|translate }}</option>' +
|
||||
'<option v-for="role in userroles">{{ role }}</option>' +
|
||||
'</select>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>',
|
||||
data: function(){
|
||||
return {
|
||||
maxsize: 20, // megabyte
|
||||
showmedialib: false,
|
||||
fileid: '',
|
||||
load: false,
|
||||
userroles: ['all'],
|
||||
selectedrole: '',
|
||||
}
|
||||
},
|
||||
mounted: function(){
|
||||
this.getrestriction();
|
||||
},
|
||||
methods: {
|
||||
update: function(value)
|
||||
{
|
||||
FormBus.$emit('forminput', {'name' : this.name, 'value' : value});
|
||||
},
|
||||
updatemarkdown: function(markdown, url)
|
||||
{
|
||||
/* is called from child component medialib if file has been selected */
|
||||
this.update(url);
|
||||
this.getrestriction(url);
|
||||
},
|
||||
createmarkdown: function(url)
|
||||
{
|
||||
/* is called from child component medialib */
|
||||
this.update(url);
|
||||
},
|
||||
openmedialib: function()
|
||||
{
|
||||
this.showmedialib = true;
|
||||
},
|
||||
deleteFile: function()
|
||||
{
|
||||
this.update('');
|
||||
this.selectedrole = 'all';
|
||||
},
|
||||
getrestriction: function(url)
|
||||
{
|
||||
var filename = this.value;
|
||||
if(url)
|
||||
{
|
||||
filename = url;
|
||||
}
|
||||
|
||||
var myself = this;
|
||||
|
||||
myaxios.get('/api/v1/filerestrictions',{
|
||||
params: {
|
||||
'url': document.getElementById("path").value,
|
||||
'csrf_name': document.getElementById("csrf_name").value,
|
||||
'csrf_value': document.getElementById("csrf_value").value,
|
||||
'filename': filename,
|
||||
}
|
||||
})
|
||||
.then(function (response) {
|
||||
myself.userroles = ['all'];
|
||||
myself.userroles = myself.userroles.concat(response.data.userroles);
|
||||
myself.selectedrole = response.data.restriction;
|
||||
})
|
||||
.catch(function (error)
|
||||
{
|
||||
if(error.response)
|
||||
{
|
||||
}
|
||||
});
|
||||
},
|
||||
updaterestriction: function()
|
||||
{
|
||||
myaxios.post('/api/v1/filerestrictions',{
|
||||
'url': document.getElementById("path").value,
|
||||
'csrf_name': document.getElementById("csrf_name").value,
|
||||
'csrf_value': document.getElementById("csrf_value").value,
|
||||
'filename': this.value,
|
||||
'role': this.selectedrole,
|
||||
})
|
||||
.then(function (response) {
|
||||
|
||||
})
|
||||
.catch(function (error)
|
||||
{
|
||||
if(error.response)
|
||||
{
|
||||
}
|
||||
});
|
||||
},
|
||||
onFileChange: function( e )
|
||||
{
|
||||
if(e.target.files.length > 0)
|
||||
{
|
||||
let uploadedFile = e.target.files[0];
|
||||
let size = uploadedFile.size / 1024 / 1024;
|
||||
|
||||
if (size > this.maxsize)
|
||||
{
|
||||
publishController.errors.message = "The maximal size of a file is " + this.maxsize + " MB";
|
||||
}
|
||||
else
|
||||
{
|
||||
sharedself = this;
|
||||
|
||||
sharedself.load = true;
|
||||
|
||||
let reader = new FileReader();
|
||||
reader.readAsDataURL(uploadedFile);
|
||||
reader.onload = function(e) {
|
||||
myaxios.post('/api/v1/file',{
|
||||
'url': document.getElementById("path").value,
|
||||
'file': e.target.result,
|
||||
'name': uploadedFile.name,
|
||||
'publish': true,
|
||||
'csrf_name': document.getElementById("csrf_name").value,
|
||||
'csrf_value': document.getElementById("csrf_value").value,
|
||||
})
|
||||
.then(function (response) {
|
||||
sharedself.load = false;
|
||||
sharedself.selectedrole = 'all';
|
||||
sharedself.update(response.data.info.url);
|
||||
})
|
||||
.catch(function (error)
|
||||
{
|
||||
sharedself.load = false;
|
||||
if(error.response)
|
||||
{
|
||||
publishController.errors.message = error.response.data.errors;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const medialib = Vue.component('medialib', {
|
||||
props: ['parentcomponent'],
|
||||
template: '<div class="medialib">' +
|
||||
|
Loading…
x
Reference in New Issue
Block a user