Notifications – I Think You Told Me Already


December 21st, 2011

Didn't you just tell me that?

People use an ever increasing number of applications, services and platforms to communicate or collaborate with each other. Each of these services needs to send out notifications to their users to inform them that certain events have occurred.

An event can be absolutely anything:

  • Comment
  • Status update
  • Task completion
  • Blog post
  • Uploaded picture
  • etc

We have an enormous amount of ways through which we can inform users that one of these actions has occurred i.e Email, Instant Message, Text message, On screen notification, Phone call (automated), RSS, Push notifications etc.

The internet, its users, creators and facilitators should all have enough experience at this stage to understand how to effectively handle notifications. Yet here I am writing a blog post about how much notifications annoy me because they are not done right.

So what is the notification problem?

Companies have a mindset that says “Oh oh oh quick! Something happened. Tell the user every which way you can so that they know!”.

I am going to single out Yammer here as my example – but many big internet companies are guilty of notification overload.

We use Yammer in the office. It is a decent tool for intra-company communication that is more flexible that email while also being quick and easy to use. It is basically Twitter for companies.

You can add messages in Yammer via their website or through their desktop client. If you are running the desktop client when someone posts a message then you’ll see a flash up notification on your desktop outside of the client – completely in your face – cannot miss it. If you then bring the client into focus on the desktop – it’ll have a red box with a number in it (indicating how many posts you’ve missed). Below that will be a big box that you have to click to load the new messages. On top of this you’ll also get a link to click “load new messages” within each thread that has new messages.

If someone has directly replied to a comment of yours – then you’ll also get ANOTHER red box at the top with a number in it (this one representing the replies you’ve missed).

How many notifications or prompts, within the desktop client alone, was that?

Outside of the desktop app – you’ll also receive an email for all comments/messages that are relevant to you. I’m glad they don’t have my mobile phone number.

If you are anything like me, you cant have all of these red boxes and emails screaming at you to be read and cleared so you feel force to give them your attention.

How about some context?

Informing users through every communication mechanism available is clearly not the answer and essentially leads to tired, irked users.

Notifications can be clever. They can ask questions about the users they need to contact and make a decision about the best way to get in touch.

Is the user logged in on the website or the desktop client? Yes – then trust that your notification mechanism will get the message through. If the user is not logged in – then send an email.

Any tricks for the website notifications being noticed?

How about checking if the window has focus? If you are using jQuery you could do this:


$(window).blur(function() {
    // Inform my app that the user
    // is not going to see my notifications
});

$(window).focus(function() {
    // Inform my app that the user will
    // be able to see notifications
});

Don’t blast me with updates. Make me like your service without having to consciously think why.

Tags: ,

Categories: General, Programming

Multiple Safecracker Instances in One Expression Engine Template


June 22nd, 2011

Are you getting heavy with User Generated Content (UGC) ? Using Expression Engine 2 and Safecracker? If so, then you could run into a nasty surprise relatively quickly.

Having only once instance of Safecracker in your EE template is just fine and dandy – everyone is happy. What happens when you add more than one instance of Safecracker is a big let down and one of the reasons I feel let down by Ellislabs decision to bundle Safecracker with EE2.

If you place two or more Safecracker forms on a page you may hit PHP errors or a complete inability for the template to be rendered at all (internal server error etc). The reason for this is that Safecracker messes with some of the internals ($this->EE variables) of Expression Engine with a complete ignorance for anything that might come after it in the template.

In essence, the first instance of Safecracker is being outputted fine. It is the second instance that hits the road block. Here is a quick fix for this issue and it will allow you to place as many Safecracker instances as you like in a template.

Edit this file:

/system/core/expressionengine/third_party/safecracker/libraries/safecracker_lib.php

before line 758 (the foreach)

put this:


foreach($this->EE->cp->js_files as $type => $files) {
     if(!is_array($files)) {
        $files  =       explode(',', $files);
    }

    $this->EE->cp->js_files[$type]  =       $files;
}

Any questions – hit the comments.

Tags: , ,

Categories: Expression Engine, Programming

Affection in Expression Engine Code Base Comments


May 12th, 2011
Comments Off

Bad Comments in Expression Engine

Sometimes it is necessary to delve deep into the heart of a CMS in order to bend it into doing what you want. Mostly that is because nothing ever seems to fulfil all existing requirements.

As a result we developers need to get into the heart of the application to see how things work. I suppose it’s a bit like being a mechanic – all they want to do is take an engine to pieces “just to see how it works“. That is a quote from my brother, a mechanic and all things (de)construction guy.

When we get into the intimate parts of an application’s code base it’s nice to see comments from the developer – indicating what this little section of code is all about. The Expression Engine (EE) developers must have gone through some dark days when programming the Extensions class in EE and decided to take a swipe at it’s users with the following comments:


// There is a possiblity that there will be three extensions for a given
// hook and that two of them will call the same class but different methods
// while the third will have a priority that places it between those two.
// The chance is pretty remote and I cannot think offhand why someone
// would do this, but I have learned that our users and developers are
// a crazy bunch so I should make shite like this work initially and not
// just fix it later.

// However, it makes no sense for a person to call the same class but different
// methods for the same hook at the same priority.  I feel confident in this.
// If someone does do this I will just have to point out the fact that they
// are a complete nutter.

Now, I don’t know about you – but this is quite the rant and not all that useful. While I might not actually ever program something that would fall into the use-case that the developer is referencing here I just don’t see why a professional product needs this kind of crap (the comments) in it.

Have you found any other great Expression Engine code comments that you’d like to share?

Comments Off

Categories: Programming

Aptana Studio 3 – Beta – SQL Explorer : Could not connect to your database alias


August 4th, 2010

I have been an avid user of Aptana Studio 2 for some time now and have used SQL Explorer within it a lot as well – because lets face it – having an SQL editor within your development environment is extremely productive if you are a web developer.

In order to keep up with the constant motion of life long learning I upgraded Aptana Studio 2 to version 3 Beta.

It’s slick. I like it a lot!

However I did run into an issue where SQL Explorer refused to connect to my remote MySQL server.

The exact error message was:

Could not connect to your database alias; the exact message was:
Cannot connect to Dev/root. Check your URL

This annoyed me for a good long while – even to the point that I was considering rolling back to version 2 of Aptana. It turns out that the reason I could not connect was due to the fact that I did not specify the database name at the end of my connection details.

i.e my connection string was this:
jdbc:mysql://192.168.2.5:3306

But it should have been this:
jdbc:mysql://192.168.2.5:3306/mysql

See the difference? The second string has “/mysql” on the end. In essence “/mysql” could actually be any database contained on the server you are connecting to.

SQL Explorer in Aptana Studio 2 never made the database part of the connection string compulsory. This most probably has more to do with SQL Explorer than Aptana – but the two work in tandem so I cannot rule it out. As I cannot compare version numbers (my old Aptana Studio 2 install has been blown away) – I will never know for sure if this was simply an update to SQL Explorer or some Aptana interference.

It also seems like there is NO ONE out to the interwebs talking about SQL Explorer for Eclipse and Aptana – the reasons for which I cannot explain. I had tried searching for resolutions to this issue before but found very limited references to SQL Explorer.

So, there you have it.

Tags: , , ,

Categories: General, Programming

Symfony 1.2.4: Build-All Bug? Cannot fetch TableMap…


February 19th, 2009

So, I have been recently trying to get back into Symfony. I had taken a break from it for a while to concentrate on Music Review Zone.

I bought a domain recently that I’d like to develop a Symfony application for and since my current knowledge base is with Symfony 1.0 – I thought I’d use it. Then I noticed the benefits of Symfony 1.2.4 and couldn’t pass it up.

So I started creating my project in the usual way:

php symfony init-project <name of project>

php symfony init-app front

I then setup my databases.yml file and completed my schema.yml file. I then ran:

php symfony propel:build-all

… and got the following output:

symfony propel:build-all
>> schema    converting "C:/wamp/vhosts/private/config/schema.yml" to XML
>> schema    putting C:/wamp/vhosts/private/config/generated-schema.xml
>> propel    Running "om" phing task
>> file-     C:/wamp/vhosts/private/config/generated-schema.xml
>> file-     C:/wamp/vhosts/private/co...enerated-schema-transformed.xml
>> autoload  reloading autoloading

  Phing was run before and used many custom classes that might conflict with
  your model classes. In case of errors try running "propel:build-forms" and
  "propel:build-filters" alone. This is due to a PHP limitation that cannot be
  fixed in symfony.

>> propel    generating form classes

  Cannot fetch TableMap for undefined table: location.  Make sure you have the  
static MapBuilder registration code after your peer stub class definition.

[?php

/**
 * User form base class.
 *
 * @package    ##PROJECT_NAME##
 * @subpackage form
 * @author     ##AUTHOR_NAME##
 * @version    SVN: $Id: sfPropelFormGeneratedTemplate.php 12815 2008-11-09 
10:43:58Z fabien $
 */
class BaseUserForm extends BaseFormPropel
{
  public function setup()
  {
    $this->setWidgets(array(
      'id'                           => new sfWidgetFormInputHidden(),
      'username'                     => new sfWidgetFormInput(),
      'location_id'                  => new sfWidgetFormPropelChoice(

I am beat as to why this is occurring.

My schema.yml file creates a user and location table, among others. Each user has a location which I state in the YAML as :


user:
    _attributes    { phpName: User }
    id:            ~
    username:      { type: varchar(50), required: true, index: unique }
    location_id:   ~

I also define the location table as:


location:
    _attributes         { phpName: Location }
    id:                 ~
    name:               varchar(50)
    created_at:         ~

So I do not know why ‘symfony propel:build-all ‘ is failing.

When I run each of the commands that makes up the ‘build-all’ option – each succeeds. i.e:

  • build-schema
  • build-sql
  • build-model
  • build-filters
  • build-forms

So is this a bug or are your fresh eyes noticing something that I am missing?

Tags: , ,

Categories: Programming, symfony

WordPress 2.6: register_activation_hook Cannot See Global Variables


September 3rd, 2008

I have recently been trying to update an old plugin I created for WordPress 2.3.2 so that it would be compatible with the latest version : 2.6.

I am currently having an issue where the register_activation_hook function (and this may not be limited to this one function) where it refuses to reference the global variables that I define in the plugin.

Take the below as an example (proper code attached at the bottom):

require 'mcn_constants.php';

$mcn_test_blabla    =   "Well ??????";

register_activation_hook(__FILE__,'mcn_test_install');
register_deactivation_hook(__FILE__, 'mcn_test_uninstall' ) ;

add_action('parse_query', 'mcn_test_check');

function mcn_test_install() {
    global $this_is_broke, $mcn_test_blabla;

    add_option("mcn_test_value", "The value is: $this_is_broke And :: $mcn_test_blabla ");
}

function mcn_test_uninstall() {
    delete_option("mcn_test_value");
}

function mcn_test_check() {
    global $this_is_broke;

    echo get_option("mcn_test_value");
    echo "Proper value : $this_is_broke";
}

When you load the front page of your blog with the above plugin code activated you should see :

The value is: YES I AM BROKE!!!!!
And ::Well ??????

Proper value : YES I AM BROKE!!!!!

… but instead you see:

The value is:
And ::

Proper value : YES I AM BROKE!!!!!

As you can see – the $this_is_broke and $mcn_test_blabla variables are not being set in the Option “mcn_test_value”.

Yet calling the global variable in the function “mcn_test_check”, which is called as part of the “parse query” action, will show the global variable – as it should.

So – is this a bug or am I missing something fundamentally different in WordPress 2.6? Using global variables in WordPress 2.3.2 worked just fine.

I have attached a sample plugin that will replicate this issue.

Any ideas?

mcn_test.rar

Tags: , , , ,

Categories: ALM, Programming, Wordpress

Symfony 1.2.4: Build-All Bug? Cannot fetch TableMap…


August 27th, 2008

So, I have been recently trying to get back into Symfony. I had taken a break from it for a while to concentrate on Music Review Zone.

I bought a domain recently that I’d like to develop a Symfony application for and since my current knowledge base is with Symfony 1.0 – I thought I’d use it. Then I noticed the benefits of Symfony 1.2.4  and couldn’t pass it up.

So I started creating my project in the usual way:

php symfony init-project <name of project>

php symfony init-app front

I then setup my databases.yml file and completed my schema.yml file. I then ran:

php symfony propel:build-all

… and got the following output:

 

symfony propel:build-all
>> schema    converting "C:/wamp/vhosts/private/config/schema.yml" to XML
>> schema    putting C:/wamp/vhosts/private/config/generated-schema.xml
>> propel    Running "om" phing task
>> file-     C:/wamp/vhosts/private/config/generated-schema.xml
>> file-     C:/wamp/vhosts/private/co...enerated-schema-transformed.xml
>> autoload  reloading autoloading

  Phing was run before and used many custom classes that might conflict with
  your model classes. In case of errors try running "propel:build-forms" and
  "propel:build-filters" alone. This is due to a PHP limitation that cannot be
  fixed in symfony.

>> propel    generating form classes

  Cannot fetch TableMap for undefined table: location.  Make sure you have the 
static MapBuilder registration code after your peer stub class definition.

[?php

/**
 * User form base class.
 *
 * @package    ##PROJECT_NAME##
 * @subpackage form
 * @author     ##AUTHOR_NAME##
 * @version    SVN: $Id: sfPropelFormGeneratedTemplate.php 12815 2008-11-09 
10:43:58Z fabien $
 */
class BaseUserForm extends BaseFormPropel
{
  public function setup()
  {
    $this->setWidgets(array(
      'id'                           => new sfWidgetFormInputHidden(),
      'username'                     => new sfWidgetFormInput(),
      'location_id'                  => new sfWidgetFormPropelChoice(

I am beat as to why this is occurring.

My schema.yml file creates a user and location table, among others. Each user has a location which I state in the YAML as :


user:
    _attributes    { phpName: User }
    id:            ~
    username:      { type: varchar(50), required: true, index: unique }
    location_id:   ~

I also define the location table as:


location:
    _attributes         { phpName: Location }
    id:                 ~
    name:               varchar(50)
    created_at:         ~

So I do not know why ‘symfony propel:build-all ‘ is failing.

When I run each of the commands that makes up the ‘build-all’ option – each succeeds. i.e:

  • build-schema
  • build-sql
  • build-model
  • build-filters
  • build-forms

So is this a bug or are your fresh eyes noticing something that I am missing?

Tags: , ,

Categories: Programming, symfony

Symfony Session Timeout Annoyance…


April 25th, 2008

I have recently found that symfony doesn’t attempt to properly maintain the user’s session after logout.

This is a tad annoying as any attributes that have been set during a valid login session will still be set when the user is communicating with the server. It is also a security risk.

Symfony provides the basic building blocks for creating an application, and session management is a part of that. The myUser class contains paramter, attribute, credentials classes as well as some variables like culture (for internationalisation), authenticated and timedout.

The ‘authenticated’ variable’s use should be pretty obvious – when the application is processing the users login it should set the authenticated variable to ‘true’ :

$this->getUser()->setAuthenticated(true);

The ‘timedout’ variable is set automatically be symfony when … alas … the users session has expired/timed out – pretty self explanatory.

The problem comes when your application sets any parameters or attributes during the sessions lifetime. For example, in my application when the user logs in I set a session variable ‘nickname’ so that I can reference/identify the user during any subsequent requests :

$this->getUser()->setAttribute('nickname', $user->getNickname());

This is pretty basic and normal functionality.

This attribute should die along with the session, when it times out – but it doesn’t. There are some perfectly valid reasons why this happens e.g The ‘Validated User’ and the ‘Browsing User’ are two different things to the system but the contents of the shopping cart are not. Another example would be website tracking software. It monitors where users are and what they are looking at – a very good tool for marketing. This type of software doesn’t have to care whether the user is logged in order not as its only concern is where they are and what they are doing. This information needs session data to follow the user. If all session data were cleared after the user logged out – then the monitoring software would be thinking a new user arrived – which would be wrong.

What is needed in this situation is a trigger or a test that determines if the session has expired and if it has – then action something appropriately.

The only way that I have determined to do this is by editing the myUser.class.php file for the application (and every other application) in your project.

As the myUser class is instantiated before any actions we can alter/clean up the session before any proper processing is done :

class myUser extends sfBasicSecurityUser
{
  public function initialize($context, $parameters = array()) {
    parent::initialize($context, $parameters = array());
    if($this->isTimedOut()) {
      $this->getAttributeHolder()->remove('nickname');
    }
  }
}

If you don’t like the idea of having to do this for all your applications – then you can create a new class and make the myUser class extend it instead (you must ensure that the new class also extends sfBasicSecurityUser. That way you have one central place to update when you need to clean up your session.

Hope this helps…

If you have any better ways of doing this – please let me know. I’d be interested to hear your take on it.

Categories: Programming, symfony

WordPress Plugin Annoyances


February 7th, 2008

Creating a plugin for wordpress can be an extremely simple task, or altogether annoying.

I’ve recently been creating a plugin for “Featured” content on a new site that I’m pushing at the moment. Basically the plugin works like this :

  • Featured Posts belong to the “Featured” Category
  • Featured content can be controlled by the plugin menu in the WordPress Admin screen (updated by Editor or Administrator)
    • This is important as you dont want to have to sift through a list of 1000 posts to see which are marked as Featured or not – Even with the filter active this can be annoying
  • The Featured content can only be “Featured” if it is part of another category.
    • i.e Lets say the purpose was for books (Fiction and Non-Fiction). The Featured section might only cater for Fiction books. So the posts would be in Featured and Fiction categories. Simple.
  • The admin area can state the total amount of Featured posts.

The plugin then provides functions to the template to output the featured content.

One issue I had was using query_posts to select the posts that were not featured but in the chosen category (Fiction). The plugin function :
query_posts('category_name=Fiction&category_name=-Featured')
…just wouldnt work. I tried many variations of the query_post command along with category_name only to discover that wordpress does not facilitate anything more than basic requirements when using category_name. So, I retreat to using ‘cat’ instead. Boo! Now I have to query the database to get the ids of the categories I want to query on first. Not exactly efficient. Not to mention using more memory with additional variables to accommodate this etc.

Any other annoyances that you have encountered?

Categories: Programming, Website

Programmatical Database Optimisation…


December 5th, 2007

The application that I am programming at the moment dictates that an object can have many participants. For improved usability the owner of the object needs to be able to add and remove participants en massé.

Creating this functionality is simple but ensuring that it is done efficiently requires some further thought.

A = List of Users who currently have access to the object

B = List of Users that should have access to the object after updating

B can contain none, some or all of A.

Method 1

The easiest thing to do when updating the list of participants is to delete all current participants (A) and grant access to all new participants (B). In doing this we would be executing two, possibly bulky, of an RDBMS’s most time intensive SQL statements :

  1. DELETE FROM X WHERE Y in (A,B,C) and D=E
  2. INSERT INTO X VALUES (A, B, C)

Method 2
Another method of setting the new participants would be to only update what has changed in the list. Doing this incurs an additional SELECT statement in order to determine what needs to be added and removed :

  1. SELECT * FROM X WHERE Y=Z
  2. DELETE FROM X WHERE Y in (A,B,C)
  3. INSERT INTO X VALUES (A, B, C)

The question now is :

Which method is the most database efficient?

Method 1 gets the job done in two SQL statements but method 2 can possibly reduce the number of inserts and deletes necessary while at the same time adding an additional SELECT statement.

To emphasize this issue further – consider the following :

In my application an object (A) can contain child-objects (B). Each child-object can have an individual list of participants – this list is a subset of those participants in the parent object.

If a user is removed from the participant list of the parent – he/she should no longer be a participant in any of its child objects – and hence should be removed.

In doing this the same situation arises above :

Is it best to do a blanket DELETE statement across the participants table for the parent object and each of its child objects for the user ID?

OR

Execute a SELECT statement to find the list of objects and child objects that the user has access to and then DELETE as necessary?

Which method in these scenarios is the most efficient?

Any thoughts?

Answers on a postcard….

Categories: ALM, Programming

"Thought leadership is how winners are differentiated in business."