Effortless, template-based dynamic tables

When you develop web applications for a living, sooner or later you’ll need to allow the user to edit collections of data. For instance:

Field 1 Options

To create something like this, I’ve been using a very rough script that simply replicated a hidden template row when required for several years. Eventually, I couldn’t stand its uglyness anymore and I decided to polish it a bit ;-)

I’ve based my new script on prototype, thus keeping it short and elegant.

Let’s see the full code required to create the table above:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- Ideally, this should go in <head> -->
<script type="text/javascript" language="javascript" src="http://files.tellini.info/dynamicTable.js"></script>

<table id="example1" class="dynamicTable">
    <tr class="template">
        <td><input type="text" name="test[#{rowId}]" /></td>
        <td>
            <input id="test#{rowId}" type="checkbox" name="cbTest[#{rowId}]" />
            <label for="test#{rowId}">Some option...</label>
        </td>
        <td><input class="deleteRow" type="button" value="Del" /></td>
    </tr>
    <tr>
        <th>Field 1</th>
        <th>Options</th>
        <th></th>
    </tr>
</table>
<input class="example1 addRow" type="button" value="Add" />

Simple, isn’t it?

Here’s what you need to know:

  1. the script automatically sets up all the tables with a dynamicTable class
  2. the template row is automatically hidden
  3. the ID of the table can be repeated as a class together with the addRow class to link the button to the table. If the button is already inside the table, you might just use the addRow class.
  4. the deleteRow class marks the button used to remove its containing row
  5. #{rowId} in the template will be replaced with an unique ID for every row that gets added. You’ll have to use sequential IDs starting from 0 for rows you add before serving the page to the client.

Exploiting prototype Classes, you can subclass DynamicTable and override addRow() or deleteRow() to perform whatever action you need on freshly created rows or when the user removes one.

By the way, you can find the code here.

Posted in Software Development | Tagged , , | Leave a comment

Keeping script kiddies at bay with mod_evasive and iptables

mod_evasive is a nice Apache module that helps to protect your server against DoS attacks.

However, when a client is blocked, it will keep on using resources on your server. Even if the request will result in a 403 error, it’s still a connection that needs to be handled. In some cases, it might require spawning a new process for no good reason.

It’s quite easy to configure mod_evasive so that the evil IPs are blocked via the machine firewall, though:

<IfModule mod_evasive20.c>
# ...your other settings...
        DOSSystemCommand "sudo /root/scripts/ban_ip.sh %s"
</IfModule>

You could put an iptables command there, but I prefer to use a small script because it’s easier to maintain. Also I don’t want to block the IP until the end of time :-) So, I use this:

#!/bin/sh

IP=$1
IPTABLES=/sbin/iptables

$IPTABLES -A banned -s $IP -p TCP --dport 80 -j DROP

echo "$IPTABLES -D banned -s $IP -p TCP --dport 80 -j DROP" | at now + 2 hours

Don’t forget to grant the permission to run the script to the account used by apache. My sudoers config contains:

www-data ALL=(ALL) NOPASSWD: /root/scripts/ban_ip.sh

And here’s the result, on a busy server victim of some abuse:

Red means “system time”, blue is “user time”. The green arrow marks the time when I configured it to use iptables.

Posted in SysAdmin, Tips'n'Tricks | Tagged , , , , | 5 Comments

Quick Sprites: CSS sprites, the easy way

The use of CSS sprites is a valuable techique for any web developer who wishes to optimize his web site, making it both faster for users and better ranked by search engines.

However, it usually involves more work to pack the images in a single sprite sheet and to update them when the site evolves.

If you never used this technique for this reason, now you won’t have any excuse left: Quick Sprites makes creating and maintaining sprite sheets a breeze :-)

You can see how easy is to work with Quick Sprites in this short video:

Currently available in the Mac App Store.

Posted in Software, Software Development | Tagged , , , , , | Leave a comment

Scraping App Store reviews, page after page…

Someone pointed out that my little scraping script was fetching only some of the reviews of his app. Indeed, it was only considering the first “page” of results.

So, I’ve just updated it to grab ‘em all. The new version is available at the same address: here.

By the way, the script was born to get reviews from the Mac App Store, but it seems to work just fine with the iOS store as well :)

Feel free to send me a promo code for your app, if you really like it ;-)
(or even an iPad 2, if it changed your life :D )

Posted in Software | Tagged , , , | 3 Comments

The Lion, the Witch and the Xcode

…or maybe it was “The Lion, the Bug and the Xcode“?

Anyway, if you just upgraded your system to Lion, then downloaded the new Xcode from the App Store, tried to run it just to get welcomed by a sparkling crash along the lines of

UNCAUGHT EXCEPTION (NSInternalInconsistencyException): Couldn’t load plug-in ‘com.apple.dt.IDE.IDEiPhoneSupport’ while firing fault for extension ‘Xcode.Device.iPhoneSimulator’

Don’t panic!

Simply go to your Applications folder and re-run the Xcode installer. The second time it’ll get it right, or at least it did for me.

Posted in Software Development, Tips'n'Tricks | Tagged , , , | 10 Comments

Special Tablatures offer coming up this week

A very special Tablatures offer will be available this weekend at Bits du Jour: a whopping 46% off the regular price!

If the only reason why you haven’t started writing your music with Tablatures yet is because you were thinking about the price, you have no excuse now. :-)

Don’t miss your chance to take advantage of this promotion: there won’t be another one like it anytime soon.

For further details, please visit http://www.bitsdujour.com/software/tablatures/ and click on the “I Want This” button to get notified when the promotion will start, to be sure not to miss the deal.

Posted in Music, Software | Leave a comment

Scraping Mac App Store reviews

You have some products on the Mac App Store, you would like to know what people think of them but you can’t bother checking for reviews one store at a time?

Don’t worry, you’re not alone. I can’t understand why Apple made it so difficult to check reviews for your own products in iTunes Connect, so here’s a little PHP script that will do the dirty work for you: it’ll check all the stores and fetch all the reviews, producing an XML file per product with the most important info (review, reviewer, version of the product…).

You just need to rename the config file to macappstore.cfg.php, fill in the $products array, create an output subdirectory and execute:

1
php macappstore.php

After a while you’ll see the XML appearing in the output directory, ready for further processing.

Posted in Software | Tagged | 12 Comments

Happy New Year!

My best wishes for a great 2011 to everyone! :)

Posted in Miscellaneous | Leave a comment

Using SQLi Import with Oracle

SQLi Import is a nice eZ Publish extension that allows you to develop data importers in a quick and elegant way.

Sadly, it comes only with MySQL support, but it takes only a minute to make it work with Oracle too. So here’s the SQL code I’ve used to make it happy :-)

(tested with SQLi Import 1.2.0 and eZ Publish 4.4)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
CREATE TABLE sqliimport_scheduled (
  id INT NOT NULL,
  handler VARCHAR(50),
  label VARCHAR(255),
  options_serialized CLOB,
  frequency VARCHAR(30) NOT NULL,
  NEXT INT DEFAULT 0,
  user_id INT,
  requested_time INT,
  is_active SMALLINT DEFAULT 0,
  manual_frequency INT DEFAULT 0,
  PRIMARY KEY (id)
);

CREATE TABLE sqliimport_item (
  id INT NOT NULL,
  handler VARCHAR(50),
  options_serialized CLOB,
  user_id INT,
  requested_time INT DEFAULT 0,
  STATUS SMALLINT DEFAULT 0,
  percentage_int SMALLINT DEFAULT 0,
  TYPE SMALLINT DEFAULT 1,
  progression_notes CLOB,
  process_time INT DEFAULT 0,
  scheduled_id INT,
  PRIMARY KEY (id)
);

CREATE SEQUENCE se_sqliimport_item;
CREATE SEQUENCE se_sqliimport_scheduled;

CREATE OR REPLACE TRIGGER tr_sqliimport_scheduled_id
BEFORE INSERT ON sqliimport_scheduled
FOR EACH ROW
WHEN ( NEW.ID IS NULL )
BEGIN
    SELECT se_sqliimport_scheduled.NEXTVAL
    INTO :NEW.ID
    FROM dual;
END;
/

CREATE OR REPLACE TRIGGER tr_sqliimport_item_id
BEFORE INSERT ON sqliimport_item
FOR EACH ROW
WHEN ( NEW.ID IS NULL )
BEGIN
    SELECT se_sqliimport_item.NEXTVAL
    INTO :NEW.ID
    FROM dual;
END;
/
Posted in Software Development | Tagged , , , , | 1 Comment

Serendipity to WordPress

I’ve moved a couple of my blogs from Serendipity to WordPress (mostly because I’ve discovered that Serendipity has some issues with newer PHP setups, but also because I’m getting lazy and WordPress’ admin panel is nicer than s9y’s ;-) ).

Luckily someone else did already write a s9y->WP importer plugin. Sadly it didn’t support nested categories, so I changed a couple of things and here’s my Serendipity (S9Y) importer for WordPress 1.5 (hoping that noone else has already used that version number… the history of the plugin contains several authors :) )

One snag: after the import, you’ll need to edit one of the categories (just open and save it) to fix the hierarchy. I don’t know why and honestly I don’t care :-P Worked for me, your mileage might vary.

Refer to this article for other useful migration tips.

Posted in Software, SysAdmin | Tagged , | 36 Comments