Drupal Blog by greg.harvey http://www.drupaler.co.uk/blog/drupal/1/page en Piping A File In To MySQL http://www.drupaler.co.uk/blog/piping-file-mysql <div class="field field-name-upload field-type-file field-label-hidden"><div class="field-items"></div></div><div class="field field-name-taxonomy-vocabulary-1 field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="/tags/mysql">mysql</a></div><div class="field-item odd"><a href="/tags/drupal">drupal</a></div><div class="field-item even"><a href="/tags/drupal-planet">drupal planet</a></div></div></div><div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even"><p>Our use case here is if you have a lot of data you want to insert in the MySQL - data that you might usually use the Drupal UI for, but there's so much of it you'd rather not if you can help it!</p> <p>The example here is for a client of ours. For this particular client we have to launch a few new sites a month, each one comes with a new access list of email addresses to add to the Drupal "Access rules" page. There's no bulk upload, and doing these one by one with the UI is laborious, to say the least!</p> <p>We could write a module, but it's an edge case really and it's pretty simple to deal with straight in MySQL. Enter the MySQL command, <code>LOAD DATA INFILE</code>.</p> <p>First, your database user needs to be able to load the "infile". To do this, login as MySQL root and execute a command like this (for your user, obviously) to give your site's MySQL use file access permissions:</p> <p><code>GRANT FILE ON *.* TO my_db_user@localhost;<br /></code></p> <p>Next up we need to prep a file. Don't worry about putting strings in <code>'</code> marks and all that - MySQL will sort itself out. It knows the data type of the column anyway. My input file looked like this (having copied and pasted a column of email addresses from a provided spreadsheet and used Replace in Gedit for Linux to provide the additional <code>,mail,1</code> at the end of each line):</p> <p><code>someone@hotmail.com,mail,1<br /> foo@yahoo.com,mail,1<br /> barperson@gmail.com,mail,1<br /> bob@ymail.com,mail,1<br /> etc.<br /></code></p> <p>I saved that on the server at <code>~/access-emails.txt</code> - now to pass it to MySQL:</p> <p>I used drush to login to MySQL with the <code>drush sql-cli</code> command from within the web root of the site and executed this command:</p> <p><code>mysql&gt; LOAD DATA INFILE '/home/greg/access-emails.txt'<br /> INTO TABLE access<br /> FIELDS TERMINATED BY ','<br /> LINES TERMINATED BY '\n'<br /> (mask, type, status);</code></p> <p>First bit is self-explanatory, as is the second. Then we tell MySQL what character the data is delimited by (I'm not sure on the default, so I explicitly stated a comma) and what a line ending looks like (again, not being sure of the default I explicitly stated a line break). Finally, and this is particularly important if there are auto_increment fields you need to deal with, which columns in MySQL the data in the rows maps to. In this case, the access table looks like this:</p> <p><code>mysql&gt; DESCRIBE access;<br /> +--------+--------------+------+-----+---------+----------------+<br /> | Field | Type | Null | Key | Default | Extra |<br /> +--------+--------------+------+-----+---------+----------------+<br /> | aid | int(11) | NO | PRI | NULL | auto_increment |<br /> | mask | varchar(255) | NO | | | |<br /> | type | varchar(255) | NO | | | |<br /> | status | tinyint(4) | NO | | 0 | |<br /> +--------+--------------+------+-----+---------+----------------+<br /> 4 rows in set (0.00 sec)<br /></code></p> <p>I don't want to provide the aid column, that's an auto_increment field, so I deliberately omit it from the end of the SQL statement so MySQL knows that data is not provided - so it will leave it NULL if it has no other instruction, or auto increment the ID if it's an auto_increment field.</p> <p>That's it! Easy import of access data supplied by a client in a spreadsheet. 83 individual form operations done in a bit of pasting and replacing and a single SQL statement. \o/</p> <p>Here's the MySQL manual page for this, for more options:<br /><a href="http://dev.mysql.com/doc/refman/5.1/en/load-data.html">http://dev.mysql.com/doc/refman/5.1/en/load-data.html</a></p> </div></div></div> Thu, 15 Dec 2011 18:14:28 +0000 greg.harvey 512 at http://www.drupaler.co.uk http://www.drupaler.co.uk/blog/piping-file-mysql#comments Look, Drupal 7! http://www.drupaler.co.uk/blog/look-drupal-7 <div class="field field-name-upload field-type-file field-label-hidden"><div class="field-items"></div></div><div class="field field-name-taxonomy-vocabulary-1 field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="/tags/drupal-planet">drupal planet</a></div><div class="field-item odd"><a href="/tags/drupal">drupal</a></div><div class="field-item even"><a href="/tags/drupal-7">drupal 7</a></div><div class="field-item odd"><a href="/tags/upgrade">upgrade</a></div><div class="field-item even"><a href="/tags/6x">6.x</a></div><div class="field-item odd"><a href="/tags/7x">7.x</a></div></div></div><div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even"><p>This is now my first Drupal 6 to Drupal 7 upgrade. Not without hitches, but all in all not too painful. Hope you like the new look. As dedicated readers may know, I never stick with the same theme between major Drupal releases, more often than not because my previous theme was never ported and I can't be bothered to port it myself. The new theme is the rather pretty Corolla, which you can find here:<br /><a href="http://drupal.org/project/corolla">http://drupal.org/project/corolla</a></p> <p>Here are some Drupal 7 upgrade notes and links others may find useful.</p> <p>Firstly, the modules that are not ready/will never actually be ported:</p> <p>CacheRouter<br /> Content Access (but there is a patch: <a href="http://drupal.org/node/690610">http://drupal.org/node/690610</a>)<br /> Messaging<br /> Notifications<br /> Nodewords<br /> Node Images<br /> TweetMeme<br /> Twitter</p> <p>So all of those I had to opt to live without for now (with the exception of Content Access, I applied the patch but I haven't had chance to verify it works yet because ACL has a critical bug - see below).</p> <p>CacheRouter is a bit of a pain, as it was one of the easier ways to get some advanced caching going without needing to install anything in your operating system. You can read the full story here:<br /><a href="http://drupal.org/node/593238">http://drupal.org/node/593238</a></p> <p>I was using the 'filecache' engine which, as far as I can tell, has not yet been ported to use Drupal's new caching API, so your only option for simple caching right now is Boost. Which is still broken:<br /><a href="http://drupal.org/node/325813">http://drupal.org/node/325813</a></p> <p>The simplest currently available caching mechanism is probably the new Drupal 7 APC module, which is by slantview who made CacheRouter in the first instance:<br /><a href="http://drupal.org/project/apc">http://drupal.org/project/apc</a></p> <p>Install APC for PHP and install that module (don't forget to open README.txt and follow the steps) and you'll have some caching that isn't using the database and *should* be a significant performance gain on a weedy VM (like this one). It's not quite as straightforward as 'filecache' or Boost (which you just enable and tweak some settings and it's done) - you have to install other stuff - but it's pretty easy.</p> <p>Next, some useful resources:</p> <p><a href="http://drupal.org/documentation/upgrade/6/7">http://drupal.org/documentation/upgrade/6/7</a><br /><a href="http://drupal.org/node/570162">http://drupal.org/node/570162</a></p> <p>The former is all the upgrade steps you should need. The latter is cutting to the chase, the actual upgrade steps - it's linked to from the first one anyway.</p> <p>So, following the steps I backed up everything, downloaded core and ran update.php over my old database. First thing you might see is this:</p> <p></p><div class="codeblock"><code>date_default_timezone_set() Timezone ID '3600' is invalid</code></div> <p>Don't worry about it - it's harmless: <a href="http://drupal.org/node/1020748">http://drupal.org/node/1020748</a></p> <p>So, that went ok. Next put the available Drupal 7 contrib modules and themes back in to sites/all (or wherever they were) and run update.php again to run their upgrade code.</p> <p>If you use the Global Redirect module, you will have a problem at time of writing. Take a look at this:<br /><a href="http://drupal.org/node/861994">http://drupal.org/node/861994</a></p> <p>Not a biggy, but you'll have to patch/alter your install file for the upgrade to work. This will change with the next release, it seems.</p> <p>And Rules currently has no upgrade path at all, so forget about that one for now. It's coming. Fago tells me [new window] clean installation should work though. Again, watch this space - an upgrade path is coming.</p> <p>Another one that got me is a bug in the current Drupal 7 version of ACL. It's a show stopper right now, so use the dev snapshot, not the release, if you want to play with ACL - beta3 onwards should be safe(r):<br /><a href="http://drupal.org/node/1024114">http://drupal.org/node/1024114</a></p> <p>Finally, once you're all set up you might see one or all of these little gotchas:</p> <p>Expect to have to rebuild some Views (especially around Comments and user Profile fields). Many handlers are changed between Views 6.x-2 and Views 7.x-3, and are not automatically upgraded. This also results in some pretty horrible failures at first and freaks you out until you realise it's just missing Views handlers (*phew*). </p> <p>No easy way around this. You have to go to Views UI and click your way through all the broken handlers, deleting the obsolete Drupal 6 handlers and creating new fields, sorts, relationships, etc. etc. with the new Drupal 7 handlers. This is a real pain. I'm hoping there'll be a Views updater at some point for your Views 2 views to clean up all the handlers. For now the upgrade path catches and repairs some instances, but nowhere near all.</p> <p>Edit: Just found another issue after saving this node! All the tokens have changed and are not automatically upgraded it seems, so you'll need to go and edit your pathauto settings (and possibly other places where tokens are used) to make sure the correct values are being used.</p> <p>Far less painful, you might see Filter module notices (I got one because of a filter provided by the Messaging module) - just re-save the Filter concerned (or delete it if it's not needed any more):<br /><a href="http://drupal.org/node/1018538">http://drupal.org/node/1018538</a></p> <p>Finally, the new Fields replacement for CCK has some minor issues being resolved. You may (probably will, it seems) see notices like this:</p> <p></p><div class="codeblock"><code>Notice: Undefined index: required in field_default_form()<br />Notice: Undefined index: description in field_multiple_value_form() <br />Notice: Undefined index: description in field_multiple_value_form() </code></div> <p>For now all you need to do is save the Manage Fields page of the content type and this goes away. Seems a coded fix is in the pipe:<br /><a href="http://drupal.org/node/931512#comment-3906836">http://drupal.org/node/931512#comment-3906836</a></p> <p>One final observation. I seem to be experiencing overly aggressive menu caching with Drupal 7. After module removal links persist in admin, other menus are slow to update, seems there might be a small bug. And before anyone points at APC, it uses the caching API anyway and besides, I hadn't actually enabled it yet. One to watch...</p> <p>That's all for now. Upgrade to Drupal 7! It's fun! (Kinda...)</p> </div></div></div> Thu, 13 Jan 2011 15:11:40 +0000 greg.harvey 508 at http://www.drupaler.co.uk http://www.drupaler.co.uk/blog/look-drupal-7#comments Security Releases Don't Work http://www.drupaler.co.uk/blog/security-releases-dont-work/504 <div class="field field-name-upload field-type-file field-label-hidden"><div class="field-items"></div></div><div class="field field-name-taxonomy-vocabulary-1 field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="/tags/drupal">drupal</a></div><div class="field-item odd"><a href="/tags/modules">modules</a></div><div class="field-item even"><a href="/tags/drupal-planet">drupal planet</a></div><div class="field-item odd"><a href="/tags/security">security</a></div></div></div><div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even"><p>Wow, long lapse in posts there. Sorry folks. On with the show:</p> <p>Here's the problem. The Drupal security team do a fine job, following up on reports, auditing contributed modules with a point release to check for security weaknesses, working on core. It's all good, except for one thing:</p> <p>Contrib release process.</p> <p>Security updates are rarely just security updates! Normally they're a whole bunch of stuff the maintainers were working on, a bunch of other minor bug fixes that have been queued for a few months, maybe some UI changes and anything else that was languishing in the issue queue when the security team came knocking. So what happens? Security hole is patched, patch is applied to -dev, release is rolled pronto and *everything* goes out the door at once, warts and all.</p> <p>So it's not a 'security update'. It's a module update that happens to fix a security problem. What if you don't want the other stuff? (Sometimes the 'other stuff' will actually even break your site - that's a whole other blog post, but changing mark-up and CSS in the same branch as a seemingly innocuous 'update' breaks people's websites. Don't do it!) What if your site is stable and you only want the security update, not the other stuff?</p> <p>You have to go and dig out the patches *specifically relating to security* and apply them. This is more than just a royal pain. Even when you do that, Update Status will continue to needle you that your modules are dangerously insecure, even though you know that is not the case. Gah!</p> <p>I am told some enterprise Drupal users (and by users, I mean big teams in the corporate world) disable Update Status completely and manage their own security patching the hard way, simply because they don't trust contrib 'security updates' and won't just blindly apply them. God knows, I've blindly applied them a few times and been bitten - hard!</p> <p>So what to do?</p> <p>In the short term, if you're a module maintainer and the security team come along and point out a flaw, don't fix the flaw in -dev and roll a release. That's the worst thing you could do. A hurried release is a mess waiting to happen and an issue queue full of support requests.</p> <p>Instead, patch the latest point release, fixing the security team's concerns, and release that. Sure, you'll have to copy over your current -dev code to push it out the door, but that's what VCS is for! Once your security release is out, and it *really is* a security release, you can reinstate your 'work-in-progress' -dev code and carry on.</p> <p>This way you have a release that truly fixes the security problem, and *only* the security problem, and you can properly plan your next release instead of doing it in a big hurry. And if people don't keep up to date with your point releases, then they're on their own when it comes to applying security releases made against newer point releases, but that is fair enough. Just don't *force* people to have to do a 'security update' that contains a load of stuff they might want to roll out more thoughtfully at a later date.</p> <p>And in the future?</p> <p>Hopefully the move to Git will make a lot of this easier. Without going in to detail, Git's superior branching abilities should make it far easier for maintainers to cleanly release a security fix without including a bunch of other stuff they're working on (perhaps using rebase). I'm not really clear yet on how the old CVS branches and tags are applied in Git, but perhaps there's scope to add a new tag for developing future changes, instead of using the head, so head and the last point release tag should always match and a 'dev' tag contains feature updates.</p> <p>These are just thoughts - I'd love to hear yours.</p> </div></div></div> Fri, 01 Oct 2010 13:24:17 +0000 greg.harvey 504 at http://www.drupaler.co.uk http://www.drupaler.co.uk/blog/security-releases-dont-work/504#comments Panels 3 And The 960 Grid System http://www.drupaler.co.uk/blog/panels-3-and-960-grid-system/498 <div class="field field-name-upload field-type-file field-label-hidden"><div class="field-items"></div></div><div class="field field-name-taxonomy-vocabulary-1 field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="/tags/drupal">drupal</a></div><div class="field-item odd"><a href="/tags/6x">6.x</a></div><div class="field-item even"><a href="/tags/theming">theming</a></div><div class="field-item odd"><a href="/tags/panels">panels</a></div><div class="field-item even"><a href="/tags/css">css</a></div><div class="field-item odd"><a href="/tags/drupal-planet">drupal planet</a></div><div class="field-item even"><a href="/tags/960-grid">960 grid</a></div></div></div><div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even"><p>This post was going to be longer, but I just lost the entire completed document because <a href="http://www.sfr.fr">the worst (and probably most expensive at $15/day, yes DAY) mobile Internet provider in the world, EVER</a>, just dropped my connection mid-submission and for the zillionth time today. So you'll have to make do with an angry and hurried synopsis of what I would've written.</p> <p>My latest project, in collaboration with <a href="http://www.markboultondesign.com" rel="nofollow">Mark Boulton Design</a>, involves working with <a href="http://960.gs">the 960 grid system</a> to theme a Panels-based layout. We are using <a href="http://drupal.org/project/ninesixty">the NineSixty theme</a> as a base, plus the <a href="http://drupal.org/project/panels">Panels module</a> and associated <a href="http://drupal.org/project/ctools">Chaos Tools</a> bits. Here's what I did to make Panels work smoothly with NineSixty so I could just use the Panels interface to build pages and forget about the CSS:</p> <p>First install the various Panels and CTools components and create a NineSixty sub-theme. See the project page of NineSixty for help with that - I also removed the left and right regions and added a content_top one to my theme and I needed to change the container class in page.tpl.php of the sub-theme to container-12 - it's container-16 by default. If you're not familiar with NineSixty, you'll need to spend a few hours reading up and playing around.</p> <p>Then create a new panel page, picking Flexible as the layout. When you get to the layout editor, add regions as you need them - in my case I needed three rows with three regions in each row, each region 4 grid columns in width. There will be a region there in a Row by default. Add a Region to the left and one to the Right, making sure they are set to Fluid. Do *not* play with widths at all, or CTools will create CSS files you don't want and need to over-ride. For all Regions in this Row, edit their settings and add the class "grid-4". You can then add further rows and regions, making sure regions are always Fluid, without any applied widths in the Panels UI and with the appropriate class in their settings.</p> <p>For example, if you need an 8 column region on the left and a 4 column one on the right, create a new Row, create a "Fluid" Region, give it the class "grid-8", create another "Fluid" Region to the right and give it a class of "grid-4". Done!</p> <p>Now to deal with any unwanted CSS Panels will generate. I used this CSS in my theme:</p> <p><code><br /> /* panels styles */<br /> .panels-flexible-region-inside {<br /> padding: 0;<br /> }</code></p> <p>.panels-flexible-row {<br /> background-color: #FFFFFF;<br /> border: 1px solid #EBEBEB;<br /> margin: auto -2px 20px -2px;<br /> display: table;<br /> float: none;<br /> padding: 0;<br /> width: auto;<br /> }</p> <p>.panels-flexible-column-1-main {<br /> float: left;<br /> width: auto;<br /> }</p> <p>.panels-flexible-region {<br /> float: left;<br /> padding: 0;<br /> }<br /></p> <p>Note, the .panels-flexible-row class contains some information specific to my theme, but I'm too annoyed with how long I spent writing this post before to tidy it up. Blame SFR.fr. ;-)</p> <p>In short, with that CSS in place I could now create Panel pages from the Panels UI and provided regions had the correct class and no widths applied then the layouts would just work.</p> <p>Off topic, but if you use the excellent BlockTheme module and want it to work with Panels 3, read this (the end bit, not the Panels 2 stuff at the start):<br /><a href="http://drupal.org/node/258377">http://drupal.org/node/258377</a></p> <p>Mobile Internet in France sucks.<br /> Happy Grid Theming!</p> </div></div></div> Wed, 12 May 2010 19:47:41 +0000 greg.harvey 498 at http://www.drupaler.co.uk http://www.drupaler.co.uk/blog/panels-3-and-960-grid-system/498#comments Drupal 7 Cheat Sheet: The Database http://www.drupaler.co.uk/blog/drupal-7-cheat-sheet-database/493 <div class="field field-name-upload field-type-file field-label-hidden"><div class="field-items"></div></div><div class="field field-name-taxonomy-vocabulary-1 field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="/tags/drupal">drupal</a></div><div class="field-item odd"><a href="/tags/database">database</a></div><div class="field-item even"><a href="/tags/module">module</a></div><div class="field-item odd"><a href="/tags/7x">7.x</a></div><div class="field-item even"><a href="/tags/drupal-planet">drupal planet</a></div><div class="field-item odd"><a href="/tags/update">update</a></div></div></div><div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even"><p>This is a quick post while it's fresh in my head, regarding porting your contributed modules to the Drupal 7 API. This post specifically focusses on database manipulation and provides the like-for-like changes. If anything here is wrong, or could be done better, please let me know!</p> <p>First up, individual results:</p> <p></p><div class="codeblock"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /></span><span style="color: #FF8000">/*<br /> * Remember this?<br /> */<br /></span><span style="color: #0000BB">$value </span><span style="color: #007700">= </span><span style="color: #0000BB">db_result</span><span style="color: #007700">(</span><span style="color: #0000BB">db_query</span><span style="color: #007700">(</span><span style="color: #DD0000">"SELECT some_field FROM {some_table}"</span><span style="color: #007700">));<br /><br /></span><span style="color: #FF8000">/*<br /> * Forget it! It now looks like this:<br /> */<br /></span><span style="color: #0000BB">$value </span><span style="color: #007700">= </span><span style="color: #0000BB">db_query</span><span style="color: #007700">(</span><span style="color: #DD0000">"SELECT some_field FROM {some_table}"</span><span style="color: #007700">)-&gt;</span><span style="color: #0000BB">fetchField</span><span style="color: #007700">();<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div> <p>Selects still look pretty much the same, at first glance, but wait:</p> <p></p><div class="codeblock"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /></span><span style="color: #FF8000">/*<br /> * Let's fetch a database object in Drupal 6:<br /> */<br /></span><span style="color: #0000BB">$result </span><span style="color: #007700">= </span><span style="color: #0000BB">db_query</span><span style="color: #007700">(</span><span style="color: #DD0000">"SELECT some_field FROM {some_table} WHERE some_other_field = %d"</span><span style="color: #007700">, </span><span style="color: #0000BB">$some_var</span><span style="color: #007700">);<br />while (</span><span style="color: #0000BB">$row </span><span style="color: #007700">= </span><span style="color: #0000BB">db_fetch_object</span><span style="color: #007700">(</span><span style="color: #0000BB">$result</span><span style="color: #007700">)) {<br />  </span><span style="color: #FF8000">// Loop through your query results and do some stuff.<br /></span><span style="color: #007700">}<br /><br /></span><span style="color: #FF8000">/*<br /> * In Drupal 7 there is no db_fetch_object() or db_fetch_array():<br /> */<br /></span><span style="color: #0000BB">$result </span><span style="color: #007700">= </span><span style="color: #0000BB">db_query</span><span style="color: #007700">(</span><span style="color: #DD0000">"SELECT some_field FROM {some_table} WHERE some_other_field = :some_other_field"</span><span style="color: #007700">, array(</span><span style="color: #DD0000">':some_other_field' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$some_var</span><span style="color: #007700">));<br />foreach (</span><span style="color: #0000BB">$result </span><span style="color: #007700">as </span><span style="color: #0000BB">$row</span><span style="color: #007700">) {<br />  </span><span style="color: #FF8000">// Loop through your query results and do some stuff.<br /></span><span style="color: #007700">}<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div> <p>Deletes and updates now look quite different. You don't use <code>db_query()</code> at all any more for those types of SQL query. Each one has its own query building function:</p> <p></p><div class="codeblock"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /></span><span style="color: #FF8000">/*<br /> * A delete query in Drupal 7:<br /> */<br /></span><span style="color: #0000BB">db_delete</span><span style="color: #007700">(</span><span style="color: #DD0000">'some_table'</span><span style="color: #007700">)<br />  -&gt;</span><span style="color: #0000BB">condition</span><span style="color: #007700">(</span><span style="color: #DD0000">'some_field'</span><span style="color: #007700">, </span><span style="color: #0000BB">$some_var</span><span style="color: #007700">)<br />  -&gt;</span><span style="color: #0000BB">execute</span><span style="color: #007700">();<br /><br /></span><span style="color: #FF8000">/*<br /> * An update query in Drupal 7:<br /> */<br /></span><span style="color: #0000BB">db_update</span><span style="color: #007700">(</span><span style="color: #DD0000">'some_table'</span><span style="color: #007700">)<br />  -&gt;</span><span style="color: #0000BB">fields</span><span style="color: #007700">(<br />    array(<br />      </span><span style="color: #DD0000">'some_field' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$some_var</span><span style="color: #007700">,<br />      </span><span style="color: #DD0000">'some_other_field' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$some_other_var</span><span style="color: #007700">,<br />    )<br />  )<br />  -&gt;</span><span style="color: #0000BB">condition</span><span style="color: #007700">(</span><span style="color: #DD0000">'some_identifying_field'</span><span style="color: #007700">, </span><span style="color: #0000BB">$some_id_var</span><span style="color: #007700">)<br />  -&gt;</span><span style="color: #0000BB">execute</span><span style="color: #007700">();<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div> <p>You should already be using <code>drupal_write_record()</code> for inserts anyway, and it has not changed, as far as I can tell.</p> <p>Good news is <code>hook_schema()</code> and the related functions don't seem to have changed at all, so many of you won't need to touch installation scripts, unless you need to manipulate data as part of the update process, of course.</p> <p>And that's that for now. Hope it's useful.</p> </div></div></div> Tue, 30 Mar 2010 09:29:39 +0000 greg.harvey 493 at http://www.drupaler.co.uk http://www.drupaler.co.uk/blog/drupal-7-cheat-sheet-database/493#comments Scared Of Features? Don't Be! http://www.drupaler.co.uk/blog/scared-features-dont-be/492 <div class="field field-name-upload field-type-file field-label-hidden"><div class="field-items"></div></div><div class="field field-name-taxonomy-vocabulary-1 field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="/tags/drupal">drupal</a></div><div class="field-item odd"><a href="/tags/module">module</a></div><div class="field-item even"><a href="/tags/6x">6.x</a></div><div class="field-item odd"><a href="/tags/drupal-planet">drupal planet</a></div><div class="field-item even"><a href="/tags/features">features</a></div></div></div><div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even"><p>I'm an experienced Drupal developer. I've been embedding stuff in code for ages now. Views, Panel pages, ImageCache presets, even putting CCK content type exports in to <code>hook_install()</code> implementations just to try to keep stuff in code. Many of you are probably in the same boat. Keeping stuff in the database brings all sorts of ugly deployment and performance issues, so we avoid like the proverbial plague.</p> <p>Now, just in case you've spent the last year on the planet Zog and have only just returned, there's a neat module these days called <a href="http://drupal.org/project/features" rel="nofollow">Features</a> which supposedly does all this for you.</p> <p>After months of procrastinating over when/if to take plunge in to Drupal development with this whizzy new module, I finally did it this week. And wow. Just... wow.</p> <p>So what took me so long? Well, mainly it was the fear of another steep learning curve and me already working 10 hour days as it is! Knowing how much of a PITA it is to do all this manually, I naturally assumed Features would be horribly complicated and I sincerely could not face learning anything under the present workload, so I put it off and put it off and put it off...</p> <p>Until I had a fresh project that I figured would really benefit from the Features model. I installed it and off I went.</p> <p>I had already created a Yahoo! Answers style element in Drupal for my client. It felt like the perfect starting point for a nice new Features-based module. It was basically made up of some content types and views. The content types had some fancy CCK stuff going on (mainly using the excellent <a href="http://drupal.org/project/nodereference_url" rel="nofollow">Nodereference URL</a>) and the Views had some fancy display settings (courtesy of <a href="http://drupal.org/project/views_attach rel=" nofollow="">Views Attach</a> and <a href="http://drupal.org/project/views_slideshow" rel="nofollow">Views Slideshow</a>), but essentially there wasn't much to it after the configuration.</p> <p>Time to turn it in to a module. Hmmmm. Deep breath:</p> <p>So, where is Features hiding?<br /> Site building -&gt; Features... that feels like a good starting point.<br /> Hmmm, no Features installed yet, that figures.<br /> Create Feature? Probably.<br /> Name, description, version, all seems obvious enough. Mkaaay...<br /> URL of update XML? I'll ignore that, it's not compulsory and I have no idea what it should be...<br /> Add components? A drop-down list... now this looks interesting...<br /> Content types, I got some of those... Question and Answer (ticky tick tick).</p> <p>That's when I nearly wet myself.</p> <p>OMFG, it just auto-added all the module dependencies! How cool is that?!<br /> Views, I got some of those too... questions and answers (again! tickety tickety tick those checkboxes).<br /> HA! It did it again! views_slideshow and views_attach added as dependencies automatically. Awesome!!<br /> Hmmm, it didn't pick up the core Statistics module. I need that for reads.<br /> No matter, there's a "Dependencies" option.<br /> And yes, it's a list of enabled modules. Hurrah! Ticky tick Statistics.</p> <p>Yup, I do believe that's it. Click the "Download feature" button, I guess?<br /> A packaged module! Content types, Views and all dependencies.<br /> Install it back on the site and bingo, there it is on the currently installed Features list.<br /> That's it, I'm finished! That took, oh, 2 minutes?<br /> Wow.</p> <p>Intuitive? Hell yeah!</p> <p>I certainly need not have been so concerned about learning curve. Actually Features is easy peasy - far easier than I ever imagined it could be. In fact, I can honestly say you no longer need to have a clue about Drupal development to make Drupal modules*. My two-year-old could build a module with this! This is the coolest thing since the bread slicing machine, seriously.</p> <p><span style="font-size: x-small">* But you will need the Features module, naturally.</span></p> <p>Then there's the other things you can do. There's drush integration (for command line Features module creation and updating), there's the Strongarm module (for exporting variables, amongst other things), there's CTools integration for creating exportable objects, etc. etc. Just read the project page.</p> <p>But even if you don't want to get in to all that. Even if you've simply made some sort of simple application, you think it's worth sharing and you don't want to have to build it all over if you need it again. Make it a Feature! You don't need to know a line of code. If you know your way around the Drupal UI well enough to create content types and Views, and you have the necessary bodily appendages to allow you to wield a pointing device and tick boxes, you can make a Drupal module.</p> <p>The only thing I really miss is the ability to bundle user roles and their related permissions settings with my Features module. (Or at least, I think I miss it? Maybe you can already do this and I've missed the option.)</p> <p>Before I sign off, quick note on sharing Features-based modules. I think I'm right in saying you can't just stick them on Drupal.org. Or people aren't doing anyway, even though thinking about it, I don't see why not if you have CVS access. Perhaps someone can expand on that in the comments?</p> <p>Anyway, I put my module on <a href="http://www.mig5.net" rel="nofollow">mig5</a>'s Features community server, here:<br /><a href="http://community.featureservers.org/project/answers">http://community.featureservers.org/project/answers</a></p> <p>You can too. There are several community servers about and, if you're feeling really adventurous, you can host your own community server. By the way, that's where "URL of update XML" comes in. Remember I didn't know what to do with it? It's simply the URL of the community server your module is hosted by, so the core Update Status module knows where to look for update information.</p> </div></div></div> Fri, 26 Mar 2010 12:55:54 +0000 greg.harvey 492 at http://www.drupaler.co.uk http://www.drupaler.co.uk/blog/scared-features-dont-be/492#comments Cleaning Up After Migrating To Drupal http://www.drupaler.co.uk/blog/cleaning-after-migrating-drupal/487 <div class="field field-name-upload field-type-file field-label-hidden"><div class="field-items"></div></div><div class="field field-name-taxonomy-vocabulary-1 field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="/tags/drupal">drupal</a></div><div class="field-item odd"><a href="/tags/apache">apache</a></div><div class="field-item even"><a href="/tags/apache2">apache2</a></div><div class="field-item odd"><a href="/tags/modrewrite">mod_rewrite</a></div><div class="field-item even"><a href="/tags/drupal-planet">drupal planet</a></div><div class="field-item odd"><a href="/tags/migration">migration</a></div><div class="field-item even"><a href="/tags/htaccess">htaccess</a></div></div></div><div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even"><p>We have just finished a migration job for a client of ours from an old .Net system in to Drupal, the last task of which was to write some Apache mod_rewrite conditions and rules to deal with the URLs of their old website. This proved to be more trouble than I thought, mainly because I struggled to find examples of how this might work.</p> <p>Firstly, the ground work. The URL pattern to be redirected looked like this:<br /></p><div class="codeblock"><code>MainArticle.aspx?m=33818&amp;amid=30301119</code></div> <p>Where the amid value is the article ID which we had taken through to Drupal and used the Pathauto module to make sure all the URLs were:<br /></p><div class="codeblock"><code>story/%amid</code></div> <p>So we now have a connection between the old URLs and the new URLs we can confidently rely on as the basis for a mechanism to make sure both bots and people are directed to the new content correctly.</p> <p>Now for the tricky bit. To do the directing we decided to use Apache's built-in redirection abilities. I started out thinking we would use RedirectMatch, but I pretty quickly found this blog post - it was a good starting point, saying this would not work and saving some time:<br /><a href="http://davidherron.com/content/cure-fail-when-using-redirectmatch-clean-urls-drupal">http://davidherron.com/content/cure-fail-when-using-redirectmatch-clean-...</a></p> <p>It also shows how you can achieve the same with RewriteRule instead, to provide a 301 redirect (permanently moved), but it only tells half the story. I'd perfected my rule and it looked like this:<br /></p><div class="codeblock"><code>RewriteRule ^.*amid=([0-9]+).*$ story/$1 [R=301,L]</code></div> <p>I tested it here and it worked too:<br /><a href="http://civilolydnad.se/projects/rewriterule/">http://civilolydnad.se/projects/rewriterule/</a></p> <p>But no matter where I put it in Drupal's .htaccess file, it did not do a thing!</p> <p>Eventually I found this comment on Drupal Groups, which made the penny finally drop:<br /><a href="http://groups.drupal.org/node/11361#comment-43246">http://groups.drupal.org/node/11361#comment-43246</a></p> <p>The rewrite rule tester I had been using has a bug! The querystring is being disregarded by the *real* mod_rewrite, so even though my rule seemed to work at the rewrite rule tester above, the querystring was simply not available. This is me not understanding things properly. Once I had that worked out, it was plain sailing.</p> <p>So here's what I finally ended up with, with comments inline:</p> <p></p><div class="codeblock"><code>  # Rewrite old-style URLs for .aspx scripts<br /><br />  # First we need to exclude files and favicon.ico, just like Drupal does<br />  # Repeat the core conditions here:<br />  RewriteCond %{REQUEST_FILENAME} !-f<br />  RewriteCond %{REQUEST_FILENAME} !-d<br />  RewriteCond %{REQUEST_URI} !=/favicon.ico<br /><br />  # Now we need to find the value we're after in the querystring:<br />  RewriteCond %{QUERY_STRING} ^.*amid=([0-9]+).*$<br /><br />  # Finally, catch any request for an aspx script and push it to our alias<br />  # Note the R=301, our permanent redirect<br />  # Also note the final ? on the end, to stop the old querystring being<br />  # passed on again:<br />  RewriteRule ^.*\.aspx.*$ /story/%1? [R=301,L]</code></div> <p>This *does* work. It will catch all the requests to any aspx script and push them on to the revised URL on the same server, as a 301 redirect, using the value from the previous condition where we parsed the querystring to get the old amid article ID out.</p> </div></div></div> Wed, 17 Feb 2010 13:35:50 +0000 greg.harvey 487 at http://www.drupaler.co.uk http://www.drupaler.co.uk/blog/cleaning-after-migrating-drupal/487#comments Multilingual Drupal: Some Dos And Don'ts http://www.drupaler.co.uk/blog/multilingual-drupal-some-dos-and-donts/482 <div class="field field-name-upload field-type-file field-label-hidden"><div class="field-items"></div></div><div class="field field-name-taxonomy-vocabulary-1 field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="/tags/drupal">drupal</a></div><div class="field-item odd"><a href="/tags/6x">6.x</a></div><div class="field-item even"><a href="/tags/drupal-planet">drupal planet</a></div><div class="field-item odd"><a href="/tags/i18n">i18n</a></div><div class="field-item even"><a href="/tags/internationalize">internationalize</a></div></div></div><div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even"><p>So we've done all French sites before. And we've done all English sites before. But a recent project was our first real forray in to multilingual sites and it's an e-commerce/Ubercart job! Talk about gluttons for punishment!</p> <p>There are bags of tutorials, so I'll keep this short but sweet. A list of dos and don'ts from our painful, recent experience:</p> <ol><li><strong>Don't</strong> change the default language after initial set-up. Set your default language right at the start and don't mess with it. Ever.</li> <li><strong>Do</strong> give each language its own weight. I'm not sure why, but this seems to fix certain situations where Drupal seems to get confused about which language it should select for an element of the UI.</li> <li><strong>Do</strong> back up your database before installing someone else's PO files. Sometimes they're a pile of crap and there's no easy way (I know of) to back out.</li> <li><strong>Don't</strong> add menu items using the Views module (unless they're tabs). Use the core Menu module or you'll confuse yourself to death.</li> <li><strong>Don't</strong> forget your default language - it may not be English even if you normally work in English.</li> <li><strong>Do</strong> enter everything in the default language first, then translate later. It's just safer that way.</li> <li><strong>Don't</strong> set Taxonomy vocabularies to a language. For some reason it seems to lock them like that forever! Leave them language agnostic and translate them. Then the language switcher will do its thing.</li> <li><strong>Don't</strong> forget, multilingual variables are set, as usual, in the admin UI. All you need to do is set the URL/sub-domain (depending on how you configured language switching) so you are in that language's view of the UI, then enter the variable value for that language. This is the case even if you set the language to be one specific one always in admin.</li> <li><strong>Don't</strong> translate blocks. You'll confuse your <em>client</em> to death when you try to explain to them how to create and edit translations. Just create one block per language, as needed.</li> <li><strong>Don't</strong> install BlockTheme. It's a great module, but it doesn't work at all with i18n (yet - I've been chatting to Jacob Singh about CVS access to fix that, when I have time).</li> </ol><p>I may add to this in time.</p> </div></div></div> Sat, 30 Jan 2010 14:06:58 +0000 greg.harvey 482 at http://www.drupaler.co.uk http://www.drupaler.co.uk/blog/multilingual-drupal-some-dos-and-donts/482#comments How Drush Make Just Changed My Life http://www.drupaler.co.uk/blog/how-drush-make-just-changed-my-life/481 <div class="field field-name-upload field-type-file field-label-hidden"><div class="field-items"></div></div><div class="field field-name-taxonomy-vocabulary-1 field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="/tags/drupal">drupal</a></div><div class="field-item odd"><a href="/tags/linux">linux</a></div><div class="field-item even"><a href="/tags/drush">drush</a></div><div class="field-item odd"><a href="/tags/drupal-planet">drupal planet</a></div><div class="field-item even"><a href="/tags/make">make</a></div></div></div><div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even"><p><strong>Note</strong>: Apparently it works fine with Windows too! See comments.</p> <p>I'm pretty excited right now. I just tried drush make for the first time. Download it here:<br /><a href="http://drupal.org/project/drush_make">http://drupal.org/project/drush_make</a></p> <p>That's an order! You'll need drush too, if you don't have it yet (in which case, shame on you ... call yourself a Drupal developer?!)<br /><a href="http://drupal.org/project/drush">http://drupal.org/project/drush</a></p> <p>Install it (the README instructions are nice and clear). Now what? Make a make file. Again, the README is pretty clear. Here's my "standard" make file, but I intend to make a "commerce" file, "services" file, "flash support" file, "social sites" file, etc. Anyway, the file:</p> <p></p><div class="codeblock"><code>core = 6.x<br /><br />projects[] = drupal<br /><br />projects[cck][subdir] = "contrib"<br />projects[filefield][subdir] = "contrib"<br />projects[imagefield][subdir] = "contrib"<br />projects[link][subdir] = "contrib"<br />projects[email][subdir] = "contrib"<br />projects[date][subdir] = "contrib"<br /><br />projects[vertical_tabs][subdir] = "contrib"<br />projects[better_formats][subdir] = "contrib"<br />projects[imce_wysiwyg][subdir] = "contrib"<br />projects[imce][subdir] = "contrib"<br />projects[wysiwyg][subdir] = "contrib"<br />libraries[tinymce][download][type] = "get"<br />libraries[tinymce][download][url] = "<a href="http://downloads.sourceforge.net/project/tinymce/TinyMCE/3.2.7/tinymce_3_2_7.zip&quot;&lt;br /&gt;libraries[tinymce][directory_name]">http://downloads.sourceforge.net/project/tinymce/TinyMCE/3.2.7/tinymce_3...</a> = "tinymce"<br /><br />projects[admin_menu][subdir] = "contrib"<br />projects[adminrole][subdir] = "contrib"<br />projects[masquerade][subdir] = "contrib"<br />projects[taxonomy_manager][subdir] = "contrib"<br />projects[backup_migrate][subdir] = "contrib"<br /><br />projects[views][subdir] = "contrib"<br />projects[imagecache][subdir] = "contrib"<br />projects[imageapi][subdir] = "contrib"<br />projects[token][subdir] = "contrib"<br />projects[logintoboggan][subdir] = "contrib"<br />projects[webform][subdir] = "contrib"<br /><br />projects[blocktheme][subdir] = "contrib"<br />projects[zenophile][subdir] = "contrib"<br /><br />projects[boost][subdir] = "contrib"<br /><br />projects[pathauto][subdir] = "contrib"<br />projects[globalredirect][subdir] = "contrib"<br />projects[nodewords][subdir] = "contrib"<br />projects[page_title][subdir] = "contrib"<br />projects[xmlsitemap][subdir] = "contrib"<br /><br />projects[zen][subdir] = "contrib"<br />projects[fusion][subdir] = "contrib"<br />projects[clean][subdir] = "contrib"<br />projects[rootcandy][subdir] = "contrib"<br />projects[seven][subdir] = "contrib"</code></div> <p>Save it as <code>standard.make</code>. Make a new directory. Go there in a terminal window. Type <code>drush make /path/to/standard.make</code> then go make a cup of tea. Come back, go to your new site, run through the installer. Done! New Drupal site with all your desired contrib pre-installed. =)</p> <p>I particularly like the way it can fetch 3rd party stuff (note the TinyMCE download - you could also use it to fetch all the players for SWF Tools, for example).</p> <p>Of course, now you can actually run this from a Linux shell script. I don't think it's possible to script a generic installation with drush (yet!) but it *is* possible to use drush, post install, to enable everything. And come Drupal 7 you could have a Linux shell script that goes something like this:</p> <p></p><div class="codeblock"><code># get core and contrib<br />drush make ~/workspace/core/makefiles/standard.make<br /># install Drupal<br />drush ic<br /># enable modules<br />drush en cck filefield views imagefield imagecache imageapi path ...</code></div> <p>You get the idea. So no more box-ticking on the modules page either. You can do the above with Drupal 6, but you'll need Aegir, and that's a whole other story!<br /><a href="http://groups.drupal.org/aegir-hosting-system">http://groups.drupal.org/aegir-hosting-system</a></p> <p>I think I need to lie down.</p> </div></div></div> Thu, 28 Jan 2010 11:18:02 +0000 greg.harvey 481 at http://www.drupaler.co.uk http://www.drupaler.co.uk/blog/how-drush-make-just-changed-my-life/481#comments Wrapping Up: A Linux Script For The End Of The Day http://www.drupaler.co.uk/blog/wrapping-linux-script-end-day/478 <div class="field field-name-upload field-type-file field-label-hidden"><div class="field-items"></div></div><div class="field field-name-taxonomy-vocabulary-1 field-type-taxonomy-term-reference field-label-hidden"><div class="field-items"><div class="field-item even"><a href="/tags/drupal">drupal</a></div><div class="field-item odd"><a href="/tags/linux">linux</a></div><div class="field-item even"><a href="/tags/deployment">deployment</a></div><div class="field-item odd"><a href="/tags/drush">drush</a></div><div class="field-item even"><a href="/tags/drupal-planet">drupal planet</a></div><div class="field-item odd"><a href="/tags/script">script</a></div><div class="field-item even"><a href="/tags/svn">svn</a></div></div></div><div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even"><p>Here's another one of my little Linux admin scripts for all you Drupal developers out there. It's a Linux shell script requiring Drush, MySQL and Subversion, but could be easily modified to work with other databases and repositories and should work fine on a Mac, I think.</p> <p>You'll need to modify it to your specific paths anyway, as this is set up for our standard working environment. It assumes the database dump name is always the same as the workspace project directory name (in our case this is true), the workspace is in the Linux user's home directory and each project contains a <code>trunk/db</code> directory for database snapshots and a <code>trunk/www</code> directory for the actual application.</p> <p>Here's what it does:</p> <ul><li>Clears Drupal's cache</li> <li>Deletes the old database dump</li> <li>Dumps and bzip2s the database in to a pre-determined location in our workspace</li> <li>Commits the new database dump</li> <li>Updates the application (to catch any updates others might have done today)</li> <li>Commits any changes to updated files/commits all added files</li> </ul><p>So when it's run, we have a database dump in synch with the code in version control. Always! I automated this because people (including myself) get lazy and can't be bothered to create the database snapshot with the code commit. This does it for them (and me) so it always happens. Yay!</p> <p>Here's the code:</p> <p></p><div class="codeblock"><code>if [ $1 = "--help" ];<br />  then<br />    echo "<br />  #########<br />  # USAGE #<br />  #########<br /><br />  Syntax for running this script is:<br />    ./end_of_day.sh projectdir databasehost databasename <br />    <br />    projectdir -          string, project directory in Eclipse workspace<br />    databasehost -        string, hostname or IP for database server (usually localhost)<br />    databasename -        string, name of database<br />    ." &amp;  <br />  else<br />    # TODO: we should really use drush to mark certain tables as excluded<br />    echo "Clearing the Drupal cache to make the db dump smaller ..."<br />    cd ~/workspace/$1/trunk/www<br />    drush cache clear<br />    echo "Updating application db directory at ~/workspace/$1/trunk/db ..."<br />    svn up ~/workspace/$1/trunk/db<br />    echo "Deleting old db dump from repository ..."<br />    svn delete ~/workspace/$1/trunk/db/$1.sql.bz2<br />    svn commit ~/workspace/$1/trunk/db/ -m "AUTO: deleting old database dump"<br />    echo "Creating new db dump of $3 from $2 ..."<br />    if [ $2 = 'localhost' ];<br />      then<br />        mysqldump -u root $3 | bzip2 &gt; ~/workspace/$1/trunk/db/$1.sql.bz2<br />      else<br />        mysqldump -u root -h $2 $3 | bzip2 &gt; ~/workspace/$1/trunk/db/$1.sql.bz2<br />    fi<br />    echo "Adding new db dump to repository ..."<br />    svn add ~/workspace/$1/trunk/db/$1.sql.bz2<br />    svn commit ~/workspace/$1/trunk/db/ -m "AUTO: adding new database dump"<br />    echo "Updating application www directory at ~/workspace/$1/trunk/www ..."<br />    svn up ~/workspace/$1/trunk/www<br />    echo "Committing any added code changes to repository ..."<br />    svn commit ~/workspace/$1/trunk/www/ -m "AUTO: committing code changes at end of day"<br />    echo "Done!"<br />fi</code></div> <p>Note the "TODO" task there.</p> </div></div></div> Wed, 27 Jan 2010 11:47:07 +0000 greg.harvey 478 at http://www.drupaler.co.uk http://www.drupaler.co.uk/blog/wrapping-linux-script-end-day/478#comments