Tim Millwood: Composer dependencies in Drupal contrib

Planet Drupal - Fri, 2016/01/22 - 12:51pm
Drupal 8 is out, Drupal 8.1 will be out before we know it, but it seems contrib is still catching...
Categories:

DrupalCon News: Application Period Open for Grants and Scholarships to DrupalCon

Planet Drupal - Fri, 2016/01/22 - 10:07am

For individuals in the Drupal community who want to attend DrupalCon but are unable to do so due to financial considerations, the Drupal Association is pleased to offer scholarships and grants. Whether you need Drupal or Drupal needs you, we're here to help qualified individuals make their dreams of attending DrupalCon a reality.

So, how does it work? There's a bit of a difference between scholarships and grants, which were introduced for DrupalCon Prague. The difference can be summarized in the two following phrases:

Categories:

Craig Aschbrenner: packagist.drupal-composer.org file could not be downloaded

Planet Drupal - Fri, 2016/01/22 - 6:44am

I'm excited to be starting a new Drupal 8 project. I get to try new and shiny things. Only this time I'm breaking things along the way as I figure out what some of our new processes will be with Drupal 8.

Part of what I've been looking into is how to go about providing local environments to team members, building out the code-base, etc. With Drupal 7 I was pretty comfortable using .make files, install profiles and distributions.  I wanted to give Drupal Composer a try.  There are several other articles that discuss Drupal Composer and more specifically Drupal Project (Composer template for Drupal projects).  I'll leave it up to you to read through those other articles.

I wanted to keep this short and sweet and point out an issue I ran into and what the work-around is in case anyone else runs into similar issues.

While running "composer install" I started to get the following error.

[Composer\Downloader\TransportException]

The "https://packagist.drupal-composer.org/p/provider-archived%241a0102814c28..." file could not be downloaded (HTTP/1.1 404 Not Found)

A good couple of hours was spent trying to track down what I may have done to break the process.  I went to https://packagist.drupal-composer.org and at first inspection the site looked up.  Then I started browsing for packages and noticed that when trying to view a package the server was returning a 500 error.  This was the likely culprit of the 404 issue I was receiving.  

Now that I know it's most likely not an issue with my composer.json file, my next task was figuring out how to work-around packagist.drupal-composer.org being essentially offline.  I needed to get some work done.

On the off chance that packagist.drupal-composer.org was just another domain for packagist.org, I modified my composer.json file changing:

"url": "https://packagist.drupal-composer.org"

to:

"url": "https://packagist.org"

Bingo!!!

While I can't use "sudo composer create-project drupal-composer/drupal-project:~8.0 drupal --stability dev --no-interaction" mentioned in the articles linked to above, I was able to simply download the project template and then run "composer install".  All of the packages downloaded without issue and things are starting to get back on track.

Hopefully this is just a random server down issue but in case anyone else runs into the same issue, maybe this will be helpful.

Categories:

Chromatic: Drupal 8 Deployments with Jenkins, GitHub & Slack

Planet Drupal - Fri, 2016/01/22 - 3:16am

We recently launched our first Drupal 8 site--actually it’s this very site that you’re reading! While this wasn’t our first time using or developing for Drupal 8, it was our first full site build and launch on the new platform. As such, it was the first time we needed to handle Drupal 8 code deployments. While I’ve previously covered the benefits of using Jenkins, this post will take you through the steps to create a proper Drupal 8 deployment and how to integrate GitHub and Slack along the way. In other words, you’ll see our current recipe for deploying code automatically, consistently and transparently.

First Things First: Some Assumptions

This post assumes you already have a Jenkins server up and running with the following plugins installed:

If you don’t yet have these things ready to go, getting a Jenkins server setup is well documented here. As is how to install Jenkins plugins. For us, we typically use a Linode instance running Ubuntu LTS for our Jenkins servers. This post also assumes that the external environment you’re trying to deploy to already has Drush for Drupal 8 installed.

Example Deployment Script with Drush

Before we dive into setting up the Jenkins job to facilitate code deployments, I’d like to take a look at what exactly we’re trying to automate or delegate to our good friend Jenkins. At the heart of virtually any of our Drupal deployments (be them Drupal 7, 8 or otherwise) is a simple bash script that executes Drush command in succession. At a macro level, this typically means doing the following, regardless of version:

  1. SSH to the server
  2. Change directory to repository docroot
  3. Pull down latest code on the master branch
  4. Clear Drush cache
  5. Run database updates
  6. Update production configuration
  7. Clear Drupal caches

In Drupal 7, where we relied heavily on Features to deploy configuration, we would typically do something like this:

echo "" echo "Switching to project docroot." cd /var/www/drupal-7-project/docroot echo "" echo "Pulling down latest code." git pull origin master echo "" echo "Clearing drush cache" drush cc drush echo "" echo "Run database updates." drush updb -y echo "" echo "Reverting features modules." drush fra -y echo "" echo "Clearing caches." echo "" drush cc all echo "" echo "Deployment complete."

In Drupal 8, we have the magical unicorn that is the Configuration Management System, so our deployments scripts now look something like this:

If you’re familiar with creating Jenkins jobs already and are just looking for a Drupal 8 deploy script, these next lines are for you.

echo "" echo "Switching to project docroot." cd /var/www/chromatichq.com/docroot echo "" echo "Pulling down the latest code." git pull origin master echo "" echo "Clearing drush caches." drush cache-clear drush echo "" echo "Running database updates." drush updb -y echo "" echo "Importing configuration." drush config-import -y echo "" echo "Clearing caches." drush cr echo "" echo "Deployment complete."

Seriously, configuration management in Drupal 8 is amazing. Hat tip to all of those who worked on it. Bravo.

Another notable difference is that with Drupal 8, clearing caches uses the cache-rebuild Drush command or drush cr for short. drush cc all has been deprecated. R.I.P. little buddy. ⚰

If you have a site that needs to be put into "Maintenance mode" during deployments, you can handle that in Drupal 8 with drush sset system.maintenance_mode 1 to enable and drush sset system.maintenance_mode 0 to disable.

Creating our Jenkins Slave & Job

Now that we’ve covered what it is we want Jenkins to handle automatically for us, let’s quickly run down the punch list of things we want to accomplish with our deployment before we dive into the actual how-to:

  1. Automatically kickoff our deployment script when merges to the master branch occur in GitHub
  2. Run our deployment script from above (deploys latest code, imports config, clears caches, etc.)
  3. Report deployment results back to Slack (success, failure, etc.)
Create Your Jenkins Slave

For Jenkins to orchestrate anything on a remote box, it first needs to know about said box. In Jenkins parlance, this is known as a "node". In our case, since we’re connecting to a remote machine, we’ll use a “Dumb Slave”. Navigate to Manage Jenkins > Manage Nodes > New Node

At Chromatic our naming convention matches whatever we’ve named the machine in Ansible. For the purposes of this article, you can just name this something that makes sense to you. Example:** Drupal-Prod-01**

As part of the creation of this node, you’ll need to specify the Host and the Credentials Jenkins should use to access the box remotely. If you don’t yet have credentials added to Jenkins, you can do so at Jenkins > Credentials > Global credentials (unrestricted). From there things are pretty self-explanatory.

Setup the Basics for our Jenkins Job

Now that we have a way for Jenkins to target a specific server (our slave node) we can start building our deployment job from scratch. Start by navigating to: Jenkins > New Item > Freestyle Project.

From there press "OK" and move on to setting up some basic information about your job, including Project Name, Description and the URL to your GitHub repository. Pro tip: take the time to add as much detail here, especially in the Description field as you can. You’ll thank yourself later when you have loads of jobs.

Configure Slack Notification Settings (optional)

Assuming you’re interested in tying your deployment status messages to Slack and you’ve installed the Slack Notification Plugin, the next step is to tell Jenkins how/where to report to Slack. You do this under the Slack Notifications options area. As far as notifications go, we prefer to use only the "Notify Failure", “Notify Success” and “Notify Back To Normal” options. This is the right mix of useful information without becoming noisy. To allow Jenkins to connect to your Slack channels, you’ll need to follow these steps for adding a Jenkins integration. Then just fill in your Slack domain, the integration token from Slack and the channel you’d like to post to. These settings are hidden under “Advanced…”.

Configure Where this Job Can Run

This is where we instruct Jenkins on which nodes the job is allowed to run. In this case, we’ll limit our job to the slave we created in step one: Drupal-8-Prod-01. This ensures that the job can’t run, even accidentally, on any other nodes that Jenkins knows about. Jenkins allows this to be one node, multiple nodes, or a group.

Configure GitHub Repository Integration

Under "Source Code Management" we’ll specify our version control system, where are repository lives, the credentials used to access the repo and the branches to “listen” to. In our example, the settings look like this:

Here we’re using a jenkins system user on our servers that has read access on our GitHub repositories. You’ll want to configure credentials that make sense for your architecture. Our "Branch Specifier" (*/master) tells Jenkins to look for changes on the master branch or any remote name by using the wildcard, “*”.

Configure Your Build Triggers

This is where the rubber meets the road in terms automation. At Chromatic, we typically opt for smaller, more frequent deployments instead of larger releases where there is a higher probability of regressions. Since we rely heavily on the GitHub pull request model, we often have many merges to master on any given day of development for an active project. So we configure our deployments to coincide with these merges. The following setup (provided via the GitHub Jenkins Plugin) allows us to automate this by selecting "Build when a change is pushed to GitHub".

Setup Your Deployment Script

Here’s where we’ll implement the example deployment script I wrote about earlier in the post. This is the meat and potatoes of our job, or simply put, this is what Jenkins is going to do now that it finally knows how/when to do it.

Under "Build" choose “Add build step” and select “Execute shell”. Depending on your installed plugins, your list of options might vary.

Then add your deployment script to the textarea that Jenkins exposed to you. If you want somewhere to start, here is a gist of my job from above. When you’ve added your script it should look something like this:

Last Step! Enable Slack Notifications

Although earlier in the job we configured our Slack integration, we still need to tell Jenkins to send any/all notifications back to Slack when a build is complete. You do this sort of thing under the "Add post-build action" menu. Select “Slack Notifications” and you’re good to go.

Our deployment job for Drupal 8 is now complete! Click "Save" and you should be able to start testing your deployments. To test the job itself, you can simply press “Build Now” on the following screen OR you can test your GitHub integration by making any change on the master branch (or whichever branch you configured). With the setup I’ve covered here, Jenkins will respond automatically to merges and hot-fix style commits to master. That is to say, when a PR is merged or when someone commits directly to master. Of course no one on your team would ever commit directly to master, would they?!

Wrapping Up

Assuming everything is setup properly, you should now have a robust automatic deployment system for your Drupal 8 project! Having your deployments automated in this way keeps them consistent and adds transparency to your entire team.

Categories:

Chromatic: The Anatomy of a Good Ticket

Planet Drupal - Fri, 2016/01/22 - 3:16am

We previously wrote about how to write a great commit message, but before that commit message is ever written, there was (hopefully) a great ticket that was the impetus for the change. Whether or not a ticket is great is subjective, and it is often how well thought out the details are that makes the difference. The ticket might have everything a developer needs to complete the task, but not provide any insight into how to test the final product. Conversely, it might provide all the details the QA team needs to verify, but not provide any insight into the technical requirements for implementation.

Before we examine the specific things that make up a great ticket let’s first examine some of the best practices that further enhance communication and efficiency:

  • Having all stakeholders using the ticket management system and not allowing any communication of requirements via email or other communication tools that silo information.

  • Ensuring that requirements which are discussed one-on-one or during a meeting are added back into the ticket so nothing is lost or forgotten.

  • Having any message conversations in open channels that allow visibility into the decisions for others working on related issues.

  • Continuously keeping tickets updated with the current status so the whole team is aware of where everyone else is with their tasks.

  • Ensure everyone is familiar with the internal jargon and acronyms used, or ensure they are provided with the tools to decipher the terms.

With some ground rules established, let’s investigate the factors that make for a great ticket.

A User Story

A user story provides a high-level description of functionality from a user’s perspective, such as, "when a user logs in they should be able to see a list of past purchases." Great tickets often start with a user story that answers what is needed and why at a high level.

Clearly Defined Goals & Scope

Clearly stated goals from the business allows the ticket to be resourced correctly. Also, a full understanding of the requested change’s scope will inform what brands, sites, regions, pages, etc. a new feature or change will affect, and is crucial to planning a proper implementation.

Accurate Estimates

A well thought through estimation of the level of effort from developers and other stakeholders will make sprint planning/resourcing much more accurate and sprint reviews/evaluations more insightful.

Understanding of Priority

A clear understanding of the business priorities will ensure timely completion and allow the team to plan ahead, avoiding late nights and weekends.

Knowledge of Blockers

Exposing any potential barriers or blockers during the estimating of the ticket will allow them to be accounted for and even potentially solved before development starts.

Screenshots

Providing screenshots and adding arrows and text for clear communication that makes it very apparent what "this" and “that” are, thus avoiding pronoun trouble.

Documented Designs

Providing detailed requirements with exact values, sizes, states, etc. with considerations for edge cases will make any developer forever grateful. Style guides with documented font names, sizes, weights, etc. are another tool that will improve design workflow efficiency. Additionally, designs can provide:

  • Where every piece of information comes from.

  • How items should appear if a given field or other information source is empty.

  • How long or short variants of a given data point are handled and what logic controls programmatic truncation if applicable.

Contact Information

Providing the names and contact information for other team members who may hold key pieces of information that didn’t make it onto the ticket will prevent blockers and help new developers learn which team members have expertise in other areas. Additionally, providing contact information for external parties when working on third-party integrations will prevent communication gaps and middlemen. Sending a quick introduction when rolling a new person into the mix will get you bonus points.

Code Context

Taking the first step is the hardest part, but often a lead developer will know right where and how to proceed. Providing other developers with the names of functions or files to look for from someone with deeper knowledge of the codebase can save an immense amount of time up front and avoids potential refactoring down the road. It also reduces guesswork and more importantly, might reinforce a best practice when there are multiple approaches that would technically work. Examples of previous implementations or before-and-after code samples are also great things to consider providing.

Reliable Information

Up-to-date acceptance criteria that is verified as accurate and updated if/when requirements changed, and the ability for a developer to have 100% confidence in this information makes everyone’s life better.

Complete Logical Requirements

Thinking through default values or fallback values/logic if a given piece of data is empty by default, instead of treating it as an edge case, allows for cleaner code and reduces emergencies and "bugs" down the road.

Component Driven Separation

Giving everyone on the team discrete chunks of work that can be documented, developed, tested, and completed will allow everyone to feel much better about the progress of the project. Providing clearly defined subtasks that allow chunks of the work to be checked off and delineated clearly when working on larger tickets will help with this. Another key to success is having properly managed dependencies for blocking work so effort is not wasted until all blockers are resolved.

This can be accomplished by separating tickets by components, not pages. For example, "social share bar" and "comment widget" tickets that probably appear on every content type rather than a "article page" ticket that includes building and styling all of those components before it can be considered complete.

Exact URLs

When possible, always provide the exact URLs of pages where the offending bug can be found or the new functionality should be placed. This keeps anyone from making assumptions and when properly paired with a nice screenshot, it really takes the guesswork out of recreating bugs or finding where new features should live.

Reproducible Steps

In addition to exact URLs, a thorough lists of steps to reproduce the bug, the expected result, and the actual result will all help a developer quickly pinpoint and understand a problem. It is just as important for developers to provide QA and business stakeholders with steps to test functionality that is not intuitive or simple to test.

Assumptions & Changes

Finally, not all developers are the same. If you ask two developers to solve the same problem, you might get very different solutions, but the more details you provide them, the better the chance of a successful outcome. Additionally, a developer with a wealth of institutional knowledge might need significantly less information to have a clear picture of what needs to be done, while a new hire, internal transfer or external contractor will likely need more information.

However, I would argue that regardless of the expected assignee’s knowledge, the extra time spent to write a good ticket is rarely wasted. Tickets often get passed around as people take vacations, flat tires happen, and children get sick. When these things inevitably occur, we don’t want to rely upon assumptions, which, no matter how good, only need to be wrong once to cause potentially large amounts of wasted time, embarrassment, liability, etc.

Ways to Work This Into Your Project
  • Create a blocked status for your Agile or Kanban boards and utilize it liberally to ensure high visibility into the unknowns and show others how much time is lost due to blockers.

  • Loop project managers into conversations with the person who filed the initial ticket so they see first hand the level of effort required to track down answers.

  • Talk through the issues during sprint review and review what did and did not go well.

  • Allow developers to review and estimate issues before they are included in a sprint and require clear requirements before accepting a ticket.

  • Don’t just complain; be a part of the solution. Educate stakeholders on the structure you want to see for requirements and provide them with tools to help communicate more clearly such as logic maps, data flow diagramming tools, etc.

What Now?

I encourage you to think of a good ticket not just as a means to an end; be that a fixed bug, a new feature, or new design. Instead, treat it as an opportunity to show respect to each of the many professionals that will touch the ticket through its lifecycle by providing them the information and tools they need to perform their job to the best of their abilities. This respect goes in all directions, from ensuring good requirements for developers, to writing good testing instructions for QA, writing quality code for a great product and providing stable hosting infrastructure. This all culminates in a well-built project that shows respect to what this is ultimately all about: the user.

Categories:

Chromatic: Unloading jQuery in Drupal 8

Planet Drupal - Fri, 2016/01/22 - 3:16am

In the process of redesigning our site, we decided to axe jQuery early on. Analytics informed us that cross-browser compatibility wouldn’t be an issue, so we took the easy performance gain and stuck it out with vanilla JavaScript.

Beginning in version 8, Drupal does not load jQuery by default. Great - no issues for us! However, the long, shadowy spectre of jQuery haunted development.

Even if you’re not using jQuery, it can still load for anonymous users.

You have your libraries.yml file free of any dependencies, and you’re deep into development when you notice jQuery is still loading. Why? Contrib modules can still require jQuery.

For us, the culprit was the Google Analytics module. It’s a great contributed module, but because of the overhead it brought, we decided to create a custom module and attach our tracking code separately.

More to come.

I’m sure there will be other gotchas as D8 proliferates in the wild, but hopefully these two will save you some time in case you find yourself wondering, much like I was, why jQuery still cast its shadow. Now get back to it, and build something awesome.

Correction

Previously, this post stated that Drupal behaviors are dependent on drupal.js, which is in turn dependent on jQuery. This is incorrect and drupal.js in D8 is not dependent on jQuery. After running into some errors, this post on the JavaScript API led me to that conclusion. Apologies and thanks to @nod_ for his tweet and sending me down the right path!

Categories:

Chromatic: Creating Links Within Twig Templates Using path() and url()

Planet Drupal - Fri, 2016/01/22 - 3:16am

Drupal 8 comes packed with loads of great new features, APIs and developer tools. There are sweeping changes aplenty. Not the least of which is a brand new templating system called Twig. Twig is the bee's knees and a welcome improvement over the much maligned PHPTemplate. However, many front-end developers who've grown accustomed to the templating ways of old might feel a bit lost as they enter the strange, new world of Drupal 8: Twig and render arrays. Furthermore, the Drupal community is still learning, documentation is still being written, etc.

As we approach the completion of our first official Drupal 8 project here at Chromatic, I thought it would be helpful to start sharing a bit of what I've learned along the way, starting with some examples on linking from within Twig templates.

If you want to just see how the hell you link to things from within Twig templates, skip these next bits on context.

Some Context

In Drupal 7 and versions prior, we used paths to define destinations for our content, APIs, etc. In Drupal 8, these are abstracted into routes. Where in Drupal 7 you would define a custom page via hook_menu(), like so:

<?php function chromatic_conact_menu() { $items['contact'] = array( 'title' => 'Chromatic Contact', 'page callback' => 'contact_page', 'access arguments' => array('access content'), 'type' => MENU_SUGGESTED_ITEM, ); return $items; } ?>

In Drupal 8, you define this in a special routing file that follows this naming convention: module_name.routing.yml. Here's a similar example, where chromatic_contact_contact is our route name and contact is the internal path where it can be accessed.

chromatic_contact_contact: path: 'contact' defaults: _form: '\Drupal\chromatic_contact\Form\ChromaticContactForm' _title: 'Contact Us!' requirements: _permission: 'access content'

Here's how Drupal 8 sets up the route for node type creation:

node.type_add: path: '/admin/structure/types/add' defaults: _entity_form: 'node_type.add' _title: 'Add content type' requirements: _permission: 'administer content types' Got it. Routes are the new hotness in D8. Now why does this matter to me? I'm a front-ender.

Glad you asked. Routes matter because if you want to generate URLs to custom pages from your templates and you want to do it properly, you need to understand routes. The old methods of using paths are "dead in Drupal 8".

The url() and path() functions are how we handle this type of linking in D8. These functions don't expect an internal path like you're probably familiar with in prior versions of Drupal. Instead, they expect proper system routes. The type of thing you now see in module_name.routing.yml.

So what's the difference?
  • path() - Generates a [relative] URL path given a route name and parameters.
  • url() - Generates an absolute URL given a route name and parameters.
Examples

// Link to the default frontpage content listing view: <a href="{{ path('view.frontpage') }}">{{ 'View all content'|t }}</a> // Link to a specific node page: <a href="{{ path('entity.node.canonical', {'node': node.id}) }}">{{ 'Read more'|t }}</a> // Link to a specific user profile page: <a href="{{ path('entity.user.canonical', {'user': user.id}) }}">{{ 'View user profile'|t }}</a> // Link to a view, and throw in some additional query string parameters: <a href="{{ path('view.articles.page_1', {'page': 2}) }}">{{ 'Go to page 2'|t }}</a> // Link to a view and pass in some arguments to the view: <a href="{{ path('view.recent_articles_by_author.page_1', {'arg_0': user.field_display_name.value|drupal_escape }) }}">{{ 'See all the articles written by'|t }} {{ user.field_display_name.value }}</a>

The source code for the path() and url() Twig extensions can be found here within Drupal 8 core: /core/lib/Drupal/Core/Template/TwigExtension.php.

If you're curious about how all of this came to pass, here's the D.O issue on which the work occurred: https://www.drupal.org/node/2073811. It's long. Really long. The tldr; is basically a lot of back and forth over how routes are a barrier to entry for beginners and that there should still be an easy way to use paths. I honestly see both sides, but in the end, routes won out in the name of consistency and compatibility. The original patch contained a urlFromPath() function but it has since been removed. :-/. Beginner or not, you should now understand how to generate URLs within your templates. Now go! Commence Twigging!

One more thing!

The Drupal.org documentation page that explained these Twig functions (and others) was marked as incomplete, so I took this opportunity to finish it up. If you have other examples to share, please do so there!

Categories:

Chromatic: Drupal Camp Chattanooga 2015

Planet Drupal - Fri, 2016/01/22 - 3:16am

A few weekends ago I was fortunate enough to attend my first Drupal Camp ever. What was even more fortunate for me was that it was located near where I grew up in Chattanooga, TN. I’ve been to several leadership/business conferences in my life, but this is the first one that I’ve been to where it felt like everyone was genuinely glad to be there, even on a rainy fall day. In the spirit of open source, it wouldn’t feel right to keep all of the helpful info I learned to myself, so I wanted to take a moment and share some of my key take-aways.

Selling Drupal

Bob Snodgrass from net2Community gave a great seminar on sales. He started his presentation on selling Drupal by stating "Don’t sell Drupal, sell results!" So many times we become entrenched in the technology that we use and sell, and that’s ok!, but we have to remember that clients are more interested in the results we can provide, instead of the medium we use to get there. A good example given was a donut in a donut box. People rarely care about what box it comes in, they just want the donut!

Bob also brought attention to how each team member contributes to "selling", whether they realize it or not. The interactions, work and relationships that we manage on a daily basis help clients decide if they will continue to work with us or recommend us to their work colleagues.

Becoming a PM Ninja

Justin Rhodes from Commerce Guys led a good discussion on increasing efficiencies as a Project Manager. One of the first things we discussed was Parkingson’s Law: tasks will swell or shrink according to the amount of time given to them. If you use this law to your advantage, you can create small window deadlines to create efficiencies in your development process.

When it comes to planning and estimating phases for a project, Justin shared the success he has had doing this by using relativity to help the team give input. For example, rather than trying to nail down hours for each process step right way, you can tell everyone to estimate the size of the task by using shirt sizes (S, M, L, XL) to align everyone by using relative sizes. This can give the resource planner a clear visual of which parts of the project are going to take the longest. Of course, there are no complete replacements for old fashioned hourly estimates. The key to success on estimates is to check the team’s accuracy by comparing estimates against actual spend.

Contributing to Open Source

Our keynote speaker was Mike Anello from Drupal Easy. He gave a great talk on why we should be contributing back to the Drupal community. It wasn’t the typical, do it because it is the right thing to do, lecture. Mike made the case that not only does contributing back to the community grow your network and visibility, but it has a very real probability of increasing your bottom line. It is easy to say that we are going to contribute, but unless you deliberately set aside time, it probably isn’t going to happen. One way to make contributing a priority is to work it into your marketing budget. Switching your perspective on contributing from a way to get karma points to a powerful marketing tool, will change your priorities around where it fits into the company’s strategy.

The last take away I got from Mike’s talk was that even those of us who are nontechnical team members can contribute back to the Drupal community. Reaching out to module owners and asking how you can help, cleaning up ticket queues on modules, confirming bugs and assisting in documentation, are all ways to help move the ball forward (and I’m told they are greatly appreciated). Don’t forget that Drupal events don’t just happen by themselves. There is always a need for folks to help with the planning and coordination of meetups, camps and conventions.

Final Thoughts

Although I was unable to attend the pre and post party, I have no doubt that the enthusiasm of the group spilled into every conversation that was had. Who knows where I will be living next year (the benefits of working for a distributed company), but if I am able to, I’ll be returning to DrupalCamp Chattanooga!

(photo credit goes to @DrupalNooga)

Categories:

Chromatic: TheaterMania: Lessons Learned on Localization

Planet Drupal - Fri, 2016/01/22 - 3:16am

We recently launched a new site for an existing client, TheaterMania. We helped launch and currently maintain and develop The Gold Club, which is a subscription-based discount theater club in New York City. The new site is the same thing, but in London - same language, same codebase, new database, different servers. We only had to migrate users, which were already exported for us, so nothing exceptional there. Shouldn't be a big deal, right? We learned that's not always the case.

Architectural Decisions

One of our first problems, besides the obvious localization issues (currency, date formats, language), was to decide what we were shipping. Were we just building another site? Were we packaging software? There will most likely be more sites in other cities in the future - how far did we want to go in terms of making this a product that we could ship? In the end, we wound up going somewhere in the middle. We had to decide initially if we would use Organic Groups to have one site with multiple "clubs," one Drupal multisite installation, or multiple Drupal installations. The final decision was to combine the latter two choices - we created multisite-style directories so that if we need to take the site in a multi-site direction, we can easily do that. The sites each have a site-specific settings file, full of various configuration variables.

Now that the site has been launched, we're not sure if this list of variables will be developer-friendly moving forward, and have been keeping in mind that we may want a more elegant solution for this. The best part about this setup is that we have one codebase, one master branch, and each site is configured to use the appropriate settings. The most important thing is that this is all very thoroughly documented, both in the code, README files, and the repo wiki.

Currency & Recurly: Easier than Expected

One of the issues I thought would be very problematic was currency, but that wasn't actually an issue. All of the existing transactions are set up in cents - ie, 100 instead of 1.00 for a dollar, and that translates perfectly from dollars to pounds. We use Recurly, an external payment and subscription processor, so we didn't have to worry about any localization issues on that front. Most of the currency abstractions I did were to remove any hard-coded references to the dollar sign, and create functions and variables to get the appropriate currency symbol.

Dealing with Dates; Ugh.

Date formats were something I expected to be easy, but that wound up being more complex. I discovered hook_date_combo_process_alter() to change the display of the date in calendar popup fields. This made what I’d thought was going to be a difficult series of view handlers really simple. We have several fields using the date combo box on both content types and entities, and this function took care of them.

/** * Implements hook_date_combo_process_alter(). * * Changes the date format. */ function gc_display_date_combo_process_alter(&$element, &$form_state, $context) { if (isset($element['#entity']->type)) { switch ($element['#entity']->type) { case 'event': $element['value']['#date_format'] = variable_get('date_format_short'); break; case 'partner': $element['value']['#date_format'] = variable_get('date_format_short'); $element['value2']['#date_format'] = variable_get('date_format_short'); break; case 'promo_offer': $element['value']['#date_format'] = variable_get('date_format_short'); $element['value2']['#date_format'] = variable_get('date_format_short'); break; default: break; } } elseif (isset($element['#entity']->field_name)) { if ($element['value']['#instance']['widget']['type'] == 'date_popup' && $element['#entity']->field_name == 'field_user_csr_notes') { $element['value']['#date_format'] = variable_get('date_format_short'); } } }

I took the dozen or so existing date formats from Drupal, altered some of them to meet our needs, and added a few more. My head also started spinning when testing because I'm so used to M/D/Y formats that D/M/Y formats look really strange after a while, especially because code changes needed to be tested on the US and UK sites, so I had to be really careful when visually testing a page to make sure that a US page was showing 9/1/15 and the UK page was showing 1/9/15. In the future, I’d definitely advocate for a testing suite on a project like this. Overall, making sure all of the dates were changed was somewhat tedious, but not difficult. It required a lot of attention to detail and familiarity with PHP date formats, and vigorous testing by the whole team to make sure nothing had been missed.

Proper Use of t() Early == Wins Later

This project made me extremely grateful for the t() function. Since both sites were in English, we didn't have a need for site-wide translation, but we did need to localize a handful of strings, both for language issues (words like 'personalize' vs 'personalise'), and the general language preference of the stakeholders. It was easy enough to find the strings and list them in locale_custom_strings_en to switch them out. One gotcha we came across that I wasn't familiar with - you cannot use t() in your settings files. The function isn't available at that point in the bootstrapping. You can use get_t(), but we opted to remove the translation strings from any variables and make sure that t() was used when the variable was called. This wasn't something I had run into before, and it caused some problems before we figured it out.

Miscellany

A few tricky miscellaneous problems cropped up, too. There was a geolocation function enabled in Recurly, which was defaulting to the US and we were unable to change the settings - we also didn't realize this when testing in the US, and we scratched our heads when the London team told us the field was defaulting to US until we came across the culprit. We were able to fix it, and put in a patch for the library causing the issue.

I also realized how many various settings default to the US when working on this project - a lot of the location-related work was just abstracting out country defaults. Something to keep in mind if you're working on a project with locations. Don't make more work for developers who live or work on projects outside of the US. Plan for the future! Assume nothing!

Looking Back

I'm really glad that I worked on this project, because it's made me develop with a better eye for abstraction of all kinds, and making sure that it's easy for developers or users to work with my code anywhere. In the future, I’d put more thought into managing our configurations from the start, as well as automating the testing process, both for time-saving and better QA.

If you’ve ever worked on a site with challenges like these, I’d love to hear how you handled them! What are your best practices for managing custom locale strings and other site-specific variables? To what extent do you abstract things like dates and currency when developing a site, even when you don’t know if those will ever change?

Categories:

Chromatic: BADCamp 2015: Transitioning From theme() and Theme Functions to Render Arrays and Templates

Planet Drupal - Fri, 2016/01/22 - 3:16am

I was fortunate to attend and speak at BADCamp for the first time this year. BADCamp is the Bay Area Drupal Camp, held annually in Berkeley, CA. I don't know the official numbers, but I believe over 1,000 were in attendance, giving it the feel of a smaller DrupalCon. The camp was well organized, the sessions were high quality, and I met and got to know quite a few great people.

Anyone who wasn't able to attend can watch my session, 7 to 8: Transitioning From theme() and Theme Functions to Render Arrays and Templates, here:

My slides are also available online. The video and slides include in-depth examples, but for the TL;DW crowd more interested in the key takeaways:

Render Arrays > theme()

The theme() function no longer exists in Drupal 8. Instead, render markup by passing render arrays to the template. Start using render arrays instead of theme() in Drupal 7 right now. Not only will it make the code easier to port to Drupal 8, but there are other advantages.

Using render arrays allows for the data to remain an array until the template renders it into markup. Waiting to render markup allows other modules or themes to intercept and alter the data before it is rendered. This is typically done with preprocess hooks.

Templates > Theme Functions

When Drupal 7 goes to render markup, it does so with either a theme function, such as theme_image(), or a template. Theme functions contain a lot of logic and are a tough way to write and alter markup. Overriding theme functions also involves copying a significant amount of code to make changes.

Instead of writing theme functions, write templates. Keep the logic in preprocess and process functions. This separation will make altering the logic and markup much easier. Other modules or themes can easily use preprocess hooks or override templates as needed.

Drupal 8 has taken this approach. While theme functions can still exist in Drupal 8, all core theme functions were converted to templates.

More information

For more on this topic, check out these resources on drupal.org:

There were also a number of other sessions at BADCamp related to this topic. They included:

Thanks again to the BADCamp organizers. I hope to see everyone again next year!

Categories:

Chromatic: Drupal 8 Configuration Management - Solving the Configuration Conundrum

Planet Drupal - Fri, 2016/01/22 - 3:16am

A common difficulty in web development is keeping configuration consistent between environments. Drupal keeps most configuration settings in the database and it can be a pain to keep databases across environments synchronized. Solving this conundrum was one of Drupal 8's goals and the focus of D8's Configuration Management Initiative (CMI). All site configuration is saved in YAML files (hooray for version control!) and can be shared between site environments.

The Way We Were

Let's take a classic example from before D8, using the beloved Views module. When you build a great new view on your local machine you still need to get it onto the production site. You could repeat what you did on your local machine and build the View by hand on prod, but that's just repeating all the work you have already completed, not to mention, prone to user error. Most developers are too lazy to do that anyways, so they depend on Drupal's Features module. The Features module works by exporting db configuration settings into Drupal module code which can be shared amongst environments. It usually works pretty well, but it turns out the Features module was not built for handling deployment and configuration. These tasks have fallen into its lap because, until now, it was the only game in town.

The Way We're Going

With every Drupal 8 installation, the configuration system creates a configuration directory*, sync, to hold the aforementioned YAML configuration files. The directory is actually created as a sub-directory in sites/default/files, into a folder with a long, random hash as its name, prefaced by config_. (Including a random hash makes it near impossible for bad guys to guess the configuration folder name.)

* At the time of writing the default installation actually created two directories: active and staging, but that was changed to just one, the staging directory which subsequently was changed to sync. The blog post has been updated to reflect this.

In picture form...a folder with a really, really long, random name

So in the above screenshot the configuration directory's name is

config_Z1fHk5YnKXiTdJl9lAfz_dqGmrMTzqT9lNnbUF6z4kwKxglnC8srJZBTcI1dIMSCOmOwvEMZ5g

and the full path of the sync directory is:

sites/default/files/config_Z1fHk5YnKXiTdJl9lAfz_dqGmrMTzqT9lNnbUF6z4kwKxglnC8srJZBTcI1dIMSCOmOwvEMZ5g/sync

So that's where the site configuration files live, but by default, for reasons of speed and security, the actual configuration settings are saved in the database. So all the configuration from those YAML files actually gets imported into the database.

The configuration management system provides a UI for uploading and double-checking changes before applying them to the live site. (Of course, there are Drush commands, drush config-export and drush config-import that allow you to import/export through the command line too.)

How do you Feel About Clones?

Now hold on, you can't upload one site's configuration to any old site; you can only synchronize configuration settings between cloned instances of a site. This restriction was a conscious decision made by the Drupal CMI team. So you need to start with one environment and then clone it. If you don't do this, Drupal simply will not allow configuration settings to be imported.

Steps for cloning a site:
  1. Do a db dump of the original database and import it into an empty database for your new clone environment.

  2. Assuming you are using git, (you are using git, right?) you can run git clone to clone the original site files into the clone environment.

  3. Check folder permissions (Did .htaccess come across? Does sites/default/files have sufficient permissions?).

  4. Change settings.php in cloned site to point to the fresh db from step 1.

Assuming your cloned site is working, you are ready to import settings from one environment to another. In my case, I created a new view called bookshelf that needs to get to prod. Below are screenshots of what the process looks like.

Export and Download from Dev

In this screenshot, I have just exported and downloaded my dev environment's configuration tar.gz file.

Upload into Prod

Having switched to my prod environment, I select the downloaded tar.gz file and upload it.

Successful Upload

After a successful upload you can see that there are changes. Note that my new bookshelf view is there. I can click the View differences button to compare the changes.

Vive la Difference

Having clicked the View differences button, I see the changes that are about to be made.

Import All

Satisfied with what I see, I can click Import all to apply the changes.

Import Progress

A progress bar displays as Drupal imports the settings.

A Site With a View

Voilà, the new view is on my prod site.

Whew, so there were a bunch of steps to get things set up, but once you have your environments in place, it's easy to move settings between them. Of course, in a full-on, professional development environment you would harness the power of Drush and use scripts to do all the dirty work. But that's a tutorial for another time.

Categories:

Chromatic: Programatically Creating and Storing WordPress Migrate Migrations in Drupal

Planet Drupal - Fri, 2016/01/22 - 3:16am

Migrations are never glamorous, but doing them right and verifying their integrity is essential to their success. The WordPress Migrate module gives you an easy turnkey solution to migrating content into Drupal from WordPress. It allows you to create each migration through an interactive admin form, allowing you to configure your migration entirely through the UI. This is great, but it does not make creating or storing the resulting migrations easy to manage across multiple environments, since the migrations are not defined in code like a typical Migrate class. Short of copying database tables or re-entering the configuration through the admin forms, developers are stuck with the migrations stored in a single database and thus it is not easy to move to other environments for testing or further development.

Copying data tables is almost always the wrong solution and manually re-entering all of the migrations would be way too time consuming, so our solution was to create the migrations programmatically. To do this, we hooked into the existing WordPress Migrate codebase and used its logic to build programmatically, what it builds from data input to its admin forms. Then we are able to define all of our migration sources in code and instantly create all of our migrations in a new environment, or recreate them after something fails during development.

As mentioned, this solution relies upon programmatically submitting admin forms, which is often not an ideal scenario. Additionally, there is the almost inevitable request to add additional customizations beyond what Wordpress Migrate supports out of the box. Sometimes this makes WordPress Migrate more of a hinderance than a help. So why not just create a custom Migrate class from the outset and avoid all of these issues? Here are some factors to consider:

  • Writing a custom Migrate class for your WordPress content always sounds more appealing until you run into problems and realize WordPress Migrate already solved those issues.
  • The WordPress Migrate module offers a lot of functionality, including file transfer, author migration, embedded video processing, internal link rewriting, comment migration, etc.
  • You might not need much custom code and just tweaking the WordPress Migrate functionality by extending one of its classes will easily do the trick.
  • You might not have the resources (time, knowledge, etc.) to write a custom Migrate class.
  • Running and testing the migrations on multiple environments might not be in your workflow, although I would argue it should be.
  • You might only have one or two WordPress sites to migrate content from, so manually re-creating them is not an issue.

If after weighing all of the factors, you decide using the WordPress Migrate module is in your best interest and manually recreating the migrations is not an option, then follow along as we walk you through our approach to creating and storing WordPress Migrate migrations programmatically.

Our Solution

First we need to define the list of source blogs. The keys of each item in this array can be added to as needed to override the default values we assign later.

/** * Define the WordPress blogs to be imported. */ function example_wordpress_migrate_wordpress_blogs() { // Any key not set here will default to the values set in the // $blog_default_settings variable in the drush command. $blogs = array( array( 'domain' => 'www.example.com/site-one/', ), array( 'domain' => 'www.example.com/site-two/', ), array( 'domain' => 'www.test.com/', ), ); return $blogs; }

Next we'll create a custom drush command so that we can easily trigger the creation of our migrations from the command line.

/** * Implements hook_drush_command(). */ function example_wordpress_migrate_drush_command() { $items = array(); // Creates WordPress migrations. $items['example-migrate-create-wordpress-migrations'] = array( 'description' => 'Creates the WordPress migrations.', 'aliases' => array('mcwm'), ); return $items; }

Be sure to note the example_migrate_wordpress_password variable below, as you will need to ensure you set that in settings.php before creating the migrations. The WordPress Migrate code needs to be able to login to your site to download the source XML file, and a password is paramount to the success of that operation!

/** * Callback for WordPress migration creation drush command. */ function drush_example_wordpress_migrate_create_wordpress_migrations() { // Reset the file_get_stream_wrappers static cache so the 'wordpress' stream // wrapper created by the wordpress_migrate module is available. $wrappers_storage = &drupal_static('file_get_stream_wrappers', NULL, TRUE); // The wordpress_migrate module's UI is a multi-step form that collects all // configuration needed to migrate a given blog. As this form's steps are // submitted and validated, an export file is downloaded for each blog and its // contents are migrated. There is no easy way to export these settings or use // code to provide that configuration and then trigger a migration, so the best // bet is simulate the submission of those form steps with the needed data. module_load_include('inc', 'migrate_ui', 'migrate_ui.wizard'); // Get a list of blogs to migrate. $blogs = example_migrate_wordpress_blogs(); $blog_default_settings = array( 'source_select' => '1', 'domain' => '', 'username' => 'admin', 'password' => variable_get('example_migrate_wordpress_password', ''), 'wxr_file' => NULL, 'do_migration' => 0, 'default_author' => 'admin', 'page_type' => '', 'blog_post_type' => 'story', 'path_action' => 1, 'tag_field' => '', 'category_field' => '', 'attachment_field' => '', 'text_format' => 'filtered_html', 'text_format_comment' => 'filtered_html', ); // Import each of the blogs. foreach ($blogs as $blog_settings) { // Combine the default settings and the custom per blog settings. $blog_settings = array_merge($blog_default_settings, $blog_settings); // Skip the import if no username or password was found. if (empty($blog_settings['username']) || empty($blog_settings['password'])) { $message = t('The :site-name migration was not created since no username and/or password could be found. Verify that the example_migrate_wordpress_password variable has been set.'); $replacements = array( ":site-name" => $blog_settings['domain'], ); drupal_set_message(t($message, $replacements), 'warning'); continue; } // Set the form state values. $form_state['values'] = $blog_settings; // Store the values so we can use them again since $form_state is // a reference variable. $form_state_values = $form_state['values']; // Build the import form. $form = drupal_get_form('migrate_ui_wizard', 'WordPressMigrateWizard'); $form = migrate_ui_wizard($form, $form_state, 'WordPressMigrateWizard'); // Create a Migrate Wizard object. $form_state['wizard'] = new WordPressMigrateWizard(); // Set the number of steps in the form. $form_steps = 6; // Go through all of the steps. foreach (range(1, $form_steps) as $step) { // Validate the form data. $form_state['wizard']->formValidate($form_state); // Submit the form page. migrate_ui_wizard_next_submit($form, $form_state); // Put any values removed from the array back in for the next step. $form_state['values'] = array_merge($form_state_values, $form_state['values']); } // Submit the form. drupal_form_submit('migrate_ui_wizard', $form_state); // Save the settings into the wizard object. $form_state['wizard']->formSaveSettings(); // Notify the user that the migration was created successfully. $replacements = array( '@site-name' => $blog_settings['domain'], ); $message = t('The @site-name migration was successfully created.', $replacements); drupal_set_message($message, 'success'); } }

With all of this in place, the source WordPress sites and the configuration needed to import them are now fully defined in code along with a custom Drush command to create the required migrations. No longer will each individual site need to be re-entered through the UI introducing opportunities for mistakes and wasted time.

Now when you are in a new environment or after you reset your migrations, you can simply run drush mcwm.

Following its successful completion, the following are done for you:

  • A new Migrate group is created for each individual blog.
  • The actual Migrate classes within each group that migrate, authors, content, terms, and attachments are created and configured as defined in the code.
  • The source WordPress XML file is downloaded for each site and stored in wordpress://.

Then simply run drush ms to verify everything was created successfully, and you are ready to migrate some content!

Now that you have the tools and knowledge to evaluate your unique migration needs, you can make a well informed decision if this approach is right for you. However, we think that more often than not, all of the incredible functionality you get pre-built with the WordPress Migrate module will outweigh the issues that arise from not being able to fully build and store your migrations in code, especially when you add the functionality outlined above that gets you the best of both worlds. So have your cake and eat it too, and define your migrations in code and utilize the WordPress Migrate module while you are at it!

If you decide to go this route, all of the code referenced here is available in this gist. Please note that this was all built for WordPress Migrate 7.x-2.3, so future updates to the module could break this functionality.

Categories:

Chromatic: Presenting at DrupalCamp Asheville 2015

Planet Drupal - Fri, 2016/01/22 - 3:16am

Last weekend I enjoyed the awesomeness of DrupalCamp Asheville. The Asheville Drupal User Group did an amazing job once again. It was a great camp filled with positive people and smart sessions. This time around I was lucky enough to deliver my own presentation, SVG: How To Have Fun Making Your Site Look Hot!.

The session was filled with a great mix of people of all skill levels - from having little SVG exposure to people who employ it in all their projects. The talk catered to both as we reviewed everything from the basics of understanding SVG and getting it displayed on your site to more advanced topics like line-stroke animations and SVG filters.

This was my first presentation at a DrupalCamp, and it was very rewarding. I think everyone learned something, and it felt great to give back to a community that shares so much information on a daily basis.

Overall, the presentations were on point. A few sessions I attended that stood out were Bayo Fodeke and Mark Shropshire’s Decoupled Drupal with Meteor, Chris Russo’s Personal git workflow, for everyone! and Matt Davis’ Headless Challenges Answered: Weather.com's Presentation Framework.

Besides being a great camp, it’s located in Asheville, which is a true gem of the East Coast with vibrant arts, food and beer scenes. If you get the chance, you should definitely try to attend next year's camp. Whether you’ve never attended a camp or you’re a grizzled veteran, you’ll love the setting, the sessions, and best of all, the awesome people you’ll meet. See you in 2016!

Categories:

OSTraining: Custom Layouts for Webforms in Drupal 7

Planet Drupal - Fri, 2016/01/22 - 1:18am

One of our users asked for a way to customize the layout for his Drupal 7 webforms.

Webforms Layout is a module that extends Webform features by providing more control over the design.

In this post, I'll share with you how to use Webform Layout module. Let's start...

Categories:

Savas Labs: How to subscribe users to MailChimp lists in a Drupal custom module

Planet Drupal - Fri, 2016/01/22 - 1:00am

A demonstration on how to use Composer Manager and the MailChimp PHP library to simply and easily subscribe users to mailing lists without using the MailChimp contributed module.

Continue reading…

Categories:

jordanpagewhite: Drupal Features, Deployment Module, and Drush Aliases

Planet Drupal - Fri, 2016/01/22 - 1:00am

Features allows you to bundle entities, and their configurations, into a feature module that is written to code. So, what is so great about that? Can't you accomplish the same functionality by building out your views, content types, etc. through the Drupal admin UI. Yes, you can, but all of the entities and configuration will be saved in the database, as opposed to in code. Saving your entities and configuration to code is an immense benefit, if not necessary, for Drupal sites that are deployed across multiple environments, and even more if there are multiple developers working on the site.

Categories:

Darren Mothersele: Session submission guidelines for conferences and camps

Planet Drupal - Fri, 2016/01/22 - 1:00am

Earlier this week I found out that the date for my nephew's christening has changed. This means I can now make it to the Drupal Camp in London. I decided to propose a session so that I can share my recent insights, research, and learning.

The Drupal Camp London team have kept the session submission process minimal. This makes it easy for people to submit proposals, but it makes it unclear what the expectations are. What are they looking for in proposed sessions? How long are the sessions expected to be? What is the review process?

As I only found out I could attend at the last minute, I had no time to enquire about the process. So I made some assumptions based on my experiences of programming at previous camps. I also did some research into other Drupal camp and conference session submission processes. The Amsterdam Drupalcon site had this to say:

"There’s a lot of elements to session selection. We need to make sure that sessions are of value to a wide audience. The presenters must be engaging speakers who can interest a large crowd of attendees. We try as hard as we can to bring in new (to DrupalCon) speakers, and speakers who bring something from outside of the Drupal sphere. We want to make sure that the diversity of the community is represented and encouraged. And we need to work across track teams to ensure that one speaker is not speaking in several tracks; both for the sake of their stress and sanity in preparing the talks and to ensure that everyone who applied has the best chance of speaking. Finally, we need to make sure that sessions fit both the theme of the track and of the conference."

While the Barcelona Drupalcon offered the following 3 part session submission formula...

Part 1: Describe a compelling topic that affects the reader directly, maybe a pain point, or juicy new technology.
Part 2: Allude to your solution, sharing just enough that the reader has a moment of "I need that ...how do I not know this already?"
Part 3: Explain how awesome the session will be because you are going to cover "XYZ" and make the reader's life so much better.

My session selection criteria

I realised that I've done session selection myself many times. At every conference, camp, and symposium I attend I make my own selection of sessions to attend. The key to making the most out of attending a conference is to attend the right sessions.

At Resonate last year I set the intention of broadening my horizons, so intentionally selected session that I would not normally be exposed to. And sessions that offered something new. At DrupalCon in Amsterdam I set the intension of getting as deep into Drupal 8 as possible. So I attended every D8 session I could. Selecting the sessions I thought offered the most opportunity for learning. It helps if they are recognisable names, or people I've seen present before and know they do a good session.

When I think about my own criteria, I get it down to five things...

Importance

Is this definitely a session worth attending? Is the session topic cutting edge, or immediately relevant to my situation. Is it ground breaking or offer a significant contribution to the field?

Purpose

Is this the right type of session? Is the purpose of the session clear? Does the description make it clear what the expected participant outcomes are? Does the description give enough specific information to make me want to know more?

Motivation

Is the session motivated by theory, practice, and/or research? Is this presented in a detailed, thorough, and comprehensible way?

Substantiated

Are all claims (practices, conclusions, proposals) well substantiated. Is the speaker presenting something they know about? Is the speaker well positioned to be giving the session.

Clarity

Is the session proposal clear? A well written session description indicates the presentation itself will be of professional quality.

I hope I managed to purvey these five points in my own session proposal. I'd love to hear from you if you have your own ideas about what makes a good Drupalcamp session. If you drop me a line or leave a comment I will take them into account while planning my own session.

Thanks,

Darren

Categories:

OSTraining: Entity Reference Views Help Drupal Content Creators

Planet Drupal - Thu, 2016/01/21 - 11:24pm

Entity Reference Views are one way you can make life easier for Drupal content creators.

Normally, when people are creating content on your site, each field consists of a single box with a single data point. For example, in a list of people, you might get only the person's name. 

Entity Reference Views allow you to provide far more information. For example, you can add photos and personal details to your list of people.

Categories:

Freelock : 8 Reasons Why Drupal 8

Planet Drupal - Thu, 2016/01/21 - 10:29pm

Drupal 8 has been out for 2 months now, and there's never been a better time to choose Drupal for your website platform. Here are 8 reasons why!

1. Mobile Experience

Drupal 8 is mobile first. Every bit of Drupal 8 can be easily managed from your smartphone, with a responsive experience that works well right up to the widest displays.

You don't need a separate mobile site.

DrupalDrupal 8Drupal PlanetDevOpsQuality
Categories: