What is the simple way to share partials between themes. Assets are possible but i am having issue with whole partials. I have a multi-theme site managed/activated by sub-domain policies/logic.
SOLUTIONS
/**
*
* Renders a requested partial in context of this component,
* see Cms\Classes\Controller#renderPartial for usage.
*/
/**
* #param $themeName
* #param $partialName
* #param $data
* #return mixed
* #throws \Cms\Classes\CmsException
*/
public function renderThemePartial($partialName, $themeName, $data)
{
$theme = Theme::getActiveTheme();
if($themeName) {
$theme = Theme::load($themeName);
}
$controller = new Controller($theme);
return $controller->renderPartial($partialName, $data);
}
/**
*
* Renders a requested content in context of this component,
* see Cms\Classes\Controller#renderContent for usage.
*/
/**
* #param $themeName
* #param $contentName
* #param $data
* #return string
* #throws \Cms\Classes\CmsException
*/
public function renderThemeContent($contentName, $themeName, $data)
{
$theme = Theme::getActiveTheme();
if($themeName) {
$theme = Theme::load($themeName);
}
$controller = new Controller($theme);
return $controller->renderContent($contentName, $data);
}
public function registerMarkupTags()
{
return [
'functions' => [
'partial_from_theme' => [$this, 'themePartial'],
'content_from_theme' => [$this, 'themeContent'],
],
'filters' => [
'theme_asset' => [$this, 'themeUrl']
]
];
}
/**
* #param $requested
* #return string
*/
public function themeUrl($requested)
{
$asset = $requested[0];
$theme = $requested[1];
$theme = Theme::load($theme);
$themeDir = $theme->getDirName();
if (is_array($asset)) {
$_url = Url::to(CombineAssets::combine($asset, themes_path().'/'.$themeDir));
}
else {
$_url = Config::get('cms.themesPath', '/themes').'/'.$themeDir;
if ($asset !== null) {
$_url .= '/'.$asset;
}
$_url = Url::asset($_url);
}
return $_url;
}
/**
* #param $partialName
* #param null $themeName
* #param array $parameters
* #return mixed
* #throws \Cms\Classes\CmsException
*/
public function themePartial($partialName, $themeName = null, $parameters = [])
{
return $this->renderThemePartial($partialName, $themeName, $parameters);
}
/**
* #param $contentName
* #param null $themeName
* #param array $parameters
* #return string
* #throws \Cms\Classes\CmsException
*/
public function themeContent($contentName, $themeName = null, $parameters = [])
{
return $this->renderThemeContent($contentName, $themeName, $parameters);
}
I guess you can use Plugin component here you can add component to page and use its partial.
You need to define partial [ you can add as many as you like ] in component.
And just use that partial like below in page , now this same thing you can do in other themes as well.
When you change partial it will affect in all other themes like its shared across multiple themes.
Down side will be that you can not change its content from backend, you need to manually change that file from ftp or other manner.
Partial from another theme
Hmm
you can add custom twig function for that may be
In any of your plugin you can add this code
public function registerMarkupTags()
{
return [
'functions' => [
'theme_partial' => [$this, 'themePartial'],
]
];
}
public function themePartial($partialName, $themeName = null, $vars = [])
{
$theme = Theme::getActiveTheme();
if($themeName) {
$theme = Theme::load($themeName);
}
$template = call_user_func([Partial::class, 'load'], $theme, $partialName);
$partialContent = $template->getTwigContent();
$html = Twig::parse($partialContent, $vars);
return $html;
}
Now inside your theme when you want to use partial you can use
{{ theme_partial('call_from_other_theme', 'stemalo' ,{'name':'hardik'}) }}
^ Your theme name here.
themes/stemalo/partials/call_from_other_theme.htm content
description = "Shared Partial."
[viewBag]
==
<div id="shared_partial">
<h1>Another Theme Partial : {{name}}</h1>
</div>
this way you can use partial from another themes.
you aksed for access to theme directory here may be you can just you need to pass theme name, and `its dynamic you can pass variable as theme name so it can pick any thing you like.
If any doubt please comment.
Related
These two:
return redirect()->route('...');
return route('...');
seem to do the same thing. Is there a reason to not use the shorter form?
route()
its helper function and it generates the URL to a named route.if you see the source code
if (! function_exists('route')) {
/**
* Generate the URL to a named route.
*
* #param array|string $name
* #param mixed $parameters
* #param bool $absolute
* #return string
*/
function route($name, $parameters = [], $absolute = true)
{
return app('url')->route($name, $parameters, $absolute);
}
}
redirect()->route()
Here redirect() is helper function to Redirector class.
if (! function_exists('redirect')) {
/**
* Get an instance of the redirector.
*
* #param string|null $to
* #param int $status
* #param array $headers
* #param bool|null $secure
* #return \Illuminate\Routing\Redirector|\Illuminate\Http\RedirectResponse
*/
function redirect($to = null, $status = 302, $headers = [], $secure = null)
{
if (is_null($to)) {
return app('redirect');
}
return app('redirect')->to($to, $status, $headers, $secure);
}
}
redirect()->route() Create a new redirect response to a named route.
/**
* Create a new redirect response to a named route.
*
* #param string $route
* #param mixed $parameters
* #param int $status
* #param array $headers
* #return \Illuminate\Http\RedirectResponse
*/
public function route($route, $parameters = [], $status = 302, $headers = [])
{
return $this->to($this->generator->route($route, $parameters), $status, $headers);
}
Here if you see the source code then you have an option to set the status code and headers but in the case of route() can only generate the URL to a named route.
For better understanding,if you dd(redirect()->route('home'));
if you dd(\route('home')); then its plain url.
return redirect()->route('...');
it will redirect user to specific route
return route('...');
will return a specific route ( link )
So I just start using Laravel and Orchid yesterday...
and now I have this problem
I want to create a data page to all posts
So I create the layout: PostListLayout
class PostListLayout extends Table
{
public $target = 'posts';
/**
* #return TD[]
*/
public function columns(): array
{
return [
TD::make('title', 'Title')
->render(function (Post $post) {
return Link::make($post->title)
->route('platform.post.edit', $post);
}),
TD::make('created_at', 'Created'),
TD::make('updated_at', 'Last edit'),
];
}
}
and after that I defined the table layer, we return to the view screen
class PostListScreen extends Screen
{
/**
* Display header name.
*
* #var string
*/
public $name = 'Blog post';
/**
* Display header description.
*
* #var string
*/
public $description = 'All blog posts';
/**
* Query data.
*
* #return array
*/
public function query(): array
{
return [
'posts' => Post::paginate()
];
}
/**
* Button commands.
*
* #return Link[]
*/
public function commandBar(): array
{
return [
Link::make('Create new')
->icon('pencil')
->route('platform.post.edit')
];
}
/**
* Views.
*
* #return Layout[]
*/
public function layout(): array
{
return [
PostListLayout::class
];
}
}
but In the end I get this error page:
Call to undefined method App\Models\Post::getContent() (View: C:\Users\KHARYA\Desktop\ORCHID\orchid-project\vendor\orchid\platform\resources\views\layouts\table.blade.php)
in app\Models\Posts.php
class Posts extends Model {use AsMultiSource ...}
Normally TYPO3 assigns a variable in FrontEnd with the name validationResults and it looks like that.
Then we have to iterate through the Error objects and get a list with all the errors and their messages. But there is no way to take each one individually and assign it on the corresponding field where the error appears.
So the question is, how do i do it?
Best regards,
After some programming and a help from larry-pete (typo3.slack.com) i found a solution.
I created my own ViewHelper and it looks like this:
ext_localconf.php
$GLOBALS['TYPO3_CONF_VARS']['SYS']['fluid']['namespaces']['mytag'][] = 'Vendor\\ExtensionName\\ViewHelpers';
Classes/ViewHelpers/Form/ErrorViewHelper.php
namespace Vendor\ExtensionName\ViewHelpers\Form;
use TYPO3\CMS\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3\CMS\Fluid\ViewHelpers\Form\AbstractFormFieldViewHelper;
class ErrorViewHelper extends AbstractFormFieldViewHelper
{
use CompileWithRenderStatic;
/**
* As this ViewHelper renders HTML, the output must not be escaped.
*
* #var bool
*/
protected $escapeOutput = false;
public function initializeArguments()
{
$this->registerArgument('for', 'string', 'The name of the error name (e.g. argument name or property name). This can also be a property path (like blog.title), and will then only display the validation errors of that property.', false, '');
$this->registerArgument('as', 'string', '', false, 'flattenedErrors');
}
/**
* #param array $arguments
* #param \Closure $renderChildrenClosure
* #param RenderingContextInterface $renderingContext
* #return mixed
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$as = $arguments['as'];
$for = $arguments['for'];
$templateVariableContainer = $renderingContext->getVariableProvider();
$controllerContext = $renderingContext->getcontrollerContext();
$validationResults = $controllerContext->getRequest()->getOriginalRequestMappingResults();
if ($validationResults !== null){
$validationResults = $validationResults->forProperty($for);
}
$flattenedErrors = $validationResults->getErrors();
$output = $renderChildrenClosure();
$withKeys = [];
if (empty($output)) {
if ($flattenedErrors) {
foreach ($flattenedErrors as $error) {
$withKeys[$error->getTitle()] = $error;
}
}
}
$templateVariableContainer->add($as, $withKeys);
return $output;
}
}
Form.html
<mytag:form.error as="error" for="error"/>
And this returns:
So now, you can do something like that:
<f:if condition="{offer.firstName}"><span style="display: block">{offer.firstName.message}</span></f:if>
Best regards
I changed your Viewhelper a bit because it didn't worked in my version (typo3 9)
<?php
namespace Vendor\Extension\ViewHelpers;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
class ErrorViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* As this ViewHelper renders HTML, the output must not be escaped.
*
* #var bool
*/
protected $escapeOutput = false;
public function initializeArguments()
{
$this->registerArgument('for', 'string', 'The name of the error name (e.g. argument name or property name). This can also be a property path (like blog.title), and will then only display the validation errors of that property.', false, '');
}
/**
* #param array $arguments
* #param \Closure $renderChildrenClosure
* #param RenderingContextInterface $renderingContext
* #return mixed
*/
public static function renderStatic(
array $arguments,
\Closure $renderChildrenClosure,
RenderingContextInterface $renderingContext
) {
$for = $arguments['for'];
$controllerContext = $renderingContext->getControllerContext();
$validationResults = $controllerContext->getRequest()->getOriginalRequestMappingResults();
if ($validationResults !== null) {
$validationResults = $validationResults->forProperty($for);
}
$flattenedErrors = $validationResults->getFlattenedErrors();
return $flattenedErrors;
}
}
you can do:
{myExt:error() -> f:variable(name: 'errors')}}
or
{ff:error( for:"user" ) -> f:variable(name: 'errors')}}
I'm using symfony 3.2 and I'd like to login with ajax form. Everything works fine except messages are not translated.
this is the AjaxAuthenticationFailureHandler class
class AjaxAuthenticationFailureHandler extends DefaultAuthenticationFailureHandler
{
/**
* #var mixed
*/
private $translator;
/**
* #param \Symfony\Component\HttpKernel\HttpKernelInterface $httpKernel
* #param \Symfony\Component\Security\Http\HttpUtils $httpUtils
* #param array $options
* #param \Psr\Log\LoggerInterface $logger
* #param mixed $translator
*/
public function __construct(HttpKernelInterface $httpKernel, HttpUtils $httpUtils, array $options, LoggerInterface $logger = null, $translator = null)
{
parent::__construct($httpKernel, $httpUtils, $options, $logger);
$this->translator = $translator;
}
/**
* {#inheritDoc}
*/
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
if ($request->isXmlHttpRequest()) {
return new Response(json_encode(array(
'has_error' => true,
'message' => $this->translator->trans($exception->getMessage())
)));
}
return parent::onAuthenticationFailure($request, $exception);
}
Solution for symfony 4:
For symfony 4 we can do like this:
'message' => $this->translator->trans($exception->getMessageKey(), array(), 'security'),
Try changing your translation line to this:
$this->translator->trans($exception->getMessage(), array(), 'messages')
then create translation file in your Resources/translations folder for each language i.e. messages.en.yml, messages.fr.yml, etc...
your $exception->getMessage() should try and return translation keys to simplify things so for example it should return "error.name.missing" and then in the yml translation file you'd have:
error.name.missing: 'The name cannot be missing'
I've configured Laravel Scout and can use ::search() on my models.
The same models also use SoftDeletes. How can I combine the ::search() with withTrashed()?
The code below does not work.
MyModel::search($request->input('search'))->withTrashed()->paginate(10);
The below does work but does not include the trashed items.
MyModel::search($request->input('search'))->paginate(10);
Update 1
I found in the scout/ModelObserver that deleted items are made unsearchable. Which is a bummer; I wanted my users to be able to search through their trash.
Update 2
I tried using ::withoutSyncingToSearch, as suggested by #camelCase, which I had high hopes for, but this also didn't work.
$model = MyModel::withTrashed()->where('slug', $slug)->firstOrFail();
if ($model->deleted_at) {
$model->forceDelete();
} else {
MyModel::withoutSyncingToSearch(function () use ($model) {
$model->delete();
});
}
This caused an undefined offset when searching for a deleted item. By the way, I'm using the TNTSearch driver for Laravel Scout. I don't know if this is an error with TNTSearch or with Laravel Scout.
I've worked out a solution to your issue. I'll be submitting a pull request for Scout to hopefully get it merged with the official package.
This approach allows you to include soft deleted models in your search:
App\User::search('search query string')->withTrashed()->get();
To only show soft deleted models in your search:
App\User::search('search query string')->onlyTrashed()->get();
You need to modify 3 files:
Builder.php
In laravel\scout\src\Builder.php add the following:
/**
* Whether the search should include soft deleted models.
*
* #var boolean
*/
public $withTrashed = false;
/**
* Whether the search should only include soft deleted models.
*
* #var boolean
*/
public $onlyTrashed = false;
/**
* Specify the search should include soft deletes
*
* #return $this
*/
public function withTrashed()
{
$this->withTrashed = true;
return $this;
}
/**
* Specify the search should only include soft deletes
*
* #return $this
*/
public function onlyTrashed()
{
$this->onlyTrashed = true;
return $this;
}
/**
* Paginate the given query into a simple paginator.
*
* #param int $perPage
* #param string $pageName
* #param int|null $page
* #return \Illuminate\Contracts\Pagination\LengthAwarePaginator
*/
public function paginate($perPage = null, $pageName = 'page', $page = null)
{
$engine = $this->engine();
$page = $page ?: Paginator::resolveCurrentPage($pageName);
$perPage = $perPage ?: $this->model->getPerPage();
$results = Collection::make($engine->map(
$rawResults = $engine->paginate($this, $perPage, $page), $this->model, $this->withTrashed, $this->onlyTrashed
)); // $this->withTrashed, $this->onlyTrashed is new
$paginator = (new LengthAwarePaginator($results, $engine->getTotalCount($rawResults), $perPage, $page, [
'path' => Paginator::resolveCurrentPath(),
'pageName' => $pageName,
]));
return $paginator->appends('query', $this->query);
}
Engine.php
In laravel\scout\src\Engines\Engine.php modify the following:
/**
* Map the given results to instances of the given model.
*
* #param mixed $results
* #param \Illuminate\Database\Eloquent\Model $model
* #param boolean $withTrashed // New
* #return \Illuminate\Database\Eloquent\Collection
*/
abstract public function map($results, $model, $withTrashed, $onlyTrashed); // $withTrashed, $onlyTrashed is new
/**
* Get the results of the given query mapped onto models.
*
* #param \Laravel\Scout\Builder $builder
* #return \Illuminate\Database\Eloquent\Collection
*/
public function get(Builder $builder)
{
return Collection::make($this->map(
$this->search($builder), $builder->model, $builder->withTrashed, $builder->onlyTrashed // $builder->withTrashed, $builder->onlyTrashed is new
));
}
And finally, you just need to modify your relative search engine. I'm using Algolia, but the map method appears to be the same for TNTSearch.
AlgoliaEngine.php
In laravel\scout\src\Engines\AlgoliaEngine.php modify the map method to match the abstract class we modified above:
/**
* Map the given results to instances of the given model.
*
* #param mixed $results
* #param \Illuminate\Database\Eloquent\Model $model
* #param boolean $withTrashed // New
* #return \Illuminate\Database\Eloquent\Collection
*/
public function map($results, $model, $withTrashed, $onlyTrashed) // $withTrashed, $onlyTrashed is new
{
if (count($results['hits']) === 0) {
return Collection::make();
}
$keys = collect($results['hits'])
->pluck('objectID')->values()->all();
$modelQuery = $model->whereIn(
$model->getQualifiedKeyName(), $keys
);
if ($withTrashed) $modelQuery->withTrashed(); // This is where the query will include deleted items, if specified
if ($onlyTrashed) $modelQuery->onlyTrashed(); // This is where the query will only include deleted items, if specified
$models = $modelQuery->get()->keyBy($model->getKeyName());
return Collection::make($results['hits'])->map(function ($hit) use ($model, $models) {
$key = $hit['objectID'];
if (isset($models[$key])) {
return $models[$key];
}
})->filter();
}
TNTSearchEngine.php
/**
* Map the given results to instances of the given model.
*
* #param mixed $results
* #param \Illuminate\Database\Eloquent\Model $model
*
* #return Collection
*/
public function map($results, $model, $withTrashed, $onlyTrashed)
{
if (count($results['ids']) === 0) {
return Collection::make();
}
$keys = collect($results['ids'])->values()->all();
$model_query = $model->whereIn(
$model->getQualifiedKeyName(), $keys
);
if ($withTrashed) $model_query->withTrashed();
if ($onlyTrashed) $model_query->onlyTrashed();
$models = $model_query->get()->keyBy($model->getKeyName());
return collect($results['ids'])->map(function ($hit) use ($models) {
if (isset($models[$hit])) {
return $models[$hit];
}
})->filter();
}
Let me know how it works.
NOTE: This approach still requires you to manually pause syncing using the withoutSyncingToSearch method while deleting a model; otherwise the search criteria will be updated with unsearchable().
this is my solution.
// will return matched ids of my model instance
$searchResultIds = MyModel::search($request->input('search'))->raw()['ids'];
MyModel::whereId('id', $searchResultIds)->onlyTrashed()->get();