Jul 15

Using a little YUI AJAXian magic, I've constructed a simple and flexible method for giving user feedback on button press.  The use case, in this case, is a form submit (user login) where credentials are YUI IO posted to a PHP form handler for authentication against user data stored in a database.  If authenticated, a user session is established, and a redirect to the private content occurs.

There is nothing even remotely special about the form button itself:

<input id="login" name="login" class="submitBtn" type="button" value=<? echo "$langvar_login_button_label" ?> title="Login..." />

The CSS is all fairly routine:

input[type=button] {
 height: 22px;
 border: 1px solid #4682B4;
 margin-left: 4px;
 margin-bottom: 4px;
 margin-right: 4px;
 margin-top: 4px;
 background: #FFF url(http://yui.yahooapis.com/3.1.0/build/assets/skins/sam/sprite.png) repeat-x scroll 0 0px;
 cursor: pointer;
 color: #4682B4;
 font-weight: bold;
 float: right;
}
input[type=button]:hover {
 border-left: 1px solid #B0C4DE;
 border-top: 1px solid #B0C4DE;
}
input[type=button]:active {
 border: 1px solid #B0C4DE;
 color: #B0C4DE;
}

The magic happens here, in the YUI block:

<script>
YUI().use('gallery-formmgr','io-form',
 function(Y) {
  var cfg = {
   method: 'POST',
   form: {
    id: 'loginform',
    useDisabled: false
   }
  };
  var f = new Y.FormManager('loginform',{});
  f.prepareForm();
  f.initFocus();
  function login() {
   Y.io('.handlers/loginout.php?foo=bar', cfg);
   Y.on('io:success', onSuccess, this);
   Y.on('io:failure', onFailure, this);
   Y.all('#login').setStyle('opacity',.5);
   Y.all('#login').setStyle('background','#FFF url(.assets/img/ajax-loader-arrows-4682B4t.gif) no-repeat scroll 50% 50%');
   Y.all('#login').setStyle('color','rgba(0,0,0,0)');
   Y.all('#login').set('disabled',true);
  };
  function onSuccess(id,response) {
   Y.all('#loginmsg').setContent(response.responseText);
   f.clearForm();
   window.location.reload(false);
  };
  function onFailure(id,response,args) {
   f.clearForm();
  };
  Y.on('click', login, '#login');
});
</script>
  • One can see that I set opacity - I do this because setting disabled=true has no visual affect on the button due to the styling override.  Setting opacity makes "disabled" look disabled. 
  • The next order of business is to set the background of the button to my animated "busy" image.  I should point out that there are many resources on the web that allow one to freely provision countless variety of animated "busy" images.  I really like "ajaxload.info" - wonderfully clean site design, great interface, does exactly what you would expect - choose from many standard animations, define your color, transparency, etc.
  • I set the color attribute using "rgba" because it allows easy access to alpha (the fourth value).  Some browsers will respect the simpler color=transparent, but it is not as universal.  Even "rgba" does not behave as-desired in IE(8), failing to mask the button text - annoying, but not annoying enough to complicate matters with an IE-specific hack.
  • Finally, to disable the button - again, it achieves nothing visually in this case, but prevents mulitple submits by impatient users.

So, to illustrate - the button in normal state, and the button in busy state:

Imagine the "busy" icon set into the button spinning like so:   Tasteful, intuitive, and informative.

Posted by Adam KrauseGo w.i.d.eTweet MeShort URL

Jul 9

I'm posting this because it took valuable minute to work out functional form submit via the YUI 3: IO data handling module (YUI calls them "utilities", I like "modules").  The intent behind YUI IO is essentially to modulize the data transport between the UI and back-end solution - in the spirit of AJAX, to give persistence in the UI while making the data transport transparent.

In an effort to preserve the fundamentals, and before I get much further along and can't unravel all of the subsequent work, I want to post my working example of YUI IO used to pass basic form data, via POST, to a PHP back-end and consume/display the response in the UI.  We start with the usual stuff - linking in the library, then building the form, scripting the IO request, and finally writing the back-end handler to interface to the database.

Step 1: Build the table that you will insert into...

-- YUI:3 IO form submit/return test table
-- PigsLipstick.com
--
CREATE TABLE IF NOT EXISTS `test_table` (
  `id` smallint(6) NOT NULL auto_increment,
  `myfield` varchar(128) NOT NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

Step 2: Create the UI - call it "iotest.php" (I give it .php out of force of habit)...

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">

<link type="text/css" rel="stylesheet" href="http://yui.yahooapis.com/3.1.0/build/cssfonts/fonts-min.css" />
<script type="text/javascript" src="http://yui.yahooapis.com/3.1.1/build/yui/yui-min.js"></script>

</head>

<body class="yui-skin-sam yui3-skin-sam" />
<form id="myform">
    <input id="myfield" type="text" name="myfield" />
    <input id="myformbutton" type="button" value="Submit" /><br />
    <span id="myformmsg"></span>
</form>

<script>
YUI().use("io-form",
    function(Y) {
        var cfg = {
            method: 'POST',
            form: {
                id: 'myform',
                useDisabled: false
            }
        };
        function login() {
            Y.io('iotesthandler.php', cfg);
            Y.on('io:success', onSuccess, this);
            Y.on('io:failure', onFailure, this);
        };
        function onSuccess(id,response,args) {
            document.getElementById('myformmsg').innerHTML = response.responseText;
            document.forms['myform'].reset();
        };
        function onFailure(id,response,args) {
            document.getElementById('myformmsg').innerHTML = "Error, retry...";
            document.forms['myform'].reset();
        };
        Y.on('click', login, '#myformbutton', this, true);
});
</script>

</body>
</html>

Step 3: Create the back-end (use your database/credentials)...

<?
$hostname = "localhost";
$db_user = "root";
$password = "root";
$db = "localtest";

mysql_connect($hostname, $db_user, $password) or die("DB Login Failure");
mysql_select_db($db);

$myfield = $_POST['myfield'];
$ins = "INSERT INTO test_table (myfield) VALUES('$myfield')";
@mysql_query($ins) or die("Failed to Insert.");

print ("Row ID submitted: ".mysql_insert_id());
?>

What to expect...

In short, this "kit" should provide a persistent UI - nothing but a text field and button at first, but upon submit (and with persistence intact), will submit the value entered into the text field, and return, if successful, the row ID of the record submitted below the text field.  Double check against the db table, and you will find the data submitted per the corresponding row ID returned.

Even if the submit fail, the UI is "smart enough" to error gracefully and return the error message to the UI - at least for errors pertaining to failure to connect to the database, or general failures submitting into the database.

From this example, one should easily be able to extend to the average use case and leverage the example for pure data retrieval vs insert & retrieval.

Download files pkg here.

Posted by Adam KrauseGo w.i.d.eTweet MeShort URL

Jul 7

I've owned the domain "mySteelBox.com" since 2008, and in fact, developed the concept several times over - always abandoning the project for other interests and technologies.  Originally, mySteelBox was to be an Adobe AIR app, then OpenLaszlo, then Flex - and the list goes on to finally land on a more traditional, functional, AJAX-based web app. 

This post probably should have been the first, but needed to feel that the project would finally materialize and would see it through to completion this time.  So, I hereby consider myself committed...

mySteelBox

Posted by Adam KrauseGo w.i.d.eTweet MeShort URL

Jun 16

I haven't posted in awhile on the doc vault project - it's been over a month.  That is mainly because it is low priority and I've changed tacks a number of times in, and around, most of the major technical components of the project.  Plus, I'm rusty - not having done any ground-up app development in a very long time.

I've flip-flopped endlessly on a key component of the project - the file upload UI.  I'm committed to FTP on the backend for reasons spelled out in the previous post, but the UI has sent me down numerous paths.  I continue to have to (but generally forget to) remind myself that simple is better in this project - simple, proven, familiar.  That said, for such a key component of a document vault application, file upload is bread and butter to what it does, and how it is used.  Due diligence required looking at logical alternatives to the standard baked-in file browse feature - these primarily included Java applet, Flex and Flash options.

All of the off-the-shelf non-html options investigated become a concern for the fact that they are compiled, and if I didn't build it myself, how can I know for certain that it is safe and secure under the covers?

Java becomes an issue because,

  • I never did find as truly simple a solution as I'd hoped,
  • those that I did find were often for-fee and/or not easily distributable,
  • end users are dependent on the appropriate runtime,
  • I have almost no skill/experience in compiled Java development.

Flex/Flash becomes an issue because,

  • there is the runtime component (of much lesser concern than Java),
  • there are the odd platforms which do not support Flash,
  • some browsers complain terribly about Flash-based file upload, but they do so quietly and give little indication as to what, exactly, is wrong.  This is a real issue in Firefox currently, and is complicated by the number of variables involved (browser variant/version, Flash plug-in version, Flash/Flex compiled version, etc).  As such, and because I can't be depended upon to recompile a Flash/Flex-based widget every time the wind shifts, this option loses favor.

Back to the ubiquitous file browse (<input type=file>) object - while it has its limitations, it fulfills the need for simplicity, as well as proven technology, and is familiar and trusted.  No funny business can happen behind the scenes as the file browser object is delivered by the browser, disallows programmatic interaction, and is inherently trusted by the OS.  The major complaint then remains the browser-specific nuances of the file browse object implementation with regard to UI styling.

I've expanded my platform testing across Firefox 3.5, IE 8.0, Chrome 5.0 and Opera 10.5 - all on Windows XP sp3.  Of particular interest at this stage in the project, is the appearance of the file browse object.  I'm not claiming that any one implementation is better than the other (actually I am, Firefox sucks/Chrome gets my vote for best-implementation), just pointing out the differences.  Fundamentally, all accomplish what they're supposed to (no surprise there), and generally behave as-expected and consistently from a functional point of view.  However, out-of-box rendering with basic styling applied varies quite a bit from one extreme to the other.

Here are some notes:

  • Firefox: you give me almost nothing to work with - you don't adhere to CSS width, border, background, positioning (float, etc), text attributes very well at all.  I'm not asking for much from my favorite, most-preferred browser - just a little consideration for the basics please.
  • IE: you do a darn good job letting me style your borders, background, text attributes, and positioning.  I wish you would pay better attention to width attributes - there is the slightest misinterpretation of object width which requires fudging with yet another block of CSS to correct.
  • Opera: like IE in essentially every way, you let me apply styling to borders, size, background, and so on.  You, too, seem to suffer from the same odd sizing problem as IE - width is width, and your "Choose" button is even integrated into the field itself.  Also, I'm not a big fan of the way you wrap the selected file in quotes...none of the others do this.
  • Chrome: not my principle browser by choice, I do recognize its growing popularity and potential.  It deserves consideration for validating work and has become another part of the testing repertoire.  In the case of the lowly file browse object, Chrome earns my nod for best overall implementation.  Recognizing the necessary attributes - border, background, float, and above all, width - Chrome is the only one of the bunch that sized the file field to the proper dimensions in comparison to a standard text field.  I also like the fact that Chrome sets only the target filename into the field, and not the full path.  Rarely do I doubt that I've chosen a file from the wrong path, but often-enough doubt that I've chosen the right file from the right path.  In a shorter field, in all other browsers tested, this means scrolling to the right to validate the filename.  I do wish that the "Choose File" button would style in accordance with the rest of the field (background and border), but it is livable.

In testing, I did find that a strict doctype is critical to forcing adherence to CSS attributes in the browsers that choose to honor them.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">

Finally, some screen shots to illustrate - pointing out that Firefox width was set using the "size=" attribute in the field attributes (yuk).

Chrome 5IE 8Opera 10.5Firefox 3.5

Posted by Adam KrauseGo w.i.d.eTweet MeShort URL

May 5

Having spent useful time on deciphering and unobtrusive means of YUI tab skinning (i.e.: without tearing into the core css proper, but rather doing all of my customer skinning/appearance work in a secondary super-ceding set of CSS), I realized in a flash that this is exactly the behavior that has delayed the project for over a year to-date.  I should not be finding new ways to complicate and otherwise ultra-simple objective.

So, I've landed back on a single panel UI with nothing more than a login, a results list (based on login), item detail, and upload widget in four panels.  Nothing fancy, just pure simplicity with function at its core.

Getting back to basics in UI design and layout has given back the much needed time to ponder methodologies for file upload - for several reasons, this is a more complex topic than it appears at face value.  The upload itself needs to be secure as per the underlying principle of the app itself - a secure document repository subject to the limitations of the weakest link in the process.  The upload also needs to function within the confines of what is allowable given standard use case, configuration, and the technology.  Certain assumptions are made which could ultimately be consumer-limiting, but which are necessary in context of the greatest good for most potential consumers.

Some upload methods considered include basic HTTP and HTTPS POST - this is the most obvious, most common, and certainly the easiest approach.  Ruled out primarily for a singular consideration that I fear would plague most - and that is PHP's typcial "upload_max_filesize" default limitation of only 2mb.  While adequate for most small images and the average doc/text file, what about the graphic-heavy PowerPoint or PDF that needs archiving?  Since control of PHP configuration of often out of the hands of the typical shared-host consumer, and/or is an unknown/unpredictable variable that would require many to engage their provider or web admin, jump through hoops, and so on, I felt it better to explore alternatives.

Second consideration - what about cutting out the middle-man process of uploading to the server first before loading in the database BLOB altogether?  What a practical sounding solution to simply stream into a BLOB...and it can be done, in MySQL, with the addition of a bolt-on engine to do so.  If only...this is so close to perfect it's almost too good to pass up, if even for exploratory purposes only.  You can read all about it here - if I were on a self-hosted server, I'd be looking really hard at this.

The third and final consideration - FTP.  Simple, elegant, and ubiquitous, FTP is not bound by PHP upload_max_filesize limitations, can be made secure over the wire (SFTP), is a time-proven, very resilient protocol, and plays nicely with PHP via a host of builtin ftp functions.

So, that settles it - now building the web app around FTP upload.  Configuration could be slightly tricky, and still debating giving the option for HTTP POST just because there may be the odd host that does not readily allow FTP, though I'm doubtful of this.  What's tricky about configuration?  To do it right/well, one should configure the webapp to connect via an established administrative account and upload to a specific temp directory outside of doc root...this introduces variables, and with variables, complication.

Posted by Adam KrauseGo w.i.d.eTweet MeShort URL

Apr 23

I've already fought my first battle against YUI3 around the TabView and skinning it for consistency with the DataTable (which will be down-rev at YUI2 given this widget does not yet exist for YUI3).

In the end, I've won the battle with a cross-browser friendly reskinning of the TabView by way of gaining access to the following classes - and finding the suitable classes was definitely the tricky part (if all browsers could be as forgiving and workable as Firefox...).

  • .yui3-widget, .yui3-tabview .yui3-tabview-content .yui3-tabview-list :: gives access to the line that separates the bottom of the tabs from the top of the tab content box.
  • .yui3-skin-sam .yui3-tab-selected .yui3-tab-label, .yui3-skin-sam .yui3-tab-selected .yui3-tab-label:focus, .yui3-skin-sam .yui3-tab-selected .yui3-tab-label:hover :: gives access to the selected tab (not the body, just the selected tab itself).
  • .yui3-skin-sam .yui3-tab-label :: gives access to all the other (un-selected) tabs.
  • .yui3-widget, .yui3-tabview .yui3-tabview-content .yui3-tabview-panel :: gives access to the panel or tab content.

I did the following to produce the results per the image below. 

.yui3-widget, .yui3-tabview .yui3-tabview-content .yui3-tabview-list {
    margin-right: 5px;
    border-color: #DDD;
    border-width: 0 0 1px;
}
.yui3-skin-sam .yui3-tab-selected .yui3-tab-label, .yui3-skin-sam .yui3-tab-selected .yui3-tab-label:focus, .yui3-skin-sam .yui3-tab-selected .yui3-tab-label:hover {
    color: #000000 !important;
    background: #2647A0 url(http://yui.yahooapis.com/3.1.0/build/assets/skins/sam/sprite.png) repeat-x scroll left -100px !important;
    -x-system-font: none;
    font-family: arial,helvetica,clean,sans-serif;
    font-size: 13px;
    font-size-adjust: none;
    font-stretch: normal;
    font-style: normal;
    font-variant: normal;
    font-weight: normal;
    -moz-border-radius-topright: 5px;
    -moz-border-radius-topleft: 5px;
    -webkit-border-top-right-radius: 5px;
    -webkit-border-top-left-radius: 5px;
}
.yui3-skin-sam .yui3-tab-label {
    -x-system-font: none;
    font-family: arial,helvetica,clean,sans-serif;
    font-size: 13px;
    font-size-adjust: none;
    font-stretch: normal;
    font-style: normal;
    font-variant: normal;
    font-weight: normal;
    -moz-border-radius-topright: 5px;
    -moz-border-radius-topleft: 5px;
    -webkit-border-top-right-radius: 5px;
    -webkit-border-top-left-radius: 5px;
}
.yui3-widget, .yui3-tabview .yui3-tabview-content .yui3-tabview-panel {
    background: #FFFFFF;
    -moz-border-radius-topright: 5px;
    -moz-border-radius-bottomright: 5px;
    -moz-border-radius-bottomleft: 5px;
    -webkit-border-top-right-radius: 5px;
    -webkit-border-bottom-right-radius: 5px;
    -webkit-border-bottom-left-radius: 5px;
}

Comparing the stock DataTable field shows that the two widgets compare well against each other cosmetically, and are more complimentary from a look & feel perspective.  You can see the rounded corners were done simply - using CSS radius.  So, they don't appear in IE.  Also, I worked diligently to come up with the shortest, simplest, most compressed solution possible, and this was the best I could do without significant time to fiddle around with it - happy for suggestions on this!


Continue reading "YUI3 TabView Skinning"

Posted by Adam KrauseGo w.i.d.eTweet MeShort URL

Apr 22

Like a segment out of Groundhog Day, I'm back trying to rekindle rusty development skills in an effort to fill a gap, a need, for a highly secure, very simple, purpose-built document vault.

I've looked high and low for something pre-built in the public domain (code for "free") that was secure to the extent that the files/data stored was fully encrypted.  Fundamentally, most systems/apps available are geared more toward document management and sharing, as opposed to storage and safe-keeping.  This is great where iterative copies need to be kept and permissioning around view vs update access is important, but is far more than I'm looking for from a workflow point of view, and not rich enough from a security point of view.

I've thought on the topic for awhile now, and have ultimately decided that data should be stored off the file system (in a database making use of BLOB storage), that YUI3 should be used for rapid development of a rich UI with asynchronous event handling, that simplicity in design and function is foremost behind security, and that the data itself should be encrypted at record create-time.

Now to begin...

Posted by Adam KrauseGo w.i.d.eTweet MeShort URL


Strict Standards: Declaration of serendipity_event_s9ymarkup::event_hook() should be compatible with serendipity_event::event_hook($event, &$bag, &$eventData, $addData = NULL) in /home1/pigslips/public_html/s9y/plugins/serendipity_event_s9ymarkup/serendipity_event_s9ymarkup.php on line 146

Strict Standards: Declaration of serendipity_event_s9ymarkup::uninstall() should be compatible with serendipity_plugin::uninstall(&$propbag) in /home1/pigslips/public_html/s9y/plugins/serendipity_event_s9ymarkup/serendipity_event_s9ymarkup.php on line 146

Strict Standards: Declaration of serendipity_event_emoticate::event_hook() should be compatible with serendipity_event::event_hook($event, &$bag, &$eventData, $addData = NULL) in /home1/pigslips/public_html/s9y/plugins/serendipity_event_emoticate/serendipity_event_emoticate.php on line 204

Strict Standards: Declaration of serendipity_event_emoticate::uninstall() should be compatible with serendipity_plugin::uninstall(&$propbag) in /home1/pigslips/public_html/s9y/plugins/serendipity_event_emoticate/serendipity_event_emoticate.php on line 204

Strict Standards: Declaration of serendipity_event_nl2br::event_hook() should be compatible with serendipity_event::event_hook($event, &$bag, &$eventData, $addData = NULL) in /home1/pigslips/public_html/s9y/plugins/serendipity_event_nl2br/serendipity_event_nl2br.php on line 395

Strict Standards: Declaration of serendipity_event_nl2br::uninstall() should be compatible with serendipity_plugin::uninstall(&$propbag) in /home1/pigslips/public_html/s9y/plugins/serendipity_event_nl2br/serendipity_event_nl2br.php on line 395

Strict Standards: Declaration of serendipity_event_browsercompatibility::event_hook() should be compatible with serendipity_event::event_hook($event, &$bag, &$eventData, $addData = NULL) in /home1/pigslips/public_html/s9y/plugins/serendipity_event_browsercompatibility/serendipity_event_browsercompatibility.php on line 80

Strict Standards: Declaration of serendipity_event_spartacus::event_hook() should be compatible with serendipity_event::event_hook($event, &$bag, &$eventData, $addData = NULL) in /home1/pigslips/public_html/s9y/plugins/serendipity_event_spartacus/serendipity_event_spartacus.php on line 1183

Strict Standards: Declaration of serendipity_event_imageselectorplus::event_hook() should be compatible with serendipity_event::event_hook($event, &$bag, &$eventData, $addData = NULL) in /home1/pigslips/public_html/s9y/plugins/serendipity_event_imageselectorplus/serendipity_event_imageselectorplus.php on line 1105

Strict Standards: Declaration of serendipity_event_imageselectorplus::uninstall() should be compatible with serendipity_plugin::uninstall(&$propbag) in /home1/pigslips/public_html/s9y/plugins/serendipity_event_imageselectorplus/serendipity_event_imageselectorplus.php on line 1105

Strict Standards: Declaration of serendipity_event_sidebarlogin::event_hook() should be compatible with serendipity_event::event_hook($event, &$bag, &$eventData, $addData = NULL) in /home1/pigslips/public_html/s9y/plugins/serendipity_plugin_sidebarlogin/serendipity_event_sidebarlogin.php on line 148

Strict Standards: Declaration of serendipity_event_popfetcher::event_hook() should be compatible with serendipity_event::event_hook($event, &$bag, &$eventData, $addData = NULL) in /home1/pigslips/public_html/s9y/plugins/serendipity_event_popfetcher/serendipity_event_popfetcher.php on line 1426

Strict Standards: Declaration of serendipity_event_lightbox::event_hook() should be compatible with serendipity_event::event_hook($event, &$bag, &$eventData, $addData = NULL) in /home1/pigslips/public_html/s9y/plugins/serendipity_event_lightbox/serendipity_event_lightbox.php on line 281

Strict Standards: Declaration of serendipity_event_lightbox::uninstall() should be compatible with serendipity_plugin::uninstall(&$propbag) in /home1/pigslips/public_html/s9y/plugins/serendipity_event_lightbox/serendipity_event_lightbox.php on line 281

Strict Standards: Declaration of serendipity_event_tinymce::event_hook() should be compatible with serendipity_event::event_hook($event, &$bag, &$eventData, $addData = NULL) in /home1/pigslips/public_html/s9y/plugins/serendipity_event_tinymce/serendipity_event_tinymce.php on line 291

Strict Standards: Declaration of serendipity_event_tinybrowser::event_hook() should be compatible with serendipity_event::event_hook($event, &$bag, &$eventData, $addData = NULL) in /home1/pigslips/public_html/s9y/plugins/serendipity_event_tinybrowser/serendipity_event_tinybrowser.php on line 150

Strict Standards: Declaration of serendipity_event_prettify::event_hook() should be compatible with serendipity_event::event_hook($event, &$bag, &$eventData, $addData = NULL) in /home1/pigslips/public_html/s9y/plugins/serendipity_event_prettify/serendipity_event_prettify.php on line 245

Strict Standards: Declaration of serendipity_event_xmlrpc::event_hook() should be compatible with serendipity_event::event_hook($event, &$bag, &$eventData, $addData = NULL) in /home1/pigslips/public_html/s9y/plugins/serendipity_event_xmlrpc/serendipity_event_xmlrpc.php on line 160

Strict Standards: Declaration of serendipity_event_podcast::event_hook() should be compatible with serendipity_event::event_hook($event, &$bag, &$eventData, $addData = NULL) in /home1/pigslips/public_html/s9y/plugins/serendipity_event_podcast/serendipity_event_podcast.php on line 939

Strict Standards: Non-static method TwitterPluginFileAccess::get_permaplugin_path() should not be called statically, assuming $this from incompatible context in /home1/pigslips/public_html/s9y/plugins/serendipity_plugin_twitter/serendipity_event_twitter.php on line 1554

Strict Standards: Non-static method TwitterPluginDbAccess::load_short_urls() should not be called statically, assuming $this from incompatible context in /home1/pigslips/public_html/s9y/plugins/serendipity_plugin_twitter/serendipity_event_twitter.php on line 1518