Feed aggregator

Palantir: The True Value of Certification

Planet Drupal - Thu, 27/08/2015 - 18:15

Drupal project lead and Acquia co-founder Dries Buytaert recently blogged about the one-year anniversary of Acquia’s certification program, which seeks to validate skills and knowledge that focus on open source Web development and Acquia products and services.

The topic of certification has long been a controversial one within the Drupal community. As an open source project that primarily measures achievement by one's contributions, some have questioned the need for developers with an established track record in the community to prove themselves through certification. Others are skeptical that the quality or skills of a developer can be judged by the results of a sixty-question multiple choice test.

Those arguments, while completely valid, miss the larger point.

Over the last few years, Drupal has become one of the leading content management platforms for high-traffic sites, powering nearly 15% of the top ten thousand CMS-backed sites on the Web. Large companies and organizations are increasingly evaluating Drupal against commercial and proprietary options like Adobe Experience Manager and SiteCore.

And for those enterprise evaluators, as well as influential research analysts like Forrester and Gartner, the presence of a robust commercial ecosystem surrounding a software platform is a strong indicator of its strength. One of the ways that those ecosystems are evaluated is by the presence of well-regarded and widely adopted certification programs, like the one that Acquia is working to build.

Those of us who have been involved with the Drupal project and community for years understand that the strength of open source software has less to do with its commercial ecosystem than it does with the level of engagement of its contributor community. We know this because we work with and alongside those contributors every single day. But that’s not the experience that most of our customers have; their perspective is more likely to be informed by the companies with whom they work.

And because Drupal is now increasingly competing against large commercial entities instead of other open source projects, those companies can no longer rely solely on their community experience and contributions to make the case for an open source solution. That experience can and should be an important factor in the evaluation process, but all too often other qualifications are necessary.

The value that open source software brings to the table isn't always apparent to those used to proprietary solutions. Acquia's certification program aims to surface that value by leveling the playing field and offering evaluators more of an apples-to-apples comparison. From that perspective, certification is less about the knowledge and skills of individual developers than it is about the investment made by companies into competing for large projects against other enterprise vendors.

Acquia's Certified Developer exam tests developers’ base level of familiarity with Drupal’s features and functionality, but in our experience that has little to do with their skills as developers, which is something that no multiple choice test can measure. Developers get better by working alongside other talented developers in an environment that promotes professional growth and development, which is what we try to build every day at Palantir. We demonstrate those skills to prospective customers both through our past experience and by demonstrating the approaches we take to solving our customers’ problems.

We don't need our developers to pass certification exams to know that they're awesome, and no matter how good a certification program might be, it’s still incumbent on evaluators to do their due diligence when picking who they want to work on their next project.

So if all that’s true, why have certified developers at all? The answer is going to be different for different people, but for us, it’s about making sure that customers understand that Palantir is willing and able to tackle large projects that might otherwise go to proprietary vendors by default. Some prospective customers have existing relationships with Acquia, and in addition to being an Acquia partner, having Acquia Certified developers on our team provides them with the justification they need to know that we're familiar with the products and services that they offer.

At the end of the day, the rise of certification programs like the one offered by Acquia are yet another example of Drupal’s growth as a project and as a community. Very few open source projects are strong enough to compete against proprietary enterprise solutions, but Drupal does it every day, delivering value to customers of all sizes and shapes. We're proud to be a part of it.

I’ll be talking more about Drupal certification and related topics next month in my DrupalCon Barcelona session, Architecting Drupal Business that are Built to Last. I’ve also proposed a session titled Building Tech Companies That Last for next year’s South by Southwest Interactive; voting is open through September 4.

Categories: Elsewhere

Palantir: D8FTW: Customizing your back-end

Planet Drupal - Thu, 27/08/2015 - 18:00

In our last episode, we talked about the various ways of storing data in Drupal 8. One important point we noted was that "in the database" is not an option. All of Drupal's storage systems are abstractions above the actual data store. In fact, I will go as far as saying that if you ever write an SQL query yourself in Drupal 8, you're probably doing it wrong.

There are two key reasons for that stance. One, there's no reason that any of those storage systems, conceptually, need to be in SQL. In fact, for Configuration, Key/Value, and the Cache, it's not even the best tool available. Any of them could be backed by MongoDB, Cassandra or Redis, instead. In fact, many sites will use one of those tools instead of SQL for some (but not all) data storage systems. If your module is hard-coded to SQL, you've now hard-coded all of your users to SQL only. And there's a good chance you've also hard-coded a specific SQL server (generally MySQL) without even intending to.

The second reason is that as a module developer, you should be thinking at a higher level than rows and columns. Most of those systems offer a lot of automation and abstraction tools that provide more power with an easier syntax than SQL, and if you write your own SQL you are bypassing all of that. Most especially, if you have any module configuration not stored in the Configuration system it will not work with any staging and deployment tools. Don't do that to your users.

If for some reason you must write a custom query, say for performance, there is a supported, flexible way to do so. First, ensure that your query is contained within a service, and that service conforms to a declared interface. (You should be doing that anyway, but it's especially important here.) For example, let's say we're creating a service that finds nodes by some highly complex logic that normal Entity Queries don't support. We'll call the class DatabaseComplexNodeFinder, with a ComplexNodeFinderInterface. When we register that service in the container, we should also tag it as one that allows its backend to be overridden, like so:

mymodule.services.yml:

services:
mymodule.nodefinder:
  class: Drupal\mymodule\DatabaseComplexNodeFinder
  arguments: ['@database']
  tags:
   - { name: backend_overridable }

And then we use that service wherever we need to use that logic. The "backend_overridable" tag tells Drupal that there may be alternate implementations it should look for. By default, the class should be written to use generic, non-engine-specific SQL. (That is, no MySQL or PostgreSQL specific features.) It doesn't have to be fast, just work.

Now comes the fun part. We can also define another service named mysql.mymodule.nodefinder, which has the same interface but is very specific to MySQL. Similarly, we can have a service named pgsql.mymodule.nodefinder or mongodb.mymodule.nodefinder, which would be specific to PostgreSQL or MongoDB, respectively. Just registering those services in the container has virtually no cost if they're not used. Those alternate services can have whatever code in them they want, and any set of dependencies they want, as long as they follow ComplexNodeFinderInterface.

Now, in the sites/default/services.yml file, a site owner can specify an alternate default backend:

parameters:
default_backend: mysql

The default is mysql, which means that if Drupal finds a mysql version of any "backend_overridable" service, it will use that instead of the generic one. If it doesn't, it just uses whatever is registered by default. (Often times an SQL-database-specific version will be unnecessary, but the capability is there if you need it.) If your site is running on PostgreSQL, change that default_backend to "pgsql". If on MongoDB, set it to "mongodb". And so on. What if we want to use something other than the default? For instance, we're on a mostly-MySQL-based site but we want to use MongoDB for the State system? That's another simple toggle in the services.yml file. To change the backend for our nodefinder service, we would simply add this to the site-specific services.yml file:

services:
mymodule.nodefinder:
  alias: mongodb.mymodule.nodefinder

That tells the container to use the MongoDB-specific version of that service instead of whatever it was going to use.
There are two big advantages of this design:
1) As a module developer, you can optimize your module for MySQL, PostgreSQL, or MongoDB at the same time. Even if you don't, any other module is free to provide an alternate backend by just registering a service with the correct name.
2) As a site owner, you can mix and match what backend services you want to use. Want a mostly-MySQL-based site, but with Redis for the lock and caching systems? Go for it. Want to store your entities in SQL but everything else in MongoDB? You can do that. Any well-written module will keep on working just fine, because it's either using Drupal's higher-level abstractions or using swappable backends. And if the backend you're looking for isn't available for that service, there's only one class that needs to be written to make it available.
That's the power of dependency injection.

Categories: Elsewhere

Acquia Developer Center Blog: Securing Your Site’s Communications with SSL

Planet Drupal - Thu, 27/08/2015 - 18:00
*/

These days, using SSL with your website isn’t just a good idea — it’s essentially a requirement. Encrypting the information that’s sent between visitors’ browsers and your website is the new price of doing business.

Fortunately, Acquia has a lot of experience helping to secure websites, and we’ve collected several of these tips into a best practices article on the Acquia Help Center.

Want to know about using Varnish with SSL (and you’re an Acquia Cloud user)?

It’s in there.

Want to ensure that all of your website’s assets are served to visitors using the HTTPS protocol?

It’s in there.

Want to set up your 301 redirects from your HTTP pages to HTTPS?

Surprise! It’s in there.

For more information on how best to use SSL with your website, visit the article and read for yourself.

And for even more information that you can use with your Drupal website, feel free to browse the articles and resources on the Acquia Help Center.

Workflow: PendingFeatured: NoTags: acquia drupal planetDrupal 8 related: NoAuthor: Lynette Miles
Categories: Elsewhere

Thorsten Glaser: Go enjoy shell

Planet Debian - Thu, 27/08/2015 - 16:12

Dimitri, I personally enjoy shell…

tglase@tglase:~ $ x=車賈滑豈更串句龜龜契金喇車賈滑豈更串句龜龜契金喇 tglase@tglase:~ $ echo ${x::12} 車賈滑豈更串句龜龜契金喇 tglase@tglase:~ $ printf '%s\n' 'import sys' 'print(sys.argv[1][:12])' >x.py tglase@tglase:~ $ python x.py $x 車賈滑豈

… much more than Python, actually. (Python is the language in which you do not want to write code dealing with strings, due to UnicodeDecodeError and all; even py3k is not much better.)

I would have commented on your post if it allowed doing so without getting a proprietary Google+ account.

Categories: Elsewhere

Mediacurrent: Mediacurrent Dropcast: Episode 10

Planet Drupal - Thu, 27/08/2015 - 13:40

This episode we have Mario Hernandez, front end developer at Mediacurrent, to talk about his upcoming talks at DrupalCamp LA, and how to properly plan for giving a presentation. We talk about the smart_trim module in our Pro Project Pick, which is an awesome module. We talk about Drupal 8 news and as always, birthday boy Ryan, brings it home with the Final Bell. Also, Mark starts his run for President of these United States.

Categories: Elsewhere

Dimitri John Ledkov: Go enjoy Python3

Planet Debian - Thu, 27/08/2015 - 12:39
Given a string, get a truncated string of length up to 12.

The task is ambiguous, as it doesn't say anything about whether or not 12 should include terminating null character or not. None the less, let's see how one would achieve this in various languages.
Let's start with python3

import sys
print(sys.argv[1][:12])
Simple enough, in essence given first argument, print it up to length 12. As an added this also deals with unicode correctly that is if passed arg is 車賈滑豈更串句龜龜契金喇車賈滑豈更串句龜龜契金喇, it will correctly print 車賈滑豈更串句龜龜契金喇. (note these are just random Unicode strings to me, no idea what they stand for).

In C things are slightly more verbose, but in essence, I am going to use strncpy function:

#include <stdio.h>
#include <string.h>
void main(int argc, char *argv[]) {
char res[12];
strncpy(res,argv[1],12);
printf("%s\n",res);
}
This treats things as byte-array instead of unicode, thus for unicode test it will end up printing just 車賈滑豈. But it is still simple enough.
Finally we have Go
package main

import "os"
import "fmt"
import "math"

func main() {
fmt.Printf("%s\n", os.Args[1][:int(math.Min(12, float64(len(os.Args[1]))))])
}
This similarly treats argument as a byte array, and one needs to cast the argument to a rune to get unicode string handling. But there are quite a few caveats. One cannot take out of bounds slices. Thus a naïve os.Args[1][:12] can result in a runtime panic that slice bounds are out of range. Or if a string is known at compile time, a compile time error. Hence one needs to calculate length, and do a min comparison. And there lies the next caveat, math.Min() is only defined for float64 type, and slice indexes can only be integers and thus we end up writing ]))))])...

12 points for python3, 8 points for C, and Go receives nul points Eurovision style.




The postings on this site are my own and don't necessarily represent Intel’s positions, strategies, or opinions.
Categories: Elsewhere

Frederic Marand: Drupal 8 tip of the day: autoloaded code in a module install file

Planet Drupal - Thu, 27/08/2015 - 08:24

Autoloading in D8 is much more convenient that in previous versions, however, it still has limitations. One such issue is with hook_requirements(), which is supposed to be present in the module install file, not the module itself: when called at runtime for the site report page, the module is loaded and the PSR/4 autoloader works fine. However, when that hook is fired during install to ensure the module can indeed be enabled, the module is not yet enabled, and the autoloader is not yet able to find code from that module, meaning the hook_requirements('install') implementation cannot use namespaced classes from the module, as they will not be autoloadable. What are the solutions ?

read more

Categories: Elsewhere

KatteKrab: D8 Accelerate - Game over?

Planet Drupal - Thu, 27/08/2015 - 02:47
Thursday, August 27, 2015 - 10:47

The Drupal 8 Accelerate campaign has raised over two hundred and thirty thousand dollars ($233,519!!).  That's a lot of money! But our goal was to raise US$250,000 and we're running out of time. I've personally helped raise $12,500 and I'm aiming to raise 8% of the whole amount, which equals $20,000. I've got less than $7500 now to raise. Can you help me? Please chip in.

Most of my colleagues on the board have contributed anchor funding via their companies. As a micro-enterprise, my company Creative Contingencies is not in a position to be able to that, so I set out to crowdfund my share of the fundraising effort.

I'd really like to shout out and thank EVERYONE who has made a contribution to get me this far.Whether you donated cash, or helped to amplify my voice, thank you SO so soooo much. I am deeply grateful for your support.

If you can't, or don't want to contribute because you do enough for Drupal that's OK! I completely understand. You're awesome. :) But perhaps you know someone else who is using Drupal, who will be using Drupal you could ask to help us? Do you know someone or an organisation who gets untold value from the effort of our global community? Please ask them, on my behalf, to Make a Donation

If you don't know anyone, perhaps you can help simply by sharing my plea? I'd love that help. I really would!

And if you, like some others I've spoken with, don't think people should be paid to make Free Software then I urge you to read Ashe Dryden's piece on the ethics of unpaid labor in the Open Source Community. It made me think again.

Do you want to know more about how the money is being spent? 
See: https://assoc.drupal.org/d8-accelerate-awarded-grants

Perhaps you want to find out how to apply to spend it on getting Drupal8 done?
See: https://assoc.drupal.org/d8-accelerate-application

Are you curious about the governance of the program?
See: https://www.drupal.org/governance/d8accelerate

And just once more, with feeling, I ask you to please consider making a donation.

So how much more do I need to get it done? To get to GAME OVER?

  • 1 donation x $7500 = game over!
  • 3 donations x $2500
  • 5 donations x $1500
  • 10 donations x $750
  • 15 donationsx $500 <== average donation
  • 75 donations x $100 <== most common donation
  • 100 donations x $75
  • 150 donations x $50
  • 500 donations x $15
  • 750 donations x $10 <== minimum donation

Thank you for reading this far. Really :-)

Categories: Elsewhere

Joey Hess: then and now

Planet Debian - Thu, 27/08/2015 - 02:01

It's 2004 and I'm in Oldenburg DE, working on the Debian Installer. Colin and I pair program on partman, its new partitioner, to get it into shape. We've somewhat reluctantly decided to use it. Partman is in some ways a beautful piece of work, a mass of semi-object-oriented, super extensible shell code that sprang fully formed from the brow of Anton. And in many ways, it's mad, full of sector alignment twiddling math implemented in tens of thousands of lines of shell script scattered amoung hundreds of tiny files that are impossible to keep straight. In the tiny Oldenburg Developers Meeting, full of obscure hardware and crazy intensity of ideas like porting Debian to VAXen, we hack late into the night, night after night, and crash on the floor.

It's 2015 and I'm at a Chinese bakery, then at the Berkeley pier, then in a SF food truck lot, catching half an hour here and there in my vacation to add some features to Propellor. Mostly writing down data types for things like filesystem formats, partition layouts, and then some small amount of haskell code to use them in generic ways. Putting these peices together and reusing stuff already in Propellor (like chroot creation).

Before long I have this, which is only 2 undefined functions away from (probably) working:

let chroot d = Chroot.debootstrapped (System (Debian Unstable) "amd64") mempty d & Apt.installed ["openssh-server"] & ... partitions = fitChrootSize MSDOS [ (Just "/boot", mkPartiton EXT2) , (Just "/", mkPartition EXT4) , (Nothing, const (mkPartition LinuxSwap (MegaBytes 256))) ] in Diskimage.built chroot partitions (grubBooted PC)

This is at least a replication of vmdebootstrap, generating a bootable disk image from that config and 400 lines of code, with enormous customizability of the disk image contents, using all the abilities of Propellor. But is also, effectively, a replication of everything partman is used for (aside from UI and RAID/LVM).

What a difference a decade and better choices of architecture make! In many ways, this is the loosely coupled, extensible, highly configurable system partman aspired to be. Plus elegance. And I'm writing it on a lark, because I have some spare half hours in my vacation.

Past Debian Installer team lead Tollef stops by for lunch, I show him the code, and we have the conversation old d-i developers always have about partman.

I can't say that partman was a failure, because it's been used by millions to install Debian and Ubuntu and etc for a decade. Anything that deletes that many Windows partitions is a success. But it's been an unhappy success. Nobody has ever had a good time writing partman recipes; the code has grown duplication and unmaintainability.

I can't say that these extensions to Propellor will be a success; there's no plan here to replace Debian Installer (although with a few hundred more lines of code, propellor is d-i 2.0); indeed I'm just adding generic useful stuff and building further stuff out of it without any particular end goal. Perhaps that's the real difference.

Categories: Elsewhere

Savas Labs: Sassy Drupal theming: a lighter version of SMACSS

Planet Drupal - Thu, 27/08/2015 - 02:00

It takes some forethought, but a well-organized theme means code that is modular and easy to maintain or pass off to another developer. SMACSS principles are becoming more and more widespread and can be applied to a Drupal theme. At Savas we've picked out what we love from SMACSS and simplified the rest, creating a stylesheet organization method that works for us. In this post (part 2 of my three-part series on Drupal theming with Sass) I'll go through our version of SMACSS and link to real examples.

Continue reading…

Categories: Elsewhere

DrupalCon News: Introduction to Headless Drupal

Planet Drupal - Thu, 27/08/2015 - 01:22

Drupal is an amazing platform for making websites, but it can also be a world-class API that can easily integrate with other technologies. In this class you will learn how to create fully featured APIs in Drupal, and you’ll build a simple Node.js application that consumes that API to make a highly interactive website.

If you are looking to learn about building APIs in Drupal, or creating websites with Node.js, this class is for you!

Categories: Elsewhere

Capgemini Engineering: Writing custom fields in Drupal 8

Planet Drupal - Thu, 27/08/2015 - 01:00

Based on my presentations at DrupalCamp London, on Saturday 28th February 2015 and DrupalCamp Bristol, July 4th 2015

Concept of a field

Fields are the data entry points to a web application. Usually, they provide HTML elements and may be responsible for any manipulation of data before it goes into and comes out of the application. The data captured from a single field can be simple or complex.

Assuming we want to create a field for country, all we need is a single HTML element - textfield or select options. An address field, on the other hand, is a collection of discrete data which may include standalone simple fields including a textfield representing postcode (provided by a core module) and a country field (maybe from a custom or contributed module).

In the Drupal world, when considering solutions the first phrase you may hear is, “there’s a module for that!”. However, for the task at hand, “you need a module for that!”. We are now going to write a module which provides this custom country field..

To create a new field in Drupal 7, a number of hooks need to be implemented in a custom module and these include the following:

  • hook_field_info() - the field type definition as well as its settings.
  • hook_field_schema() - the database schema for the field structure.
  • hook_field_widget_info() - the widget types to use for the field type.
  • hook_field_formatter_info() - the display of field values.

The task in Drupal 8 is founded on the same principles although the implementation differs. The first thing to remember here is, “there is a class for that!”. A lot of the hard work has been done in Drupal core and all we need to do is extend some classes and override default methods to suit our implementation.

Creating a module

All contrib and custom modules should be placed inside the “modules” folder in your site root. Core-related code is now in “core”. However, it’s also best practice to have “contrib” and “custom” sub-folder in “modules” for clear separation of these types of modules. So we’ll create our “country” folder under modules\custom. What used to go inside *.info file is now in country.yml, so we create that file too and add the following:

name: Country type: module description: Defines a simple country field type. package: Field types version: VERSION core: 8.x dependencies: - field

Inside your module directory, you need a “src” subdirectory to keep all your class files. To organise things further, you need a “Plugin” sub-folder in “src”. There are different types of plugins e.g. fields, actions, blocks and menus. So you need to create another folder called “Field” inside Plugin and you’ll end up with a directory structure like src\Plugin\Field

Next, we need to define our data type, widget and formatter. These will be in classes with each occupying its own folder again. Let’s take them one by one.

Data Type

The folder is called FieldType, so create it - src\Plugin\Field\FieldType. Create a class, which we shall call “CountryItem”. The file is called CountryItem.php and in it we should have:

class CountryItem { }

How do we define our field type? With the new plugin system, this requires an annotation1 - something like a special comment block to allow core classes know about our field type. Your code should now look like this:

/** * Plugin implementation of the 'country' field type. * * @FieldType( * id = "country", * label = @Translation("Country"), * description = @Translation("Stores the ISO-2 name of a country."), * category = @Translation("Custom"), * default_widget = "country_default", * default_formatter = "country_default" * ) */ class CountryItem { }

The information provided in our annotation is quite similar to that provided when implementing hook_field_info() in Drupal 7. Next, at the top of our file, we add a few things like namespaces and import required core classes.

namespace Drupal\country\Plugin\Field\FieldType; use Drupal\Core\Field\FieldItemBase; use Drupal\Core\TypedData\DataDefinition; use Drupal\Core\Field\FieldStorageDefinitionInterface;

Then we make our class inherit from core FieldItem class by extending it.

class CountryItem extends FieldItemBase { }

There are two functions we must implement in our class - schema() and propertyDefinitions(). They’re as follows:

public static function schema(FieldStorageDefinitionInterface $field_definition) { return array( 'columns' => array( 'value' => array( 'type' => 'char', 'length' => static::COUNTRY_ISO2_MAXLENGTH, 'not null' => FALSE, ), ), 'indexes' => array( 'value' => array('value'), ), ); }

Here we define the schema for this field. The column is to be called “value”, and will hold a 2-character string, representing the ISO-2 name of countries. Oh, don’t forget to add the constant for the length in your class:

const COUNTRY_ISO2_MAXLENGTH = 2; public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) { $properties['value'] = DataDefinition::create('string') ->setLabel(t('Country')); return $properties; }

However, we need to add two methods to make our life easier. Firstly, we want to know when our field is considered empty which is what hook_field_is_empty() does in Drupal 7. Then, we want to add some validation so that the country code we want to store doesn’t exceed the maximum length we have defined for our schema. When we are through, our class should look like this:

/** * @file * Contains \Drupal\country\Plugin\field\field_type\CountryItem. */ namespace Drupal\country\Plugin\Field\FieldType; use Drupal\Core\Field\FieldItemBase; use Drupal\Core\TypedData\DataDefinition; use Drupal\Core\Field\FieldStorageDefinitionInterface; /** * Plugin implementation of the 'country' field type. * * @FieldType( * id = "country", * label = @Translation("Country"), * description = @Translation("Stores the ISO-2 name of a country."), * category = @Translation("Custom"), * default_widget = "country_default", * default_formatter = "country_default" * ) */ class CountryItem extends FieldItemBase { const COUNTRY_ISO2_MAXLENGTH = 2; /** * {@inheritdoc} */ public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) { $properties['value'] = DataDefinition::create('string') ->setLabel(t('Country')); return $properties; } /** * {@inheritdoc} */ public static function schema(FieldStorageDefinitionInterface $field_definition) { return array( 'columns' => array( 'value' => array( 'type' => 'char', 'length' => static::COUNTRY_ISO2_MAXLENGTH, 'not null' => FALSE, ), ), 'indexes' => array( 'value' => array('value'), ), ); } /** * {@inheritdoc} */ public function isEmpty() { $value = $this->get('value')->getValue(); return $value === NULL || $value === ''; } /** * {@inheritdoc} */ public function getConstraints() { $constraint_manager = \Drupal::typedDataManager()->getValidationConstraintManager(); $constraints = parent::getConstraints(); $constraints[] = $constraint_manager->create('ComplexData', array( 'value' => array( 'Length' => array( 'max' => static::COUNTRY_ISO2_MAXLENGTH, 'maxMessage' => t('%name: the country iso-2 code may not be longer than @max characters.', array('%name' => $this->getFieldDefinition()->getLabel(), '@max' => static::COUNTRY_ISO2_MAXLENGTH)), ) ), )); return $constraints; } } Data Input

Now we have defined our data type and we want to store the ISO-2 country code. How do we want users to input data? We have two options - select dropdown options and an autocomplete textfield. The select options can be the default widget.

We start by creating a class called CountryDefaultWidget in src\Plugin\Field\FieldWidget\CountryDefaultWidget.php with the following code:

namespace Drupal\country\Plugin\Field\FieldWidget; use Drupal; use Drupal\Core\Field\FieldItemListInterface; use Drupal\Core\Field\WidgetBase; use Drupal\Core\Form\FormStateInterface; class CountryDefaultWidget extends WidgetBase { }

There’s still an important thing missing from our widget class - annotation of the class as provider of a FieldWidget. Add this just above the class statement;

/** * Plugin implementation of the 'country_default' widget. * * @FieldWidget( * id = "country_default", * label = @Translation("Country select"), * field_types = { * "country" * } * ) */

This is similar to the old array keys for the old hook_widget_info() in Drupal 7. Additional annotation keys may be defined by a hook_field_widget_info_alter() function.

Our CountryDefaultWidget class isn’t complete yet. Widgets handle how fields are displayed in edit forms. The missing method we need to implement will do this for us. Add this formElement() method:

public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) { $countries = \Drupal::service('country_manager')->getList(); $element['value'] = $element + array( '#type' => 'select', '#options' => $countries, '#empty_value' => '', '#default_value' => (isset($items[$delta]->value) && isset($countries[$items[$delta]->value])) ? $items[$delta]->value : NULL, '#description' => t('Select a country'), ); return $element; }

Other modules may alter the form element provided by this function using hook_field_widget_form_alter() or hook_field_widget_WIDGET_TYPE_form_alter().

This default country widget is a simple widget of select options. Drupal core provides a list of all countries in the world as an array of country names keyed by ISO-2 country codes. This is made available as a service for use anywhere in a Drupal project.

Country autocomplete widget

Let’s start off with a complete implementation for this. Create src\Plugin\Field\FieldWidget\CountryAutocompleteWidget.php with this code:

namespace Drupal\country\Plugin\Field\FieldWidget; use Drupal\Core\Field\FieldItemListInterface; use Drupal\Core\Field\WidgetBase; use Drupal; use Drupal\Core\Form\FormStateInterface; /** * Plugin implementation of the 'country_autocomplete' widget. * * @FieldWidget( * id = "country_autocomplete", * label = @Translation("Country autocomplete widget"), * field_types = { * "country" * } * ) */ class CountryAutocompleteWidget extends WidgetBase { }

There’s nothing unusual here at all. We need to implement same defaultSettings() and formElement() methods as for the default widget. Add this to the class:

public static function defaultSettings() { return array( 'size' => '60', 'autocomplete_route_name' => 'country.autocomplete', 'placeholder' => '', ) + parent::defaultSettings(); }

We want a textfield that’s wide enough (‘size’ => 60). For the autocomplete_route_name key we provide the name of the route from which our autocomplete functionality will return matching values. We’ll be implementing that shortly. We don’t want anything as the placeholder.

Finally, let’s add our formElement() method:

public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) { $countries = \Drupal::service('country_manager')->getList(); $element['value'] = $element + array( '#type' => 'textfield', '#default_value' => (isset($items[$delta]->value) && isset($countries[$items[$delta]->value])) ? $countries[$items[$delta]->value] : '', '#autocomplete_route_name' => $this->getSetting('autocomplete_route_name'), '#autocomplete_route_parameters' => array(), '#size' => $this->getSetting('size'), '#placeholder' => $this->getSetting('placeholder'), '#maxlength' => 255, '#element_validate' => array('country_autocomplete_validate'), ); return $element; }

This is a standard autocomplete widget. Looking at the FAPI array keys, some are very familiar. #autocomplete_route_name matches what we entered in our defaultSettings() a while ago. The value is retrieved from there with $this->getSetting(‘autocomplete_route_name’). The same goes for #size and #placeholder. Our #autocomplete_route_parameters has no default value. In order to ensure that the final value to be submitted doesn’t include unwanted values, we add #element_validate and enter the name of our callback function. We will also implement this shortly.

Creating a route

Create a YAML configuration file called country.routing.yml in your main module directory with the following:

country.autocomplete: path: '/country/autocomplete' defaults: _controller: '\Drupal\country\Controller\CountryAutocompleteController::autocomplete' requirements: _permission: 'administer content types'

The key or name of the route is country.autocomplete which is how this route will be referred to anywhere in the application. At least a route should define 3 things: path, code to execute and who can access the path.

  • path: This is the URL for our AJAX autocomplete calls.
  • _controller: This is the code we want to execute when we visit the defined path. It’s written as CLASS::FUNCTION. If our function requires any parameters, this is where they will be specified. We will be creating our controller shortly.
  • _permission: The string representation of the permission for access control

Now we move on to the creation of our controller class. Create a folder called Controller under src. Then add CountryAutocompleteController.php inside it. Add this code:

/** * @file * Contains \Drupal\country\Controller\CountryAutocompleteController. */ namespace Drupal\country\Controller; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Drupal\Component\Utility\Unicode; use Drupal; /** * Returns autocomplete responses for countries. */ class CountryAutocompleteController { /** * Returns response for the country name autocompletion. * * @param \Symfony\Component\HttpFoundation\Request $request * The current request object containing the search string. * * @return \Symfony\Component\HttpFoundation\JsonResponse * A JSON response containing the autocomplete suggestions for countries. */ public function autocomplete(Request $request) { $matches = array(); $string = $request->query->get('q'); if ($string) { $countries = \Drupal::service('country_manager')->getList(); foreach ($countries as $iso2 => $country) { if (strpos(Unicode::strtolower($country), Unicode::strtolower($string)) !== FALSE) { $matches[] = array('value' => $country, 'label' => $country); } } } return new JsonResponse($matches); } }

Whatever we type in our autocomplete widget will get passed to our autocomplete method. Then, we simply search for it in the array of country names which we pull from the country_manager service we have come across before. Finally, we return any matches or an empty array in a JSON response.

That looks more like it now and we’re nearly there. If you look back at our formElement() method in CountryAutocompleteWidget.php we specified a validation callback. We are going to do that now in our country.module file. Add this code:

/** * @file * Defines simple country field type. */ use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Form\FormStateInterface; /** * Form element validate handler for country autocomplete element. */ function country_autocomplete_validate($element, FormStateInterface $form_state) { if ($country = $element['#value']) { $countries = \Drupal::service('country_manager')->getList(); $iso2 = array_search($country, $countries); if (!empty($iso2)) { $form_state->setValueForElement($element, $iso2); } } }

We get our array of countries, and compare the value we want to send to the database with possible values. Now that we’re here, let’s just implement hook_help to give some information about our module. Just add this below the last use statement:

/** * Implements hook_help(). */ function country_help($route_name, RouteMatchInterface $route_match) { switch ($route_name) { case 'help.page.country': $output = ''; $output .= '&lt;h3&gt;' . t('Country') . '&lt;/h3&gt;'; $output .= '&lt;p&gt;' . t('The Country module defines a simple country field type for the Field module. It provides 2 widgets - select options and autocomplete textfield - for this purpose. See the &lt;a href="!field-help">Field module help page&lt;/a&gt; for more information about fields.', array('!field-help' => url('admin/help/field_ui'))) . '&lt;/p&gt;'; return $output; } }

We have now finished our autocomplete widget and learned something about routing. Not bad at all!

Data Output

We have everything ready for creating our field and allowing users to input data. Everything should work. There’s little work left before we can display the output. That’s where the need for a field formatter comes in. Add a new folder: src\Plugin\Field\FieldFormatter and inside it create CountryDefaultFormatter.php. Then add this code.

/** * @file * Definition of Drupal\country\Plugin\field\formatter\CountryDefaultFormatter. */ namespace Drupal\country\Plugin\Field\FieldFormatter; use Drupal\Core\Field\FieldItemListInterface; use Drupal\Core\Field\FormatterBase; use Drupal; /** * Plugin implementation of the 'country' formatter. * * @FieldFormatter( * id = "country_default", * module = "country", * label = @Translation("Country"), * field_types = { * "country" * } * ) */ class CountryDefaultFormatter extends FormatterBase { }

If we don’t do anything now, everything will work except where we expect to see a country name, we will see the ISO-2 which was saved as the value of the field. To display our country name, we need to override the viewElements() method. So let’s do it:

/** * {@inheritdoc} */ public function viewElements(FieldItemListInterface $items) { $elements = array(); $countries = \Drupal::service('country_manager')->getList(); foreach ($items as $delta => $item) { if (isset($countries[$item->value])) { $elements[$delta] = array('#markup' => $countries[$item->value]); } } return $elements; }

Once again, we get our array of countries, find the country name for our ISO-2 value and return it as markup. Job done. The module we have created is now functional and can now be installed.

Field UI

Let’s take a look at what fields UI offer us with our Country field. You may use an existing content type or start off with a new one. From the list of content types (admin/structure/types) clicking on the Edit operation will take you to the edit form with three tabs at the top: Manage fields, Manage form display, and Manage display which give us access to our field type, field widget and field formatter, respectively.

  • Field type (Manage fields):

Click “Add field”. Select “Country” from the “Add a new field” select dropdown. That dropdown is populated by all @FieldType plugins from all enabled modules. They are grouped according to the “category” key of field plugins. This is optional, and if it isn’t defined for a field, it goes under “General”. If you inspect it, you’ll see that the name is “country_default” which matches the “id” key of our plugin and the “Country” we see there is the “label”.

Enter “Location” as the field name in the “Label” textfield and save it.

If you want to see how the field type settings are reflected in the field UI, go back to the annotation in CountryItem.php. Edit your plugin label, change it to something like “Country - edited” and save it. Then go back to the fields UI page and you’ll see not only the new value in the select dropdown when creating a new field but also under the “Field Type” column for the new Location field. You can revert the change now.

  • Field widget (Manage form display):

On this screen you can see what was defined in the FieldWidget plugin. In the URL you can see the plugin “id” and the “label” is the description.

  • Field formatter (Manage display):

There isn’t much choice here so we just leave the default formatter as is.

You may now take the completed field module for a spin. Finish by adding it to a content type, create a node and view the output. That’s all. We’ve come to the end of looking at how to create custom field modules and in the process learned a few things about Drupal 8. Go make yourself a coffee. You deserve it!

1: See https://www.drupal.org/node/1882526 for an excellent introduction to annotation-based plugins.

Writing custom fields in Drupal 8 was originally published by Capgemini at Capgemini on August 27, 2015.

Categories: Elsewhere

DrupalCon News: DrupalCon Asia Comes to Mumbai!

Planet Drupal - Wed, 26/08/2015 - 23:50

The Drupal community is a vibrant, global group of people with a colorful range of professions, hobbies, and interests. That’s why we couldn’t be more excited to bring DrupalCon to Mumbai, a city of color, excitement, diversity, and light.

Typically, the Drupal Association produces DrupalCons in North America and Europe, but starting in 2015 we decided to do something a little different. We wanted to make sure that our DrupalCon programming and site selection reflected our community’s wide and diverse spread. That’s why we’ve introduced a third DrupalCon to the mix.

Categories: Elsewhere

Carl Chenet: Retweet 0.2 : bump to Python 3

Planet Debian - Wed, 26/08/2015 - 23:01

Follow me on Identi.ca  or Twitter  or Diaspora*

Don’t know Retweet? My last post about it introduced this small Twitter bot whichs just retweets (for now) every tweets from a Twitter account to another one.

Retweet

Retweet was created in order to improve the Journal du hacker Twitter account. The Journal du hacker is a Hacker News-like French-speaking website.

Especially useful to broadcast news through a network of Twitter accounts, Retweet was improved to bump Python version to 3.4 and to improve pep8 compliance (work in progress).

The project is also well documented and should be quite simple to install, configure and use.

After my first blog post about Retweet, new users gave me feedback about it and I now have great ideas for future features for the next release.

What about you? If you try it, please tell me what you think about it, opening a bug request or ask for new features. Or just write your comment here ;)


Categories: Elsewhere

DrupalCon News: First Asia Post

Planet Drupal - Wed, 26/08/2015 - 19:57

words words namaste words

Categories: Elsewhere

Acquia Developer Center Blog: Terrific Tools for Drupal 8 Training

Planet Drupal - Wed, 26/08/2015 - 19:20

We couldn’t have trained dozens of Acquia employees on Drupal 8 without using various applications to schedule, measure our progress, and follow communications about the massive undertaking.

Let’s take some time in this, the fifth blog in a series about Drupal 8 instruction, to give a quick overview of the tools we used to train.

We used the collaboration software Confluence because we could set it up quickly. Kent Gale, the co-author of this blog and my partner in the training project, and I would have preferred to have built a tool within Drupal. But we went with Confluence because we were able to deploy it quickly and edit content. It’s where we placed project documentation to enable in-place editing.

Calendars kept in Google Docs spreadsheets accounted for every employee’s time. Every trainee had time blocked off each week for training. It was protected time, so to speak, agreed upon by employee and supervisor – time that was free of any other obligation. Each week, I’d also schedule 30 minutes with each employee – although meetings didn’t last that long – to talk about training. I wanted to see if they faced any barriers or had questions they were too embarrassed to ask in training sessions. We found this hands-on check-in – which was brief but frequent – kept everyone on track.

We also used email and a chatroom to communicate. Initially, email was the preferred route, but as more people enrolled in training, the chatroom was better able to handle the flow of communications. The spontaneity of a chatroom allowed a lot of questions to be answered quickly. It built a resource as well as type of behavior.

On our tracking sheet, we followed the time each employee spent on training and could see how much time was left. We also tracked the lessons they completed and could see if a team fell behind and could quickly address why.

When you’re ready to train, find a tool and use it a lot before starting the program to ensure it will accomplish what you need. Then pick an end date and work backwards. That will tell you how much time you’ll have. From there, you’ll be able to determine if you should compress training to reach a deadline or if you can spread it out over time.

As organized and motivated as we were, and despite starting early, it was still difficult to carve out time for nearly 50 people, and keep our end-date within reach. It was a commitment. So keep that in mind: Your managers and employees have to commit to protecting training time to make it all happen. Otherwise, it’s very easy to let the opportunity slip away.

Blog series: Organizing to Rock with Drupal 8Workflow: PublishedFeatured: NoTags: acquia drupal planetDrupal 8 related: YesAuthor: Thomas Howell
Categories: Elsewhere

Mediacurrent: Accessible Names - Label All the Things! (Part 2)

Planet Drupal - Wed, 26/08/2015 - 18:52
Label the Rest of the Things!
Categories: Elsewhere

Singlebrook Technology: Styleguide Driven Development in Drupal

Planet Drupal - Wed, 26/08/2015 - 18:37

At Singlebrook, we're using Styleguide Driven Development to create efficiency and strengthen code quality.

Jeff Amaral led a session at Cornell Drupal Camp 2015 on the topic of Styleguide Driven Development. He has shared his slides here as a downloadable PDF: "Singlebrook Styleguide Driven Development in Drupal"

The slides contain an outline of major points to consider when using Styleguide Driven Development, as well as some links to helpful resources. 

Introduction:

Theming in Drupal has many challenges. The biggest, in our experience at Singlebrook, is coordinating the creation of new site functionality and the CSS styling of the new markup, especially when multiple developers are working on a site.

In this session, we discuss our brand new theming process using CSS/HTML components, some extensions to the styleguide module, and improved developer/themer communication. We cover:...

Categories: Elsewhere

Advomatic: Altering Data For a Drupal 8 Migration, Step-by-Step

Planet Drupal - Wed, 26/08/2015 - 18:00

We’re working on our first Drupal 8 project here at Advomatic, and Jim and I have been tasked with implementing a content migration from the client’s existing Drupal 6 site. My first assignment was to write a plugin which rewrites image assist tags in node body fields as regular HTML image tags. Fortunately, lots of... Read more »

The post Altering Data For a Drupal 8 Migration, Step-by-Step appeared first on Advomatic.

Categories: Elsewhere

ThinkShout: Migrating from Luminate CRM to Drupal and the Salesforce Nonprofit Starter Pack

Planet Drupal - Wed, 26/08/2015 - 17:00

We've helped a number of nonprofits move from Luminate CRM to Drupal-Salesforce solutions, including the Young Survival Coalition, Facing History and Ourselves, the National Kidney Foundation, and the Los Angeles Conservancy.

Without getting too deep into the technical architecture - which you can nerd out on in other posts on our blog - our open source Salesforce integration can map any object/field in Salesforce to a corresponding entity type/field in Drupal. We can then sync these records bidirectionally. We can even support complex, cascading upserts of multiple records in real time.

This allows us to leverage everything that’s great about Drupal (CMS tools, personalization, paid and unpaid event registrations, membership purchases, general ecommerce, and user access controls) with everything that’s great about Salesforce's Nonprofit Starter Pack (NPSP) 3.0 (best-in-class donor management, unlimited extendability and scalability, flexible and intuitive reporting tools, and the most robust Application Exchange available).

It's always been clear that Salesforce provides much better constituent relationship management tools on the backend than Luminate or other Convio/Blackbaud products. What’s kept Luminate in the game for so long has been its public-facing web features, such as membership management and event registration tools.

That said, there is no way that Luminate could ever keep up with the pace of innovation that we see with comparable features in Drupal. The open source model and volume of contributions from the Drupal community is unparalleled. Leveraging Drupal as a donor/constituent front-end for Salesforce, we can provide seamless user experiences that engage with website visitors more deeply, because we can personalize these experience based upon data pulled from Salesforce’s API.

Take the Los Angeles Conservancy as a case study:

When we met the Conservancy, they were struggling to engage stakeholders through an aging website and cumbersome collection of Luminate donation and event management tools. Asking website visitors to click away from their website to third-party forms provided by Convio severely hurt their conversion rates. Mobile event registrations and contributions were almost nonexistent on their site.

The Conservancy wanted an interactive and mobile-friendly solution that would allow their constituents to easily sign up for free walking tours, buy tickets to movie events, update their membership information, set up recurring membership payments, and make donations towards different fundraising campaigns.

By leveraging Drupal event registration and ecommerce tools, we were able to build all of these features within the Conservancy’s new responsive website. This provided a much more seamless user experience. Conversion rates soared as a result. In fact, the Conservancy staff came to us 3 months after the relaunch concerned that their Google Analytics showed decreased traffic on their event registration pages. They worried that they were losing registrations - when in reality, their conversation rates were going up so dramatically, and the time for completing an ecommerce transaction was dropping so quickly, that these forms were seeing less page clicks while their volume of transactions and their revenue was going up.

With Drupal-based event registration and donation tools, we have 100% control over ecommerce workflows. We can also support complex pricing options based upon constituent data in Salesforce. For example, we can adjust ticket pricing based upon membership status. Again, we benefit from Drupal Commerce, an ecommerce solution that powers over 60,000 websites, including some of the largest stores on the Internet.

Further, integrating these Drupal-Salesforce solutions with iATS Payments, we can create Drupal-based donation portals that support “card on file” as well as recurring donations. And with iATS’s integration with Salesforce, donors can update their credit card information or make a donation over the phone by calling the Conservancy, storing this payment information for their next online transaction.

In short, Convio/Blackbaud just can’t compete… With what our clients save in confusing and expensive Blackbaud licensing fees, we can build more effective fundraising solutions that lead to much higher returns for their investment.

Categories: Elsewhere

Pages

Subscribe to jfhovinne aggregator