Silverstripe 4 - SiteConfig module Image not working in template - image

I can't seem to get Silverstripe 4 to display images included in SiteConfig in my templates at all.I used to be able to just doe something like $SiteConfig.Logo and it would print out a automatic tag.
CustomSiteConfig:
<?php
use SilverStripe\Forms\FieldList;
use SilverStripe\ORM\DataExtension;
use SilverStripe\Forms\TextField;
use SilverStripe\Forms\TextareaField;
use SilverStripe\Forms\HeaderField;
use SilverStripe\AssetAdmin\Forms\UploadField;
use SilverStripe\Assets\Image;
use SilverStripe\ORM\DataObject;
use SilverStripe\CMS\Model\SiteTree;
class CustomSiteConfig extends DataExtension
{
private static $db = [
];
private static $has_one = [
'Logo' => Image::class,
'MobileLogo' => Image::class
];
private static $owns = [
'Logo',
"MobileLogo"
];
public function updateCMSFields(FieldList $fields)
{
$uploader = UploadField::create('Logo');
$uploader->setFolderName('Logo');
$uploader->getValidator()->setAllowedExtensions(['png','gif','jpeg','jpg']);
$fields->addFieldsToTab('Root.Main', [
HeaderField::create('hf2','Default logo'),
$uploader
]);
$uploader2 = UploadField::create('MobileLogo');
$uploader2->setFolderName('MobileLogo');
$uploader2->getValidator()->setAllowedExtensions(['png','gif','jpeg','jpg']);
$fields->addFieldsToTab('Root.Main', [
HeaderField::create('hf3','Mobile Logo'),
$uploader2
]);
}
}
But when I try in my template file. I get no URL
$SiteConfig.Logo
or
$SiteConfig.Logo().Link
etc
Nothing works?

A few things to check:
Verify that $SiteConfig is available as variable at that point in your template (Try using $SiteConfig.Title)
Verify that the extension is actually added to SiteConfig (do you see the CMS Fields?)
Did you add $owns later? run ?flush=1 again and re-save the SiteConfig *
Verify that both the SiteConfig and the File is published. (Save & Publish the SiteConfig twice, then check in the file manager if the file is published) **
[*] $owns is just a directive that when SiteConfig->doPublish() is called, it will also publish all files
[**] I've seen a bug that DataObjects don't actually publish files sometimes. Saving twice might work.

Just like Zauberfisch said, your image is probably not published. However, publishing the image after writing the owner can be tricky.
I usually through in this code
public function onAfterWrite()
{
parent::onAfterWrite();
if ( $this->LogoID ) {
$this->Logo()->doPublish();
}
if ( $this->MobileLogoID ) {
$this-> MobileLogo()->doPublish();
}
}
It's messy, I know, but it can save you a couple of hours. After saving you can remove it as the $owns hook will start to kick-in to all newly created objects.

We can Use This one
$SiteConfig.Logo.URL

Related

TYPO3: clear cache of a page when file metadata is changed

I have a custom plugin which shows files for download based on sys_category.
When an editor changes the meta data of a file, e.g. changes the title or category, the changes are only reflected in the frontend when the complete frontend cache is cleared.
I've tried to add this to page TSconfig:
[page|uid = 0]
TCEMAIN.clearCacheCmd = 17
[global]
But this doesn't work. Any other idea how to clear the cache, when a sys_file_metadata record is changed?
Here is my solution. Thx Aristeidis for the hint.
ext_localconf.php
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processDatamapClass']['my_extension_key'] =
\Vendor\ExtKey\Hooks\DataHandler::class;
Classes/Hooks/DataHandler.php
<?php
namespace Vendor\ExtKey\Hooks;
use TYPO3\CMS\Core\Cache\CacheManager;
use TYPO3\CMS\Core\Utility\GeneralUtility;
class DataHandler
{
public function processDatamap_afterDatabaseOperations(
$status,
$table,
$recordUid,
array $fields,
\TYPO3\CMS\Core\DataHandling\DataHandler $parentObject
) {
if ($table === 'sys_file_metadata') {
// hardcoded list of page uids to clear
$pageIdsToClear = [17];
if (!is_array($pageIdsToClear)) {
$pageIdsToClear = [(int)$pageIdsToClear];
}
$tags = array_map(function ($item) {
return 'pageId_' . $item;
}, $pageIdsToClear);
GeneralUtility::makeInstance(CacheManager::class)->flushCachesInGroupByTags('pages', $tags);
}
}
}
Of course this could be improved more:
Currently the list of page uids is hardcoded. That could be made configureable via extension manager settings.
A check could be implemented to only delete the cache if the file has a certain sys_category assigned, e.g. Downloads
But for the moment this solution is enough for my needs.

I can't find the implementation for Storage Facade in laravel

I'm new with laravel and I'm working in fileststem on laravel
(I want to do usual fileststem process like -make dir - copy - put -delete -ect)
I'm using laravel "Storage" Facade
but when i type
i referenced the class above like this in my code
use Illuminate\Support\Facades\Storage;
for example below :
if (file_exists(public_path($oldImage))) {
Storage::delete($oldImage);
}
nothing happens ,and when i refer to the class code i found this :
namespace Illuminate\Support\Facades;
/**
* #see \Illuminate\Filesystem\FilesystemManager
*/
class Storage extends Facade
{
/**
* Get the registered name of the component.
*
* #return string
*/
protected static function getFacadeAccessor()
{
return 'filesystem';
}
}
so where is the implementation and if you have alternative way to deal with
filesystem process rather than "Storage" facade ??
Storage is a facade and accesses the class Filesystem located here: vendor/laravel/framework/src/Illuminate/Filesystem/Filesystem.php
As you can see in the official filesystem documentation the code snippets use Storage.
UPDATE:
You should add use Storage; to be able to use the Storage facade.
I recommend reading the Laravel 8.X docs to get an initial heads up: https://laravel.com/docs/8.x/filesystem
NOTE: Before you get too carried away, make sure you understand the difference between local and public.
For starters, you should make your first goal to upload a file and acquire the UploadedFile type.
You can access a single file via something like $request->file('name'), or an array of images via something like:
// $request->input('images')
foreach ($images as $image) {
\Log::debug($image->getClientOriginalName());
}
If your file upload can be single and/or multiple, I recommend going with the array approach because a single file wrapped in an array allows you to use the same syntax for single and multi uploads (ie: that foreach loop works fine with one image, no extra code).
Here's an example:
use Illuminate\Support\Facades\Storage;
$slug = 'davids-sandwich-photos';
foreach ($images as $image) {
Storage::putFileAs(
'images' .'/'. $slug,
$image,
$image->getClientOriginalName()
);
}
Storage::putFileAs() can take 3 parameters: directory, content, filename. You can see above in the code that I interpolated a mix of static and derived directory name. You could do something like 'images' .'/'. $slug .'/'. Auth::user()->id to save the file in /images/davids-sandwich-photos/11.
Then, check in your repo directory: /storage/app/ and look for the images directory.
You can manually delete the folders while testing to get your bearings straight.
That should be enough to get most people started.
To avoid using the Storage facade, you can use:
foreach ($images as $image) {
$image->storeAs(
'examples' .'/'. $slug,
$image->getClientOriginalName(),
'public'
);
}
--
Check out config/filesystems.php under the disks section if you want to start manipulating the drivers, but I'm not a DB admin expert here.
I also saved this along my journey: https://medium.com/#shafiya.ariff23/how-to-store-uploaded-images-in-public-folder-in-laravel-5-8-and-display-them-on-shared-hosting-e31c7f37a737. You might need that if you get stuck with something like symlinking.
<img
v-for="image in example.images"
:key="image.filename"
:src="`/storage/examples/${example.slug}/${image.filename}`"
>
NOTE: The important part with Vue JS is to use <img src="/storage/examples/slug/filename.jpg"> if your file is located in your repository as /storage/app/public/examples/slug/filename.jpg Pay close attention to every character.
The public_path function returns the fully qualified path to the public directory ie public directory inside the laravel application. When using Storage, the path is set to the storage/app directory.
if (file_exists(public_path($oldImage))) {
//public_path($oldImage) will check for file in public directory
Storage::delete($oldImage); //Will delete file in storage/app directory
}
The modified code should be
if(Storage::has($oldImage)){
Storage::delete($oldImage);
}

How do I disable Laravel view cache?

I have an exception in one of my views. However, instead of telling me the name of the view so I can find it and fix it, laravel says it is in app/storage/views/110a3ecc0aa5ab7e6f7f50ef35a67a8b, which is meaningless.
How do I disable this view caching, so that laravel uses and refers to the actual files?
Out of the box? You can't. But you can extend the BladeCompiler class, overriding the method resposible for checking if the view has been expired:
class MyBladeCompiler extends BladeCompiler {
public function isExpired($path)
{
if ( ! \Config::get('view.cache'))
{
return true;
}
return parent::isExpired($path);
}
}
You'll need to replace the BladeCompiler instance in IoC container, with your own compiler:
$app = App::make('app'); // or just $app = app();
$app->bindShared('blade.compiler', function($app)
{
$cache = $app['path.storage'].'/views';
return new MyBladeCompiler($app['files'], $cache);
});
And then you just need to create that key in your app/config/view.php file
<?php
return [
'cache' => false,
'paths' => [base_path().'/resources/views'],
'pagination' => 'pagination::slider-3',
];
Or, like I do here:
return [
'cache' => in_array(App::environment(), ['production', 'staging']),
];
this worked for me... added this to the .env file
CACHE_EXPIRE=-1
In latest version of laravel (> v9.7.0), you can now add inside config/view.php:
'cache' => App::environment('local') ? false : true
Here is the PR: https://github.com/laravel/framework/pull/41859
Solution
open php.ini
opcache.revalidate_freq=0
opcache.fast_shutdown=0
change to this. restart apache.
check your .env file
Change CACHE_DRIVER=file to CACHE_DRIVER=array
If you have artisan, it's easy to clear the cache
php artisan view:clear
If you don't have or don't want artisan (can't think why you wouldn't want it, it's very useful), you can from the root of your project do
cd storage/framework/views/
rm *.php
Laravel Creates view cache file because it has been told to do that. In .env File you will come across cache_driver which has default property as file change it to array.
You can clear cache in this way, as well:
// Clear cache in laravel
Route::get('/clear-cache', function() {
Artisan::call('cache:clear');
// return what you want
return "Cache is cleared";
});
Here is the full answer
Go to vendor/illuminate/BladeCompiler.php
change these 2 lines
use Illuminate\View\Compilers\Compiler;
class BladeCompiler extends Compiler implements CompilerInterface
with the following:
use App\Support\CustomCompiler;
class BladeCompiler extends CustomCompiler implements CompilerInterface
in your app/support folder (or whatever structure you are using)
create the following class
namespace App\Support;
use Illuminate\View\Compilers\Compiler;
class CustomCompiler extends Compiler {
public function isExpired($path) {
if ( !\config('blade.use_cache'))
return true;
return parent::isExpired($path);
}
}
your blade config file will look like this
return [
'use_cache' => false,
'cache' => storage_path('cache'),
'views' => resources_path('views')
];
auto dump and run....
If you are using MAMP, disable OPCache under Preferences, General, PHP-Cahce. just select off. thank me later.
Although some would call this sketchy, this was the quickest and most minimal way to do this on a small application I was working on
On the controller(s) that my routes pointed to:
public function __construct()
{
exec('php /full/path/to/artisan view:clear');
}
A bit late to the party, however.
I had the same issue: the browser not reflecting changes to the php code.
Simple solution for me was:
set the clock on the server to the same time as the dev computer !
sudo date +%T -s "11:14:00"
In development environment, I just add and modify the next:
bootstrap/start.php
$env = $app->detectEnvironment(function(){return 'testing';});
app/config/testing/cache.php add in array
'cache' => false,
app/config/view.php add in array
'cache' => false,

custom view helper error in zend framework 2

I cannot seem to register a custom view helper in zend Framework 2.02 I tried all solutions posted here and anything I can think I should do but I keep getting this error:
Fatal error: Class 'ModuleName\view\Helper\mylinkhelper' not found in C:\wamp\vhosts\projectName\vendor\zendframework\zendframework\library\Zend\ServiceManager\AbstractPluginManager.php on line 177
And here's how my module.config.php looks like:
return array{
'controllers'=>array(
....
),
'view_manager' => array(
'template_path_stack' => array(
'ModuleName' => __DIR__ . '/../view',
),
),
'view_helpers' => array(
'invokables' => array(
'mylink' => 'ModuleName\view\Helper\mylinkhelper',
),
),
};
in my view file, I have:
echo $this->mylink($someparameter);
I appreciate any feedback on this. I don't really know what else to do here.
<?php
// ./module/Application/src/Application/View/Helper/AbsoluteUrl.php
namespace Application\View\Helper;
use Zend\Http\Request;
use Zend\View\Helper\AbstractHelper;
class AbsoluteUrl extends AbstractHelper
{
protected $request;
public function __construct(Request $request)
{
$this->request = $request;
}
public function __invoke()
{
return $this->request->getUri()->normalize();
}
}
You’ll notice that this particular helper has a dependency — a Zend\Http\Request object. To inject this, we’ll need to set up a factory with the initialization logic for our view helper:
<?php
// ./module/Application/Module.php
namespace Application;
use Application\View\Helper\AbsoluteUrl;
class Module
{
public function getViewHelperConfig()
{
return array(
'factories' => array(
// the array key here is the name you will call the view helper by in your view scripts
'absoluteUrl' => function($sm) {
$locator = $sm->getServiceLocator(); // $sm is the view helper manager, so we need to fetch the main service manager
return new AbsoluteUrl($locator->get('Request'));
},
),
);
}
// If copy/pasting this example, you'll also need the getAutoloaderConfig() method; I've omitted it for the sake of brevity.
}
That’s it! Now you can call your helper in your view scripts:
The full URL to the current page is: <?php echo $this->absoluteUrl(); ?>
thanks to evan to create this tutorial
It looks like the View Helper is correctly added to the ServiceManager since invoking mylink() is trying to create ModuleName\view\Helper\mylinkhelper.
I'd make sure the class is creatable with new College\view\Helper\mylinkhelper(); from a controller, this is likely to throw up some clues. Also check the filename and classname are correct.
Your approach is correct, but there might be two things which cause you this trouble:
You talk about a top level namespace ModuleName, but in your example configuration you have the top level namespace College. When you have a ModuleName namespace and you try to load College, that obviously does not work
Your view helper cannot be autoloaded. Are you sure the class name is correct (MyLinkHelper), the namespace is correct (College\View\Helper, see also above) and the file name is correct (MyLinkHelper.php). And have you enabled class-name autoloading for this module in your module class?
A third option might be the lower case "view" and "mylinkhelper" as usually you would write College\View\Helper\MyLinkHelper with a capital V, M, L and H. But since you are on Windows that should not matter afaik. I know for Linux you must be aware of case sensitiveness of class names.
The problem is that the class file is not being loaded. Its supposed to be included in autoload_classmap.php.
<?php
return array(
'{module}\View\Helper\{helper}' => __DIR__ . '\View\Helper\{helper}.php',
);
?>
I ran in the same issue and this page helped me.
As I'm new to ZF, i don't know if there is another way to add the paths in autoload_classmap, i think probably there is, but i just edited the file manually.
Got the same problem, found out by myself that view helper file was not included. while putting it into controller for testing it worked
e.g.: require_once('module/Pages/view/Helper/RenderNav.php');
why it has not been autoloaded ?

Codeigniter: How can I check if a library method exists without loading the library?

I want to separate certain functions from controller methods into libraries to make them modular. Based on the URI I'm trying to route to a library method if it exists. But I'd have to load the library to check for the method with the php method_exists function. The only other way I've found to check for it is to put all the library methods in a config array and check for the method name there. Such as:
<?php
/**
* Application_config.php
*/
$config['extensions'] = array(
'News' => array(
'library' => 'articles_library',
'methods' => array(
'articles',
'article',
'edit_article',
'add_article',
'delete_article'
)
)
);
and
<?php
/**
* admin_controller.php
*
* all admin routes go to index
*/
class C3_base_controller extends Controller {
public function index() {
$lib_chk = 0;
$ext = $this->config->item('extensions');
foreach($ext as $item) {
foreach ($item['methods'] as $meth) {
if ($this->uri->segment(2) == $meth) {
$lib = $item['library'];
echo $this->$lib->$meth();
$lib_chk = 1;
}
}
}
if ($lib_chk == 0) {
// rest of controller...
}
}
}
Is there a better way to check for the existence of a library method in each library without loading the libraries? Having to duplicate every method in the application config is just asking for it.
You can use
method_exists
For example, if you are checking for the database object if it's exists
if(method_exists($this->CI->db, 'set')){
// code ...
}
Are you pursuing modularity for reusable code or are you creating an application that has features that may or may not be present...as in plugins?
It's hard for me to imagine your base application having hard-coaded url's that lead to a library that may not exist. Can you test for the library rather than the method? That may be simpler to check a directory and load file names into an array, then see if the library required by a url or uri is in that array.
But if you need to stick to your array of existing method names, could you just regex for the method names by that occur after "function (space)" and followed by parenthesis?

Resources