Error declaring MediaLibrary/CustomPathGenerator.php file - laravel

In laravel 9 with spatie/laravel-medialibrary 10 I tyry to make custom path for uploaded file
looking at docs : https://spatie.be/docs/laravel-medialibrary/v9/advanced-usage/using-a-custom-directory-structure
But making app/Services/MediaLibrary/CustomPathGenerator.php file :
<?php
namespace App\Services\MediaLibrary;
//namespace App\MediaLibrary;
use Spatie\MediaLibrary\MediaCollections\Models\Media;
use Spatie\MediaLibrary\PathGenerator\PathGenerator;
//class CustomPathGenerator implements BasePathGenerator
class CustomPathGenerator implements PathGenerator
{
/*
* Get the path for the given media, relative to the root storage path.
*/
public function getPath(Media $media): string
{
return md5($media->id . config('app.key')) .'/';
}
/*
* Get the path for conversions of the given media, relative to the root storage path.
*/
public function getPathForConversions(Media $media): string
{
return md5($media->id . config('app.key')) .'/conversions/';
}
/*
* Get the path for responsive images of the given media, relative to the root storage path.
*/
public function getPathForResponsiveImages(Media $media): string {
return md5($media->id . config('app.key')) .'/responsive-images/';
}
}
I got error :
[2022-02-16 17:52:44] local.ERROR: Interface "App\Services\MediaLibrary\BasePathGenerator" not found {"userId":1,"exception":"[object] (Error(code: 0): Interface \"App\\Services\\MediaLibrary\\BasePathGenerator\" not found at /mnt/_work_sdb8/wwwroot/lar/hostels4j/app/Services/MediaLibrary/CustomPathGenerator.php:8)
Looks like header of my file is invalid.
Which is valid way to fix it ?
Thanks in advance!

You have defined the BasePathGenerator incorrectly please use the following instead
<?php
namespace App\Services\MediaLibrary;
use Spatie\MediaLibrary\MediaCollections\Models\Media;
use Spatie\MediaLibrary\Support\PathGenerator\PathGenerator as BasePathGenerator;
class CustomPathGenerator implements BasePathGenerator {
/*
* Get the path for the given media, relative to the root storage path.
*/
public function getPath(Media $media): string {
return '';
}
/*
* Get the path for conversions of the given media, relative to the root storage path.
*/
public function getPathForConversions(Media $media): string {
return '';
}
/*
* Get the path for responsive images of the given media, relative to the root storage path.
*/
public function getPathForResponsiveImages(Media $media): string {
return '';
}
}
This should solve your issue.

Related

Spring boot 2: Mapping addResourceHandler for each language returns 404

I'm trying to set-up to serve the static content based on the language param in the URL and I'm getting 404, what mapping is wrong in the following code?
private PathResourceResolver getResolver() {
return new PathResourceResolver() {
#Override
protected Resource resolveResourceInternal(HttpServletRequest request, String requestPath, List<? extends Resource> locations, ResourceResolverChain chain) {
logger.info("REQ PATH :: " + requestPath + " " + locations);
return super.resolveResourceInternal(request, requestPath, locations, chain); //To change body of generated methods, choose Tools | Templates.
}
};
}
And static folder structure:
Below is the complete logs that shows 404 when I try to access URL https://localhost:8443/fr
I was able to solve the issue, if others have faced similar issue below solution worked for me.
addResourceLocations path must end with / else the StringUtils applyRelativePath method will remove the last part of the path provided, in my case en|fr|de..
/**
* Apply the given relative path to the given Java resource path,
* assuming standard Java folder separation (i.e. "/" separators).
* #param path the path to start from (usually a full file path)
* #param relativePath the relative path to apply
* (relative to the full file path above)
* #return the full file path that results from applying the relative path
*/
public static String applyRelativePath(String path, String relativePath) {
int separatorIndex = path.lastIndexOf(FOLDER_SEPARATOR);
if (separatorIndex != -1) {
String newPath = path.substring(0, separatorIndex);
if (!relativePath.startsWith(FOLDER_SEPARATOR)) {
newPath += FOLDER_SEPARATOR;
}
return newPath + relativePath;
}
else {
return relativePath;
}
}

October CMS extend System/Models/File

I trying to keep original file name when using System/Models/File, I got following code to extend this model:
namespace System\Models;
class NewFile extends File { public function fromPost($uploadedFile) { if ($uploadedFile === null) { return; }
$this->file_name = $uploadedFile->getClientOriginalName();
$this->file_size = $uploadedFile->getClientSize();
$this->content_type = $uploadedFile->getMimeType();
$this->disk_name = $this->getDiskName();
/*
* getRealPath() can be empty for some environments (IIS)
*/
$realPath = empty(trim($uploadedFile->getRealPath()))
? $uploadedFile->getPath() . DIRECTORY_SEPARATOR . $uploadedFile->getFileName()
: $uploadedFile->getRealPath();
//$this->putFile($realPath, $this->disk_name);
$this->putFile($realPath, $this->file_name);
return $this;
It works with file itself, it keeps original name but problem is link to attached file is still being generated. Broke my mind but cant get this work. Can anyone elaborate how to fix it?
Oh I see it seems its try to use disk_name to generate URL
so you did well for saving an image
//$this->putFile($realPath, $this->disk_name);
$this->putFile($realPath, $this->file_name);
but you just need to replace one line .. just undo your changes and make this one change
$this->file_name = $uploadedFile->getClientOriginalName();
$this->file_size = $uploadedFile->getClientSize();
$this->content_type = $uploadedFile->getMimeType();
// $this->disk_name = $this->getDiskName();
$this->disk_name = $this->file_name;
// use same file_name for disk ^ HERE
Link logic ( for referance only ) vendor\october\rain\src\Database\Attach\File.php and modules\system\models\File.php
/**
* Returns the public address to access the file.
*/
public function getPath()
{
return $this->getPublicPath() . $this->getPartitionDirectory() . $this->disk_name;
}
/**
* Define the public address for the storage path.
*/
public function getPublicPath()
{
$uploadsPath = Config::get('cms.storage.uploads.path', '/storage/app/uploads');
if ($this->isPublic()) {
$uploadsPath .= '/public';
}
else {
$uploadsPath .= '/protected';
}
return Url::asset($uploadsPath) . '/';
}
Just make disk_name also same as file_name so when file saved on disk it will use original name and when the link is generated it also use disk_name which is original file_name
now your link and file name are synced and will be same always.
if any doubt please comment.

Symfony custom filename for uploaded image files

I'm aware of VichUpload and namers, but I have to face two different file uploads, and I need special naming conventions that I'm unable to get with the current VichUpload documentation.
First file upload is for an entity called "School", which manages every single school, and its logo. So, taking as web root '../web/files/schools/', then I'd take the school_id and then the 'logo' folder with the uploaded file name, so it could be '../web/files/schools/000001/logo.png'.
Then, the second entity is 'students' to store the photo, with a school_id foreign key from School entity. So, the resulting file name would depend on the school id, and the student id, being the root for student '../web/files/schools/<school_id>/students/<student_id>.[jpg|png]', having student_id a six zero padding on the left.
This is the section in config.yml about this (updated info):
school_image:
upload_destination: "../web/files/images/schools"
uri_prefix: "/files/images/schools"
directory_namer:
service: vich_uploader.directory_namer_subdir
options: { property: 'schoolDirectory', chars_per_dir: 6, dirs: 1 }
namer:
service: vich_uploader.namer_property
options: { property: 'idSlug' }
inject_on_load: true
delete_on_update: true
delete_on_remove: true
student_image:
upload_destination: "../web/files/images/schools"
uri_prefix: "/files/images/schools"
directory_namer:
service: vich_uploader.directory_namer_subdir
options: { property: 'userDirectory', chars_per_dir: 6, dirs: 1 }
namer:
service: vich_uploader.namer_property
options: { property: 'userImage'}
inject_on_load: true
delete_on_update: true
delete_on_remove: true
in school entity (might work with a dirty workaround):
/**
* NOTE: This is not a mapped field of entity metadata, just a simple property.
*
* #Vich\UploadableField(mapping="school_image", fileNameProperty="imageName")
*
* #Assert\Image()
*
* #var File
*/
private $imageFile;
public function getIdSlug()
{
$slug = sprintf("%06d", $this->getId());
return $slug;
}
public function getSchoolDirectory()
{
$slug = sprintf("%06d", $this->getId());
return $slug;
}
in student entity (not working, as explained below):
/**
* NOTE: This is not a mapped field of entity metadata, just a simple property.
*
* #Vich\UploadableField(mapping="student_image", fileNameProperty="imageName")
*
* #Assert\Image()
*
* #var File
*/
private $imageFile;
public function getUserDirectory()
{
$schoolDir = $this->getSchool()->getSchoolDirectory();
$dir = $schoolDir.'/students/'.sprintf("%06d", $this->getId());
return $dir;
}
public function getUserImage()
{
return $this->getUsername() . $this->getImageFile()->getExtension();
}
This setup with both "namer" and "directory_namer" seems to ignore the paths in directory_namer and use a path "<namer>/<namer>.ext" instead of "<directory_namer>/<namer>.ext". If I change the getUserImage() function and prepend the result of getUserDirectory() (i.e., the db stores "<directory_namer>/<namer>.ext" instead of just "<namer>.ext"), the "<directory_namer>" path is ignored and just "<namer>.ext" is created.
Since upload_prefix and uri_destination don't seem to handle variable data, how can I setup a namer or whatever to get this path for both cases?
BTW, I'm using Symfony 3.1 and composer hasn't allowed to update vich beyond "1.7.x-dev", according to bundled composer.json in vendor folder. If you find that it should work with this setup and the possible solution is to upgrade, I would thank to be pointed to the specific files which fix the problem, so I can manually paste whatever is wrong.
SOLUTION:
The problem was that, due to the "old" version, there was a missing directory_namer class, PropertyDirectoryNamer, with vich_uploader.namer_directory_property servicename, instead of vich_uploader.directory_namer_subdir, which was a wrong class for this purpose. I copied this class and registered it in the namer.xml file, and then I got the expected results. Now I'm going to try to mark this as solved.
You need to create custom directory namer class that implements
Vich\UploaderBundle\Naming\DirectoryNamerInterface
For example:
namespace App\Services;
use Vich\UploaderBundle\Mapping\PropertyMapping;
class SchoolDirectoryNamer implements DirectoryNamerInterface
{
public function directoryName($object, PropertyMapping $mapping): string
{
// do what you want with $object and $mapping
$dir = '';
return $dir;
}
}
Then, make it as a service.
services:
# ...
app.directory_namer.school:
class: App\Services\SchoolDirectoryNamer
Last step is config vich_uploader
vich_uploader:
# ...
mappings:
school:
upload_destination: school_image
directory_namer: app.directory_namer.school
student:
upload_destination: student_image
directory_namer: app.directory_namer.student
source : VichUploaderBundle - How To Create a Custom Directory Namer

Composer: Troubleshooting autoloading

I'm submitting this question because I have been searching on the topic of troubleshooting autoloading in Composer and can't seem to find anything straightforward. I added several classes to a library and then referenced them in a project. The project can't find the classes. Here are the pertinent facts:
• I've already been using the library in the project for some time.
• I double-checked the namespace declaration in the code file for the class in the library:
namespace \Library\Package\Subpackage;
class Widget { ...
• I double-checked the use statement that aliases the library and the instantiation:
use \Library\Package\Subpackage as Subpackage;
$e = new Subpackage\Widget();
• For grins, I altered the alias:
use \Library\Package\Subpackage\Widget as Widget;
$e = new Widget();
• For even more grins, I tried a direct reference:
$e = new \Library\Package\Subpackage\Widget();
• I also made sure to composer update and make sure the file is actually there, at the right path:
composer update
find . -name Widget.php
./vendor/organization/library/Package/Subpackage/Widget.php
• As a sanity check, I added a reference to another item in the same library but in a different subpackage.
$f = new \Library\Package2\Widget2(); // this works fine
• As an absolute last resort, I put the reference in a PHP file all by itself and ran that.
require_once __DIR__.'/vendor/autoload.php';
$e = new \Library\Package\Subpackage\Widget();
• And for the coup de grace, I wrote a unit test that checks every file in the library's vendor folder:
class AutoloadTest extends \PHPUnit\Framework\TestCase
{
public function classDataProvider()
{
$data = array();
$path = realpath('organization/library/Package/');
$directory_iterator = new \RecursiveDirectoryIterator($path);
$iterator_iterator = new \RecursiveIteratorIterator($directory_iterator);
foreach ($iterator_iterator as $iterator_result) {
$real_path = $iterator_result->getRealPath();
if (substr($real_path, -4, 4) != '.php') {
continue;
}
$split = explode('organization/library/Package/', $real_path);
$processed_path = substr($split[1], 0, -4);
$class_fqname = sprintf('\Library\%s', str_replace("/", '\\', $processed_path));
$data[] = array($class_fqname);
}
return $data;
}
public function test($class_name)
{
$assertion = class_exists($class_name) || trait_exists($class_name) || interface_exists($class_name);
$this->assertTrue($assertion, "$class_name is not recognized as a class, trait or interface.");
}
}
My questions are:
What is the most likely culprit?
What can I do with Composer to get tracing information about the paths that it is trying? Is there some way to feed Composer a path and have it tell me whether or not it can resolve it?
Extraneous backslash.
namespace \Library\Package\Subpackage;
^ oops!
Removing this backslash resolved the problem.
Ultimately, the unit test that I wrote helped me find and resolve the problem. So for the second part of my question, I'm offering the test class that I wrote. The provider receives pairs of the path to scan and the namespace in which the files belong. It scans the paths given and finds the classes in that namespace. The test checks asserts that each qualified-class name corresponds to a class, trait or interface. (Requires an autoloader to load the classes, of course.)
abstract class AutoloadTestBase extends \PHPUnit\Framework\TestCase
{
public function pathProvider()
{
$data = array();
foreach ($this->directories() as $directory_specification) {
list($path, $namespace) = $directory_specification;
if (empty($path)) {
continue;
}
$directory_iterator = new \RecursiveDirectoryIterator($path);
$iterator_iterator = new \RecursiveIteratorIterator($directory_iterator);
foreach ($iterator_iterator as $iterator_result) {
$real_path = $iterator_result->getRealPath();
$split1 = explode($path.'/', $real_path);
if (count($split1) < 2) {
continue;
}
$success = preg_match('/^(.+)\.php$/', $split1[1], $split2);
if (!$success || substr($split2[1], 0, 5) == 'Tests') {
continue;
}
$class_fqname = sprintf('%s\%s', $namespace, str_replace("/", '\\', $split2[1]));
$data[] = array($class_fqname);
}
}
return $data;
}
/**
* #dataProvider pathProvider
*/
public function test($class_name)
{
$assertion = class_exists($class_name) || trait_exists($class_name) || interface_exists($class_name);
$this->assertTrue($assertion, "$class_name is not recognized as a class, trait or interface.");
}
}
class AutoloadTest extends AutoloadTestBase
{
protected function directories()
{
return array(
array('Package1/Subpackage1','\Library1\Package1\Subpackage1'),
array('vendor/organization2/package2/Subpackage2','\Library2\Package2\Subpackage2')
);
}
}

In freemarker is it possible to check to see if a file exists before including it?

We are trying to build a system in freemarker where extension files can be optionally added to replace blocks of the standard template.
We have gotten to this point
<#attempt>
<#include "extension.ftl">
<#recover>
Standard output
</#attempt>
So - if the extension.ftl file exists it will be used otherwise the part inside of the recover block is output.
The problem with this is that freemarker always logs the error that caused the recover block to trigger.
So we need one of two things:
Don't call the include if the file doesn't exist - thus the need to check for file existence.
-OR-
A way to prevent the logging of the error inside the recover block without changing the logging to prevent ALL freemarker errors from showing up.
easier solution would be:
<#attempt>
<#import xyz.ftl>
your_code_here
<#recover>
</#attempt>
We've written a custom macro which solves this for us. In early testing, it works well. To include it, add something like this (where mm is a Spring ModelMap):
mm.addAttribute(IncludeIfExistsMacro.MACRO_NAME, new IncludeIfExistsMacro());
import java.io.IOException;
import java.util.Map;
import org.apache.commons.io.FilenameUtils;
import freemarker.cache.TemplateCache;
import freemarker.cache.TemplateLoader;
import freemarker.core.Environment;
import freemarker.template.TemplateDirectiveBody;
import freemarker.template.TemplateDirectiveModel;
import freemarker.template.TemplateException;
import freemarker.template.TemplateModel;
/**
* This macro optionally includes the template given by path. If the template isn't found, it doesn't
* throw an exception; instead it renders the nested content (if any).
*
* For example:
* <#include_if_exists path="module/${behavior}.ftl">
* <#include "default.ftl">
* </#include_if_exists>
*
* #param path the path of the template to be included if it exists
* #param nested (optional) body could be include directive or any other block of code
*/
public class IncludeIfExistsMacro implements TemplateDirectiveModel {
private static final String PATH_PARAM = "path";
public static final String MACRO_NAME = "include_if_exists";
#Override
public void execute(Environment environment, Map map, TemplateModel[] templateModel,
TemplateDirectiveBody directiveBody) throws TemplateException, IOException {
if (! map.containsKey(PATH_PARAM)) {
throw new RuntimeException("missing required parameter '" + PATH_PARAM + "' for macro " + MACRO_NAME);
}
// get the current template's parent directory to use when searching for relative paths
final String currentTemplateName = environment.getTemplate().getName();
final String currentTemplateDir = FilenameUtils.getPath(currentTemplateName);
// look up the path relative to the current working directory (this also works for absolute paths)
final String path = map.get(PATH_PARAM).toString();
final String fullTemplatePath = TemplateCache.getFullTemplatePath(environment, currentTemplateDir, path);
TemplateLoader templateLoader = environment.getConfiguration().getTemplateLoader();
if (templateLoader.findTemplateSource(fullTemplatePath) != null) {
// include the template for the path, if it's found
environment.include(environment.getTemplateForInclusion(fullTemplatePath, null, true));
} else {
// otherwise render the nested content, if there is any
if (directiveBody != null) {
directiveBody.render(environment.getOut());
}
}
}
}
I had this exact need as well but I didn't want to use FreeMarker's ObjectConstructor (it felt too much like a scriptlet for my taste).
I wrote a custom FileTemplateLoader:
public class CustomFileTemplateLoader
extends FileTemplateLoader {
private static final String STUB_FTL = "/tools/empty.ftl";
public CustomFileTemplateLoader(File baseDir) throws IOException {
super(baseDir);
}
#Override
public Object findTemplateSource(String name) throws IOException {
Object result = null;
if (name.startsWith("optional:")) {
result = super.findTemplateSource(name.replace("optional:", ""));
if (result == null) {
result = super.findTemplateSource(STUB_FTL);
}
}
if (result == null) {
result = super.findTemplateSource(name);
}
return result;
}
}
And my corresponding FreeMarker macro:
<#macro optional_include name>
<#include "/optional:" + name>
</#macro>
An empty FTL file was required (/tools/empty.ftl) which just contains a comment explaining its existence.
The result is that an "optional" include will just include this empty FTL if the requested FTL cannot be found.
You can use also use Java method to check file exist or not.
Java Method-
public static boolean checkFileExistance(String filePath){
File tmpDir = new File(filePath);
boolean exists = tmpDir.exists();
return exists;
}
Freemarker Code-
<#assign fileExists = (Static["ClassName"].checkFileExistance("Filename"))?c/>
<#if fileExists = "true">
<#include "/home/demo.ftl"/>
<#else>
<#include "/home/index.ftl">
</#if>
Try this to get the base path:
<#assign objectConstructor = "freemarker.template.utility.ObjectConstructor"?new()>
<#assign file = objectConstructor("java.io.File","")>
<#assign path = file.getAbsolutePath()>
<script type="text/javascript">
alert("${path?string}");
</script>
Then this to walk the directory structure:
<#assign objectConstructor = "freemarker.template.utility.ObjectConstructor"?new()>
<#assign file = objectConstructor("java.io.File","target/test.ftl")>
<#assign exist = file.exists()>
<script type="text/javascript">
alert("${exist?string}");
</script>

Resources