Web Development

A Bunch of Responsive Tools

Respon­sive Menu
Design­ing nav­i­ga­tion with more than 4 links on mobile devices will quickly prove to be a chal­lenge if you try to set­tle with CSS and media queries. In “Adap­tive Web Design” Aaron Gustafson pro­poses trans­form­ing the usual ordered or unordered list of nav­i­ga­tional links into a <select> drop down menu with JavaScript. Matt Ker­s­ley has turned that approach into a very use­ful jQuery plu­gin, which turns an ordi­nary list of links into a drop down below a spec­i­fied screen size.
You can get it on Github here: jQuery Respon­sive Menu Plugin

Test your respon­sive design
Also, if you want to test your respon­sive web design, you can try it out in Matt Kersley’s Respon­sive Web Design Test­ing Tool. Other than it’s test­ing abil­ity, this tool is a great ref­er­ence for remem­ber­ing the most com­mon breakpoints.

Dynamic Carousel
There are lots and lots of jQuery carousel, image slid­ers and gallery plu­g­ins out there on teh inter­webs, but they are rarely respon­sive. Mat Mar­quis has made a respon­sive one, which scales beau­ti­fully and works pretty well.
You can get it on Github here: Dynamic Carousel 

Making WordPress image management easier for your editors

I am man­ag­ing a Word­Press site with a lot of edi­tors and con­trib­u­tors. They are not all equally up to pace with Word­Press and how it works, or at least they have dif­fer­ent ways of doing things. One thing that has proved to be a chal­lenge is the way Word­Press han­dles thumb­nails. My edi­tors and con­trib­u­tors often miss the ‘set as post thumb­nail’ link,  lead­ing to the lack of thumb­nails on the front page.

In this post I will try to explain, how I have made a fall­back, mak­ing it sort of OK for my users to for­get about the thumbnail.

A lit­tle history

Once upon a time, before ver­sion 2.9, Word­Press did not sup­port post thumb­nails. But there were work arounds. One of the best, I ever found, was from a web­site called wp-fun.co.uk. Sadly, the site is gone today, but I man­aged to dig up the old post ‘Easy Peasy Images Sug­ges­tion Roundup’ by Andrew Rick­mann on Archive.org.

Andrew wrote a nice lit­tle PHP class to include in your func­tions file, which gets the first thumb­nail image attach­ment from Word­Press posts. Luck­ily, I also man­aged to dig that one up on Archive.org. Go grab it here: post_imagephp.txt (remem­ber to rename .txt to .php, before includ­ing it in your functions.php file).

So why are You telling me about antiques like this?”, You ask. Because we can use it with advan­tage in our fall­back for peo­ple, who for­get to click the lit­tle ‘set as post thumb­nail’ link.

That way, a thumb­nail will still be dis­played with the post, as long as the edi­tor has just attached an image. If there are no images attached at all, we can always dis­play a default thumbnail.

The fall­back

Let’s take a peek at the code. Put the fol­low­ing where you would nor­mally use the_post_thumbnail().

<?php
// First of all, check if there are any image attachments at all.
$imgargs = array(
'order' => 'ASC',
'post_type' => 'attachment',
'post_parent' => $post->ID,
'post_mime_type' => 'image',
'post_status' => null,
'numberposts' => -1,
);
$attachments = get_posts($imgargs);
// Next, check if there is a manually chosen thumbnail.
if ( has_post_thumbnail() ) {
the_post_thumbnail('thumbnail');
}
// If there are no manually chosen thumbnails, check if there is an image attachment.
elseif ( $attachments ) {
// If there is an image attachment, call Andrew Rickmanns the_image function to get the thumbnail
the_image('thumbnail','attachment-thumbnail wp-post-image',false,false,true);
} else {
// If there are no attachments at all, display a default thumbnail.
?><img class="attachment-thumbnail wp-post-image" alt="" src="<?php bloginfo('stylesheet_directory'); ?>/the/path/to/your/image.jpg" />
<?php }?>

Require­ments:

Remem­ber to include this file, renamed to .php, in your functions.php: post_imagephp.txt

Krimimessen 2012

Screen dump of Krimimessen 2012

I have just launched a re-design of Krimimessen.dk. Krim­imessen is the biggest fes­ti­val about crime lit­er­a­ture and crim­i­nal fic­tion in Den­mark. A fes­ti­val which is arranged by Hors­ens Pub­lic Library, where I work. The site is designed so it resizes to fit the screen it is being viewed on, also known as respon­sive web design.

This is the first respon­sive site, I have cre­ated. It is built in Word­Press on a base of the awe­some HTML5 Boil­er­plate frame­work, which makes it eas­ier to build a mod­ern web site from scratch.

I learned a lot from read­ing Ethan Marcotte’s “Respon­sive Web Design” prior to this project. Marcotte’s “tar­get ÷ con­text = result” tech­nique, a way to cal­cu­late dimen­sions from pix­els into per­cent, was an invalu­able tool in the process. In fact, and very unusual indeed, it wasn’t even nec­es­sary to test a lot in Inter­net Explorer.

Flexible Widgets for WordPress

Flex­i­ble Wid­gets is a plu­gin for Word­Press that makes it pos­si­ble to dis­play a wid­get on selected cat­e­gories and/or pages.


The Flex­i­ble Wid­gets plu­gin lets you dis­play a wid­get on any cat­e­gory or page you wish. When set­ting up the wid­get, you are able to select the cat­e­gories and/or pages where you want to dis­play the wid­get. If none are selected, the wid­get will be dis­played glob­ally on your site, exactly like a default Word­Press widget.

The plu­gin comes in handy if you need your side­bar con­tent to change con­tex­tu­ally from page to page in rela­tion to your main con­tent. It is espe­cially use­ful if you want to use Word­Press as a CMS.

Flex­i­ble Wid­gets will replace the default Word­Press widgets.

Sreen­shot

Widget administration interface in the WordPress plugin Flexible Widgets

Select­ing cat­e­gories and pages in each widget’s back end.

Instal­la­tion

  1. Upload the flexible-widgets folder to the /wp-content/plugins/ directory
  2. Acti­vate the plu­gin through the ‘Plu­g­ins’ menu in WordPress

or search for “Flex­i­ble Wid­gets” in the “Add new” sec­tion of the plu­g­ins menu in your Word­Press admin inter­face and hit “Install now”.

Change log

0.3

  • Fixes an issue: Option­ally dis­play wid­gets on the home page, whether it is set up to be a sta­tic page or the blog posts page.

0.2

  • Tiny update to get the plu­gin and author URIs right.
  • Update on the instal­la­tion info.

0.1

  • Brand new plu­gin. Still play­ing with the bub­ble wrap.

Known issues

May con­flict with themes or plu­g­ins which include cus­tom widgets.

Please see the plu­gin sup­port forum.

Addi­tional information

The Flex­i­ble Wid­gets plu­gin con­sists of a bunch of ideas and lines of code, I wrote for the Word­Press theme in use at the web­site of Hors­ens Pub­lic Library.

This is my first plu­gin for Word­Press. I am sure, there is room for improve­ment. The code is far from per­fect as my pro­gram­ming skills are some­what lim­ited. But I do think the idea is quite good, so if you can write lean code and are des­per­ately in need of a small side project, you should be very wel­come to help me out.

If you have any ques­tions or com­ments, feel free to leave a reply.

Sådan finder man variabler i WordPress

Når man arbe­jder med tem­plates i Word­Press, kan det være enormt nyt­tigt at vide, hvilke vari­able, man har at arbe­jde med. Jeg havde et konkret prob­lem omkring en tag tem­plate (tag.php), hvor jeg havde brug for at finde ID-nummeret på det aktuelle tag til brug i funk­tio­nen get_tag_link().

Jeg fandt det ved at skrive lis­ten over mulige vari­able i query_vars ud med føl­gende stump kode:
<?php print_r($wp_query->query_vars); ?>

… og fandt, at den vari­abel, jeg havde brug for, hed tag_id.

Særde­les anven­deligt, når man har brug for overb­lik over tilgæn­gelige variable.

Sådan fixes IE7’s z-index bug med jQuery

Det er sæd­van­ligvis Inter­net Explorer, der kræver ekstra opmærk­somhed, workarounds og udviklingstid, når man skal gøre et web­site kom­pat­i­belt på tværs af web­browsere. Jeg har lige spenderet en del tid på at komme omkring en grov ren­der­ings­fejl i IE før ver­sion 8, der ram­mer den orden hvori CSS posi­tionerede HTML ele­menter  sta­bles. Jeg er rendt ind i IE’s z-index bug.

Opgaven: En lang række list items skal udstyres med fork­laringer i form af drop downs. HTML koden ser således ud.

<ul class="linklist">
  <li>
    <h4>Overskrift 1</h4>
    <a href="#" class="dropdown-trigger">Vis beskrivelse</a>
    <div id="beskrivelse-1">
      Beskrivende note der falder ned, når man klikker på linket "Vis beskrivelse".
    </div>
  </li>
  <li>
    <h4>Overskrift 2</h4>
    <a href="#" class="dropdown-trigger">Vis beskrivelse</a>
    <div id="beskrivelse-2">
      Beskrivende note der falder ned, når man klikker på linket "Vis beskrivelse".
    </div>
  </li>
</ul>

CSS koden ser således ud.

.linklist li {position:relative;width:270px;}
.linklist li div.hidden {display:none;} /* skjuler div'en, før den aktiveres med jQuery .slideDown() */
.linklist li div {
  background:#ddd;
  border:1px solid #ccc;
  left:0;
  padding:6px 12px 12px;
  position:absolute;
  top:20px;
  width:244px;
}

<div> ele­mentet bør således placere sig ovenpå <li> ele­menterne. Det virker bare ikke i IE før ver­sion 8, fordi stakke­or­de­nen er fejl­be­hæftet. Jeg prøvede at løse prob­lemet ved at definere en lavere z-index på <li> ele­menterne, end på <div> ele­menterne, men det løste ikke prob­lemet. De efter­føl­gende <li> ele­menter blev stadig vist ovenpå det aktive <div>ele­ment.

Efter en del søgn­ing på net­tet fandt jeg Vance Lucas’ jQuery-baserede løs­ning af prob­lemet:

$(function() {
  var zIndexNumber = 1000;
  $('div').each(function() {
    $(this).css('zIndex', zIndexNumber);
    zIndexNumber -= 10;
  });
});

Ideen er, dynamisk at vende stan­dard z-index stakke­or­de­nen af HTML ele­menterne om. Således sikrer man, at ele­menter, der lig­ger højere i kildeko­den også har en højere z-index orden på siden, hvilket burde løse ren­der­ing­sprob­lemet i IE.

Det løste ikke umid­del­bart mit prob­lem. Jeg havde imi­dler­tid andet­st­eds læst, at man skulle kunne løse prob­lemet ved at give det rel­a­tivt posi­tionerede omgivende ele­ment en højere z-index orden, end det abso­lut posi­tionerede element.

Det gav mig ideen til også at lave en Vance Lucas jQuery løs­ning på det rel­a­tivt posi­tionerede <li> ele­ment, men med en højere z-index orden. Altså:

$(function() {
  var LizIndexNumber = 1100;
  $('li').each(function() {
    $(this).css('zIndex', LizIndexNumber);
    LizIndexNumber -= 10;
  });
  var DivzIndexNumber = 1000;
  $('div').each(function() {
    $(this).css('zIndex', DivzIndexNumber);
    DivzIndexNumber -= 10;
  });
});

Og voila! Så virker det.

Sådan kaldes javascripts i WordPress

Da ver­sion 1 af stylesheets og tem­plates til Hors­ens kom­munes bib­lioteker i sin tid blev kodet op, havde jeg hen­tet javascript-biblioteket jQuery + diverse plu­g­ins, plac­eret dem i wp-theme map­pen og kaldt dem manuelt i theme’ets header.php script. Det var før jeg fandt ud af, at jQuery + plu­g­ins fak­tisk er inklud­eret i Word­Press og kan kaldes med ind­byggede funktioner.

Den “rigtige” måde at kalde javascripts på i Word­Press er med fuk­tio­nen wp_enqueue_script. Med denne funk­tion kan man kalde en række ind­byggede javascript bib­lioteker, heri­b­landt jQuery, Scrip­tac­u­lous og Pro­to­type. For at kalde jQuery, skriver man således føl­gende i header.php:

<?php wp_enqueue_script('jquery'); ?>

Har man sam­tidig brug for diverse jQuery afhængige plu­g­ins, f.eks. Thick­box eller jQuery UI, kan man nøjes med at kalde disse, da jQuery så kaldes automa­tisk. I så fald kan man nøjes med følgende:

<?php wp_enqueue_script('thickbox'); ?>
<?php wp_enqueue_script('jquery-ui-core'); ?>

Har man brug for javascripts, der ikke er ind­bygget i Word­Press, kan disse også kaldes med wp_enqueue_script. Hent koden, f.eks. jQuery Cycle, og placér den i en der­til indret­tet mappe i dit theme. Kald det dernæst på føl­gende måde:

<?php wp_enqueue_script('jquery.cycle.all.pack','/wp-content/themes/dit-theme-navn/din-javascript-mappe/jquery.cycle.all.pack.js',array('jquery')); ?>

Se doku­men­ta­tion til funk­tio­nen wp_enqueue_script her.

Virtual hosts på mac under MAMP

Jeg roder i øjeb­likket med en testin­stal­la­tion af Magento på min mac. I den forbindelse havde jeg brug for at rette i etc/hosts/ filen, men den er ikke umid­del­bart til at finde på en mac, da den er skjult. Det findes der heldigvis råd for. Jeg fandt en glim­rende vejled­ning her:

Foun­da­tion PHP: Cre­at­ing a vir­tual host in Mac OS X 10.5 (Leopard)

Jeg bruger imi­dler­tid ikke mac’ens ind­byggede Apache server, men MAMP. Der­for kunne jeg ikke rigtig bruge oven­stående vejled­ning til opsæt­ning af vir­tual hosts i Apache’s kon­fig­u­ra­tions­fil, httpd.conf. Det findes der også råd for. Her er en fin vejled­ning i opsæt­ning af vir­tual hosts under MAMP:

Sawyer McFar­land Media, Inc.: Set­ting Up Vir­tual Hosts for MAMP

Samme site har en fin ind­førsel i, hvor­dan man slip­per for MAMP’s default porte 8888 og 8889 i sine lokale URL’er (Se artik­lens figur 3).

WordPress som CMS: En sidebar pr. kategori

Opnå mere flek­si­bilitet i bru­gen af Word­Press som CMS med én side­bar pr. kat­e­gori. Få PHP koden her.

Mange designere og udviklere væl­ger at bruge Word­Press som CMS fordi det er så enkelt og rel­a­tivt nemt at imple­mentere og udvikle til. Én af Word­Press’ store begræn­sninger i rollen som CMS er imi­dler­tid sidebar-metaforen. Som udgangspunkt er der i de fleste themes kun én eller to side­bars til rådighed sitewide, netop fordi Word­Press er skabt som blog­ging værk­tøj. I en blog er det sjældent nød­vendigt med flere.

Men har man brug for et decideret CMS, er der behov for rel­e­vant sekundært ind­hold på alle sider. Noget, der relaterer sig til sidens primære ind­hold og ikke bare en generel liste med kat­e­gorier, et arkiv og en blog roll.

Og så fik jeg den ide at man måske kunne opnå noget brug­bart, hvis man kunne oprette en side­bar pr. emnekat­e­gori. Så ville det være muligt at mål­rette widget-indhold til de enkelte emner og dermed øge rel­e­vansen af sekundært ind­hold på indlæg og kat­e­gorisider. Efter en del eksper­i­menteren, lykkedes det mig at skrue neden­stående sammen.

Neden­stående kode skal stå i dit themes functions.php fil:


<?php
# Get sidebar names from db.
global $wpdb;
$my_widget_name = $wpdb->get_col("SELECT slug
FROM $wpdb->terms, $wpdb->term_taxonomy
WHERE $wpdb->terms.term_id=term_taxonomy_id
AND taxonomy='category' AND count!=0 ORDER BY name ASC");

# Register one sidebar per category name.
foreach($my_widget_name as $my_widget) {
  register_sidebar(array(
    'name' => 'Category: ' . $my_widget,
    'before_widget' => '<div id="%1$s" class="%2$s widget">',
    'after_widget' => '</div>',
    'before_title' => '<h3 class="widget-title">',
    'after_title' => '</h3>', ));
}
?>

Først opret­ter vi en forbindelse til data­basen, hen­ter kat­e­gori­navn og id fra alle kat­e­gorier, der ikke er tomme, samt sorterer kat­e­gori­erne efter navn. Dernæst reg­istr­erer vi vores nye side­bars i Word­Press med funk­tio­nen register_sidebar samt navn­giver hver side­bar med det enkelte kategorinavn.

Ind­sæt de nye side­bars i dit themes sidebar.php fil:


<?php
# If this is a category archive page.
if (is_category()) {
	global $wp_query;
	$cat_obj = $wp_query->get_queried_object();
	$category_slug = $cat_obj->slug;
}
# If this is a post.
elseif (is_single()) {
	$category = get_the_category();
	$category_slug = $category[0]->category_nicename;
}
# The widget.
if ( !function_exists('dynamic_sidebar') || !dynamic_sidebar('category_' . $category_slug) ) : ?>
<div id="search" class="widget widget_search">
	<h3><?php _e('Search'); ?> <?php bloginfo('name'); ?></h3>
	<?php include (TEMPLATEPATH . '/searchform.php'); ?>
</div>
<?php endif; ?>

For at kalde den rigtige side­bar, skal vi have fat i det rigtige kat­e­gori­navn. Hvis siden er en kategori-side, kalder vi kat­e­goriens navn. Men hvis siden er et indlæg, kalder vi navnet på den første kat­e­gori, siden er gemt i. Nu udskrives kat­e­goriens side­bar, hvis du befinder dig på en kategori-side eller et indlæg.

Hvad så med ‘sider’?
Man kan lave noget tilsvarende med Word­Press’ side-metafor. I stedet for at tage fat i kat­e­gori­erne i data­basekaldet, skal man så bare have fat i tilsvarende for ‘sider’. I så fald vil data­basekaldet se ud som følger:


$page_sidebars = $wpdb->get_col("SELECT post_name
FROM $wpdb->posts WHERE post_type='page'
AND post_status='publish' AND post_parent='0'
ORDER BY menu_order ASC");

Bemærk!
Man skal være for­sigtig med meto­den, hvis man har mange kat­e­gorier og/eller sider, da Word­Press laver et kald til data­basen for hver side­bar, den skal loade i backend’en. Skal den lave for mange kald, får man time out og så fun­gerer meto­den naturligvis ikke.

WordPress hacked, defaced og inficeret med en grim iframe

For 1½ års tid siden skift­ede jeg web­ho­tel til Giga­host efter i flere år at have været kunde hos Unoeuro. Det tillokkende var funk­tion­aliteten og mulighe­den for flere domæner og data­baser på samme konto. Jeg havde aldrig sikker­hed­sprob­le­mer hos Unoeuro, men siden skiftet til Giga­host er jeg blevet hacket 3 gange. I sid­ste uge måtte mine sites så ned og ligge igen.

Hvad var blevet hacket?

Mine Word­Press sites havde fået nye for­sider og alle index og default filer havde fået til­fø­jet en iframe, der sendte evt. besø­gende hen et sted, hvor de kunne få infi­ceret deres com­puter med mal­ware eller virus.

Det så alt sam­men meget automat­gener­eret ud, for det var det eneste, der var gjort. Ingent­ing i data­baserne, så det har utvivl­somt været en robot. Det lignede det, som denne Word­Press bruger har været ude for.

Jeg er usikker på, hvor­dan hack­eren er kom­met ind. Det kan være via remote file inclu­sion (RFI), men jeg har en grum mis­tanke om at mine ftp oplysninger er blevet opsnap­pet, efter­som hack­eren er kom­met godt omkring på alle mine sites.

Hvor­dan kan man opsnappe ftp oplysningerne?

Ofte vil det kunne gøres via infi­cer­ing med vira eller mal­ware på den foruret­tedes com­puter. Jeg har scan­net mine mask­iner for begge dele og tror ikke det er sket ad den vej. Men hos Giga­host bruger man kon­ton­avn og kon­tokode til alt. Der­for står det også i wp-config.php, kon­fig­u­ra­tions­filen til Word­Press, så Word­Press kan komme i kon­takt med data­basen. Har man først fået fat på bruger­navn og kode­ord her­fra, har man således også bruger­navn og kode­ord til ftp og så er der fri adgang.

Hvad gør man for at rense et infi­ceret site?

Alle toplevel index.php filer var udskiftet med en index.htm fil og der var til­fø­jet iframes i bun­den af index.php fil­erne i mine theme-mapper samt default og index filer i øvrige map­per. Jeg fjernede alle iframes, slet­tede Word­Press helt og installerede for­fra, for at være sikker på at de infi­cerede filer var helt væk.

rzaman.com har en udmær­ket opskrift på, hvor­dan man gør.

Dnx­pert har et fint indlæg om, hvor­dan man også under­søger, om data­basen er inficeret.

Også Smack­down har en glim­rende artikel om emnet.

Hvor­dan kan man øge sikkerheden?

Brug .htac­cess filer til at blokere adgan­gen til bestemte map­per og filer. Her er et par ressourcer, jeg har fun­det brugbare:

Sørg også for at begrænse ret­tighed­erne på dine filer og map­per. Jeg bemærkede, at man nemt overser wp-config.php i den henseende. Sæt manuelt ret­tighed­erne på denne fil til 600, så andre ikke kan læse den. Se udførlig artikel hos Word­Press om hvor­dan man gør.

Instal­lér sikker­hed­splu­g­ins i Word­Press, f.eks.:

  • AskA­pache Pass­word Pro­tect — der gener­erer .htac­cess filer for dig.
  • Login Lock­Down — der tjekker om der har været mange forgæves login­forsøg fra en given ip adresse, samt blok­erer denne.
  • AntiVirus — der tjekker dine theme-filer for infektion.
  • WP Secu­rity Scan — der scan­ner din Word­Press instal­la­tion for sår­barheder og fores­lår udbedringer.

Og endelig: Skift kode­ord, skift kode­ord, skift kode­ord. The Blog Her­ald har gode tips til, hvor­dan man finder på et godt ét.

Har Giga­host et sikker­hed­shul og bliver jeg hacket igen?

Giga­host har ikke nød­vendigvis et sikker­hed­shul, men det er da prob­lema­tisk, at man skal bruge samme bruger­navn og kode­ord til alt. Og ja, efter­som jeg ikke helt ved, hvor­dan hack­eren kom­mer ind, bliver jeg måske hacket igen. Men så kan det altså godt ske, jeg skifter web­ho­tel og ser om det løser problemet.