BAD Monday 1st August: Drupal Development - Take Control with Code-driven Development & Finalising Drupal Discovery Day Schedule

We encourage users to post events happening in the community to the community events group on https://www.drupal.org.
Anonymous's picture
Start: 
2011-08-01 19:00 - 21:00 Europe/London
Organizers: 
Event type: 
User group meeting

The Brighton Area Drupal Association invite you to our meeting on Monday 1st August 7-9pm at http://theskiff.org in downtown Brightoncisco!

This will be the first of our 'new-format' meetings - on 1st Monday of each month we will focus on developing using Drupal, on 3rd Monday of each month we will focus on learning Drupal from an end-user's point of view.

Topics at this month's development meeting are:

7-8pm: Take Control with Code-driven Development

One of the main issues when developing sites using Drupal is the amount of settings stored in the database. This week Steve Purkiss will show how you can take control of your sites using Features, Profiler, and Drush make to create an install profile for every site you build, so instead of having to carry a big database around from developer to designer, database-independent development can be achieved, with the benefit of being able to try out new modules and rebuild your sites in a matter of minutes, and eliminating the jenga-like stack which database-driven development ends up causing.

8-9pm: Finalising Drupal Discovery Day Schedule

In order to be able to create and distribute promo material about our Drupal Discovery Day on September 16th we need to finalise more of the schedule for the event. We now have a good list of speakers, including Jakub Suchy, Director of European Professional Services for Acquia - if you'd like to join the list then please come along to the meeting and suggest your topic/session - non-technical please as this is a business event!

9pm+ pub (usually The Basket Makers)

This meeting is open to anyone with an interest in the Drupal CMS/Web App Platform - we look forward to seeing you @theskiff, Monday 1st August at 7pm.

Comments

Thanks for coming along!

stevepurkiss's picture

Thanks for coming along, had a great night!

Here's the main modules I used to do the magic last night:

http://drupal.org/project/drush
http://drupal.org/project/drush_make
http://drupal.org/project/features
http://drupal.org/project/profiler
http://drupal.org/project/strongarm
http://drupal.org/project/defaultcontent

Note on Profiler: Using master branch and removing remove blocks code patch/hack! (i.e. if you look in the code for 'block' you'll see a routine where it deletes all the blocks, remove the code! It's removed in the other branch but it's taking a while to get it done on this one so I just host locally a version I like for my needs...

Here's the session it's based on:
http://www.archive.org/details/drupalconchi_day2_from_zero_to_distributi...

Locally I have set up the following:
Sites
Sites/sites
Sites/sites/mysite1
Sites/sites/mysite2
Sites/components/features
Sites/components/modules
Sites/components/profiles
Sites/components/themes
Sites/components/libraries
Sites/components/patches
Sites/scripts
Sites/scripts/rebuildtest

In the rebuildtest script I just have the drush make command specific for my site and the drush si (site install) again specific for my particular needs. I also have commands to rebuild/update the repositories for my custom features, modules, themes, etc. so they get rebuild as I develop.

There's probably 'better' ways, but this is the way that suits me right now - Aegir is also good for platforms and worth checking out.

I'll be making something more informative to pass around now I can see how the first run went, in the mean time don't hesitate to ask any questions!

Perfect timing

thecodecutter's picture

I was planning to look at Features and Profiler so you've saved me a whole load of time.

Thanks all

YMMV

stevepurkiss's picture

cool! All I can say is... YMMV ;) My directory structure was just put in place for last night really, don't usually store everything under sites. Works though, and that's the main point!

One point I left out from above is most of the profile you can copy from the standard profile included in D6/D7 - mostly the .install and .info files for setting up the default input filters etc.

I'm posting my code below, will format it better/more useful over time but might as well dump it here...

Sites/components/profiles/test/testprofile.make

core = 7.x
api = 2

projects[] = "drupal"

projects[test][type] = "profile"
projects[test][download][type] = "get"
projects[test][download][url] = "http://profiles/test/test.tgz"

Sites/components/profiles/test/test/test.info

name = test
description = Test distribution
core = 7.x

; Core
dependencies[] = block
dependencies[] = color
dependencies[] = comment
dependencies[] = contextual
dependencies[] = dashboard
dependencies[] = help
dependencies[] = image
dependencies[] = list
dependencies[] = menu
dependencies[] = number
dependencies[] = options
dependencies[] = path
dependencies[] = taxonomy
dependencies[] = dblog
dependencies[] = search
dependencies[] = shortcut
;dependencies[] = toolbar
dependencies[] = overlay
dependencies[] = field_ui
dependencies[] = file
dependencies[] = rdf

; Contrib
dependencies[] = admin_menu
dependencies[] = admin_menu_toolbar
dependencies[] = ctools
dependencies[] = features
dependencies[] = module_filter
dependencies[] = views
dependencies[] = views_ui
;dependencies[] = partner
; Users

users[siteadmin][uid]    = 2
users[siteadmin][name]   = siteadmin
users[siteadmin][mail]   = you@yourdomain.com
users[siteadmin][roles]  = administrator
users[siteadmin][status] = 1

; Variables
variables[site_footer] = Test Profile
variables[site_frontpage] = node/1
variables[theme_settings][toggle_node_info_page] = 0

; Nodes
nodes[front][type] = page
nodes[front][title] = Isn't Profiler nifty?
nodes[front][uid] = 2
nodes[front][body] = Woo-yay!

Sites/components/profiles/test/test/test.make

; Test Drush Make File

core = 7.x

api = 2

; Modules
; -------

; Admin Menu
projects[admin_menu][version] = "3.0-rc1"
projects[admin_menu][subdir] = "contrib"

; CTools
projects[ctools][version] = "1.0-rc1"
projects[ctools][subdir] = "contrib"

; Default Content
projects[defaultcontent][version] = "1.0-alpha4"
projects[defaultcontent][subdir] = "contrib"

; Features
projects[features][version] = "1.0-beta3"
projects[features][subdir] = "contrib"

; Module Filter
projects[module_filter][version] = "1.4"
projects[module_filter][subdir] = "contrib"

; Omega Tools
;projects[omega_tools][version] = "3.0-beta2"
;projects[omega_tools][subdir] = "contrib"

; Partner feature
;projects[partner][download][type] = "get"
;projects[partner][download][url] = "http://features/partner.tgz"

; Strongarm
projects[strongarm][download][type] = "git"
projects[strongarm][download][revision] = "b53da0793b85fb1ff16d41747098386fd546d22d"
projects[strongarm][subdir] = "contrib"

; Views
projects[views][version] = "3.0-rc1"
projects[views][subdir] = "contrib"

; Themes
; ------

; Omega
;projects[omega][version] = "3.0-rc2"
;projects[omega][subdir] = "contrib"

; Libraries
; ---------

; Profiler
libraries[profiler][download][type] = "file"
libraries[profiler][download][url] = "http://libraries/profiler/profiler.tgz"
libraries[profiler][directory_name] = "profiler"

Sites/components/profiles/test/test/test.profile

<?php
!function_exists('profiler_v2') ? require_once('libraries/profiler/profiler.inc') : FALSE;
profiler_v2('test');

Sites/components/profiles/test/test/test.install

<?php
/
* @file test.install
* Install file for the test profile.
*/

/

* Implementation of hook_install().
*/
function test_install() {
  // Add text formats.
  $filtered_html_format = array(
    'format' => 'filtered_html',
    'name' => 'Filtered HTML',
    'weight' => 0,
    'filters' => array(
      // URL filter.
      'filter_url' => array(
        'weight' => 0,
        'status' => 1,
      ),
      // HTML filter.
      'filter_html' => array(
        'weight' => 1,
        'status' => 1,
      ),
      // Line break filter.
      'filter_autop' => array(
        'weight' => 2,
        'status' => 1,
      ),
      // HTML corrector filter.
      'filter_htmlcorrector' => array(
        'weight' => 10,
        'status' => 1,
      ),
    ),
  );
  $filtered_html_format = (object) $filtered_html_format;
  filter_format_save($filtered_html_format);

  $full_html_format = array(
    'format' => 'full_html',
    'name' => 'Full HTML',
    'weight' => 1,
    'filters' => array(
      // URL filter.
      'filter_url' => array(
        'weight' => 0,
        'status' => 1,
      ),
      // Line break filter.
      'filter_autop' => array(
        'weight' => 1,
        'status' => 1,
      ),
      // HTML corrector filter.
      'filter_htmlcorrector' => array(
        'weight' => 10,
        'status' => 1,
      ),
    ),
  );
  $full_html_format = (object) $full_html_format;
  filter_format_save($full_html_format);

  // Enable some standard blocks.
  $default_theme = variable_get('theme_default', 'bartik');
  $admin_theme = 'seven';
  $values = array(
    array(
      'module' => 'system',
      'delta' => 'main',
      'theme' => $default_theme,
      'status' => 1,
      'weight' => 0,
      'region' => 'content',
      'pages' => '',
      'cache' => -1,
    ),
    array(
      'module' => 'search',
      'delta' => 'form',
      'theme' => $default_theme,
      'status' => 1,
      'weight' => -1,
      'region' => 'sidebar_first',
      'pages' => '',
      'cache' => -1,
    ),
    array(
      'module' => 'node',
      'delta' => 'recent',
      'theme' => $admin_theme,
      'status' => 1,
      'weight' => 10,
      'region' => 'dashboard_main',
      'pages' => '',
      'cache' => -1,
    ),
    array(
      'module' => 'user',
      'delta' => 'login',
      'theme' => $default_theme,
      'status' => 1,
      'weight' => 0,
      'region' => 'sidebar_first',
      'pages' => '',
      'cache' => -1,
    ),
    array(
      'module' => 'system',
      'delta' => 'navigation',
      'theme' => $default_theme,
      'status' => 1,
      'weight' => 0,
      'region' => 'sidebar_first',
      'pages' => '',
      'cache' => -1,
    ),
    array(
      'module' => 'system',
      'delta' => 'powered-by',
      'theme' => $default_theme,
      'status' => 1,
      'weight' => 10,
      'region' => 'footer',
      'pages' => '',
      'cache' => -1,
    ),
    array(
      'module' => 'system',
      'delta' => 'help',
      'theme' => $default_theme,
      'status' => 1,
      'weight' => 0,
      'region' => 'help',
      'pages' => '',
      'cache' => -1,
    ),
    array(
      'module' => 'system',
      'delta' => 'main',
      'theme' => $admin_theme,
      'status' => 1,
      'weight' => 0,
      'region' => 'content',
      'pages' => '',
      'cache' => -1,
    ),
    array(
      'module' => 'system',
      'delta' => 'help',
      'theme' => $admin_theme,
      'status' => 1,
      'weight' => 0,
      'region' => 'help',
      'pages' => '',
      'cache' => -1,
    ),
    array(
      'module' => 'user',
      'delta' => 'login',
      'theme' => $admin_theme,
      'status' => 1,
      'weight' => 10,
      'region' => 'content',
      'pages' => '',
      'cache' => -1,
    ),
    array(
      'module' => 'user',
      'delta' => 'new',
      'theme' => $admin_theme,
      'status' => 1,
      'weight' => 0,
      'region' => 'dashboard_sidebar',
      'pages' => '',
      'cache' => -1,
    ),
    array(
      'module' => 'search',
      'delta' => 'form',
      'theme' => $admin_theme,
      'status' => 1,
      'weight' => -10,
      'region' => 'dashboard_sidebar',
      'pages' => '',
      'cache' => -1,
    ),
  );
  $query = db_insert('block')->fields(array('module', 'delta', 'theme', 'status', 'weight', 'region', 'pages', 'cache'));
  foreach ($values as $record) {
    $query->values($record);
  }
  $query->execute();

  // Insert default pre-defined node types into the database. For a complete
  // list of available node type attributes, refer to the node type API
  // documentation at: http://api.drupal.org/api/HEAD/function/hook_node_info.
  $types = array(
    array(
      'type' => 'page',
      'name' => st('Basic page'),
      'base' => 'node_content',
      'description' => st("Use <em>basic pages</em> for your static content, such as an 'About us' page."),
      'custom' => 1,
      'modified' => 1,
      'locked' => 0,
    ),
    array(
      'type' => 'article',
      'name' => st('Article'),
      'base' => 'node_content',
      'description' => st('Use <em>articles</em> for time-sensitive content like news, press releases or blog posts.'),
      'custom' => 1,
      'modified' => 1,
      'locked' => 0,
    ),
  );

  foreach ($types as $type) {
    $type = node_type_set_defaults($type);
    node_type_save($type);
    node_add_body_field($type);
  }

  // Insert default pre-defined RDF mapping into the database.
  $rdf_mappings = array(
    array(
      'type' => 'node',
      'bundle' => 'page',
      'mapping' => array(
        'rdftype' => array('foaf:Document'),
      ),
    ),
    array(
      'type' => 'node',
      'bundle' => 'article',
      'mapping' => array(
        'field_image' => array(
          'predicates' => array('og:image', 'rdfs:seeAlso'),
          'type' => 'rel',
        ),
        'field_tags' => array(
          'predicates' => array('dc:subject'),
          'type' => 'rel',
        ),
      ),
    ),
  );
  foreach ($rdf_mappings as $rdf_mapping) {
    rdf_mapping_save($rdf_mapping);
  }

  // Default "Basic page" to not be promoted and have comments disabled.
  variable_set('node_options_page', array('status'));
  variable_set('comment_page', COMMENT_NODE_HIDDEN);

  // Don't display date and author information for "Basic page" nodes by default.
  variable_set('node_submitted_page', FALSE);

  // Enable user picture support and set the default to a square thumbnail option.
  variable_set('user_pictures', '1');
  variable_set('user_picture_dimensions', '1024x1024');
  variable_set('user_picture_file_size', '800');
  variable_set('user_picture_style', 'thumbnail');

  // Allow visitor account creation with administrative approval.
  variable_set('user_register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL);

  // Create a default vocabulary named "Tags", enabled for the 'article' content type.
  $description = st('Use tags to group articles on similar topics into categories.');
  $help = st('Enter a comma-separated list of words to describe your content.');
  $vocabulary = (object) array(
    'name' => st('Tags'),
    'description' => $description,
    'machine_name' => 'tags',
    'help' => $help,

  );
  taxonomy_vocabulary_save($vocabulary);

  $field = array(
    'field_name' => 'field_' . $vocabulary->machine_name,
    'type' => 'taxonomy_term_reference',
    // Set cardinality to unlimited for tagging.
    'cardinality' => FIELD_CARDINALITY_UNLIMITED,
    'settings' => array(
      'allowed_values' => array(
        array(
          'vocabulary' => $vocabulary->machine_name,
          'parent' => 0,
        ),
      ),
    ),
  );
  field_create_field($field);

  $instance = array(
    'field_name' => 'field_' . $vocabulary->machine_name,
    'entity_type' => 'node',
    'label' => 'Tags',
    'bundle' => 'article',
    'description' => $vocabulary->help,
    'widget' => array(
      'type' => 'taxonomy_autocomplete',
      'weight' => -4,
    ),
    'display' => array(
      'default' => array(
        'type' => 'taxonomy_term_reference_link',
        'weight' => 10,
      ),
      'teaser' => array(
        'type' => 'taxonomy_term_reference_link',
        'weight' => 10,
      ),
    ),
  );
  field_create_instance($instance);


  // Create an image field named "Image", enabled for the 'article' content type.
  // Many of the following values will be defaulted, they're included here as an illustrative examples.
  // See http://api.drupal.org/api/function/field_create_field/7

  $field = array(
    'field_name' => 'field_image',
    'type' => 'image',
    'cardinality' => 1,
    'locked' => FALSE,
    'indexes' => array('fid' => array('fid')),
    'settings' => array(
      'uri_scheme' => 'public',
      'default_image' => FALSE,
    ),
    'storage' => array(
      'type' => 'field_sql_storage',
      'settings' => array(),
    ),
  );
  field_create_field($field);


  // Many of the following values will be defaulted, they're included here as an illustrative examples.
  // See http://api.drupal.org/api/function/field_create_instance/7
  $instance = array(
    'field_name' => 'field_image',
    'entity_type' => 'node',
    'label' => 'Image',
    'bundle' => 'article',
    'description' => st('Upload an image to go with this article.'),
    'required' => FALSE,

    'settings' => array(
      'file_directory' => 'field/image',
      'file_extensions' => 'png gif jpg jpeg',
      'max_filesize' => '',
      'max_resolution' => '',
      'min_resolution' => '',
      'alt_field' => TRUE,
      'title_field' => '',
    ),

    'widget' => array(
      'type' => 'image_image',
      'settings' => array(
        'progress_indicator' => 'throbber',
        'preview_image_style' => 'thumbnail',
      ),
      'weight' => -1,
    ),

    'display' => array(
      'default' => array(
        'label' => 'hidden',
        'type' => 'image',
        'settings' => array('image_style' => 'large', 'image_link' => ''),
        'weight' => -1,
      ),
      'teaser' => array(
        'label' => 'hidden',
        'type' => 'image',
        'settings' => array('image_style' => 'medium', 'image_link' => 'content'),
        'weight' => -1,
      ),
    ),
  );
  field_create_instance($instance);

  // Enable default permissions for system roles.
  $filtered_html_permission = filter_permission_name($filtered_html_format);
  user_role_grant_permissions(DRUPAL_ANONYMOUS_RID, array('access content', 'access comments', $filtered_html_permission));
  user_role_grant_permissions(DRUPAL_AUTHENTICATED_RID, array('access content', 'access comments', 'post comments', 'skip comment approval', $filtered_html_permission));

  // Create a default role for site administrators, with all available permissions assigned.
  $admin_role = new stdClass();
  $admin_role->name = 'administrator';
  $admin_role->weight = 2;
  user_role_save($admin_role);
  user_role_grant_permissions($admin_role->rid, array_keys(module_invoke_all('permission')));
  // Set this as the administrator role.
  variable_set('user_admin_role', $admin_role->rid);

  // Assign user 1 the "administrator" role.
  db_insert('users_roles')
    ->fields(array('uid' => 1, 'rid' => $admin_role->rid))
    ->execute();

  // Create a Home link in the main menu.
  $item = array(
    'link_title' => st('Home'),
    'link_path' => '<front>',
    'menu_name' => 'main-menu',
  );
  menu_link_save($item);

  // Update the menu router information.
  menu_rebuild();

  // Enable the admin theme.
  db_update('system')
    ->fields(array('status' => 1))
    ->condition('type', 'theme')
    ->condition('name', 'seven')
    ->execute();
  variable_set('admin_theme', 'seven');
  variable_set('node_admin_theme', '1');
}

Sites/scripts/rebuildtest (OSX specific, with set-up according to this article http://mearra.com/blogs/vesa-palmu/how-setup-mac-os-x-106-drupal-environ...) - I also rebuild my custom modules/themes/etc. in here too so it gets the latest versions of what I'm working on:

#!/bin/sh
cd /Users/<your_username>/Sites/sites
sudo rm -rf testprofile
drush make --prepare-install /Users/<your_username>/Sites/components/profiles/test/testprofile.make testprofile
cd testprofile
chmod -R 777 sites/default/files
drush si test --db-url=mysql://testprofile:testprofile@localhost:3306/testprofile --account-name=admin --account-pass=admin --account-mail=you@yourdomain.com --locale=GB --site-name="Test Profile" --site-mail=you@yourdomain.com -y

HTH...

Last point...

stevepurkiss's picture

Sorry for filling up inboxes!

There are other bits in order to get this all working - setting up the machine, git repositories, zips, etc. so I need to work on bringing it all together. What format works do you think? Blog post? Screencast? Both? What info do you need given the above?

Feel free to let me know before I blindly go along my happy but potentially not the best route!

I think

guy_schneerson's picture

I like a Screencast or slideshow accompanied by a post (if time allows for both), the Screencast is great for understanding the processes and the post works great as a reference especially if you are including actual code

Not in Brighton.

rodtatham's picture

Hi - just found out I'm not in Brighton that week, I'm afraid. Sereno will contribute to drinks, though - and I'll ask co-worker John Newton if he can get down to Lighthouse to support the event...

Rod

Thank you Steve

guy_schneerson's picture

and thank you Steve for a great talk

United Kingdom

Group notifications

This group offers an RSS feed. Or subscribe to these personalized, sitewide feeds:

Hot content this week