Always the same view content on slim framework with twig after save changes - twig

I'm using Slim and Twig and I'm trying to change a view content, but it doesn't change after I saved the changes.
This is the controller:
<?php
use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;
require '../../vendor/autoload.php';
require './classes/connection.php';
$app = new \Slim\App;
$container = $app->getContainer();
$container['view'] = function ($container) {
$view = new \Slim\Views\Twig('../../views', [
'cache' => '../../views_cache'
]);
// Instantiate and add Slim specific extension
$router = $container->get('router');
$uri = \Slim\Http\Uri::createFromEnvironment(new \Slim\Http\Environment($_SERVER));
$view->addExtension(new \Slim\Views\TwigExtension($router, $uri));
return $view;
};
$app->get('/hello/{name}', function (Request $request, Response $response, array $args) {
/*$name = $args['name'];
$response->getBody()->write("Hello, $name");
echo getcwd();
return $response;*/
return $this->view->render($response, 'login.html', [
'name' => $args['name']
]);
});
$app->post('/login', function (Request $request, Response $response, array $args) {
$post_data = $request->getParsedBody();
if (isset($post_data['user']) && isset($post_data['pass'])) {
$mongo = new Connection();
$conn = $mongo->getConnection();
$collection = $conn->prueba->users;
$result = $collection->find(['user' => $post_data['user']])->toArray();
$dbUser = $result[0]['username'];
$dbPass = $result[0]['password'];
if (password_verify($post_data['pass'], $dbPass)) {
echo '¡La contraseña es válida!';
} else {
echo 'La contraseña no es válida.';
}
} else {
return $response->withJson(array('login_status' => 'ko'));
}
});
$app->run();
What I missed to see the view changes? I think it's something about compile view but I'm not sure. It's the first time I use this framework.

Related

Silverstripe 4 PaginatedList get Page-number - Link (Backlink)

In Silverstripe 4 I have a
DataObject 'PublicationObject',
a 'PublicationPage' and
a 'PublicationPageController'
PublicationObjects are displayed on PublicationPage through looping a PaginatedList. There is also a Pagination, showing the PageNumbers and Prev & Next - Links.
The Detail of PublicationObject is shown as 'Dataobject as Pages' using the UrlSegment.
If users are on the Detail- PublicationObject i want them to get back to the PublicationPage (the paginated list) with a Back - Link.
Two situations:
The user came from PublicationPage
and
The User came from a Search - Engine (Google) directly to the
DataObject- Detail.
I have done this like so:
$parentPage = Page::get()->Filter(array('UrlSegment' => $request->param('pageID')))->First();
$back = $_SERVER['HTTP_REFERER'];
if ((isset($_SERVER['HTTP_REFERER']) && !empty($_SERVER['HTTP_REFERER']))) {
if (strtolower(parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST)) != strtolower($_SERVER['HTTP_HOST'])) {
// referer not from the same domain
$back = $parentPage->Link();
}
}
Thats not satisfying.
Question:
How do i get the Pagination - Link ( e.g: ...publicationen?start=20 ) when we are on the Detail - DataObject? How can we find the Position of the current Dataobject in that paginatedList in correlation with the Items per Page? (The Page- Link This Dataobject is on)
<?php
use SilverStripe\Control\Controller;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\View\Requirements;
use SilverStripe\Core\Convert;
use SilverStripe\SiteConfig\SiteConfig;
use SilverStripe\ORM\PaginatedList;
use SilverStripe\Control\Director;
use SilverStripe\ORM\DataObject;
use SilverStripe\ErrorPage\ErrorPage;
use SilverStripe\Dev\Debug;
use SilverStripe\Dev\Backtrace;
class PublicationPageController extends PageController
{
private static $allowed_actions = ['detail'];
private static $url_handlers = array(
);
public static $current_Publication_id;
public function init() {
parent::init();
}
public function detail(HTTPRequest $request)
{
$publication = PublicationObject::get_by_url_segment(Convert::raw2sql($request->param('ID')));
if (!$publication) {
return ErrorPage::response_for(404);
}
// HERE I WANT TO FIND THE POSITION OF THE DATAOBJECT IN THE PAGINATEDLIST OR RATHER THE PAGE - LINK THIS DATAOBJECT IS IN
//$paginatedList = $this->getPaginatedPublicationObjects();
//Debug::show($paginatedList->find('URLSegment', Convert::raw2sql($request->param('ID'))));
//Debug::show($paginatedList->toArray());
$parentPage = Page::get()->Filter(array('UrlSegment' => $request->param('pageID')))->First();
$back = $_SERVER['HTTP_REFERER'];
if ((isset($_SERVER['HTTP_REFERER']) && !empty($_SERVER['HTTP_REFERER']))) {
if (strtolower(parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST)) != strtolower($_SERVER['HTTP_HOST'])) {
// referer not from the same domain
$back = $parentPage->Link();
}
}
static::$current_Publication_id = $publication->ID;
$id = $publication->ID;
if($publication){
$arrayData = array (
'Publication' => $publication,
'Back' => $back,
'SubTitle' => $publication->Title,
'MetaTitle' => $publication->Title,
);
return $this->customise($arrayData)->renderWith(array('PublicationDetailPage', 'Page'));
}else{
return $this->httpError(404, "Not Found");
}
}
public function getPaginatedPublicationObjects()
{
$list = $this->PublicationObjects()->sort('SortOrder');
return PaginatedList::create($list, $this->getRequest()); //->setPageLength(4)->setPaginationGetVar('start');
}
}
EDIT:
is there a more simple solution ? than this ? :
public function detail(HTTPRequest $request)
{
$publication = PublicationObject::get_by_url_segment(Convert::raw2sql($request->param('ID')));
if (!$publication) {
return ErrorPage::response_for(404);
}
//*******************************************************
// CREATE BACK - LINK
$paginatedList = $this->getPaginatedPublicationObjects();
$dO = PublicationObject::get();
$paginationVar = $paginatedList->getPaginationGetVar();
$sqlQuery = new SQLSelect();
$sqlQuery->setFrom('PublicationObject');
$sqlQuery->selectField('URLSegment');
$sqlQuery->setOrderBy('SortOrder');
$rawSQL = $sqlQuery->sql($parameters);
$result = $sqlQuery->execute();
$list = [];
foreach($result as $row) {
$list[]['URLSegment'] = $row['URLSegment'];
}
$list = array_chunk($list, $paginatedList->getPageLength(), true);
$start = '';
$back = '';
$i = 0;
$newArray = [];
foreach ($list as $k => $subArr) {
$newArray[$i] = $subArr;
unset($subArr[$k]['URLSegment']);
foreach ($newArray[$i] as $key => $val) {
if ($val['URLSegment'] === Convert::raw2sql($request->param('ID'))) {
$start = '?'.$paginationVar.'='.$i;
}
}
$i = $i + $paginatedList->getPageLength();
}
$back = Controller::join_links($this->Link(), $start);
// END CREATE BACK - LINK
//*****************************************************
static::$current_Publication_id = $publication->ID;
$id = $publication->ID;
if($publication){
$arrayData = array (
'Publication' => $publication,
'Back' => $back,
'MyLinkMode' => 'active',
'SubTitle' => $publication->Title,
'MetaTitle' => $publication->Title,
);
return $this->customise($arrayData)->renderWith(array('PublicationDetailPage', 'Page'));
}else{
return $this->httpError(404, "Not Found");
}
}
You can simply pass the page number as a get var in the URL from the PublicationPage. The detail() method can then grab the get var and if there's a value for the page number passed in, add it to the back link's URL.
In the template:
<% loop $MyPaginatedList %>
Click me
<% end_loop %>
In your controller's detail() method:
$pageNum = $request->getVar('onPage');
if ($pageNum) {
// Add the pagenum to the back link here
}
Edit
You've expressed that you want this to use the page start offset (so you can just plug it into the url using ?start=[offset]), and that you want an example that also covers people coming in from outside your site. I therefore propose the following:
Do as above, but using PageStart instead of CurrentPage - this will mean you don't have to re-compute the offset any time someone clicks the link from your paginated list.
If there is no onPage (or pageOffset as I've renamed it below, assuming you'll use PageStart) get variable, then assuming you can ensure your SortOrder values are all unique, you can check how many items are in the list before the current item - which gives you your item offset. From that you can calculate what page it's on, and what the page offset is for that page, which is your start value.
public function detail(HTTPRequest $request)
{
$publication = PublicationObject::get_by_url_segment(Convert::raw2sql($request->param('ID')));
$paginatedList = $this->getPaginatedPublicationObjects();
// Back link logic starts here
$pageOffset = $request->getVar('pageOffset');
if ($pageOffset === null) {
$recordOffset = $paginatedList->filter('SortOrder:LessThan', $publication->SortOrder)->count() + 1;
$perPage = $paginatedList->getPageLength();
$page = floor($recordOffset / $perPage) + 1;
$pageOffset = ($page - 1) * $perPage;
}
$back = $this->Link() . '?' . $paginatedList->getPaginationGetVar() . '=' . $pageOffset;
// Back link logic ends here
//.....
}

Resend specific order state emails e.g. order delivery shipped in shopware 6

I'm wondering if there is any simpler solution in sending specific order state emails.
Currently i use the following code to send e.g. the "order delivery shipped" email again.
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('orderNumber', $orderNumber));
$criteria->addAssociation('orderCustomer.salutation');
$criteria->addAssociation('orderCustomer.customer');
$criteria->addAssociation('stateMachineState');
$criteria->addAssociation('deliveries.shippingMethod');
$criteria->addAssociation('deliveries.shippingOrderAddress.country');
$criteria->addAssociation('deliveries.shippingOrderAddress.countryState');
$criteria->addAssociation('salesChannel');
$criteria->addAssociation('language.locale');
$criteria->addAssociation('transactions.paymentMethod');
$criteria->addAssociation('lineItems');
$criteria->addAssociation('currency');
$criteria->addAssociation('addresses.country');
$criteria->addAssociation('addresses.countryState');
/** #var OrderEntity|null $order */
$order = $this->orderRepository->search($criteria, $context)->first();
$context = new Context(new SystemSource());
$salesChannelContext = $this->orderConverter->assembleSalesChannelContext($order, $context)->getContext();
$salesChannelContext->addExtension(
MailSendSubscriber::MAIL_CONFIG_EXTENSION,
new MailSendSubscriberConfig(false)
);
$event = new OrderStateMachineStateChangeEvent(
'state_enter.order_delivery.state.shipped',
$order,
$salesChannelContext
);
$flowEvent = new FlowEvent('action.mail.send', new FlowState($event), [
'recipient' => [
'data' => [],
'type' => 'default',
],
'mailTemplateId' => $mailTemplateId,
]);
$this->sendMailAction->handle($flowEvent);
Given that you don't actually want to change the state, this looks like a good solution.
If you actually needed to programmatically change the state you could use the StateMachineRegistry service.
$transition = new Transition(OrderDeliveryDefinition::ENTITY_NAME, $orderDeliveryId, StateMachineTransitionActions::ACTION_SHIP, 'stateId');
$this->stateMachineRegistry->transition($transition, Context::createDefaultContext());
However if the current state is equal to the state you want to transition to, then an UnnecessaryTransitionException is expected to be thrown. You'd have to change the state back to a previous one before doing this and at that point your code is probably less trouble, if all you want to do is to repeat sending that mail.
Do you want to resend the mail or regenerate it? If you want to resend the mail you could use this out-of-the-box solution from FriendsOfShopware -> https://store.shopware.com/en/frosh97876597450f/mail-archive.html
You can build and send the mail with the Shopware\Core\Content\Mail\Service\MailService by yourself. But is way more code then just triggering it with a state change.
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('mailTemplateTypeId', self::ORDER_CONFIRMATION_MAIL_TYPE_ID));
$criteria->addAssociation('media.media');
$criteria->setLimit(1);
if ($order->getSalesChannelId()) {
$criteria->addFilter(new EqualsFilter('mail_template.salesChannels.salesChannel.id', $order->getSalesChannelId()));
/** #var MailTemplateEntity|null $mailTemplate */
$mailTemplate = $this->mailTemplateRepository->search($criteria, $this->context)->first();
// Fallback if no template for the saleschannel is found
if (null === $mailTemplate) {
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('mailTemplateTypeId', self::ORDER_CONFIRMATION_MAIL_TYPE_ID));
$criteria->addAssociation('media.media');
$criteria->setLimit(1);
/** #var MailTemplateEntity|null $mailTemplate */
$mailTemplate = $this->mailTemplateRepository->search($criteria, $this->context)->first();
}
} else {
/** #var MailTemplateEntity|null $mailTemplate */
$mailTemplate = $this->mailTemplateRepository->search($criteria, $this->context)->first();
}
if (null === $mailTemplate) {
return;
}
$data = new DataBag();
$data->set('recipients', [
$order->getOrderCustomer()->getEmail() => $order->getOrderCustomer()->getFirstName() . ' ' . $order->getOrderCustomer()->getLastName(),
]);
$data->set('senderName', $mailTemplate->getTranslation('senderName'));
$data->set('salesChannelId', $order->getSalesChannelId());
$data->set('templateId', $mailTemplate->getId());
$data->set('customFields', $mailTemplate->getCustomFields());
$data->set('contentHtml', $mailTemplate->getTranslation('contentHtml'));
$data->set('contentPlain', $mailTemplate->getTranslation('contentPlain'));
$data->set('subject', $mailTemplate->getTranslation('subject'));
$data->set('mediaIds', []);
$attachments = [];
if (null !== $mailTemplate->getMedia()) {
foreach ($mailTemplate->getMedia() as $mailTemplateMedia) {
if (null === $mailTemplateMedia->getMedia()) {
continue;
}
if (null !== $mailTemplateMedia->getLanguageId() && $mailTemplateMedia->getLanguageId() !== $order->getLanguageId()) {
continue;
}
$attachments[] = $this->mediaService->getAttachment(
$mailTemplateMedia->getMedia(),
$this->context
);
}
}
if (!empty($attachments)) {
$data->set('binAttachments', $attachments);
}
try {
if (
null === $this->mailService->send(
$data->all(),
$this->context,
$this->getTemplateData($order)
)
) {
throw new \Exception('Could not render mail template');
}
$this->orderMailRepository->upsert([[
'orderId' => $order->getId(),
'confirmationSend' => true,
]], $this->context);
} catch (\Exception $e) {
$this->logger->error(sprintf(
'Could not send mail for order %s: %s',
$order->getOrderNumber(),
$e->getMessage()
));
}
}

In EasyAdmin 3 configureActions method how do I get the current entity?

I wish to add an action to another to the index action with a predefined filter.
To build the filter I need the get current entity in the configureActions method.
public function configureActions(Actions $actions): Actions
{
parent::configureActions($actions);
$adminUrlGenerator = $this->get(AdminUrlGenerator::class);
$url = $adminUrlGenerator
->setController(SiteCrudController::class)
->setAction(Action::INDEX)
->set('filters', [
'agent' => [
'comparison' => '=',
'value' => 2194, // How to get current entity here??
]
])
->generateUrl()
;
$viewRelatesSites = Action::new('viewRelatedSites', 'Sites', 'fa fa-file-invoice')
->linkToUrl($url)
;
$actions->add(Action::DETAIL, $viewRelatesSites);
$actions->add(Action::EDIT, $viewRelatesSites);
return $actions;
}
}
How can I get the entity here?
To get the current entity the AdminContext is needed.
The best place to get this with the entity set is in a BeforeCrudActionEvent.
final class AgentCrudActionEventListen implements EventSubscriberInterface
{
private AdminUrlGenerator $adminUrlGenerator;
private AdminContextProvider $adminContextProvider;
public function __construct(AdminUrlGenerator $adminUrlGenerator, AdminContextProvider $adminContextProvider)
{
$this->adminUrlGenerator = $adminUrlGenerator;
$this->adminContextProvider = $adminContextProvider;
}
public static function getSubscribedEvents(): array
{
return [
BeforeCrudActionEvent::class => 'onBeforeCrudActionEvent',
];
}
public function onBeforeCrudActionEvent(BeforeCrudActionEvent $event): void
{
$crud = $event->getAdminContext()->getCrud();
if ($crud->getControllerFqcn() !== AgentCrudController::class) {
return;
}
$entity = $this->adminContextProvider->getContext()->getEntity();
if (!$entity) {
return;
}
$url = $this->adminUrlGenerator
->setController(SiteCrudController::class)
->setAction(Action::INDEX)
->set('filters', [
'agent' => [
'comparison' => '=',
'value' => $entity->getPrimaryKeyValue(),
]
])
->generateUrl()
;
$viewRelatedSites = Action::new('viewRelatedSites', 'Sites', 'fa fa-file-invoice')
->linkToUrl($url)
;
$actions = $crud->getActionsConfig();
$actions->appendAction(Action::DETAIL, $viewRelatedSites->getAsDto());
$actions->appendAction(Action::EDIT, $viewRelatedSites->getAsDto());
}
}
After a day looking for the current entity into my AbstractCrudController, I found this :
$currentEntity = $this->getContext()->getEntity()->getInstance();
Since EasyAdmin 3 events are discouraged per the docs:
Starting from EasyAdmin 3.0 everything is defined with PHP. That's why it's easier to customize backend behavior overloading PHP classes and methods and calling to your own services. However, the events still remain in case you want to use them.
public function configureActions(Actions $actions): Actions
{
// Create your action
$viewRelatedSites = Action::new('viewRelatedSites', 'Sites', 'fa fa-file-invoice');
//set the link using a string or a callable (function like its being used here)
$viewRelatedSites->linkToUrl(function($entity) {
$adminUrlGenerator = $this->get(AdminUrlGenerator::class);
$url = $adminUrlGenerator
->setController(SiteCrudController::class)
->setAction(Action::INDEX)
->set('filters', [
'agent' => [
'comparison' => '=',
'value' => $entity->getId()
]
])
->generateUrl();
return $url;
});
$actions->add(Crud::PAGE_INDEX, $viewRelatedSites);
return $actions;
}

Overwrite backend template in bolt.cms

I am trying to overwrite a template file located in vendor/bolt/bolt/app/view/twig/editcontent/fields/_block.twig (I want to replace the "block selection" dropdown). Regarding to #1173, #1269, #5588, #3768 and #5102 this is not supported by default so I have to write a extension for this. So I tried this:
BackendBlockSelectionExtension:
namespace Bundle\Site;
use Bolt\Filesystem\Adapter\Local;
use Bolt\Filesystem\Filesystem;
use Silex\Application;
use Bolt\Extension\SimpleExtension;
class BackendBlockSelectionExtension extends SimpleExtension
{
public function getServiceProviders()
{
return [
$this,
new BackendBlockSelectionProvider(),
];
}
}
BackendBlockSelectionProvider:
namespace Bundle\Site;
use Bolt\Filesystem\Adapter\Local;
use Bolt\Filesystem\Filesystem;
use Silex\Application;
use Silex\ServiceProviderInterface;
class BackendBlockSelectionProvider implements ServiceProviderInterface
{
public function register(Application $app)
{
$side = $app['config']->getWhichEnd();
if ($side == 'backend') {
$path = __DIR__ . '/App/templates/Backend';
$filesystem = $app['filesystem'];
$filesystem->mountFilesystem('bolt', new Filesystem(new Local($path)));
$app['twig.loader.bolt_filesystem'] = $app->share(
$app->extend(
'twig.loader.bolt_filesystem',
function ($filesystem, $app) {
$path = __DIR__ . 'src/App/templates/Backend/';
$filesystem->prependPath($path, 'bolt');
return $filesystem;
}
)
);
}
}
public function boot(Application $app)
{
}
}
This seems to do the job, but I got an error I don't understand at all: The "bolt://app/theme_defaults" directory does not exist.
So my final question is: Does anyone have some example code how to overwrite/modify vendor/bolt/bolt/app/view/twig/editcontent/fields/_block.twig without touching the vendor folder?
This should be much simplier than this.
In your extension class overwrite protected function registerTwigPaths() function like this:
protected function registerTwigPaths()
{
if ($this->getEnd() == 'backend') {
return [
'view' => ['position' => 'prepend', 'namespace' => 'bolt']
];
}
return [];
}
private function getEnd()
{
$backendPrefix = $this->container['config']->get('general/branding/path');
$end = $this->container['config']->getWhichEnd();
switch ($end) {
case 'backend':
return 'backend';
case 'async':
// we have async request
// if the request begin with "/admin" (general/branding/path)
// it has been made on backend else somewhere else
$url = '/' . ltrim($_SERVER['REQUEST_URI'], $this->container['paths']['root']);
$adminUrl = '/' . trim($backendPrefix, '/');
if (strpos($url, $adminUrl) === 0) {
return 'backend';
}
default:
return $end;
}
}
Now you can crete a view directory in your extensions directory in which you can define templates in structure like in Bolt's default. I would start with copy and overwrite.

kohana 3 incorporate file validate in orm

I cant figure out how to add file validation code to the orm model
As the code is now the text inputs are validated in the orm, but the file has no validation on it at all. I was wondering if it was possible to merge the file validation in the 'profile' model where I have the text validation?
Here is the code in my controller
public function action_public_edit()
{
$content = View::factory('profile/public_edit')
->bind('user', $user)
->bind('profile', $profile)
->bind('errors', $errors);
$user = Auth::instance()->get_user();
$profile = $user->profiles;
$this->template->content = $content;
if ($_POST)
{
if ($profile->values($_POST)->check())
{
$picture = Upload::save($_FILES['profile_picture']);
// Resize, sharpen, and save the image
Image::factory($picture)
->resize(100, 100, Image::WIDTH)
->save();
$profile->profile_picture = basename($picture);
$profile->save();
$redirect = "profile/private";
Request::instance()->redirect($redirect);
}
else
{
$errors = $profile->validate()->errors('profile/public_edit');
}
}
}
And the ORM model
protected $_belongs_to = array('user' => array());
protected $_rules = array(
'first_name' => array(
'not_empty' => NULL,
),
'last_name' => array(
'not_empty' => NULL,
),
You don't need to check() if you save() later - save will do the checking and throw ORM_Validation_Exception if validation is not passed. I suggest to create a function in your model for saving - move the lines after check() to this model. In controller you'll only need to write:
if ($this->request->method() === 'POST')
{
$errors = $profile->upload('profile_picture');
if (empty($errors))
{
try
{
$profile->your_save_function();
$this->request->redirect('profile/private');
}
catch (ORM_Validation_Exception $e)
{
$errors = $e->errors('profile');
}
}
}
In your model:
public function your_save_function()
{
// Do your stuff before uploading the picture
// Save the model
$this->save();
}
public function upload($filename)
{
// Set the default upload directory
Upload::$default_directory = 'your_path';
// Validate the file first
$validation = Validation::factory($_FILES)
->rules($filename, array(
array('not_empty'),
array('Upload::not_empty'),
array('Upload::valid'),
array('Upload::type', array(':value', array('your_valid_extensions'))),
array('Upload::size', array(':value', 'your_max_size')),
));
// Validate and upload OK
if ($validation->check())
{
$picture = Upload::save($_FILES[$filename]);
// Resize, sharpen, and save the image
Image::factory($picture)
->resize(100, 100, Image::WIDTH)
->save();
$this->profile_picture = basename($picture);
}
else
{
return $validation->errors('upload');
}
}
Much simpler and cleaner code.

Resources