As of Beta 1 and possibly a bit earlier, hook_permission() is gone. In its place, there is yet another YAML file.
Drupal 7 optimizely.module file,
function optimizely_permission() {
return array(
'administer optimizely' => array(
'title' => t('Administer Optimizely module'),
'description' =>
t('Administer access to everything in module'),
'restrict access' => TRUE,
),
);
}
Drupal 8 optimizely.permissions.yml file,
administer optimizely:
title: 'Administer Optimizely module'
description: 'Administer access to everything in module'
restrict access: true
Exactly the same pieces of information are provided in the new definition as in the old. However, there are no calls to the t() function for translation purposes. In the source article, there is a comment that "We can then make sure static permissions run strings through t()", so it sounds like the intent is for t() to be called automatically as part of the processing of the file.
Obsolete post on hook_permission() is at http://optimizely-to-drupal-8.blogspot.com/2014/05/hookpermission-is-same-as-in-d7.html
Sources:
Defining permissions via $module.permissions.yml
https://www.drupal.org/node/2311427
Nov 9, 2014
Nov 8, 2014
Beta 1 released, more changes
It's great news that Drupal 8 Beta 1 was released. It implies that the API's have reached a reasonably stable (but not immutable) state.
In moving from the final alpha 15 to beta 1, I stumbled across the following.
(1) The url() function has been removed. In its place, you can use \Drupal::url() which takes a route name, not a path. For example, instead of
url('/admin/config/system/optimizely/settings')
use this call, where optimizely.settings is a route that is defined in the module's .routing.yml file.
\Drupal::url('optimizely.settings')
Where the route takes parameters, pass a second argument to url() that is a keyed array which provides the names and values of those parameters. For example, for the following route,
optimizely.delete.oid:
path: /admin/config/system/optimizely/delete/{oid}
# The rest of the route definition ...
you can make a call such as,
\Drupal::url('optimizely.delete.oid', array('oid' => $oid))
Caveat: you cannot use this function in hook_install(). Doing so will cause the install to bail out and leave things in a weird state that will likely leave you befuddled and frustrated. I got the generic, unhelpful message: The website has encountered an error. Please try again later.
So in hook_install() I removed the links in a message that is displayed upon completion of the installation. Some plain explanatory text is provided instead.
(2) The l() function takes a Url object as its second argument instead of a path. It is now in the Drupal class. Instead of
l(t('Cancel'), 'admin/config/system/optimizely')
use something like this,
\Drupal::l(t('Cancel'), new Url('optimizely.settings'))
However, same caveat as above. You can't use this function in hook_install() or it will bomb out and leave the install in a partially completed state. Not good.
Sources:
Drupal 8.0.0 beta 1 released
https://www.drupal.org/drupal-8.0.0-beta1
Remove most remaining url() calls
https://www.drupal.org/node/2340251
In moving from the final alpha 15 to beta 1, I stumbled across the following.
(1) The url() function has been removed. In its place, you can use \Drupal::url() which takes a route name, not a path. For example, instead of
url('/admin/config/system/optimizely/settings')
use this call, where optimizely.settings is a route that is defined in the module's .routing.yml file.
\Drupal::url('optimizely.settings')
Where the route takes parameters, pass a second argument to url() that is a keyed array which provides the names and values of those parameters. For example, for the following route,
optimizely.delete.oid:
path: /admin/config/system/optimizely/delete/{oid}
# The rest of the route definition ...
you can make a call such as,
\Drupal::url('optimizely.delete.oid', array('oid' => $oid))
Caveat: you cannot use this function in hook_install(). Doing so will cause the install to bail out and leave things in a weird state that will likely leave you befuddled and frustrated. I got the generic, unhelpful message: The website has encountered an error. Please try again later.
So in hook_install() I removed the links in a message that is displayed upon completion of the installation. Some plain explanatory text is provided instead.
(2) The l() function takes a Url object as its second argument instead of a path. It is now in the Drupal class. Instead of
l(t('Cancel'), 'admin/config/system/optimizely')
use something like this,
\Drupal::l(t('Cancel'), new Url('optimizely.settings'))
However, same caveat as above. You can't use this function in hook_install() or it will bomb out and leave the install in a partially completed state. Not good.
Sources:
Drupal 8.0.0 beta 1 released
https://www.drupal.org/drupal-8.0.0-beta1
Remove most remaining url() calls
https://www.drupal.org/node/2340251
Sep 30, 2014
FormStateInterface, and drupal_valid_path()
As we move into Drupal 8, alpha 15, a few more API changes have come along for the Optimizely module.
(1) For objects whose class implements interface FormStateInterface, you can no longer index into the object as though it were an array as in D7.
Instead, there are a number of methods to get and set values. In our case, we only need to retrieve values from simple form text fields.
So, for example,
function validateForm(array &$form, FormStateInterface $form_state)
{
$proj_code = $form_state['values']['optimizely_project_code'];
is replaced with,
function validateForm(array &$form, FormStateInterface $form_state)
{
$proj_code = $form_state->getValue('optimizely_project_code');
This change broke the code in a lot of the methods for validating and for submitting forms.
(2) Regarding drupal_valid_path(), the function has been removed. So,
if (drupal_valid_path($path, TRUE) == FALSE) {
is replaced with,
if (\Drupal::pathValidator()->isValid($path) == FALSE) {
Sources:
Replace instances of deprecated drupal_valid_path with PathValidator
https://www.drupal.org/node/2303361
(1) For objects whose class implements interface FormStateInterface, you can no longer index into the object as though it were an array as in D7.
Instead, there are a number of methods to get and set values. In our case, we only need to retrieve values from simple form text fields.
So, for example,
function validateForm(array &$form, FormStateInterface $form_state)
{
$proj_code = $form_state['values']['optimizely_project_code'];
is replaced with,
function validateForm(array &$form, FormStateInterface $form_state)
{
$proj_code = $form_state->getValue('optimizely_project_code');
This change broke the code in a lot of the methods for validating and for submitting forms.
(2) Regarding drupal_valid_path(), the function has been removed. So,
if (drupal_valid_path($path, TRUE) == FALSE) {
is replaced with,
if (\Drupal::pathValidator()->isValid($path) == FALSE) {
Sources:
Replace instances of deprecated drupal_valid_path with PathValidator
https://www.drupal.org/node/2303361
Sep 7, 2014
url() function in Twig templates
As of Drupal 8 alpha 14, several relative links that had been hard-coded in Twig templates were no longer working.
<li>Add the account ID to the
<a href="/admin/config/system/optimizely/settings">
Account Info</a> settings page</li>
The url constructed by the theming system did not include the root of my development site, which is opti. Instead, it would come out as
localhost/admin/config/system/optimizely/settings
Dropping the leading slash character in the href also produced an incorrect result. Here, add_update is the last part of the path for the page that is rendered using the template.
localhost/opti/admin/config/system/optimizely/add_update/admin/config/system/optimizely/settings
After some poking around, I found the Twig url() function, which does the trick.
<li>Add the account ID to the
<a href="{{ url('optimizely.settings') }}">
Account Info</a> settings page</li>
This produces the desired url, which is
localhost/opti/admin/config/system/optimizely/settings
But note that the parameter to url() is the name of a route, not a path. In the optimizely.routing.yml file, the route is defined as
optimizely.settings:
path: /admin/config/system/optimizely/settings
defaults:
_form: \Drupal\optimizely\AccountSettingsForm
_title: Optimizely
requirements:
_permission: administer optimizely
The path() and the l() functions might also work, but I haven't tried them. See the source articles.
Sources:
Creating and Using Templates
http://symfony.com/doc/current/book/templating.html
Create best practices about use of the l() function and the url() function in templates
https://www.drupal.org/node/1812562
<li>Add the account ID to the
<a href="/admin/config/system/optimizely/settings">
Account Info</a> settings page</li>
The url constructed by the theming system did not include the root of my development site, which is opti. Instead, it would come out as
localhost/admin/config/system/optimizely/settings
Dropping the leading slash character in the href also produced an incorrect result. Here, add_update is the last part of the path for the page that is rendered using the template.
localhost/opti/admin/config/system/optimizely/add_update/admin/config/system/optimizely/settings
After some poking around, I found the Twig url() function, which does the trick.
<li>Add the account ID to the
<a href="{{ url('optimizely.settings') }}">
Account Info</a> settings page</li>
This produces the desired url, which is
localhost/opti/admin/config/system/optimizely/settings
But note that the parameter to url() is the name of a route, not a path. In the optimizely.routing.yml file, the route is defined as
optimizely.settings:
path: /admin/config/system/optimizely/settings
defaults:
_form: \Drupal\optimizely\AccountSettingsForm
_title: Optimizely
requirements:
_permission: administer optimizely
The path() and the l() functions might also work, but I haven't tried them. See the source articles.
Sources:
Creating and Using Templates
http://symfony.com/doc/current/book/templating.html
Create best practices about use of the l() function and the url() function in templates
https://www.drupal.org/node/1812562
Sep 2, 2014
Twig inline_template render elements
Updating to D8 alpha 14 resulted in one of the module's pages showing a form field of url paths literally like this,
<ul><li>admin/config/system</li></ul>
In other words, there was HTML markup that was being escaped too many times and being rendered as content. Somehow, this issue appeared because of changes in alpha 14.
Researching this, I found two things. First, the core maintainers have been grappling with the same problem in the core UI itself. That's a bit of consolation for a newcomer like me swimming in the ocean of Drupal.
Second, for render arrays there is a new recognized value for the #type property. That value is inline_template. The idea is that when you don't want to create a full-blown Twig template file, this #type enables you to define a snippet of template in the render array itself.
♦ Here's a simplistic example.
$render_title = array(
'#type' => 'inline_template',
'#template' => '{{ title }}',
'#context' => array('title' => 'My Project'),
);
// Insert this array into the full form.
$form['#project_title'] = $render_title;
The #template property provides the template using Twig syntax. The double braces mean print the value of the enclosed Twig variable, in this case, title.
The #context property provides a mapping from Twig variables to their values.
This example doesn't do anything except print the value of a variable. Depending on what might be in the value, you could just implement that without the use of a template.
♦ Here's something more useful and interesting.
$render_link = array(
'#type' => 'inline_template',
'#template' => '<a href="{{ url }}">{{ update }}</a>',
'#context' => array(
'update' => $this->t('Update'),
'url' => url('/admin/config/system/optimizely/add_update/'),
),
);
$form['#admin_link'] = $render_link;
The template consists of markup for a link to be rendered, using a translated word and a string that is built dynamically.
♦ Lastly, Twig templates allow for looping so that you can iterate over an array.
$paths = array( ... );
$render_paths = array(
'#type' => 'inline_template',
'#template' => '<ul>' .
'{% for p in paths %}' .
'<li>{{ p }}</li>' .
'{% endfor %}' .
'</ul>',
'#context' => array('paths' => $paths),
);
$form['#paths'] = $render_paths;
The Twig for construct is like foreach in PHP. This builds an HTML unordered list with list items that are determined at runtime.
♦ Getting back to the issue that started me down this road, the original code was building snippets of markup and inserting the strings directly into the form render array. What was intended as markup would be escaped and displayed as content.
$form['#paths'] = '<ul><li>some path</li></ul>';
With use of an inline template, the markup is handled correctly.
$form['#paths'] = array(
'#type' => 'inline_template',
'#template' => '<ul><li>some path</li></ul>',
);
♦ Another way: calling the t() function on a string that contains markup also produces the correct result. For example,
$project_code =
t('Set ID in <a href="@url">@acct_info</a> page to enable',
array('@url' => url('optimizely/settings'),
'@acct_info' => t('Account Info'),
)
);
$form['#project_code'] = $project_code;
Sources:
New inline_template render element for HTML code in PHP
https://www.drupal.org/node/2311123
Double escaping on /admin/modules/uninstall
https://www.drupal.org/node/2305831
<ul><li>admin/config/system</li></ul>
In other words, there was HTML markup that was being escaped too many times and being rendered as content. Somehow, this issue appeared because of changes in alpha 14.
Researching this, I found two things. First, the core maintainers have been grappling with the same problem in the core UI itself. That's a bit of consolation for a newcomer like me swimming in the ocean of Drupal.
Second, for render arrays there is a new recognized value for the #type property. That value is inline_template. The idea is that when you don't want to create a full-blown Twig template file, this #type enables you to define a snippet of template in the render array itself.
♦ Here's a simplistic example.
$render_title = array(
'#type' => 'inline_template',
'#template' => '{{ title }}',
'#context' => array('title' => 'My Project'),
);
// Insert this array into the full form.
$form['#project_title'] = $render_title;
The #template property provides the template using Twig syntax. The double braces mean print the value of the enclosed Twig variable, in this case, title.
The #context property provides a mapping from Twig variables to their values.
This example doesn't do anything except print the value of a variable. Depending on what might be in the value, you could just implement that without the use of a template.
♦ Here's something more useful and interesting.
$render_link = array(
'#type' => 'inline_template',
'#template' => '<a href="{{ url }}">{{ update }}</a>',
'#context' => array(
'update' => $this->t('Update'),
'url' => url('/admin/config/system/optimizely/add_update/'),
),
);
$form['#admin_link'] = $render_link;
The template consists of markup for a link to be rendered, using a translated word and a string that is built dynamically.
♦ Lastly, Twig templates allow for looping so that you can iterate over an array.
$paths = array( ... );
$render_paths = array(
'#type' => 'inline_template',
'#template' => '<ul>' .
'{% for p in paths %}' .
'<li>{{ p }}</li>' .
'{% endfor %}' .
'</ul>',
'#context' => array('paths' => $paths),
);
$form['#paths'] = $render_paths;
The Twig for construct is like foreach in PHP. This builds an HTML unordered list with list items that are determined at runtime.
♦ Getting back to the issue that started me down this road, the original code was building snippets of markup and inserting the strings directly into the form render array. What was intended as markup would be escaped and displayed as content.
$form['#paths'] = '<ul><li>some path</li></ul>';
With use of an inline template, the markup is handled correctly.
$form['#paths'] = array(
'#type' => 'inline_template',
'#template' => '<ul><li>some path</li></ul>',
);
♦ Another way: calling the t() function on a string that contains markup also produces the correct result. For example,
$project_code =
t('Set ID in <a href="@url">@acct_info</a> page to enable',
array('@url' => url('optimizely/settings'),
'@acct_info' => t('Account Info'),
)
);
$form['#project_code'] = $project_code;
Sources:
New inline_template render element for HTML code in PHP
https://www.drupal.org/node/2311123
Double escaping on /admin/modules/uninstall
https://www.drupal.org/node/2305831
Subscribe to:
Posts (Atom)