Why are characters inserted in my PHP link? - php

I created a module, but the link is not correct.
My site now shows :
/store/2?0=/cgv
The correct link should be :
/store/2/cgv
Why doesn't it work ? where is the error ?
What should I change in the code below, to get the link ?
<?php
namespace Drupal\commerce_agree_cgv\Plugin\Commerce\CheckoutPane;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Form\FormStateInterface;
use Drupal\commerce_checkout\Plugin\Commerce\CheckoutPane\CheckoutPaneBase;
use Drupal\commerce_checkout\Plugin\Commerce\CheckoutPane\CheckoutPaneInterface;
use Drupal\Core\Link;
use Drupal\Core\Url;
/**
* Provides the completion message pane.
*
* #CommerceCheckoutPane(
* id = "agree_cgv",
* label = #Translation("Agree CGV"),
* default_step = "review",
* )
*/
class AgreeCGV extends CheckoutPaneBase implements CheckoutPaneInterface {
/**
* {#inheritdoc}
*/
public function buildPaneForm(array $pane_form, FormStateInterface $form_state, array &$complete_form) {
$store_id = $this->order->getStoreId();
$pane_form['#attached']['library'][] = 'core/drupal.dialog.ajax';
$attributes = [
'attributes' => [
'class' => 'use-ajax',
'data-dialog-type' => 'modal',
'data-dialog-options' => Json::encode([
'width' => 800,
]),
],
];
$link = Link::createFromRoute(
$this->t('the general terms and conditions of business'),
'entity.commerce_store.canonical',
['commerce_store' => $store_id, '/cgv'],
$attributes
)->toString();
$pane_form['cgv'] = [
'#type' => 'checkbox',
'#default_value' => FALSE,
'#title' => $this->t('I have read and accept #cgv.', ['#cgv' => $link]),
'#required' => TRUE,
'#weight' => $this->getWeight(),
];
return $pane_form;
}
}

Because $link is not built correctly :
$link = Link::createFromRoute(
$this->t('the general terms and conditions of business'),
'entity.commerce_store.canonical',
['commerce_store' => $store_id, '/cgv'], # -> this is wrong
$attributes
)->toString();
$route_parameters: (optional) An associative array of parameter names
and values.
You did not specify any name for the 2nd route parameters, so the corresponding array key fallback to the first available numeric indice, that is 0, meaning [ '/cgv' ] becomes [ 0 => '/cgv' ] and you don't get the link you expected.
I think (if I understood your issue correctly) what you need is to define in the first place that specific route handling cgv's for a given commerce_store, that is with the /cgv appended :
$route_collection = new RouteCollection();
$route = (new Route('/commerce_store/{commerce_store}/cgv'))
->addDefaults([
'_controller' => $_controller,
'_title_callback' => $_title_callback,
])
->setRequirement('commerce_store', '\d+')
->setRequirement('_entity_access', 'commerce_store.view');
$route_collection->add('entity.commerce_store.canonical.cgv', $route);
... so that you can build links based on that specific route :
$link = Link::createFromRoute(
$this->t('the general terms and conditions of business'),
'entity.commerce_store.canonical.cgv',
['commerce_store' => $store_id],
$attributes
)->toString();

Related

Why does my custom code show warnings in the logs?

I have a site on Drupal 8.6 and Bootstrap 3.3.7
I created a custom module for the customer to accept the terms and conditions of the store when placing an order.
This displays a link to a checkbox before payment to display the terms and conditions in a modal window.
When I place an order here are the warnings in the logs (sorry my code is too long to be published here):
https://pastebin.com/1p5m1Ved
https://pastebin.com/XYbqDJje
https://pastebin.com/P93bStKh
Here is the file that created the problem :
<?php
namespace Drupal\commerce_marketplace_terms_and_conditions\Plugin\Commerce\CheckoutPane;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Form\FormStateInterface;
use Drupal\commerce_checkout\Plugin\Commerce\CheckoutPane\CheckoutPaneBase;
use Drupal\commerce_checkout\Plugin\Commerce\CheckoutPane\CheckoutPaneInterface;
use Drupal\Core\Link;
use Drupal\Core\Url;
/**
* Provides the completion message pane.
*
* #CommerceCheckoutPane(
* id = "marketplace_terms_and_conditions",
* label = #Translation("Marketplace Terms and Conditions"),
* default_step = "review",
* )
*/
class MarketplaceTermsAndConditions extends CheckoutPaneBase implements CheckoutPaneInterface {
/**
* {#inheritdoc}
*/
public function buildPaneForm(array $pane_form, FormStateInterface $form_state, array &$complete_form) {
$store_name = $this->order->getStore()->getName();
$store_id = $this->order->getStoreId();
$pane_form['#attached']['library'][] = 'core/drupal.dialog.ajax';
$attributes = [
'attributes' => [
'class' => 'use-ajax',
'data-dialog-type' => 'modal',
'data-dialog-options' => Json::encode([
'width' => auto
]),
],
];
$link = Link::fromTextAndUrl(
$this->t('terms and conditions of the store "#store_name"', ['#store_name' => $store_name]),
Url::fromUri("internal:/store/$store_id/cgv", $attributes)
)->toString();
$pane_form['marketplace_terms_and_conditions'] = [
'#type' => 'checkbox',
'#default_value' => FALSE,
'#title' => $this->t('I have read and accept #terms.', ['#terms' => $link]),
'#required' => TRUE,
'#weight' => $this->getWeight(),
];
return $pane_form;
}
}
What's wrong with my custom module and how to fix the problem ? Thank you
Change this to
'width' => auto
this
'width' => 'auto'
It is assuming it to be a constant, as you can see from the errors, it has to be either a variable or a string.

How to open a link in a new tab?

I created a custom module for Drupal 8
I want my link to open in a new tab, but it does not work.
Yet I added ['attributes' => ['target' => '_blank']]
Why does not it work ?
<?php
namespace Drupal\commerce_agree_cgv\Plugin\Commerce\CheckoutPane;
use Drupal\Core\Form\FormStateInterface;
use Drupal\commerce_checkout\Plugin\Commerce\CheckoutPane\CheckoutPaneBase;
use Drupal\commerce_checkout\Plugin\Commerce\CheckoutPane\CheckoutPaneInterface;
use Drupal\Core\Link;
use Drupal\Core\Url;
/**
* Provides the completion message pane.
*
* #CommerceCheckoutPane(
* id = "agree_cgv",
* label = #Translation("Agree CGV"),
* default_step = "review",
* )
*/
class AgreeCGV extends CheckoutPaneBase implements CheckoutPaneInterface {
/**
* {#inheritdoc}
*/
public function buildPaneForm(array $pane_form, FormStateInterface $form_state, array &$complete_form) {
$pane_form['cgv'] = [
'#type' => 'checkbox',
'#default_value' => FALSE,
'#title' => $this->t('I have read and accept the general terms and conditions of business.', ['#cgv' => Url::fromRoute('entity.commerce_store.canonical', ['commerce_store' => 3], ['attributes' => ['target' => '_blank']])->toString()]),
'#required' => TRUE,
'#weight' => $this->getWeight(),
];
return $pane_form;
}
}
Its normal URL::fromRoute return path, not link
you have 2 solution her:
1 - use Link
$options = ['absolute' => TRUE, 'attributes' => ['target' => '_blank']];
$link_object = Drupal\Core\Link::createFromRoute(t('the general terms and conditions of business'),
'entity.node.canonical', ['node' => "123"],
$options);
$link = $link_object->toString();
and the result : the general terms and conditions of business
or
2 - use URL
'#title' => $this->t('I have read and accept the general terms and conditions of business.', ['#cgv' => Url::fromRoute('entity.commerce_store.canonical', ['commerce_store' => 3], ['absolute' => true])->toString()]),
i hope this helps.

Different levels of URL with CakePHP routes

I have a site that needs to allow multiple URL structures. For example:
www.examplesite.com/people/add // <-- example company
www.otherexample.com/xyz/people/add // <-- "xyz" company (not location based)
www.otherexample.com/florida/abc/people/add //<-- "abc" company (location based)
Each URL should be able to detect which company it is based on the URL.
So far, I've been able to parse out the URL just fine to determine which company it is, but how to I add these extra /florida/abc/ parts to the routes to allow the rest of the app to work?
I've tried a number of things including setting a variable to the '/florida/abc' (or whatever it is) at the top of the routes file, then adding that before each route, but that doesn't handle every controller/action and seems very hit or miss/buggy.
I also use the admin prefix, so for example, it would also need to work like this:
www.otherexample.com/admin/florida/abc/people/add
My assumption is that I need to use the routes.php file, but I can't determine within that how I can make this happen.
I used that approach in the web application farm.ba (not more maintained by the owner).
What I did:
Create table "nodes" with fields id, slug, model, foreign_key, type, ..
Create custom route (1),(2) class that handles Node model
After save post, store and cache slug in Node Model
After deleting the post, delete the cache and node records
This works much like wordpress routing, allows you to enter custom slug, etc.
EDIT:
Create custom route class in App/Lib/Routing/Router/MultiRoute.php like:
<?php
App::uses('CakeRoute', 'Routing/Route');
/**
* MultiRoute
*/
class MultiRoute extends CakeRoute
{
public function parse($url)
{
// debug($url); '/florida/abc/people/add'
// Add custom params
$params = array(
'location' => null,
'company' => null,
'controller' => 'peoples',
);
$params += parent::parse($url);
// debug($params);
/**
* array(
* 'location' => null,
* 'company' => null,
* 'controller' => 'peoples',
* 'named' => array(),
* 'pass' => array(
* (int) 0 => 'florida', // location
* (int) 1 => 'abc', //company
* (int) 2 => 'people', // controller
* (int) 3 => 'add' // action, default index
* ),
* 'action' => 'index',
* 'plugin' => null
* )
*
*/
// reverse passed params
$pass = array_reverse($params['pass']);
// debug($pass);
/**
* array(
* (int) 0 => 'add',
* (int) 1 => 'people',
* (int) 2 => 'abc',
* (int) 3 => 'florida'
* )
*/
if(isset($pass[3])) { $params['location'] = $pass[3]; }
if(isset($pass[2])) { $params['company'] = $pass[2]; }
// if you need load model and find by slug, etc...
return $params;
}
public function match($url)
{
// implement your code
$params = parent::match($url);
return $params;
}
}
in routes.php
App::uses('MultiRoute', 'Lib/Routing/Route');
Router::connect('/admin/*',
array('admin' => true),// we set controller name in MultiRoute class
array('routeClass' => 'MultiRoute')
);
Router::connect('/*',
array(),// we set controller name in MultiRoute class
array('routeClass' => 'MultiRoute')
);
In your controller find results using extra request params, like:
$this->request->location;
$this->request->company;
I hope this is helpful.
Creating a route for each case seems the way to go:
Router::connect('/people/add', array('controller' => 'people', 'action' => 'add'));
Router::connect('/:company/people/add', array('controller' => 'people', 'action' => 'add'), array('pass' => array('company'), 'company' => '[a-z]+'));
Router::connect('/:location/:company/people/add', array('controller' => 'people', 'action' => 'add'), array('pass' => array('company', 'location'), 'company' => '[a-z]+', 'location' => '[a-z]+'));
Then the controller can receive these values:
public function add($company = '', $location = '') {
var_dump($company, $location); exit;
}
Mind the regex in routes and amend to match your incoming data.

Changing display logic for a field in sugarcrm

I have the following situation: Contacts without a first or last name, in fact, they only have a email address.
I can work with these contacts fine, but when I use the listview anywhere (for instance to show all contacts from a company) there now is no way to click through to the contact (normally you would click on the name).
I'm looking for a way to solve this, for instance by showing a clickable text like 'name not known', but can't figure out how to do this. I've been looking at the manual and in the files in the modules directory and the sugarfields dir, but can't quite figure it out.
The closest I got was in /sugarcrm/modules/Contacts/metadata/listviewdefs.php
where this piece of code resides:
$listViewDefs['Contacts'] = array(
'NAME' => array(
'width' => '20%',
'label' => 'LBL_LIST_NAME',
'link' => true,
'contextMenu' => array('objectType' => 'sugarPerson',
'metaData' => array('contact_id' => '{$ID}',
'module' => 'Contacts',
'return_action' => 'ListView',
'contact_name' => '{$FULL_NAME}',
'parent_id' => '{$ACCOUNT_ID}',
'parent_name' => '{$ACCOUNT_NAME}',
'return_module' => 'Contacts',
'return_action' => 'ListView',
'parent_type' => 'Account',
'notes_parent_type' => 'Account')
),
'orderBy' => 'name',
'default' => true,
'related_fields' => array('first_name', 'last_name', 'salutation', 'account_name', 'account_id'),
),
Somewhere there has to be a function that joins the first and lastname together...
Edit: I found a solution:
The actual concatenation function is in /sugarcrm/include/SugarObjects/templates/person/person.php and is called _create_proper_name_field()
I can modify the output for my specific case by adding something like this to the end of the function:
if (empty(trim($full_name))){
$full_name = 'Name unknown';
}
However, I would rather have a upgrade safe solution, so that will be the next challenge.
Don't edit the core because the next upgrade will break your SugarCRM instance. Use logic hooks to be upgrade safe:
create a file 'logic_hooks.php' in /custom/modules/Contacts/
In that file, add the followin code:
<?php
$hook_array['before_save'][] = Array(1,'logic_fill_name','custom/modules/Contacts/logic_hooks/logics.php','ContactLogics','logic_fill_name');
After you have done this. create the file 'logics.php' in /custom/modules/Contacts/logic_hooks.
In the logics.php file, add something like:
<?php
require_once 'include/SugarQuery/SugarQuery.php';
/**
* Class ContactLogics
*/
class ContactLogics {
/**
* #param $bean
* #param $event
* #param $arguments
*/
public function logic_fill_name($bean, $event, $arguments) {
if (empty(trim($bean->first_name)) && empty(trim($bean->last_name))){
$bean->last_name = 'Name unknown';
}
}
}
Now some explanation. When you edited a recordview and pressed the save button, the logic hook 'before_save' will be triggered. This code will change the full name to 'Name unknown' when the full name is empty. When the 'before_save' is executed, the actual save will take place.

Customizing layout to sfWidgetFormDoctrineChoice disable checkbox

Good morning,
In Symfony 1.4,
I tried to do what is explained here : Customizing layout to sfWidgetFormDoctrineChoice
But it doesn't work. Instead of adding a thumbnail, I just want to hide the <li> before the input, and in some condition disable/hide the checkbox input but show the label anyway.
When I add the renderer without argument, I get this error :
sfWidgetFormMySelectCheckbox requires the following options: 'choices'.
Here is my formatter code :
class sfWidgetFormMySelectCheckbox extends sfWidgetFormSelectCheckbox
{
public function configure($options = array(), $arguments = array())
{
parent::configure($options, $arguments);
}
protected function formatChoices($name, $value, $choices, $attributes)
{
.....
// new
$inputs[$id] = array(
'input' => sprintf('| test | %s',
$this->renderTag('input', array_merge($baseAttributes, $attributes))
),
'label' => $this->renderContentTag('label', self::escapeOnce($option), array('for' => $id)),
);
}
return call_user_func($this->getOption('formatter'), $this, $inputs);
}
}
And now the form where I call it :
$this->setWidget('aaa', new sfWidgetFormDoctrineChoice(array(
'model' => 'Aaa',
'expanded' => true,
'multiple' => true,
'add_empty' => false,
'query' => $query,
'renderer' => new sfWidgetFormMySelectCheckbox()
)));
Thanks for your help !
According to the docs you have to pass the choices option to the renderer object. Try something like this:
$this->setWidget('aaa', new sfWidgetFormDoctrineChoice(array(
'model' => 'Aaa',
'expanded' => true,
'multiple' => true,
'add_empty' => false,
'query' => $query,
)));
$this->widgetSchema['aaa']->setOption('renderer', new sfWidgetFormMySelectCheckbox(array(
'choices' => new sfCallable(array($this->widgetSchema['aaa'], 'getChoices'))
)));
So basically you want the renderer object get the choices from the parent widget. To do that you have to pass a sfCallable object which takes an array as the first argument in which you pass the instance of your parent widget and the name of the function getChoices.
Remember also that the expanded option is not used when you override the renderer.

Categories