Geek Stuff: Frameworks, jQuery and MooTools

Work on old websites yielded an interesting situation recently when an upgrade caused this JavaScript error in Chrome:

Uncaught TypeError: Object [object Object] has no method ‘createDocumentFragment’

The code worked flawlessly in Firefox and a review of the stack trace in Google’s Chromium 23 (along with testing unminified code) revealed that jQuery’s .find() function was not returning kosher data. It turns out that both jQuery and MooTools were being used by plugins of this very outdated website. We are talking about Joomla 1.5, MooTools 1.1.2 and jQuery 1.3. As part of this upgrade, I migrated the site to jQuery 1.8 (yes, 1.9 is out and 2.0 is on the horizon). Switching from $() to jQuery() did not solve the problem, but simply bumping MooTools to 1.2.x seems to.

While testing, I found that this could be a suitable work-around in our situation if upgrading MooTools were not an option (I would have to replace the .find() calls with .altFind()):

$.fn.altFind = function(selector) {
   var ret = this.find(selector);
   
   // Yes, this is stupid. Got a better idea?
   for (var x = 0; x < ret.length; x++) {
      ret[x] = $(ret[x])[0];
   }

   return ret;
}

This wrapper for .find() is a really goofy work-around since it just runs the faulty jQuery objects through jQuery again. Pick your poison. Either way, you now have another reason to prefer a custom solution with a standardized set of tools for your project.

Geek Stuff: Redhat's Virtual Machine Manager and dnsmasq

Up until recently, I have been using aqemu to manage my Qemu virtual machines which provides side-by-side access to IE7, IE8, IE9 and IE10. It was useful and supported VDE networks (with a small patch). For various reasons, I have moved the Virtual Machine server to Fedora (F17) recently and Fedora does not compile VDE support into their Qemu package. While looking over my options, I found a nifty utility from Redhat: the Virtual Machine Manager. The ability to watch each and every virtual machine’s CPU, network and harddrive usage from one screen is fantastic. I installed it on one system and found the ability to configure the image directory, virtual networks, and most of the other features I needed. It does not yet support booting snapshots, but that is promised in the next version.

Since everything went smoothly, I installed it on the main server… and the Virtual Machine Manager began to throw Dnsmasq errors whenever I attempted to bring the virtual network up:

Error starting network 'default': internal error Child process (/sbin/dnsmasq --strict-order --bind-interfaces --local=// --domain-needed --pid-file=/var/run/libvirt/network/default.pid --conf-file= --except-interface lo --listen-address 192.168.122.1 --dhcp-range 192.168.122.2,192.168.122.254 --dhcp-leasefile=/var/lib/libvirt/dnsmasq/default.leases --dhcp-lease-max=253 --dhcp-no-override) status unexpected: exit status 2n
Traceback (most recent call last):
  File "/usr/share/virt-manager/virtManager/asyncjob.py", line 96, in cb_wrapper
    callback(asyncjob, *args, **kwargs)
  File "/usr/share/virt-manager/virtManager/asyncjob.py", line 117, in tmpcb
    callback(*args, **kwargs)
  File "/usr/share/virt-manager/virtManager/network.py", line 82, in start
    self.net.create()
  File "/usr/lib64/python2.7/site-packages/libvirt.py", line 1847, in create
    if ret == -1: raise libvirtError ('virNetworkCreate() failed', net=self)
libvirtError: internal error Child process (/sbin/dnsmasq --strict-order --bind-interfaces --local=// --domain-needed --pid-file=/var/run/libvirt/network/default.pid --conf-file= --except-interface lo --listen-address 192.168.122.1 --dhcp-range 192.168.122.2,192.168.122.254 --dhcp-leasefile=/var/lib/libvirt/dnsmasq/default.leases --dhcp-lease-max=253 --dhcp-no-override) status unexpected: exit status 2

After literally hours of running commands and searching Google (and DuckDuckGo), a post on these forums pointed the way:

I believe the problem is caused by libvirt not shutting down dnsmasq.

Duh. Dnsmasq does not allow more than one instance of itself. I know this and should have remembered it. I had been experimenting with PXE booting through dnsmasq on that Fedora server, and will have to configure a few things by hand. I am still toying with the Virtual Machine Manager, but it shows promise. Hopefully this extra post will help others who find themselves in the same boat with the Virtual Machine Manager and Dnsmasq.

Geek Stuff: ExpressionEngine/CodeIgniter Adventures with ActiveRecord, Union and Join

I have been doing a lot of work with ExpressionEngine over the last couple of years, and have logged more time in the database and with the PHP code than in the Control Panel (that includes time spent editing templates). I have the found the ASCII art of Pinky and the Brain. Anyway, if you know anything about ExpressionEngine, you know that it is built on top of an open-source framework called CodeIgniter that is released by the same company as ExpressionEngine (EllisLabs).

CodeIgniter has a database abstraction layer called ActiveRecord. This abstraction is supposed to allow CI-based sites to switch between database engines very easily. ExpressionEngine continues to support only MySQL, but developers are encouraged to utilize CI’s ActiveRecord class.

Tonight I am hacking Structure in the name of building an accurate sitemap. Another add-on has been interacting with it and caused some entries to get tied to the wrong parents (it corrects the entries as they come out of the Structure tags). I have tried several approaches to retrieve the correct URLs. That other add-on has class functions which could correct this but they are “private” and I want this to continue to work if upgrades are made. Copying those functions seems like a bad idea. My current approach is to start off with the entries themselves and then to work up Structure’s exp_structure and exp_structure_listings tables.

The cleanest way to write this in PHP involves merging the data from those two tables. ExpressionEngine uses MySQL, so a UNION would fit the bill perfectly.

Unfortunately, if you glance over the ActiveRecord docs or code, there is no support for the UNION instruction. I did a quick search for the proper way to use UNION with ActiveRecord and turned up a post on StackOverflow. Someone by the name of Souvik recommended doing exactly what I was already thinking, and that is to use one of ActiveRecord’s “private” class functions that is accessible. It looked like the cleanest hybrid for ActiveRecord usage:

$this->EE->db->select('ct.entry_id AS parent_id, ct.url_title, s.entry_id');

$this->EE->db->from('structure s');
$this->EE->db->join('channel_titles ct', 'ct.entry_id = s.parent_id');
$this->EE->db->where_in('s.entry_id', $tracker);
$this->EE->db->where('s.site_id', $this->EE->config->config['site_id']);
$first = $this->EE->db->_compile_select();
$this->EE->db->_reset_select();
$this->EE->db->select('ct.entry_id AS parent_id, ct.url_title, sl.entry_id');
$this->EE->db->from('structure_listings sl');
$this->EE->db->join('channel_titles ct', 'ct.entry_id = sl.parent_id');
$this->EE->db->where_in('sl.entry_id', $tracker);
$this->EE->db->where('sl.site_id', $this->EE->config->config['site_id']);
$second = $this->EE->db->_compile_select();
$this->EE->db->_reset_select();

$query = $this->EE->db->query('('.$first.') UNION ('.$second.')');

Now, if we lived in a perfect world, we would have a good solution. Since we do not, MySQL throws this error:

Error in query: Syntax error near 'JOIN `exp_channel_titles` ct ON `ct`.`entry_id` = `sl`.`parent_id` WHERE `sl`.`' at line 7

It turns out that ActiveRecord wraps the FROM clause for both queries in parenthesis and MySQL dislikes this when combined with a UNION. The fix is to send $first and $second through a simple string replacement:

$first = preg_replace('/FROM \(([^\)]+)\)/', 'FROM $1', $first);
$second = preg_replace('/FROM \(([^\)]+)\)/', 'FROM $1', $second);

And that should get you on your way. Happy hacking.

Geek Stuff: Qemu Changes

The one weekend that I upgrade from Qemu 0.15, qemu.org’s DNS servers went down. That is really unfortunate since I was installing 1.2, which means that the “qemu” binary disappeared and was replaced by the new name “qemu-system-i386.”

This is just a short post to help anyone else out who is facing the same problem. According to the mailing list, the Qemu project is currently bringing a secondary domain online: qemu-project.org.

A Christian Nation?

Someone recently pointed me to Article 11 of the Treaty of Tripoli (Remember the Marines’ Hymn?):

Art. 11. As the Government of the United States of America is not, in any sense, founded on the Christian religion,—as it has in itself no character of enmity against the laws, religion, or tranquility, of Mussulmen [Muslims],—and as the said States never entered into any war or act of hostility against any Mahometan [Muslim] nation, it is declared by the parties that no pretext arising from religious opinions shall ever produce an interruption of the harmony existing between the two countries.

This text was recommended by John Adams and ratified by the United States Senate. Should we be outraged that “Christian” ideals are not always championed at the Federal level?

Geek Stuff: WordPress and Memory, Memory Everywhere...

One of my recent clients insisted that his shopping cart be created in WordPress and WP e-commerce. He made this decision because he wanted the software to be free, and that is one of the strengths that the Free/Open Source Software (FOSS) community has been leveraging for a long while. I even agree with some parts of the philosophy.

Unfortunately several of his products are highly customizable. When I say “highly,” I mean that there are over 2200 possible variations for at least two of his products. That is easy to understand when you start to talk about Color Choices x Length x Logos x &c. There are 10 variation categories. WP e-commerce wants to create a separate product item and title for each one of these so that you can clearly distinguish between what was purchased.

The endeavor is noble. I like to see exactly what the customer purchased. But WHY do we have to generate 2200+ unique titles when we save a product? And worse, WHY do we have to keep all of these possible modifications in server memory at the same time? Even worse, WHY do we have to see all 2200+ product titles the next time we try to edit the product? On the same page? The plain listing is more than 5 MB of HTML.

In a world where 8 GB of memory is becoming common, that may not sound like much but the parsed HTML is larger than what gets sent over the wire and browsers keep that data in memory for the sake of your “Back” button. There are also people who access their websites over cell phone networks or other mediums that charge by bandwidth usage. My client said that this “locked up” his computer – which probably just means that it was brought to a crawl. 5 MB can take a while to download and parse, especially on older hardware.

I modified WP e-commerce to generate these titles “as needed” instead of during product creation, and to not show the product modifier titles while editing the product (seriously, who is going to customize 2200+ titles for a single product?). That was when I noticed the next issue. After we configure the variations, you can view the product within the shopping cart. There are dropdowns for you to use in selecting your product modifiers, and then an AJAX request is fired off to ask how much the product is with those modifiers. I was getting errors like this from the AJAX query:

Fatal error: Allowed memory size of 67108864 bytes exhausted (tried to allocate 72 bytes) in ...

We exceeded 64 MB of memory in a price lookup? I mean, this is WordPress but 64 MB? It did not look like the code was dying near anything that I had modified, so I verified that it was not my fault with a Google search. Sure enough, this appears to be a common problem. And here I thought my custom shopping cart software was using a lot of memory at 6 MB per page load, or that a commercial solution was bad at 15 MB. I had to up the WordPress memory limit to a 128 MB cap per page load.

Despite my actions, one of the long-time users asked about this so non-nonchalantly that I am still shaking my head:

Hmmm why not just up the PHP memory limit?

We should not have to. That is a lot of server memory to expend on an AJAX request which happens every time an end user clicks on a dropdown.

I offered to patch some of these issues so that my client could upgrade his software without fear of losing our ‘customizations,’ my forum post was deleted. Maybe I have to purchase “Premium Support” to contribute a fix? Sometimes “free” is not always better.

UPDATE: After rewriting their product modifier code, the administrative product listing page was still maxing out its execution time on a semi-regular basis. It turns out that WP e-commerce loads all of the product’s variations (some have 2200+!) for each and every column in the table. There are 10 columns, multiplied by the default pagination of 20 products. On this site alone, that opens the possibility of WordPress processing over 440,000 table rows through filters and whatnot on a single page load. Of course this total depends on the exact products that appear on the page, but the right search terms will work wonders for killing server CPU.

The Amber Spyglass; A Book Review

As promised in my review of The Golden Compass, I have found The Subtle Knife and The Amber Spyglass second-hand and read them. Philip Pullman did manage to entertain me at several points with his creativity and he took a lot of pot shots at Christianity.

Some of the reviews that I read when The Golden Compass was turned into a movie had claimed that Pullman’s knocks were only aimed at the Catholic church. One or two also claimed that the main character, Lyra, was convinced by her external soul (or “daemon”) to have sex with someone. After reading the books for myself, I have to wonder what these reviewers were reading. Several of them may be deserving of the pot shots since neither was true.

One of the most troubling parts of this final book is Pullman’s attempts to define good and evil. Lyra’s father claimed that he chose an empty world to build a new city in and that he had not chosen a confrontation with “The Authority” (“God”). This was an attempt at neutrality despite the father’s killing of a child to arrive in this world. Is there any sense of “right” in which this would be allowed? In my opinion, Pullman never adequately deals with his actions.

At another point in the book, Lyra and and her cohort (Will) go to the place of the dead. To accomplish this, they learned that each living being has a guide to the underworld that follows them as long as they live. Lyra managed to bring hers into the open and convinced him to lead her. He left once they were past the point of return (which begs the question of whether she can “die” now).

The underworld is a location where the good and the bad are all tormented and the reader is asked to make a truth assessment about The Authority from this fictional scenario. This is just as bad as any contrivance created by other authors unless Pullman decided, for once, to limit his argument to the Catholic church’s Pergatory. Even then, this may or may not be a valid argument. I have not read extensively on the Pergatory teachings except for what Luther wrote…

Lyra and Will eventually create an opening out of the underworld. When dead spirits pass through the opening, they disolve and become a part of everything. Somehow their spirit will find its way back to the world where their soul was left when they died. Think of this like “The Force” from Star Wars and I will come back to it in a moment.

After the way that The Authority is built up as a tyrant, it turns out that he has since retired and left his regent, Metatron (“Enoch”) in charge. In his retirement, he has become a very whimsical and fragile being that is kept in a sealed box. The box is accidentally broken and a breeze disintigrates him while Metatron is waging a war against Lyra’s father. God is dead.

Lyra is repeatedly compared to Eve in this trilogy. It is said that she will decide the fate of all multi-dimensional races. As the time approaches for her “fall,” the infamous “Dust” flies out “windows” – out of the universe and into nothingness – at an insane rate. This loss would soon prevent all manner of knowledge from being grasped by the intelligent races. Then Lyra falls in love and this exodus stops.

Pardon my incredulity, but why didn’t everyone else who fell in love have this effect? What makes Lyra special in this way? (And don’t most people lose their heads when they fall in love? Eek, it is like a really poor parody of the son of God dying in our place as punishment for our wrongdoing.)

The angels insist that some dust is still leaking out and offer to close all the “windows” (except for the new exit from the underworld). This is accomplished but I have to wonder how the spirits are now to reunite with their souls since the exit from the underworld opens to one, specific world.

When everything is said and done, some parts of the story were amusing. I was not terribly impressed with the philosophic side of the story. It is not as bad as some reviewers would have you believe and it is worse than others say. Then again, Pullman is an agnostic rather than a full atheist. How many people know the difference between these two?

Geek Stuff: Safari is *NOT* PCI Compliant

After spending the last year and a half in going between hosting company, client, and PCI compliance firm, I succeeded at becoming PCI compliant. That means we can take credit card orders securely.

The cost? Safari (you know, Apple computers and iPhones?) can not check out on our shiny system. The complaints of the users of these devices have led to this blog post.

The layout of our server:

/shopping/booking.php /shopping/js/jquery.min.jsn/shopping/js/bookings.js

To test, I am using Safari 5.1.2 (latest version) on Windows XP (virtual machine). The booking.php file loads over https://, BUT none of the .js files will.

That’s right, if you type in the direct URL to the .js file you are greeted with this message:

Safari can't open the page.
Safari can't open the page "https://www.[our-domain].com/shopping/js/jquery.min.js" because Safari can't establish a secure connection to the server "www.[our-domain].com".

Switch back to /shopping/booking.php. Yep, it loads the HTML code. The certificate chain is installed correctly.

After digging around, there seem to be a few possible causes:

Since I am on Windows and am not using a proxy, it does not appear that options #1-3 could be affecting my tests, so I opened up the .htaccess file:

<IfModule mod_ssl.c>
    # See http://blog.ivanristic.com/2011/10/mitigating-the-beast-attack-on-tls.htmln    #SSLHonorCipherOrder O
    
SSLCipherSuite ECDHE-RSA-AES256-SHA384:AES256-SHA256:RC4+RSA:HIGH:!MD5:!aNULL:!eNULL:!LOW:!MEDIUM:!EXP:!SSLv2:!EDH:!AESGCM

SSLRenegBufferSize 4194304;
</IfModule>

Correcting the problem involved enabling MD5 and MEDIUM ciphers, neither of which is allowed in PCI compliance:

    SSLCipherSuite ECDHE-RSA-AES256-SHA384:AES256-SHA256:RC4+RSA:HIGH:MD5:!aNULL:!eNULL:!LOW:MEDIUM:!EXP:!SSLv2:!EDH:!AESGCM:

Yet without both of these “ciphers” enabled, Safari refused to load the .js files.

This leaves me with the choice of whether to remain PCI compliant or to allow users to check out. If anyone has a solution to this problem, I am all ears. Hopefully the post will cut down debugging time for other developers.

** NOTE: I realize that requiring JavaScript support for checkout is not perfect. In our situation, we have a lot of “bookings” available and really do need a way to filter them for our users. That is why we require it.

The Subtle Knife; A Book Review

More than two years have passed since my review of the first book in Philip Pullman’s His Dark Materials trilogy, begun with The Golden Compass. The second book is called The Subtle Knife and it has been sitting on my shelves for a few months. With the first review, it was difficult to address the story first because the underlying philosophies literally jumped off the page at me. This book is not much different in that respect, although I must admire Pullman as a storyteller. He spins his story better than the Left Behind authors have told theirs.

To summarize the philosophic themes quickly: The worldview is somewhere between pantheism or panentheism. A large push exists to destroy the “Authority” (God). This second book also pushes trascendental meditation as a way to contact the global consciousness in or around all of us, which does appear to predestine events (oddly, at one point there is talk about ending predestination?).

In this book, we find the main character, Lyra, in an alternate reality from her own (multiverse theory). It isn’t long before she bumps into a boy named Will from our reality whose father was researching Lyra’s ‘Dust’ years earlier. This ‘Dust’ is called, in our world, by the name “Dark Matter.” The plot line and various characters maintain that it begins to settle on and around people at puberty.

Throughout the adventures of Lyra and Will, we learn that Lord Asriel (Lyra’s father) is intent on restaging the luciferian rebellion against God, but it is left up to the reader’s imagination to determine how the original rebellion occurred. A scientist in our world learns how to communicate with the Dark Matter, and is told by it that she must “play the serpent.”

Given the way that Lyra and Will are finding themselves impressed and drawn towared each other, and the way that all things pleasurable are condemned by the Magisterium (church), my expectation is that Pullman is going to make their fall about the relationship between them.

Before I close, there is one other idle wondering. The book’s name comes from a special (“Subtle”) knife that is able to cut through anything – whether it is matter or immaterial. This is supposed to aid in overthrowing the Creator but all that was demonstrated in this book was the ability to cut holes between realities. For some reason all of the holes that were cut were into the same reality (our own from the middle world), but there are clearly other realities that have been opened in the past. Odd.

Stay tuned for the final installment. Eventually.

Geek Stuff/iptables: Spammers are Annoying, Right?

During the last few months, I have become a system administrator for two dedicated web servers. One of them is hosting ~100 accounts, and one of the smallest accounts got onto the “bad list” of some eastern block spammer last weekend. The account owners don’t even have their MX record pointed at our server – we aren’t supposed to see any mail for them.

On this past Monday alone we received over 1 million emails from over 31,000 unique IP addresses. Yes, it was all for this one account. After sending “their mail” into a black hole, our server was still unusable. All of the mail lookups, log writing, &c. continued to take a toll. The company that houses the server added an additional iptables rule:

iptables -I INPUT -m string --algo bm --string "@example.com"  -p tcp --dport 25 -j REJECT --reject-with tcp-reset -m comment --comment "STMP Block"

I had never used the “string” module for iptables but have seen it referenced before. This command basically causes iptables to search every packet on port 25 for the string “@example.com” using an efficient search algorithm. When it finds a match, it drops the connection. Great idea.

There are a couple of weaknesses to this method:

  • Iptables only notifies the remote computer that it has closed the connection. Our local Exim process thinks the other server is still connected until we hit the timeout threshold. With limited memory, and Apache processes, we want to limit how many Exim processes get started.

  • There is nothing to stop the bot from coming back (~32 emails per IP in a 24 hour period).

There are programs designed for this scenario, such as fail2ban. Its built-in scripting should handle this well but, being me, I wanted to find something with a smaller footprint (especially since we were already under attack).

Several years ago I had kept a publicly-available server at my house but hid it behind a “port knocking” script. You had to hit three specific ports in a specific order to gain access. Iptables had been flexible enough to support that so I merged that idea with the command above to create a reusable script:

IPTABLES=/usr/sbin/iptablesn

STRING='example.com'

PORT=25

# Check new connections to ${PORT} against the blacklist.

$IPTABLES -A INPUT -p tcp --dport ${PORT} -m recent --name blacklist --update --seconds 86400 -j DROP

# When blackchain is triggered, add IP to blacklist and drop the current connection.

$IPTABLES -N blackchain

$IPTABLES -A blackchain -m recent --set --name blacklist -p tcp -j REJECT --reject-with tcp-reset

# Trigger the entry of our BLACKCAIN chain...

$IPTABLES -A INPUT -p tcp --dport ${PORT} -m string --string ${STRING} --algo bm -j blackchain

It creates a “blacklist” of the most recent IP addresses that have sent an email to “@example.com” on port 25. Anybody who attempts this will be dropped immediately and prevented from reconnecting to port 25 for 24 hours.

There is just one tiny problem. The “recent” module only remembers the last 100 IP addresses by default. This can be remedied one of two ways.

The first method might be difficult to pull off when your firewall is running:

rmmod xt_recentn

modprobe xt_recent ip_list_tot=5000

Thankfully there is an alternate method that is available on some systems:

chmod +w /sys/module/ipt_recent/parameters/ip_list_totn

echo 5000 > /sys/module/ipt_recent/parameters/ip_list_tot

Make sure you don’t adjust this value rashly. I had tried 36,000 and that caused problems. At the moment I have our list up to 10,000 entries and the server seems to be handling the number without issue.

You can view your list at /proc/net/ipt_recent/blacklist. wc -l can be used to determine how full your list is.

Happy anti-(?) hacking!

Democrats Stop Passing Stimulus Bills... In Name.

From http://thehill.com/homenews/house/179623-pelosi-drops-the-word-stimulus:

We have tried shrinking the economy and sowing economic uncertainty for 8 months now, and it has failed. It's time for job creation and economic growth as our first order of business to strengthen our middle class.

And… for the preceeding 8 months, we tried spending our way out of economic decline. Stale-mate?

That the democrats are trying to drop the word “Stimulus” from their vocabulary is amusing. As Shakespeare once wrote, “A rose by any other name would prick just the same” (or something like that).

Hide yo kids!

As a small reminder that I haven’t forgotten this poor, lonely blog (despite the lack of posts), I want to present you with a picture of this dog:

Hide yo kids, hide yo wife!

(Just in case you missed it, try this video and the auto-tuned version.)

Should Christianity be a Political Force?

Only when it is a majority? What happens when it is?

A Waldensian Poem: The Noble Lesson

We ought to covet little, for we are at the remainder.

We daily see the signs to be accomplished

In the increase of evil and the decrease of good.

These are the perils which the Scripture mentions;

The gospel recognizes it, and Saint Paul writes it, that no man living can know the end,

And therefore we ought the more to fear, for we are uncertain.

If death shall seize us today or tomorrow;

But when Jesus shall come at the day of judgement,

Every one shall receive his full payment.

And who shall have done either ill, or done well.

But the Scripture saith, and we ought to believe,

That all men of the world shall pass two ways.

The good shall go to glory, the wicked to torment.

But he that shall not believe this departure,

Let him search the Scripture from its beginning,

Since Adam was formed, till the present time,

In it he shall find, if he has understanding,

That “few are saved” in comparison to the rest.

But every person who will do good,

The honor of God the Father ought to be his beginning,

And to call the aid of his glorious Son, the Son of Saint Mary,

And the Holy Spirit who gives us the way. These three are the Holy Trinity,

As being one God, ought to be invoked,

Full of all Power, of all Wisdom, and of all Goodness.

For this we ought often to beg and pray

That he give us strength to encounter the enemies,

And overcome them before our end,

Which are the World, the Devil, and the Flesh;

And that he would give us wisdom, accompanied with goodness,

That we may know the way of truth,

And keep pure that soul which God has given us,

The soul and body in the way of charity –

Now after the apostles were certain teachers,

Who taught the way of Jesus Christ our Saviour;

And these are found even at this present day,

But they are known to very few,

Who have a great desire to teach the way of Jesus Christ,

But they are so persecuted, that they are able to do but little,

So much are the false Christians blinded with error,

And more than the rest they that are pastors,

For they persecute and hate those who are better than themselves,

And let those live quietly who are false deceivers.

But by this we may know that they are not good pastors,

For they love not the sheep, but only for the fleeces.

The Scripture saith, and it is evident,

That if any man love those who are good,

He must needs love God, and Jesus Christ,

Such an one will neither curse, swear, nor lie,

He will neither commit adultery, nor kill;

He will neither defraud his neighbor,

Nor avenge himself of his enemies.

Now such an one is termed a Waldensian, and worthy to be punished,

And they find occasion by lies and deceit,

To take from him that which he has gotten by his just labor.

However, he that is thus persecuted for the fear of the Lord, strengthens himself greatly,

By this consideration, that the kingdom of heaven shall be given him at the end of the world.

Then he shall have a weight of glory in recompense for all such dishonor.

But in this is clearly manifest the malice of those men,

That they who will curse, lie, and swear,

He that will frequently put his money to usury, kill, and avenge himself on those who hate him;

This they say is a good man, and to be accounted faithful –

For I dare say, and it is very true,

That all the popes that have been from Sylvester to the present,**

And all Cardinals, Bishops, Abbots, and the like,

Have no power to absolve or pardon,

Any creature so much as one mortal sin,

It is God alone who pardons, and no other.

But this ought they do who are pastors,

They ought to preach to the people, and pray with them,

And feed them often with divine doctrine, –

That we may be of the number of his elect to dwell in his courts forever,

Praise be to God. Amen.** The Waldensians were counting their 1,100 years from the time the corruption of the Roman Catholic church. This poem was written roughly that length of time after the supposed “Donation of Constantine” to Pope Sylvester.

Geek Stuff: Website Pop-up to Close After Download

Suppose that your website allows people to download information. Now pretend that you are providing this information as a way to attract people to your business – a common situation on the web today. It follows that you would want to collect some contact information from your users before allowing them to begin the download.

Most websites require a registration for this. Some are blatant about it while others create the account behind the scenes, so that the user does not know they have been granted access to a member module and protected downloads.

What happens if you don’t want to sign up new users? This may be a fringe case but it is desired more commonly than it is implemented. This was the situation for one of my customers. He wanted a pop-up window that would collect the user’s name and email address before allowing the download.

If you open a new window (or tab) where the address belongs to a downloadable file, web browsers will then close the window once it starts. I thought this might be the best way:

  • Open popup
  • Collect information
  • Spawn new popup for download
  • Close the original popup
  • Allow the browser to close the second popup, once the download starts

For some reason, the browser refused to close the second popup after the first was closed. I could script around this by using Javascript’s setTimeout to close the first popup, but there is no definitive cross-browser method to know that a download has started. Suppose that the user is on dial-up or satellite? We may need to wait 20 seconds, 30 seconds, or more to be sure no “random” anomalies occur.

Scratch that option. The next possibility is to set document.location.href to the URL of the download except that, in my experience, the browser stops executing scripts the same way it does if you navigated to another page. The first popup window remains open.

Our third choice is more promising. We could create a function called “some_custom_function()” on the parent document (that created the popup) and then call it as window.opener.some_custom_function() before window.close() in the popup window. This will notify the original page that the form has been completed. some_custom_function() can set document.location.href for the parent document, and voila! The popup closes and the user is given a download.

If Internet Exploder did not continue to control 25% of internet browser share, we could stop here. Unfortunately Microsoft is always introducing poor browser design. Their information bar, by default, will prevent these downloads from your website (which can sometimes be good to slow the spread of viruses). The user has to click on the yellow bar, tell it to allow the download, and then the browser reloads your page! This kills any ability you had to give the user a download via Javascript.

Microsoft does provide a way to turn off the Information Bar in Internet Exploder:

http://windows.microsoft.com/en-US/windows7/Internet-Explorer-Information-bar-frequently-asked-questions

But let’s be realistic: You can’t ask your customers to do this before entering their contact details. They will go somewhere else instead.

It may be easier at this point to create a hack using a lightbox and an iframe, but many of the same problems would exist so I continued to expand this hack:

  • Open popup
  • Collect Information
  • Notify parent that we were successful
  • Parent rewrites its own url (ie. document.location.href = document.location.href.replace(/[#?].*$/, ‘?download’))
  • Close popup
  • Parent window reloads and checks for the presence of a custom $_GET parameter via Javascript (ie. document.location.href.indexOf(’?download’) != -1). If found, it sets window.onload** with an anonymous function that sets document.location.href to the path of the download.

Our entire purpose in modifying the URL is to pass information to ourselves when Internet Exploder performs a page refresh. This method works on Firefox 4, IE 8, and Chrome 5.

The reason for our usage of window.onload is that when document.location.href is changed, the browser will stop loading the page. We want the user to remain on the site if they wish to continue browsing.

Are you confused yet? Did this make sense? I hope that it did, and would love to hear of any improvements you make.

** I am aware that several Javascript frameworks, such as jQuery, allow for cross-browser compatibility with multiple onload functions. Implementing that on the site would have been a large ordeal (as lightboxes would have been) and window.onload was not being used for anything important. I do not foresee that changing on this particular site.

The Political Blame Game

I don’t typically read USA Today, but this week I am on vacation and there was a copy laying around. An opinion piece by two friends – a conservative columnist (Cal) and a liberal strategist (Bob) – caught my attention:

Cal: People have become too reliant on what President Obama sees as the "social contract." I don't see anything about that in the Constitution.</div><div> </div><div>Bob: Right, Cal. And we don't see anything in the Constitution about regulating the aviation industry, either, but the world changes, and government's role evolves with it.</div><div> </div><div>Cal: Democrats say "evolves" when they mean "grows." But go on.</div><div> </div><div>Bob: My point is the public can't have it both ways. On the one hand people complain about the deficit, but on the other hand they won't give up one bleeping thing to make it happen. OK homeowners, will you give up your mortgage interest deductions? Rich seniors, how about passing on your Social Security check? Corn farmers in Iowa, enough with your ethanol subsidies. What happened to the idea of shared sacrifice?</div><div> </div><div>Cal: My turn? Or Planned Parenthood? Ante up. Or NPR? Build an audience the free-market way. Or the Department of Education? Vouchers, anyone? Or

Or college tuition, or military spending, or… yes, the list goes on. Spending will never drop unless we actually stop spending. Their discussion continues further on:

Cal: The real problem is that too many people continue to put too much faith in government to solve the nation's problems. This would include some conservatives. We ought to be teaching the old-time virtues that best prepare someone to be able to stand on their own, make a living and provide for their families.</div><div> </div><div>Bob: Ah, the same old song. You sound like my friend Haley Barbour who wisely decided against running for president.</div><div> </div><div>Cal: Right, because our current path is working out so well. How dare we take a virtuous path. Bottom line, we can't go on like this, demanding more from government and less from ourselves.</div><div> </div><div>Bob: Your point?</div><div> </div><div>Cal: That we are doomed economically and probably in other ways.</div><div> </div><div>Bob: Doomed? What happened to the sunny optimism of conservatives?</div><div> </div><div>Cal: It's been undermined by the Change and Hope wrecking crew. Besides, cultural conditioning has brought us to this point. We must be conditioned to start thinking in a new way, which is really an old way, or else.</div><div> </div><div>Bob: I couldn't agree more. We must all face the deficit now  and I mean every citizen. Instead of complaining, every American ought to consider what he or she is willing to sacrifice in federal benefits and programs. And then vote that way. Engage your government. Contact your representative. Provide political cover for tough decisions on spending and, yes, taxes. The whining needs to stop, and the action needs to begin.

What? Is he talking about the end of federal quality of life insurance? I sure hope so.

A friend sent me an article last week by Wendell Berry, whom another friend had told me to read up on (and I hadn’t found the time yet). He had written an article some while back about why he will not own a computer, and several readers accused him of exploiting his wife because of this. The article was his reply to them, and he asks whether our “advances” are always worth the cost they come at.

It is very thought provoking, and I highly encourage you to read it (despite its length):

http://www.crosscurrents.org/berryspring2003.htm

Geek Stuff: Hacking cPanel's cPAddons

cPanel has some bugs. If you are trying to install a cPAddon and only see this error message:

Unable to set configuration! This will need done manually!

You may be falling prey to a bug in the procconfigfile sub. This is located in /usr/local/cpanel/Cpanel/cPAddons.pm.

The fault lies in the usage of _log_say(), which utilizes print(). Normally that would be fine except that procconfigfile() replaces the STDOUT pointer (due to mapping) that print defaults to using. This is only a temporary and is shortly restored – but it does obscure the error message.

This fix is to update the WARN call (circa line 2662):

my $inplace_errors = '';
my $inplace_error_count = 0;

{
    # my %inplace_errors;
    local $^I              = '.bak';
    local @ARGV            = map { "$dir/$_" } @{$fls};
    local $SIG{'__WARN__'} = sub {
        # my $err = shift();
        # $inplace_errors{$ARGV} = {
        #     'errno_int' => int($!),
        #     'errno_str' => "$!",
        #     'raw_warn'  => $err,
        # };
        $inplace_error_count++;
        $inplace_errors .= "Could not open $ARGV: $!";
    };

And then print out the error messages at the end of the function:

_log_say($inplace_errors) if $inplace_errors ne '';
rnreturn if $inplace_error_count;
rnreturn 1;

Happy debugging. In my case, it turned out that the configuration files were not being created because the site layout tarball that I had uploaded was not world-readable (cPanel extracts it as the user that is installing).

Once you know the location of the cPAddon processing code, the rest becomes easier. Hopefully you already know Perl if you are creating a new cPAddon.

Some Economic Theory for Tax Day 2011

This year the United States federal government required 13.39% of the money I earned last year. That wiped out the savings account that I had opened earlier this year, which was the first that I have had in three years.

For that amount, I honestly think we should do away with state and local governments (or keep the lower and shrink the higher – the Libertarian’s cry).

Those dollars were planned for use, and while it was accumulating it helped to provide a job at the bank… and was surely being lent to other bank customers so that they could spend the money in our current economy. I fail to see how this tax burden is helping to stimulate the economy.

In fact, to complete the irony of the “taxation stimulates” idea that our politicians love, I have finally chosen to use the $300 stimulus check that was sent out under the order of George W. Bush. In the last few years, I have intentionally chosen not to use it (as the country has not had the money to back this check) and the choice this year was as a protest.

The “Making Work Pay Credit” offers a $400 tax break in some situations. However, comma, this credit is discounted by any “economic recovery payment” that you accept from the Federal government. That means that my tax payment is essentially the same whether I shred the stimulus check or mail it to the government as part of my payment.

Or that’s how I’m reading it. We’ll see if the IRS wants to interpret the law differently. In the mean time, I wish that I could be there when the guys opening the mail see a check from the “U.S. Treasury” that is endorsed over to the “U.S. Treasury.” I just want them to leave me alone.

Here are a few appropriate words from economist Lawrence Reed (taken from an essay entitled “The Love of Power vs. The Power of Love”):

A mature, responsible adult neither seeks undue power over other  adults nor wishes to see others subjected to anyones controlling  schemes and fantasies: This is the traditional meaning of liberty. Its  the rationale for limiting the force of government in our lives. In a  free society the power of love, not the love of power, governs our  behavior.

Consider what we do in our political lives these daysand an  unfortunate erosion of freedom becomes painfully evident. Its a  commentary on the ascendancy of the love of power over the power of  love. We have granted command of over 40 percent of our incomes to  federal, state, and local governments, compared to 6 or 7 percent a  century ago. And more than a few Americans seem to think that 40 percent  still isnt enough. [...]

Millions of Americans think government should impose an endless array  of programs and expenses on their fellow citizens, from nationalized  health insurance to child daycare to subsidized art and recreation.  Weve already burdened our children and grandchildren, whom we claim to  love, with trillions in national debtall so that the leaders we elected  and reelected could spend more than we were willing to pay.

Just in case you think my 13.39% isn’t 40%, remember that I still have to pay taxes to state and local governments. Most states have income taxes. Then there are sales taxes, gasoline taxes (some of which are also Federal), property taxes, filing fees, ad infinitum. We also should not forget business licenses or the costs of inspections, which are government-mandated and therefore must be checked by specialists.

Insurance has also made its way onto the Form 1040 this year, and by 2014 that will become a $695 penalty for people who do not use (or want) it.

Okay, that’s enough rant for today.

Geek Stuff: vBulletin Follow-Up

As it turns out, my customer did not want only one imported page modified by vBulletin before it was displayed. He wanted all imported pages to be condensed if the user was not logged in, and he wanted to add or remove pages at will.

Again, minimal documentation. To do this, I modified my polls-process.php file to work as a plugin via the “vba_cmps_print_output” hook. “var_dump(get_defined_vars())” was very useful for finding the proper PHP variables to modify. vBulletin had not one or two, but three copies of the page loaded in memory. I needed to change “$home[’leftblocks’]” for my modifications to show on the output page.

Happy hacking.

Geek Stuff: vBulletin rant + hacks

Paid CMSes can be useful or painful. I’ve ranted about vBulletin before, and have recently had a chance to work with ExpressionEngine v2 (built on CodeIgniter). The phraseology is strange at first, but I actually kind of like EE. Quick reference charts like this one or this one make most of the coding a breeze.

vBulletin, on the other hand, I thoroughly loath. The customer who had wanted it installed several months back now needs the install to be modified. Where the EE community is fairly open because the underlying framework is open source, vBulletin decided to silence complaining customers by locking them out of the forums. Their stranglehold also prevents people like me, who freelance but do not own our own vBulletin licenses, from accessing any useful community information on how to modify vBulletin.

So I hack. And I’m going to publish my hacks.

First up, my customer wants users to be redirected to the registration page instead of the login page when document permissions require them to sign up. I know, I prefer a login page as well, but this is what was requested.

You can read about how necessary search tools like grep are on my last vBulletin post. The login page for user permissions is coded into a template called STANDARD_ERROR. I was unable to find a way to make vBulletin perform a redirect (and including the “register” template was a fail), so I inserted a small JavaScript snippet inside of a logic block:

    <vb:if condition="$show['permission_error']">
    <script type="text/javascript">
        var url = document.location.href;
        url = escape(url.replace(/^.*?\.com/i, ''));
        document.location.href = '/register.php?link='+url;
    </script>
    </vb:if>

This takes the current URL, strips off the domain name, and then passes it as a url-encoded string to register.php.

The second update requested was to give unregistered users a preview of a custom CMPS page. CMPS allows you to upload static HTML pages to the server and then serve them through vBulletin.

My first step was to change the page type from “HTML File” to “PHP File,” and to upload a simple PHP script (polls-process.php) for vBulletin to use instead of the HTML file (polls.html). The PHP script used file_get_contents() to read the original file (polls.html) and print it to STDOUT. That worked beautifully.

The second step was to learn how to detect logged-in users in the PHP file. As usual, grep was a very good friend because I am locked out of anything useful on the vBulletin forums. With a few searches and trials, I found that the $vbulletin global is available, which makes this the code to detect a signed-in user: $vbulletin->session->vars[’loggedin’].

I am going to keep some of the processing quiet because this is on a live website, but here is an example to help you perform the same hack on your site:

<?php
# Read the HTML file and strip it down for vBulletin.n$polls = file_get_contents('polls.html');n$polls = preg_replace('/^.*<body[^>]*>(.*)<\/body>.*$/si', '\1', $polls);

if (!$vbulletin->session->vars['loggedin']) {
    # Additional processing here.

    $polls .= <<<EOF
        You must <a href="/register">Register</a> or log in to view the rest of this page.
EOF;
}

# Print the results for the user to see.necho $polls;
?>

Happy hacking.