Tuesday, February 10, 2015

An alternative to the laravel 5 contact form

Introduction

I was reading the contact form tutorial, and after that I'm trying to get the tutorial working on my pc.

There was a bit of information missing, but that's because the writes wants you to buy his book. And there are a few ways the code can be written different.

Get on your marks, GO!

If you don't know how to install laravel, check the excellent documentation.
To run the development server, run php artisan serve in the command line.

alternative routes

Laravel has a way to prefix the controller methods with the http verbs, they call it implicit controllers.

In the file app/Http/routes.php you add the line Route::controller('contact', 'ContactController');.

To create the controller, run php artisan make:controller ContactController --plain.
The --plain parameter creates the controller without methods.

The controller

You can ignore line 5 and 6 for the moment, they will be created later.

Add the lines 10 to 23 to your controller.

To get the getIndex method working the index.blade.php file needs to exist.

index.blade.php

This file has to be created in the resources/views/contact directory

The missing information in the original article is to make the Form methods work.

  1. Add "illuminate/html": "5.*" to the required section of the composer.json, and run composer update
  2. Add 'Illuminate\Html\HtmlServiceProvider', to the config/app.php file in the providers array.
  3. Add 'Form'=> 'Illuminate\Html\FormFacade', to the file above in the aliases array

intermezzo

If you put the postIndex method of the controller and lines 5 and 6 in comments. And start the development server, you will see the contact form at http://localhost:8000/contact.

ContactFormRequest

run php artisan make:request ContactFormRequest.

On line 14 you change false to true, because you want anonymus users to send the contact form.

In the rules method you add the items from the code above.

ContactFormValid event

The original article lets you add the mail sending code to the store controller method. This gives the controller to much responsibility. I will use an event to trigger the mail sending.

In the app/Providers/EventServiceProvider.php listen array you add following item;
'App\Events\ContactFormValid' => ['App\Handlers\Events\EmailContactForm', ],

As you can see the event itself is in the app/events directory and all the code that needs to be executed is in the app/Handlers/Events directory.

Then you run php artisan event:generate.

ContactFormValid.php

As you can see in the controller on line 18 the ContactFormRequest instance is added to the constructor.
In the event file the all method is called to fill the data variable.

EmailContactForm.php

On line 2รง you see the data variable is added to the Mail send call.

Because it is an array you can use this handler for other events, the only thing you need to check are the data keys and their value.

contact.blade.php

In the handler on line 28 we defined this view file, which is in resources/views/emails directory.

The big finish

Remove all comments from the controller and fill the contact form in your browser.

Now the controllers only function is to display a view. And when the form is posted with valid data it fires an event and redirects the user to the form.

Saturday, January 31, 2015

No more static site generators

Preface

I read the article Getting started with web components and I thought how can I create a product with this knowledge. A static generator came to mind because the solutions out there want to fix the problem of reusing html snippets.

The workhorse

If you don't want to browse the code, just scroll down to see the usage.

Usage

To create a page with reusable parts you have to at least create the page itself and the reusable part.

On line 3 you see the link to the reusable part.

On line 6 I added the tag which will contain the content of the reusable part.

On line 7 there is the link to our workhorse.

On line 9 the addTemplate function to add a specific reusable part to the page

The specific reusable part in this case is;

The content of the reusable is inside the template tag. And by convention the id of the template tag has the same value than the filename. Developers will recognize this pattern, because they use it for classes.

On line 73 of the workhorse code you can see there is also a addHeader function. It works as follows:

  • There needs to be a header tag on the page.
  • The header.html needs to exist and added to the import links of the page.
  • When you call the function it has to have a title parameter

To get a better idea of how it works you can check the demo.

Is this a dream?

let me say it's a very lucid dream.

The code is more proof of concept than a product I want to ship.

At the moment the only browsers this will work on out of the box are chrome and opera.

All in all I think this could become a product with a lot more work.

Tuesday, November 18, 2014

From node to website

This post is for people who want to learn to create a website with node.js

During the process some steps are there to let you understand what the code does, this doesn't mean you should use the code as a part of a real website.

Setting up the project

After installing node on your computer, you open the command prompt or terminal and create a directory which will contain your files.

When you are in the directory you run npm init. This command will ask you some questions and generate a package.json file.

Starting to create the site

On the front page of the site you see how to create a webserver which shows Hello world when you go to the url in your browser

I named the file app.js so to run the website with npm start you need to change "start": "node index" to "start": "node app" in the package.json file.

The first webpage

The code above displays plain text but most of you know browsers display html nicer than plain text. So lets change the code to make this possible.

The changes I made:

  1. I moved the output of the site to its own function. This keeps the webserver code clean.
  2. I put the html code in a separate file. This allows you to change the html without changing the code.
  3. I used the fs module to read the file with the html in.
  4. Thanks to the callback parameters of the readFile method I can show a page not found error if the file isn't found.
  5. And to end it I display the html code that was read from the file.

If the website is running you need to stop it with the key combination ctrl+c, and start it again. Now you see that hello world is bigger and bolder.

More pages

A website has more than one page. It is possible to create multiple pages in the browser using frameworks, but we are going to keep using node to do this.

The changes I made

  1. I added page2.html
  2. I added a link to page2 in index.html and a link to home in page2.html
  3. I added a new parameter to the render function to make it possible to read different files.
  4. I checked if the browser send a GET request. At the moment we don't care about POST, PUT, DELETE or PATCH requests
  5. Then I checked which url the browser wants

After restarting the webserver again you will see the link to the second page. And clicking on it will display the second page.

Cleanup the webserver code

Because checking the routes is a responsibility of the app it's best to move the code out the webserver code.

  1. Created a Router object with the public methods get, nopage and handle. And one private method find.
  2. Added an instance of the Router object with the name router.
  3. Added the pages with get method, and the 404 page with the nopage method.
  4. Used router.handle instead of a anonymus function to let the app decide which content should be visible.

  5. Changed the render function to set the status code of the page if there is an error. This is important for SEO and other automated page visits.
  6. Added the 404.html file to display a nice page.

Making it real

We can build websites with this code but it isn't very powerful, for instance you have to add all the routes instead of using variables for groups of similar routes.

The most used framework is express

run npm install express --save, mac os users will need to add sudo in front of the command. This will add the express package to the project in the node_modules directory.

Express takes care of the routing for us, and allows us to add a template engine. At the moment we use static html files but when the data comes from somewhere else the html code is going to contain variables and logic.

As you can see express also has a get method to set GET request routes. To display a html 404 error you need to define it with the use method below all other routes.

Express needs to know the root template engine files directory, what method the template engine uses to render the template and declare the default template engine. You can use multiple template engines but this is not a good idea if you want performance and consistency.

Express also has a listen method so you don't need to create the webserver yourself.

Conclusion

I hope this gives you a clue how we went from a webserver that only displayed hello world to a website with multiple pages and html output.

Wednesday, October 29, 2014

Meteor and the findOne no result problem

Now that meteor has a version 1.0 release I wanted to give it another try.
As a test I was creating a simple blog.

The problem I encountered was when i tried to display a post detail. The code looked simple enough.

Router.route('/:_id', function(){
   this.render('detail', {
       data: function(){ 
           var templateData = { post: Posts.findOne({_id: this.params._id}) };
           return templateData;
       }    
   });
});

this.params._id looks almost like req.params._id from the express framework.
And I was sure the document with the _id existed because it came from the database.

During my search I found a few different ways to pass the _id to the findOne function.

The first one I tried comes from the official documentation:

Posts.findOne(this.params.id)

It looks like the mongo library that comes with meteor adds a little abstraction to the mongodb method. Great, but it didn't work.

Next there the id key without the underscore. That didn't work either.

Then i got the idea the hexstring wasn't enough. I needed the ObjectId instance like it's needed in the mongodb shell.
Of course the mongo ObjectId isn't available in Meteor.

After a bit of searching i found that the meteor Mongo library has a ObjectID method.

Router.route('/:_id', function(){
   this.render('detail', {
       data: function(){ 
           var postId = new Mongo.ObjectID(this.params._id); 
           var templateData = { post: Posts.findOne({_id:postId}) };
           return templateData;
       }    
   });
});

And the document data was visible.

Update

It looks like the meteor mongo shell produces _ids that are ObjectId objects, but inserting documents with the meteor Mongo library produces _ids that are 'plain' hexstrings.

So to insert documents you best use the meteor Mongo library because that's the way you interact with mongo the most.

Saturday, January 04, 2014

Belgian rail and usability

After the bus company, De lijn, introduced the sms-ticket I never use other way to buy tickets for a bus ride. Now the Belgian rail introduced a sms-ticket and I wanted to see if it is going to be the way to buy train tickets from now on.

I suspected it to be a little more difficult because of the price range between short and long rides but the experience was much harder than expected.

First attempt

There are two ways to buy a ticket, an iphone app and the mobile website. Why is there only an iphone app? As a developer I know there are tools to create apps for the different smartphone operating systems form a single code base. And Belgian rail has an official app for android, so why not update that app for sms-tickets?

Some bright minds probably already started thinking, why do they call it an sms-ticket if you need an internet connection. The ticket is send to your phone in an sms.

So I go to the mobile site and before you can buy a ticket you need to have an account.  If you want to buy a ticket on the regular website you need to provide you name and address, so I guess creating an account is their solution to make the buying easier because it is a one time deal.
I'm filling in the form. After I click on the what I think is the last step to complete the account I get an error message.

For a spur of the moment buyer this is enough reason never to buy an sms-ticket.

The long way

Because I'm crazy or addicted to technology or both I really want to know how to buy an sms-ticket. So I go to the Belgian rail website on my computer and create an account.

The first thing that strikes me as a webdeveloper is that the form to create an account is in a popup. What other things do you want to do other than creating an account at that time?
For your date of birth they use the JQuery UI datepicker with default settings which makes it impossible to select your date faster than you can type. There is a demo on the JQuery UI site which makes it a better datepicker.

After I completed the form I get a message to confirm the account. I guess you also need to do this on the mobile site so if you are on a smartphone you need to switch between apps, which is another threshold before buying a ticket.

Back to the mobile site now. I log in and go through the ticket data selection. When I come to the payment part there are only creditcard options. This is strange because on the regular website you have more options.
After selecting a creditcard option there is a checkbox to remember Belgian rail as seller to make the payments easier. It seems that the service Belgian rail uses to handle the payments only provides this option for creditcards?

 Conclusion

After creating an account and having a creditcard and remebering Belgian rail as a seller, the only one that makes it hard to buy an sms-ticket is the bank you are with. Some banks make you to verify the online creditcard purchase so you need to have your digipass with you and also your creditcard.

SMS is a quick platform for on the road and buying and sms-ticket isn't, there are too much actions needed  and less options than on the regular website. 

Aftermath analysis (how I would create the sms-ticket) 

After looking at all the data they need and all the ticket options they have it looks like the sms ticket is a six step process.

  1. Personal data and data about the ride 
  2. ride options provided by Belgian rail
  3. ride option selection sms
  4. payment info by Belgian rail
  5. payment sms
  6. sms-ticket by Belgian rail
So it takes three sms messages without the need of an internet connection.

Personal data and data about the ride

They use parts of the account information, date of birth gets mentioned in their faq, to secure the sms ticket.
Why not make the personal information that they need required lines in the sms.

The ride data they need is begin and end station, go date and return date is optional.

An example sms could be:
  1. START
  2. first name
  3. last name
  4. date of birth
  5. begin station
  6. end station
  7. go date
The first line is to make the sms service aware this is a new ticket. 

Ride options provided by Belgian rail

SMS is a peer to peer platform so there are no sessions and because it is a multistep process a token should be send to add to the following sms messages. The token should be as short as possible to make it easy to remember but big enough to allow all the travelers to create a ticket for a two day period. The two day period is in case the traveler starts making a ticket close to midnight.

The options should have numbers to identify them.

An example sms:
  1. abc123
  2. 1 adult : 2.5 €
  3. 2 child : 2 €

Ride option selection sms

The ticket buyer now needs to  verify which options he needs.

Example sms:
  1. OPTIONS abc123
  2. 1 1

Payment info by Belgian rail

Provide a code that the buyer can send to reduce his telecom account by the amount of the ticket and send the sms ticket to the buyer.
This is what the bus company does with the two options they provide.

Conclusion

It can be a bumpy ride especially the first sms because it needs a lot of information that has to verified. But it is a pure sms solution which makes the public who can use the sms ticket bigger.  

Thursday, August 29, 2013

Drupal 8 and Twig

Research

I want to create a Drupal 8 module which is up to the goals that are set for the version, especially the templates.  Drupal 8 includes Twig as template engine.

When you look for Drupal 8/Twig tutorials you get the explanation why it's good to have a template engine and how to use it. Which is great, but when you use symfony the views go in a Recources/views directory, and that is something the tutorials fail to mention most of the time.

When you see Twig files they are stored in a templates directory. The strange thing is that the templates directory is located in the root of the module. If you want a controller to use a template it feels wrong because that is stored in the lib/Drupal/module_name/Controller directory.

Solution

After a bit of code searching I found the twig loader instance is attached to the container as twig.loader. Which resulted in following controller code.

For people who are not familiar with controllers I will go line by line.

On line 3 you set the namespace of your controller. This prevents problems with other controllers in Drupal.

Lines 5 to 7 tell which objects are going to be used.

On line 9 you create the class.

The __construct method is called when the controller is created as an object.
Line 12 is the line I was searching for. This adds the View directory on the same level as the Controller directory to the loader, and this is how twig will find your templates.
On line 14 twig is attached to controller.

The create method is called by Drupal, and it makes an object of the class. 

The displayTwig method shows how to render a Twig file from the View directory.

Caveats

It seems best to prefix the Twig files of your module with the module name to prevent similar named views to be loaded.

If you have multiple controllers it is best to create a class your actual controllers can inherit form so you don't have to repeat the Twig binding code.

Wednesday, November 07, 2012

Crowdsourcing code equals good code?

I just read about a new crowdsoursing site and curious as I am i was checking which code gets the money.

I found a php task for a dollar. It's a nine line solution, with two updates, which does following things:

  • parses the input
  • looks for a specific key from the parsed array
  • seaches for www. substring
  • removes the www. substring if it's found

One solution uses str_ireplace, which is a step in the right direction.
What they needed to use is preg_replace.

$cleaned = preg_replace('#^(?:https?://)?(?:www.)?(.+)$#', '$1', $input);

The regex has three groups, two of them that aren't needed for the output.

One dollar of one line of code is still too low, I just gave it away for free.

Sunday, October 14, 2012

Blog layout

In my previous post I added a PS to create a theme.

The reason for this was i didn't have much control over it I thought.
I was wrong.

What I wanted

  • code highlighting
  • scrollbar if the code width is bigger than the content area

As this is a blog about code the examples should stand out.
I could use gists for every code example but for the examples in my previous article it seems like overkill.

What i did

Being lazy i changed my theme to a dynamic blogger theme. That kept my code in the content area, but it's an ugly theme.
And i didn't have syntax highlighting. So I changed back to my old theme.

Having a scrollbar isn't that hard in css. I just needed to know the content area width.

pre { overflow-x: auto; width: 700px; }

The side effect is that the gists all have a scrollbar or two, but scrollbars are fun.

Syntax highlighting isn't a big problem either because a lot of programmers blog, and they also want that.

When you search for syntax highlighting on blogger the first results are about SyntaxHighlighter.
My gripe with it is that it uses colons in classnames. I know from experience it causes troubles.

So I used shjs.
The installation requires you to add at least 3 files, two javascript files and a css file.

On the bog dashboard there is a template link and when you go there you find a button 'edit html'.
After a warning you see blogger xml code. I added following just before the ean head tag.

<link href='http://shjs.sourceforge.net/sh_style.css' rel='stylesheet' type='text/css'/>
<script src='http://shjs.sourceforge.net/sh_main.min.js'/>
<script src='http://shjs.sourceforge.net/lang/sh_php.min.js'/>

You may notice script is a self closing tag, this doesn't work in html.

the body tag needed the onload attribute to call the sh_highlightDocument function.
There are better ways to do this but I'm not using any other javascript that needs to be run on page load.

And now I have what i needed with the theme I like.

Friday, October 12, 2012

idiomatic php

It has been over a year since i blogged. It seems work is always taking over my free time. But I will never let my blog die.

Today i read an article about idiomatic python and I thought lets reproduce the code in php as an exercise. Working with .NET languages makes me rusty.
I will stay away from the style examples to avoid discussions that go nowhere.

I'm going to take the subtitles of the article and add the php code, enjoy!

Avoid using a temporary variable when swapping two variables

$a = 'a'; $b = 'b';
list($foo,$bar) = array($bar,$foo);

Use tuples to unpack data

There are no tuples in php, an array will do.

$list_from_comma_seperated_value_file = array('dog','Fido',10);
list($animal, $name, $age) = $list_from_comma_seperated_value_file;

Use ''.join when creating a single string for list elements

Join in php is an alias for implode. In php 5.4 square brackets can be used to create an array.

$result_list = ['True','False','File not found'];
$result_string = join('',$result_list);

Use the 'default' parameter of dict.get() to provide default values

The closest thing in php is the ternary operator.

$log_severity = isset($configuration['severity']) 
                    ? $configuration['severity'] : $loginfo;

Use Context Managers to ensure resources are properly cleaned up

It would be nice to have this in php.

Avoid repeating variable name in compound if Statement

if(array_search($name,['Tom', 'Dick', 'Harry']) !== false) {
    $generic_name = true;
}

Use list comprehensions to create lists that are subsets of existing data

The closest thing in php is to use an anonymous function and array_map.

$some_other_list = range(1,100);
$my_weird_list_of_numbers = array_map(
                              function($element){ 
                                if(is_prime($element){ return $element + 5; }                             
                              }
                            ,$some_other_list);

Use the 'in' keyword to iterate over an Iterable

The foreach loop in php uses the keyword as

$my_list = [ 'Larry' , 'Moe' , 'Curly' ];
foreach($my_list as $element){
    echo $element;
}

Use the enumerate function in loops instead of creating an 'index' variable

No need to use a function in a foreach loop to do this. It feels good to write that.

foreach($my_list as $index=>$element){
    echo $element;
}

PS note to self: create own blogger theme and never use blogger compose again.

Sunday, September 11, 2011

My first knockout.js experience

Because I wanted to see what the hype is all about, I dove in the documentation and tutorials of knockout.js.

The tutorial site is great!
But the first thing that bothered me was the data-bind attribute everywhere.

So I pulled up my sleeves and pimped the 'templates and lists' tutorial

The easiest way to test the code is to copy and paste the gist content from the article in the html and javascript fields of the tutorial site.

The html

The final markup of the tutorial looks like this:

As you can see the data-bind attribute is all over the place.

And this is my markup:

The data-bind attribute is only in the template.
The other bound elements have an id.

Instead of putting the seat count in the title, I put it together with the surcharge sum.
The tutorial code displays the surcharge sum when there actually is a surcharge, my code displays it from the moment a passenger is present.

The javascript code

The tutorial code looks like this:

Very readable if you are used to do all the legwork in jQuery.
I'm in the knockout camp if I need to create complicated interfaces from now on.

Before I show you my code I will do a blow-by-blow on what I did and what the hurdles were.

The first thing I wanted was to remove the data-bind attribute from the markup.
I knew someone wrote a jQuery plugin to make this possible.

It works fine until you want to use the plugin to bind the html elements in the template.
Because I didn't want to spend too much time looking for a way to make it work, I left the data-bind attributes.

Instead of using a ko.dependentObservable to make a sum of the surcharges I added it to the view model.
You should only use the method if the code depends on outside factors.

When I added the remove functionality it didn't work. I did some debugging and found out the this in the remove function was the view model object where it needs to be the seatReservation object.

I guess the unobtrusive plugin has to do something with it, but the quick solution is to bind the seatReservation object to the remove function.
Because jQuery is loaded I used $.proxy.

The tutorial adds an anonymous passenger but because that isn't allowed anymore, I added a prompt to the addSeat function.

So my code comes down to:

Conclusion

Amazing you can achieve so much with so little code. And the tutorial is still verbose because the availableMeals will be fetched using AJAX or generated by a server language, <?php echo json_encode($availableMeals); ?>

I grabbed it and I will not let it loose until something better comes along.

Tuesday, September 06, 2011

Ugling: the start of the template engine

The changes

If you look at the previous build.xml file you know processing content in phing will turn in spaghetti code very soon. So I moved it to the template task.

The new build.xml file is easy to read because now you just have two tasks.

To get the build result I want i needed to add an attribute to the markdown task, removefilesetdir.
For me it was a cleaner way to process the markdown files and add the processed files to another directory than the way the rSTTask documentation shows.

The template engine vision

I want working with the generator to be as easy as possible so I try to stay away from code-like constructions as much as possible.
But at the same time it has to be as flexible as possible too.

The first problem I encounter is how can I provide a default template but allow specific templates at the same time.

Because i started to work with base template I got the idea that each directory could have a template.html that wraps all files in that directory and its subdirectories.
The only exception is the root template directory, it's required to have a template.html there.

To allow file specific templates it seemed the most logical to use the name of the file, so if your content file is called test.md and you want a file template you create a test.html file in the templates directory.

The template task today

I took my MarkdownTask.php code to start the TemplateTask.php code.
I concentrated on getting it to work so the code isn't that clean yet.

The default template code is working.

To prevent placeholders showing up in the online files they are set to an empty string before the actual content is added or generated.

Next time

The next blog post I will have the template vision working and have come up with a plan to add generated navigation content.

Saturday, September 03, 2011

Ugling: the phing powered static site generator (the begin)

Preface


I was looking for a project to use phing in a way that isn't expected.

Using it to remove version software directories or run tests is done so many times before.


The cool programmers started using static site generators like jekyll and petrify.

Why would you need a database if most of your content is static.


So why not use phing to do the same thing.


I also want to make the threshold as low as possible.
Markdown is one thing but other elements like the navigation are going to be not that easy.


Transforming the markdown


Creating a task in phing is easy, certainly if someone already did a similar task.


I just copied the code from the rSTTask, removed the parts i didn't need and changed the code to use the markdown class.



The source layout



  • content

    • index.md



  • media

  • online

  • templates

    • base.html



  • build.xml


As you can see the content directory holds the markdown files.


You can add as many subdirectories as needed. And use index.md files to display content for an url without the html extension.


The media directory will hold css files, js files, and other public content.

I'm not sure i'm going to keep it because the only phing action will coppy the files to the online directory.


The online directory holds all the files you can place online.


The templates directory holds html files that have placeholders.
This will be for the 'expert' users.


The build.xml file is the phing hook which will take care of all the actions.


Later there can come a build.properties file to move the user accessible properties outside the build script.


Build.xml content



I think it's a readable file.



  1. Set the start target: default="loop"

  2. Add properties

  3. Add markdown task

  4. Find all markdown files and send the file names to the single target

  5. Process the markdown file and move it to the online directory


The future


Now that the easy part is over I can go to work on the necessary plugins.
Navigation is the first that needs attention.

Wednesday, August 17, 2011

My first phing buildscript

Because I get more and more components from version management tools it gets harder and harder to move from development to staging level. SVN repositories are the worst offenders, certainly if you see how clean the get and mercurial repositories are.

If you clone or checkout a repository most of the time it also includes test files, build files, and so on. It's stuff you don't need for your application.
Swiftmailer for example comes with a full blown test suite. What if hackers find a way to use it to their advantage?

The phing installation via pear is painless.

The way to delete a directory didn't appear in any of the searches i did. So i moved on to the documentation and I found what i needed.



It's not very elegant but i'm taking babysteps at the moment.

Sunday, July 03, 2011

Being a good programmer

First of all this is not a post about best practices, being smart and all that jazz. It's about me exploring new areas to use my programming knowledge.


Update


I put the code on github


The problem


I'm a fan of the WFMU radiostation. They have a wide variety of music and they archive all their shows.
The downside is that after a month they delete the mp3 archive of the shows and because I don't listen all the time I'm missing episodes of my favorite shows.
That is why I started to download the mp3's but it takes too many steps and I needed to switch programms.



  1. Download the m3u file.

  2. Open the m3u file

  3. Copy the content

  4. Open a browsertab

  5. Paste the m3u content in the locationbar


So i went in developer-mode and started to work out a faster way to download the mp3's.


The brainstorm session


As i'm getting the mp3 archives as an rss feed and i'm using google reader to view it, the step to making it a browser add-on was taken quickly.


The easiest way to download the mp3 for me is to right click the link, click on a context item and the download starts.


The development process


I've done only a little chrome extension development, but the browser makes it a painless process with it's extension developers-mode.

This mode lets you choose a directory on your pc where the extension files are located and once you 'installed' the extension you can reload it when you changed the source code.


Because i never used contextmenu code i donwloaded the sample code from the google code site, which i also use to learn more about the api's.


The contextmenu item


The code is simple enough to understand.



  • Define when the menu item has to be added, the contexts.

  • Iterate over all the defined contexts to add the menu item and click handler, the genericOnClick.


The m3u content


From the sample I learned the first parameter of the click handler, info, holds the m3u url, linkUrl. Not a lot of brainpower needed here.


From my previous experience i know to get the file content i have to do an ajax request.

To make a cross-origin request possible you need to add the sites to the permissions variable of the manifest.json. This file contains all the metadata of the extension.


The download


Now we come to the head scratching part. As much as I searched I couldn't find a way to use the ajax responseText to automate the download.


I was trying to inject the mp3 url in the originating tab and then programmatically clicking on it. To do this i had to use the tabs.executeScript method but as i found out the code is executed in a sandbox so it can not use values from the click handler.


So i had to settle with opening a new tab which has as url the mp3 url.


The future


Now the number of actions has reduced to 3 and i can stay in the browser.



  1. Right click the link.

  2. Click the contextmenu item.

  3. Save file from tab.


I'm not going to show the code now because I want to make it more robust and universal.

There are no messages when something went wrong. I assume the content of the file only contains one mp3 link but i know it can contain multiple files.


Later i could support more metadata files and use a converting service like zamzar to make it an allround downloading extension.

Sunday, February 08, 2009

Chromeless browser 2

My second day in a chromeless browser taught me i visually depend on more UI elements that i was aware of.

For instance the throbber. Because there is no statusbar or a locationbar that shows the page loading progress the throbber becomes a crucial element. Lucky for me vimperator gives a messaget when a background tab is opened. This gave me the idea to have a two tone throbber. The darker colour throbber would appear when the current tab loads and the lighter colour throbber would appear when a background tab is loading.

The throbber should be clickable. In the current tab it should stop the page loading. With background tabs it should do the same if only one background tab is loading and open a list of loading background tabs if there are more. There has to be an option to exclude often refreshing pages like passpack.com with one click on.

Another UI element is the download progress of files. I'm using the download statusbar add-on for a long while now and i find i come to depend on it. The add-on has an option to keep downloading in the download manager after the browser is closed but you can't see when a download is finished during the browser session.

The thing i miss the most is the tab overview the tabsbar gives me. So i installed the ctrl-tab add-on which gives an overview of the tabs using the shortcut ctrl-shift-A.

As a power user i want to have access to the url. I could make a ubiquity command for this but i don't know how to put the fetched url in the commandline.

Saturday, February 07, 2009

Chromeless browser



After seeing the video of Aza and Alex about designing a chromeless browser I was wondering how to get my browser chromeless to feel what it's like.

The first thing i did was hiding the statusbar. Next on the list is the tabsbar, as i already was using the tree style tab add-on i only needed to set the autohide options.
Then i needed to remove all toolbars, for that i installed the vimperator add-on.

I'm planning to use the Ubiquity add-on as my main chrome replacement. It's more user-friendly than the vimperator commands and it can be extended very easy.

For instance i want the awesombar functionality and i found the go 3.0 command, now i have the awsomebar and a screenshot preview as a bonus.

The first thing i noticed changing to the chromeless browser is how much i return to the tabsbar to select the tabs. Using the tab command to search for the tab with a certain name is useful when you have an overview of the tabs.

This will be an experiment as long as i can stand it so this could turn into a series.

Wednesday, December 17, 2008

Laptop future



source

There are two things i think are wrong with the picture above, both the keyboard and the screen are too small.
Lately it seems all cellphones get a full keyboard but they suffer the same defect as the mini pc.

If you have a computer the input and output should be made for a human not for a mouse.



source

This could be the future but i think the five parts that form a whole is too inflexible and you will not always have the possibility to project the keyboard or the screen.

I think the laptop of the future comes in three parts; the computer, the input device and the screen. This modularity makes the computer multifunctional.

Stan goes to work and he has a presentation to give. When he is dressed he puts the computer in one of his pockets and puts his projector and 24" rolled-up screen and alphanumeric keyboard with touchpad in his briefcase.
When he goes out of the door he puts on an ear piece and puts a hardcover touchscreen in another pocket.

Using his earpiece he recieves and makes calls while he does visual tasks on his touchscreen. When he arrives at work he places his 24" screen and keyboard on his desk.
Uh oh ten o'clock time to set up in the meeting room. He takes his projector and keyboard.

This is a computer orientated scenario but other input devices could be photo and video camera's, pens, cooking utilities, and so on. An other output device is a speaker.

I don't think we are that far of this picture of the future but it seems manufacturers rather want to make everything convenient then configurable.

Monday, September 29, 2008

locale number formating

With money_format you can do great things to display the number.

// Let's justify to the left, with 14 positions of width, 8 digits of
// left precision, 2 of right precision, withouth grouping character
// and using the international format for the de_DE locale.
setlocale(LC_MONETARY, 'de_DE');
echo money_format('%=*^-14#8.2i', 1234.56) . "\n";
// DEM 1234,56****

So it's very tempting to do

echo money_format('%!n',1234.56);

But the with the money function is that you either display decimals or you don't. I was looking for a function that has the possibility to only display decimals if the the number is floating.

After a few tries i came up with this

function locale_number($number,$decimals=2,$always_decimals=false)
{
if(is_int($number) && ! $always_decimals){ $decimals = 0; }
return number_format($number, $decimals, nl_langinfo(RADIXCHAR), nl_langinfo(THOUSEP));
}

I set the $always_decimals flag to FALSE because the money_format function will be faster if you always want to display decimals.

Saturday, September 27, 2008

CI/php trick for easier to remember function names

Most of the people who use php, including me, are not so keen on the inconsistent naming of the functions but with a php trick and the help op CI you can create easier to remember function names without performance loss.

Maybe some people will find this awkward at first but in php it's possible to assign a function to a variable by adding the function name as a value to the variable.

$STR_REMOVE_BEFORE = 'strstr';


In CI 1.6.1 they added the constants.php file which gets loaded early on so you could misuse this file to add your function variables but at the same time you could use all upper-case letters to distinguish the function variables from all the other variables. See the example above which gives you the opportunity to do;

echo $STR_REMOVE_BEFORE('name@example.com','@');


There is one problem with using a variable as a function and that is that you can't use it with parameters that are passed on by reference.

$ARR_LAST_VALUE = 'end';

Using this variable as a function will result in an error.

Saturday, September 20, 2008

Simple ubiquity commands

If you go to the command editor you can add these commands if you want.

// -------------------------------------
// firefox interfaces
// -------------------------------------
CmdUtils.CreateCommand({
name: "add-ons",
icon: "http://www.spreadfirefox.com/files/spreadfirefox_RCS_favicon.png",
execute: function(){
Utils.openUrlInBrowser( "chrome://mozapps/content/extensions/extensions.xul" );
}
});

CmdUtils.CreateCommand({
name: "downloads",
icon: "http://www.spreadfirefox.com/files/spreadfirefox_RCS_favicon.png",
execute: function(){
Utils.openUrlInBrowser( "chrome://mozapps/content/downloads/downloads.xul" );
}
});

CmdUtils.CreateCommand({
name: "places",
icon: "http://www.spreadfirefox.com/files/spreadfirefox_RCS_favicon.png",
execute: function(){
Utils.openUrlInBrowser( "chrome://browser/content/places/places.xul" );
}
});
// -------------------------------------
// website based functions
// -------------------------------------
CmdUtils.CreateCommand({
name: "copy-url",
icon: "http://www.spreadfirefox.com/files/spreadfirefox_RCS_favicon.png",
execute: function(){
Utils.openUrlInBrowser( context.focusedWindow.location.href );
}
});

As you can see the openUrlInBrowser function is the engine behind all of these functions. The firefox interface commands are based on the behavior of the vimperator, where the add-ons and downloads are presented in a tab.