mirror of
https://github.com/typemill/typemill.git
synced 2025-08-04 13:17:29 +02:00
V2.17 add unused media button
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,5 +1,6 @@
|
||||
cache/sitemap.xml
|
||||
cache/timer.yaml
|
||||
media/tmp/.gitkeep
|
||||
system/vendor
|
||||
cypress
|
||||
data/navigation
|
||||
|
2
cache/timer.yaml
vendored
2
cache/timer.yaml
vendored
@@ -1 +1 @@
|
||||
licenseupdate: 1745608151
|
||||
licenseupdate: 1745867608
|
||||
|
BIN
media/live/web-optimized-1200x800-97kb.webp
Normal file
BIN
media/live/web-optimized-1200x800-97kb.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 77 KiB |
BIN
media/original/web-optimized-1200x800-97kb.webp
Normal file
BIN
media/original/web-optimized-1200x800-97kb.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 126 KiB |
BIN
media/thumbs/web-optimized-1200x800-97kb.webp
Normal file
BIN
media/thumbs/web-optimized-1200x800-97kb.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
@@ -0,0 +1 @@
|
||||
.gitkeep
|
@@ -6,17 +6,13 @@ use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Typemill\Models\Media;
|
||||
use Typemill\Models\StorageWrapper;
|
||||
use Typemill\Models\Navigation;
|
||||
use Typemill\Models\User;
|
||||
use Typemill\Extensions\ParsedownExtension;
|
||||
use Typemill\Static\Translations;
|
||||
|
||||
class ControllerApiImage extends Controller
|
||||
{
|
||||
|
||||
# MISSING
|
||||
#
|
||||
# return error messages and display in image component
|
||||
# check if resized is bigger than original, then use original
|
||||
|
||||
public function getPagemedia(Request $request, Response $response, $args)
|
||||
{
|
||||
$url = $request->getQueryParams()['url'] ?? false;
|
||||
@@ -61,6 +57,145 @@ class ControllerApiImage extends Controller
|
||||
return $response->withHeader('Content-Type', 'application/json');
|
||||
}
|
||||
|
||||
public function getUnusedMedia(Request $request, Response $response, $args)
|
||||
{
|
||||
$storage = new StorageWrapper('\Typemill\Models\Storage');
|
||||
|
||||
# load all images
|
||||
$imagelist = $storage->getImageList();
|
||||
|
||||
# load all files
|
||||
$filelist = $storage->getFileList();
|
||||
|
||||
# get navigation
|
||||
$urlinfo = $this->c->get('urlinfo');
|
||||
$langattr = $this->settings['langattr'];
|
||||
$navigation = new Navigation();
|
||||
$draftNavigation = $navigation->getFullDraftNavigation($urlinfo, $langattr);
|
||||
|
||||
$fullNavigation = $navigation->getHomepageItem($urlinfo['baseurl']);
|
||||
$fullNavigation->folderContent = $draftNavigation;
|
||||
|
||||
$usedmediaList = $this->getMediaFromPages([$fullNavigation], $storage, $media = []);
|
||||
|
||||
# get media from users
|
||||
$userModel = new User();
|
||||
$userList = $userModel->getAllUsers();
|
||||
$usedmediaList = $this->getMediaFromUsers($storage, $usedmediaList, $userList);
|
||||
|
||||
# get media from settings
|
||||
$settingsfile = $storage->getFile('settingsFolder', '', 'settings.yaml');
|
||||
$settingsmedia = $this->findMediaInText($settingsfile);
|
||||
if(isset($settingsmedia[2]) && !empty($settingsmedia[2]))
|
||||
{
|
||||
$usedmediaList = array_merge($usedmediaList, $settingsmedia[2]);
|
||||
}
|
||||
|
||||
|
||||
if(empty($usedmediaList))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
$usedMedia = [];
|
||||
foreach($usedmediaList as $name)
|
||||
{
|
||||
$usedMedia[$name] = true;
|
||||
}
|
||||
|
||||
$unusedMedia = [];
|
||||
foreach($imagelist as $key => $item)
|
||||
{
|
||||
if(!isset($usedMedia[$item['name']]))
|
||||
{
|
||||
$unusedMedia[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
foreach($filelist as $key => $item)
|
||||
{
|
||||
if(!isset($usedMedia[$item['name']]))
|
||||
{
|
||||
$unusedMedia[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
$response->getBody()->write(json_encode([
|
||||
'used' => $usedMedia,
|
||||
'unused' => $unusedMedia
|
||||
]));
|
||||
|
||||
return $response->withHeader('Content-Type', 'application/json');
|
||||
}
|
||||
|
||||
protected function getMediaFromUsers($storage, $usedmediaList, $userList)
|
||||
{
|
||||
foreach($userList as $username)
|
||||
{
|
||||
$userfile = $storage->getFile('settingsFolder', 'users', $username . '.yaml');
|
||||
$usermedia = $this->findMediaInText($userfile);
|
||||
if(isset($usermedia[2]) && !empty($usermedia[2]))
|
||||
{
|
||||
$usedmediaList = array_merge($usedmediaList, $usermedia[2]);
|
||||
}
|
||||
}
|
||||
|
||||
return $usedmediaList;
|
||||
}
|
||||
|
||||
protected function getMediaFromPages($navigation, $storage, $usedMedia)
|
||||
{
|
||||
foreach($navigation as $item)
|
||||
{
|
||||
$pagemedia = [];
|
||||
$path = $item->pathWithoutType;
|
||||
$draftmd = $storage->getFile('contentFolder', '', $path . '.txt');
|
||||
if($draftmd)
|
||||
{
|
||||
$markdownArray = json_decode($draftmd);
|
||||
$parsedown = new ParsedownExtension();
|
||||
$markdown = $parsedown->arrayBlocksToMarkdown($markdownArray);
|
||||
$draftmedia = $this->findMediaInText($markdown);
|
||||
if(isset($draftmedia[2]) && !empty($draftmedia[2]))
|
||||
{
|
||||
$pagemedia = array_merge($pagemedia, $draftmedia[2]);
|
||||
}
|
||||
}
|
||||
|
||||
$livemd = $storage->getFile('contentFolder', '', $path . '.md');
|
||||
if($livemd)
|
||||
{
|
||||
$livemedia = $this->findMediaInText($livemd);
|
||||
if(isset($livemedia[2]) && !empty($livemedia[2]))
|
||||
{
|
||||
$pagemedia = array_merge($pagemedia, $livemedia[2]);
|
||||
}
|
||||
}
|
||||
|
||||
$meta = $storage->getFile('contentFolder', '', $path . '.yaml');
|
||||
if($meta)
|
||||
{
|
||||
$metamedia = $this->findMediaInText($meta);
|
||||
if(isset($metamedia[2]) && !empty($metamedia[2]))
|
||||
{
|
||||
$pagemedia = array_merge($pagemedia, $metamedia[2]);
|
||||
}
|
||||
}
|
||||
|
||||
if(!empty($pagemedia))
|
||||
{
|
||||
$usedMedia = array_merge($usedMedia, $pagemedia);
|
||||
}
|
||||
|
||||
if($item->elementType == 'folder' && !empty($item->folderContent))
|
||||
{
|
||||
$usedMedia = $this->getMediaFromPages($item->folderContent, $storage, $usedMedia);
|
||||
}
|
||||
}
|
||||
|
||||
return $usedMedia;
|
||||
}
|
||||
|
||||
protected function findMediaInText($text)
|
||||
{
|
||||
preg_match_all('/media\/(live|files)\/(.+?\.[a-zA-Z]{2,4})/', $text, $matches);
|
||||
|
@@ -41,22 +41,31 @@ const medialib = {
|
||||
</button>
|
||||
</div>
|
||||
<div class="relative inline-block">
|
||||
<!-- Hidden File Input -->
|
||||
<input
|
||||
ref="uploadInput"
|
||||
type="file"
|
||||
class="hidden"
|
||||
@change="onFileChange($event)"
|
||||
accept="*/*"
|
||||
/>
|
||||
<div class="flex">
|
||||
<button
|
||||
@click.prevent="loadUnusedMedia()"
|
||||
:class="isActive('unusedmedia')"
|
||||
class="px-2 py-1 mr-2 hover:bg-stone-700 hover:dark:bg-stone-900 hover:text-stone-50 transition duration-100">
|
||||
<svg class="icon icon-eye-blocked"><use xlink:href="#icon-eye-blocked"></use></svg>
|
||||
</button>
|
||||
|
||||
<!-- Upload Button -->
|
||||
<button
|
||||
@click.prevent="$refs.uploadInput.click()"
|
||||
class="px-2 py-2 bg-stone-600 text-white hover:bg-stone-700 hover:dark:bg-stone-900 hover:text-stone-50 transition duration-100 flex items-center"
|
||||
>
|
||||
<svg class="icon icon-upload w-4 h-4"><use xlink:href="#icon-upload"></use></svg>
|
||||
</button>
|
||||
<!-- Hidden File Input -->
|
||||
<input
|
||||
ref="uploadInput"
|
||||
type="file"
|
||||
class="hidden"
|
||||
@change="onFileChange($event)"
|
||||
accept="*/*"
|
||||
/>
|
||||
|
||||
<!-- Upload Button -->
|
||||
<button
|
||||
@click.prevent="$refs.uploadInput.click()"
|
||||
class="px-2 py-2 bg-stone-600 text-white hover:bg-stone-700 hover:dark:bg-stone-900 hover:text-stone-50 transition duration-100 flex items-center"
|
||||
>
|
||||
<svg class="icon icon-upload w-4 h-4"><use xlink:href="#icon-upload"></use></svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="totalPages > 1">
|
||||
@@ -77,8 +86,14 @@ const medialib = {
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-3/4">
|
||||
<div class="px-5">
|
||||
<div v-if="error" class="w-full px-5 mb-4 p-2 text-center bg-rose-500 text-stone-50">{{error}}</div>
|
||||
<div v-if="active == 'unusedmedia'" class="px-5 flex">
|
||||
<div class="px-5 mb-4 p-2 bg-rose-500 text-stone-50">!!!</div>
|
||||
<div class=" px-5 mb-4 p-2 bg-stone-200">The media listed below are not used in content files, user files, or settings. We do not check for usage in any other places, so please be careful and double-check before deleting any media.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-wrap justify-start px-5 relative">
|
||||
<div v-if="error" class="w-full mb-4 p-2 text-center bg-rose-500 text-stone-50">{{error}}</div>
|
||||
<TransitionGroup name="list">
|
||||
<div
|
||||
v-for = "(media, index) in paginatedItems"
|
||||
@@ -303,8 +318,9 @@ const medialib = {
|
||||
|
||||
filedata: false, /* holds the files */
|
||||
imagedata: false, /* holds the images */
|
||||
pagedata: false, /* holds the page media */
|
||||
unuseddata: false, /* holds media that are not in use */
|
||||
mediadetails: false, /* holds the details of a single media file */
|
||||
pagedata: false, /* holds the page media */
|
||||
|
||||
showmediadetails: false,
|
||||
showmedialist: false, /* show list of media files */
|
||||
@@ -320,6 +336,7 @@ const medialib = {
|
||||
|
||||
error: false,
|
||||
load: false,
|
||||
refresh: false,
|
||||
adminurl: false,
|
||||
baseurl: data.urlinfo.baseurl,
|
||||
}
|
||||
@@ -354,7 +371,19 @@ const medialib = {
|
||||
computed: {
|
||||
filteredItems()
|
||||
{
|
||||
const medialist = this.active === 'images' ? this.imagedata : this.filedata;
|
||||
var medialist = false;
|
||||
if (this.active === 'images')
|
||||
{
|
||||
medialist = this.imagedata;
|
||||
}
|
||||
else if(this.active === 'files')
|
||||
{
|
||||
medialist = this.filedata;
|
||||
}
|
||||
else if(this.active === 'unusedmedia')
|
||||
{
|
||||
medialist = this.unuseddata;
|
||||
}
|
||||
|
||||
if (!medialist) return {};
|
||||
|
||||
@@ -455,7 +484,6 @@ const medialib = {
|
||||
},
|
||||
reset()
|
||||
{
|
||||
/* this.active = false; */
|
||||
this.error = false;
|
||||
this.showmedialist = false;
|
||||
this.showmediadetails = false;
|
||||
@@ -465,7 +493,7 @@ const medialib = {
|
||||
},
|
||||
showImages()
|
||||
{
|
||||
if(!this.imagedata)
|
||||
if(!this.imagedata || this.refresh)
|
||||
{
|
||||
this.loadImages();
|
||||
return;
|
||||
@@ -476,7 +504,7 @@ const medialib = {
|
||||
},
|
||||
showFiles(filetype)
|
||||
{
|
||||
if(!this.filedata)
|
||||
if(!this.filedata || this.refresh)
|
||||
{
|
||||
this.loadFiles(filetype);
|
||||
return;
|
||||
@@ -485,6 +513,17 @@ const medialib = {
|
||||
this.active = filetype;
|
||||
this.showmedialist = true;
|
||||
},
|
||||
showUnusedMedia()
|
||||
{
|
||||
if(!this.unuseddata)
|
||||
{
|
||||
this.loadUnusedMedia();
|
||||
return;
|
||||
}
|
||||
this.reset();
|
||||
this.active = 'unusedmedia';
|
||||
this.showmedialist = true;
|
||||
},
|
||||
showUpload()
|
||||
{
|
||||
this.reset();
|
||||
@@ -523,6 +562,48 @@ const medialib = {
|
||||
this.$emit('addFromMedialibEvent', media);
|
||||
}
|
||||
},
|
||||
removeMedia(name)
|
||||
{
|
||||
if(this.active === 'images')
|
||||
{
|
||||
const index = this.imagedata.findIndex(item => item.name === name);
|
||||
if(index !== -1)
|
||||
{
|
||||
this.imagedata.splice(index, 1);
|
||||
}
|
||||
this.showImages(this.active);
|
||||
}
|
||||
else if(this.active == 'unusedmedia')
|
||||
{
|
||||
const index = this.unuseddata.findIndex(item => item.name === name);
|
||||
if(index !== -1)
|
||||
{
|
||||
this.unuseddata.splice(index, 1);
|
||||
}
|
||||
this.showUnusedMedia();
|
||||
this.refresh = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
const index = this.filedata.findIndex(item => item.name === name);
|
||||
if(index !== -1)
|
||||
{
|
||||
this.filedata.splice(index, 1);
|
||||
}
|
||||
this.showFiles(this.active);
|
||||
}
|
||||
},
|
||||
deleteMedia(media)
|
||||
{
|
||||
if(media.src_live)
|
||||
{
|
||||
this.deleteImage(media);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.deleteFile(media);
|
||||
}
|
||||
},
|
||||
loadFiles(filetype)
|
||||
{
|
||||
var fileself = this;
|
||||
@@ -551,6 +632,34 @@ const medialib = {
|
||||
}
|
||||
});
|
||||
},
|
||||
loadUnusedMedia()
|
||||
{
|
||||
var mediaself = this;
|
||||
|
||||
tmaxios.get('/api/v1/unusedmedia',{
|
||||
params: {
|
||||
'url': data.urlinfo.route,
|
||||
}
|
||||
})
|
||||
.then(function (response)
|
||||
{
|
||||
mediaself.unuseddata = response.data.unused;
|
||||
mediaself.showUnusedMedia();
|
||||
})
|
||||
.catch(function (error)
|
||||
{
|
||||
if(error.response)
|
||||
{
|
||||
let message = handleErrorMessage(error);
|
||||
if(message)
|
||||
{
|
||||
eventBus.$emit('publishermessage', message);
|
||||
}
|
||||
|
||||
mediaself.error = message;
|
||||
}
|
||||
});
|
||||
},
|
||||
loadPageMedia()
|
||||
{
|
||||
this.error = false;
|
||||
@@ -640,33 +749,6 @@ const medialib = {
|
||||
}
|
||||
});
|
||||
},
|
||||
deleteMedia(media)
|
||||
{
|
||||
if(media.src_live)
|
||||
{
|
||||
this.deleteImage(media);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.deleteFile(media);
|
||||
}
|
||||
},
|
||||
removeImage(name)
|
||||
{
|
||||
const index = this.imagedata.findIndex(item => item.name === name);
|
||||
if(index !== -1)
|
||||
{
|
||||
this.imagedata.splice(index, 1);
|
||||
}
|
||||
},
|
||||
removeFile(name)
|
||||
{
|
||||
const index = this.filedata.findIndex(item => item.name === name);
|
||||
if(index !== -1)
|
||||
{
|
||||
this.filedata.splice(index, 1);
|
||||
}
|
||||
},
|
||||
deleteImage(image)
|
||||
{
|
||||
imageself = this;
|
||||
@@ -679,8 +761,7 @@ const medialib = {
|
||||
})
|
||||
.then(function (response)
|
||||
{
|
||||
imageself.showImages();
|
||||
imageself.removeImage(image.name);
|
||||
imageself.removeMedia(image.name);
|
||||
})
|
||||
.catch(function (error)
|
||||
{
|
||||
@@ -708,8 +789,7 @@ const medialib = {
|
||||
})
|
||||
.then(function (response)
|
||||
{
|
||||
fileself.showFiles(fileself.active);
|
||||
fileself.removeFile(file.name);
|
||||
fileself.removeMedia(file.name);
|
||||
})
|
||||
.catch(function (error)
|
||||
{
|
||||
@@ -782,7 +862,6 @@ const medialib = {
|
||||
})
|
||||
.then((response) =>
|
||||
{
|
||||
console.info(file);
|
||||
var type = 'files';
|
||||
if (file.type.startsWith('video/'))
|
||||
{
|
||||
|
@@ -47,6 +47,7 @@ $app->group('/api/v1', function (RouteCollectorProxy $group) use ($acl) {
|
||||
|
||||
# IMAGES
|
||||
$group->get('/pagemedia', ControllerApiImage::class . ':getPagemedia')->setName('api.image.pagemedia')->add(new ApiAuthorization($acl, 'mycontent', 'read')); # author
|
||||
$group->get('/unusedmedia', ControllerApiImage::class . ':getUnusedMedia')->setName('api.image.unusedmedia')->add(new ApiAuthorization($acl, 'mycontent', 'read')); # author
|
||||
$group->get('/images', ControllerApiImage::class . ':getImages')->setName('api.image.images')->add(new ApiAuthorization($acl, 'mycontent', 'read')); # author
|
||||
$group->post('/image', ControllerApiImage::class . ':saveImage')->setName('api.image.create')->add(new ApiAuthorization($acl, 'mycontent', 'create')); # author
|
||||
$group->put('/image', ControllerApiImage::class . ':publishImage')->setName('api.image.publish')->add(new ApiAuthorization($acl, 'mycontent', 'create')); # author
|
||||
|
Reference in New Issue
Block a user