as i said earlier i was writing a plugin (or rather a widget) for wp, for work. there’s alotta examples of plugins out there but i had a hard time finding more extensive widget examples (wp-engineer has a very nice, but basic one). So here’s an example of a widget with some extras: scripts, localization, (de)activation hooks, etc.
Note that the code is based on this folder structure:
my_widget/
css/
mystyle.css
img/
myicon.png
js/
myscript.js
lang/
widget_name_domain-xx_XX.po
widget_name_domain-xx_XX.mo
widget_name_domain-yy_YY.po
widget_name_domain-yy_YY.mo
my_widget_name.php
Note again, i haven’t tried this code. its just based on the widget i wrote earlier.
<?
/*
Plugin Name: name of it
Plugin URI: plugin uri
Description: description
Author: your name
Version: widget version
Author URI: your webpage
*/
// ^^ first the standard comment (include it!) ^^
// since the name of the widget is used in a bunch of places i'd
// recommend to define() it
define("MYWIDGET_NAME", 'my_widget_name');
// if you're gonna translate your widget i'd also recommend
// define()ing the domain-name. your translation files are supposed
// to be named: this + dash + locale
// eg. widget_name_domain-sv_SE.po/mo
define("MYWIDGET_DOMAIN", 'widget_name_domain');
// to include custom styles/scripts you need unique identifiers
// for them too, i'd suggest define()ing them too:
define("MYWIDGET_STYLE", 'widget_name_style');
define("MYWIDGET_SCRIPT", 'widget_name_script');
// if you want to use data from your widget outside of the class you
// need to store that in an "option", which also needs a unique
// identifier...
define("MYWIDGET_OPTS", 'widget_name_options');
// here are some other define()s that may or may not be useful
define("MYWIDGET_URL", plugins_url().'/'.dirname(plugin_basename(__FILE__)));
// the path relative wp-root.
define("MYWIDGET_PATH", str_replace(site_url(), '', MYWIDGET_URL));
// the absolute path
define("MYWIDGET_APATH", parse_url(MYWIDGET_URL, PHP_URL_PATH));
// here's the widget class, for the gory details see official docs.
// make _sure_, if you define()d you widget name you use the same
// here!
class my_widget_name extends WP_Widget
{ // constructor
function my_widget_name()
{ // wordpress options
$wops = array
(
'classname' => MYWIDGET_NAME,
// translation in wp is easy, use __(string, domain) to get
// translated string, _e(string, domain) to echo it.
'description' => __("Internationalized description", MYWIDGET_DOMAIN)
);
$cops = array
( // w/h on the admin-page
'width' => 250,
'height' => 140,
// id-prefix for content of the box on the admin page (lower case)
'id_base' => 'mywidget_baseid'
);
$this->WP_Widget
( // id-base
$cops['id_base'],
// translated name, displayed on the admin-page
__("My Widget Name", MYWIDGET_DOMAIN),
$wops, $cops
);
}
// these two functions are not mandatory but practical if you want
// to add/remove options
function activate()
{ // lets use an option to provide some data for our javascript
$options = array( 'opt1' => 'default_one',
'opt2' => 'default_two' );
add_option(MYWIDGET_OPTS, $options, '', 'no');
}
function deactivate()
{ // you should always clean up after yourself!!
delete_option(MYWIDGET_OPTS);
}
// theses three functions are pretty much mandatory.
// this is the form printed in the admin-interface, the argument
// instance contain all you in-widget data! wp takes care of
// storing that for you.
function form($inst)
{ // lets define some default values for your data
$defs = array
(
'title' => __("Int. title", MYWIDGET_DOMAIN),
'setting' => '',
'option1' => 'default_one',
'option2' => 'default_two'
);
// now parse any allready stored values
$inst = wp_parse_args($inst, $defs);
// wp provides some functions to use when printing your admin
// form. as mentioned before, if you want the details about em
// see the docs
?>
<label for="<? print $this->get_field_id('title'); ?>"><? _e("Int. label 1", MYWIDGET_DOMAIN); ?></label>
<input type="text"
name="<? print $this->get_field_name('title'); ?>"
id="<? print $this->get_field_id('title'); ?>"
value="<? print $inst['title']; ?>"
size="20" /><br />
<label for="<? print $this->get_field_id('setting'); ?>"><? _e("Int. label 2", MYWIDGET_DOMAIN); ?></label>
<input type="text"
name="<? print $this->get_field_name('setting'); ?>"
id="<? print $this->get_field_id('setting'); ?>"
value="<? print $inst['setting']; ?>"
size="20" /><br />
<label for="<? print $this->get_field_id('option1'); ?>"><? _e("Int. label 3", MYWIDGET_DOMAIN); ?></label>
<input type="text"
name="<? print $this->get_field_name('option1'); ?>"
id="<? print $this->get_field_id('option1'); ?>"
value="<? print $inst['option1']; ?>"
size="20" /><br />
<label for="<? print $this->get_field_id('option2'); ?>"><? _e("Int. label 4", MYWIDGET_DOMAIN); ?></label>
<input type="text"
name="<? print $this->get_field_name('option2'); ?>"
id="<? print $this->get_field_id('option2'); ?>"
value="<? print $inst['option2']; ?>"
size="20" /><br />
<?
}
// this is where you update your data,
function update($newinst, $oldinst)
{ // newinst holds the newly entered values (from above)
$inst = $oldinst;
$inst['title'] = $newinst['title'];
// example of operation on data before storing:
$inst['setting'] = trim($newinst['setting']);
$inst['option1'] = $newinst['option1'];
$inst['option2'] = $newinst['option2'];
// we need to update our option manually!
$opts = get_option(MYWIDGET_OPTS);
$opts['opt1'] = $inst['option1'];
$opts['opt2'] = $inst['option2'];
update_option(MYWIDGET_OPTS, $opts);
// return the data that is to be stored by wp
return $inst;
}
// called (by the sidebar) when the widget is displayed on your page
// the second argument holds some formatting from the template,
// you need to extract and print it..
function widget($args, $inst)
{ // this produces $before/after_widget and $before/after_title
extract($args);
// please set yourself up correctly in the template
print $before_widget;
// fix up your title
$title = apply_filters('widget_title', $inst['title'], $inst, $this->id_base);
// and print it
if($title) print $before_title . $title . $after_title;
// here's some bogus output (example usage of APATH)
?>
<img src="<? print MYWIDGET_APATH . "/img/myicon.png"; ?>"
alt="my icon"
title="my icon" />
<p>
<? // and here's another internationalized string
_e("I am using a setting here, it's called: ", MYWIDGET_DOMAIN);
print $inst['setting'];
?>
</p>
<?
print $after_widget;
}
}
// woha! were outta the class, what now?!
// well this is where we register our widget, styles, scripts and
// translation!
// register the widget
function my_widget_register()
{
register_widget(MYWIDGET_NAME);
}
// load your styles
function my_widget_style()
{
wp_register_style(MYWIDGET_STYLE, MYWIDGET_PATH.'/css/mystyle.css');
wp_enqueue_style(MYWIDGET_STYLE);
}
// and your script
function my_widget_script()
{
wp_register_script(MYWIDGET_SCRIPT, MYWIDGET_PATH.'/js/myscript.js', array(), '0.1');
wp_enqueue_script(MYWIDGET_SCRIPT);
// now, lets load the options for the script!
// this is why we used an option, we have no $inst here..
$opts = get_option(MYWIDGET_OPTS);
wp_localize_script(MYWIDGET_SCRIPT, 'myoptions', $opts);
// now myoptions.opt1/2 in your javascript can be
// used to retrieve the values of $inst['option1/2']
}
// to make use of the functions above we need to hook em to actions.
// whether or not these actions are The Right Way (tm) i do not know,
// but they work and seem pretty much right.
// first the (de)activation:
register_activation_hook(__FILE__, array(MYWIDGET_NAME, 'activate'));
register_deactivation_hook(__FILE__, array(MYWIDGET_NAME, 'deactivate'));
// then the others
add_action('widgets_init', 'my_widget_register');
add_action('wp_print_styles', 'my_widget_style');
add_action('wp_print_scripts', 'my_widget_script');
// i haven't figured out when to load this so ill just dump it
// right here..
load_plugin_textdomain(MYWIDGET_DOMAIN, false, MYWIDGET_PATH . '/lang');
There’s a bunch of info on how to localize your widget out there. if you don’t want to install extra editors to produce pot/po/mo-files here’s some commands for ya!
# this is one way to produce your pot-file (run it in the folder containing you php-files):
grep --color=never -HonP '_(_|e)\s*\(\s*[^\)]+\)' *.php | sed 's/\([^:]\+[^:]\+:[^(]\+\)\(.\+\)/\1 \2/' | awk '{a = gensub(/^[^ ]+ /,"","g"); if(!x[a]++) print $0}' | gawk '{a = gensub(/([^:]+:[^:]+):_(_|e) *\( *("[^"]+").*/, "#: ../\\1\nmsgid \\3\nmsgstr \"\"\n", "g"); print a}' > lang/widget_name_domain.pot
# note that you have to use double quotes in __("string", domain) and _e("string", domain) for this to work (and you phps names may not contain any spaces).
# then to batch-compile your translations cd into lang and run
ls *.po* | gawk '{a = gensub(/([^\.]+).*/, "\\1", "g"); system("msgfmt -o "a".mo " $1)}'
that’s it for now.
// sluggo