Tuesday, March 16

Accept Disclaimer and Redirect or Sessions in Joomla

Customer wants users to accept a disclaimer one time each visit to the site, then be redirected to the page. So if the user accepts the disclaimer, a session variable will be written and they will be redirected. This is a Joomla site, so the framework is already built for sessions and I wouldn't want to use regular php sessions.


So first, we'll put a checker on the page to see if they've accepted the disclaimer. I just included this php page in a custom code module on the menu item.
//load joomla files
require_once ( JPATH_BASE .DS.'includes'.DS.'defines.php' );
require_once ( JPATH_BASE .DS.'includes'.DS.'framework.php' );

$mainframe =& JFactory::getApplication('site');
$mainframe->initialise();

// get the session
$session =& JFactory::getSession();
$defaultvalue = 'reject';
$discl = $session->get('discl', $defaultvalue, 'disclaimer'); //get discl session value or 'reject' if not set

if($discl <> 'accept'){
$link = JRoute::_('index.php?option=com_content&view=article&id=43&Itemid=52');
$mainframe->redirect($link);//route them back to disclaimer page
}
So we have to load the Joomla files or none of the built in stuff will work. Then we get the session with JFactory::getSession. When doing a ->get, we can define a default variable. It's not necessary as if it's not set, the value will be null, but I set one just because. So then we test and see if the value is set to accept, and if not, route them to the disclaimer page. JRoute is pretty neat cause you put in the real url and it'll change it to the SEF url if you have that enabled. Pretty!

Now on the disclaimer page, they will accept and we will write the session variable.
// load joomla files
require_once ( JPATH_BASE .DS.'includes'.DS.'defines.php' );
require_once ( JPATH_BASE .DS.'includes'.DS.'framework.php' );

$mainframe =& JFactory::getApplication('site');
$mainframe->initialise();

// get the session
$session =& JFactory::getSession();

$session->set('discl', 'accept', 'disclaimer');//set accept
$link = JRoute::_('index.php?option=com_content&view=article&id=1&Itemid=49');
$mainframe->redirect($link);//route them to page
Get the session then add our own variable using ->set('sessionName', 'sessionValue', ['uniqueNameSpace']). You may have noticed the namespace defined in the last script. The namespace isn't required, if you don't define it, any session variables you state here will be added to the default namespace.

The way I implemented this in the Joomla site was create a page, add a menu item, then add a custom code module including this file to the page. It redirects so quickly, it's barely even noticeable.

So the result is user comes to site, session variable isn't 'accept' so they are redirected to the disclaimer. They accept and are directed to the site. They are free to move about the site until they close the browser or the session times out, at which point, when they come back, they must accept again.

Thursday, February 11

Updating Submit Disable to Include isNaN

The datanumber should be numeric, so I wanted to check on the frontend, before they even submit it. It was easy to add an else to the previous javascript function SearchVal().

function searchVal(){
if(document.getElementById('fname').value == 0
&& document.getElementById('lname').value == 0
&& document.getElementById('datanum').value == 0){
msg = 'At least one search field must be filled';

}else if(isNaN(document.getElementById('datanum').value)){
msg = 'Data number must be numeric';

}else{
dis = false;
msg = '';
}

if(dis == true){
document.getElementById('sub').disabled = true;
}else{
document.getElementById('sub').disabled = false;
}
document.getElementById('validmsg').innerHTML = msg;
}

Tuesday, February 9

Get Age From Birthday

function GetAge($Birthdate){

// Explode the date into meaningful variables
list($BirthYear,$BirthMonth,$BirthDay) = explode("-", $Birthdate);

// Find the differences
$YearDiff = date("Y") - $BirthYear;
$MonthDiff = date("m") - $BirthMonth;
$DayDiff = date("d") - $BirthDay;

// If the birthday has not occured this year
if (($MonthDiff < 0) || ($MonthDiff == 0 && $DayDiff < 0))
$YearDiff--;
return $YearDiff;
}
Shamelessly stolen from:
Geekpedia

edited: apparently neither
($MonthDiff >=0 && $DayDiff >= 0)
nor
($DayDiff < 0 || $MonthDiff < 0)
were taking all the math correctly into account. We managed to figure out something better.

Friday, February 5

Setting up DB2 for PHP on CentOS

Using a remote DB2 8.1 database with php 5.1.6 and Apache 2.2 on CentOS 5.4 (64 bit)

To download the IBM drivers for DB2, go to the IBM page and click on DB2 Client. If you don't have an account, you can request a free one. After logging in, download the IBM Data Server Client for linux. I also installed Express-C. I think this gave me some of the scripts I needed, though I'm not exactly sure. It was pretty self explanatory, download, extract, run the install script.

These instructions assume you've installed apache (httpd) via yum

# yum install php-pear
# mkdir /opt/ibm/db2/
# cp ./ibm_data_server /opt/ibm/db2/
# cd /opt/ibm/db2
# tar -xzf ibm_data_server
# cd dsdriver
# sh ./installDSDriver
# pecl download ibm_db2
# pecl install ./ibm_db2
when it asks for the installation dir, tell it: /opt/ibm/db2/dsdriver

Now we set up php to use the driver.
# vi /etc/php.ini
Find the section of Dynamic Extensions and add
extension=ibm_db2.so
To make sure it worked, run:
# php -i | grep -i db2
and you should get some results like:
ibm_db2
IBM DB2, Cloudscape and Apache Derby support => enabled
Binary data mode (ibm_db2.binmode) => DB2_BINARY
DB2 instance name (ibm_db2.instance_name) =>
PWD => /opt/ibm/db2/dsdriver
OLDPWD => /opt/ibm/db2/dsdriver/bin
.... and so on

So this is where I got stuck for a long time. At this point, db2_ commands will work from php, but it won't be able to find any database. All the documentation I read said things like "if you've created a db2 instance, point to it in your php.ini file" and stuff like that. It took me much longer to find how to actually create the instance.

Add the user db2inst1 (this is the default name php looks for an instance, but it can really be whatever.) I also put the user in a group I made (called db2grp1), but I don't think you need to.
# useradd db2inst1
# ./opt/ibm/db2/V8.1/instance/db2icrt db2inst1
This creates the instance, which basically adds sqllib and bin dirs to the home directory, and changes the .bashrc. I also don't know where this script came from, maybe from Express-C.

You can see all instances using:
# /home/db2inst1/sqllib/bin/db2ilist
If you want, you can go ahead and add this to the php.ini right now. I couldn't find anywhere that said where it had to be, so I just stuck it at the end before the end tag.
ibm_db2.instance_name=db2inst1
This will update the results when you do a php -i|grep -i db2 as well.

Now we have an instance, php knows where it is, but the instance doesn't know where the database is. If you're on a local database, it's easy, but I'm not so I have to configure the local instance to go find the remote db.
su to the instance user.
# su db2inst1
$ db2
=> catalog tcpip node remoteinst remote hostname.or.ip server 50000
=> catalog database testdb as remotedb at node remoteinst authentication server
=> terminate
This created the alias to the server and to the database. Everything in italics should be your information. To test this has worked type
=> connect to testdb user username using password
Your connection should succeed and tell you database information:
Database Connection Information

Database server = DB2/6000 8.1.6
SQL authorization ID = USERNAME
Local database alias = TESTDB
If it fails, it should tell you why.
SQL30082N  Attempt to establish connection failed with security reason "24"
("USERNAME AND/OR PASSWORD INVALID"). SQLSTATE=08001
Now we're almost done (finally). We just need to put the alias info into the db2dsdriver.cfg where php looks for it. To do this, there's another fancy IBM script, yay! This one should be in /opt/ibm/db2/dsdriver/bin, assuming you followed my instructions up above.
# cd /opt/ibm/db2/dsdriver/bin
# ./db2dsdcfgfill -i db2inst1 -o /home/db2inst1/sqllib/cfg
That should fill in the db2dsdriver.cfg file. It took mine a few tries to succeed, but I don't think i did anything differently, so I'm not sure why it was failing. If it keeps failing, try moving the script to /home/db2inst1/sqllib. Maybe I did that to make it work.

One last thing before it'll work (and yes, I forgot this at first)
# httpd -k restart
That should be it. In php using the connection string, for the database, use the alias you created with the catalog commands.
<?php
$conn = db2_connect('testdb', 'user', 'password');
if (!$conn) {
echo "Connection failed.". db2_conn_errormsg();
}?>
Resources:
php.net
IBM Library
db2ude
sqlrelay
and 100 more google search results

Anomaly:
Since the db2dsdriver can only be updated (in my experiences) using
db2dscfgfill -i db2inst1 -o /home/db2inst1/sqllib/cfg
that means the db2dsdriver located in /opt/ibm/db2/dsdriver/cfg will need to be updated manually. Even though php knows the instance is db2inst1, it still seems to look in the opt location for the driver. When I updated the driver, I just copied and overwrote the one in opt, then restarted the httpd server and all was fine. Before I overwrote the old one, it wasn't finding the alias.

Thursday, January 14

Disable Submit Button until Valid

The Case:
There are three search options, first name, last name or id number. You can search by any one (or more) but at least one has to be filled. I want to disable the submit button until one is filled, and have a little message that says you need to fill one.

The Javascript:

function searchVal(){

if(document.getElementById('fname').value.length == 0
&& document.getElementById('lname').value == 0
&& document.getElementById('datanum').value == 0){
dis = true;
}else dis = false;
if(dis == true){
document.getElementById('sub').disabled = true;
msg = 'At least one search field must be filled';
}else{
document.getElementById('sub').disabled = false;
msg = '';
}
document.getElementById('validmsg').innerHTML = msg;
}

function formVal(){
document.getElementById('sub').disabled = true;
document.getElementById('validmsg').innerHTML = 'At least one search field must be filled';

document.getElementById('fname').onchange = searchVal;
document.getElementById('lname').onchange = searchVal;
document.getElementById('num').onchange = searchVal;
}
The HTML:
<form action="results.php" class="pform" method="get">
<ol>
<li><label for="lname">Last Name</label><input type="text" name="lname" id="lname" /></li>
<li><label for="fname">First Name</label><input type="text" name="fname" id="fname" /></li>
<li><label for="num">ID Number</label><input type="text" name="num" id="num" /></li>
<li id="validmsg"></li>
<li><label for="submit"></label><input type="submit" name="sub" id="sub" value="Search" /></li>
</ol>

Other Comments:
The form items are in a list to look pretty as suggested by this ALA Article. The js is two functions because I'm using this handy dandy window onload manager.

Maybe tomorrow I'll be able to figure out how to check that the ID number is numeric.

Note: I found out later that even though the button is disabled, you can still submit the form using Enter.

Wednesday, December 30

The File You're Looking for is...

/templates/system/html/modules.php

This is where you can change the module titles from h3 to h2. Also from tables to not tables. I tried to copy this like normal to the template/html folder, but I get some error that makes me angry, so just forget about that.

I changed all the tables to divs too cause tables make me sad. I think pretty much the one that my site uses is like this:

function modChrome_xhtml($module, &$params, &$attribs)
{
if (!empty ($module->content)) : ?>
<div class="moduletable->get('moduleclass_sfx'); ?>">
<?php if ($module->showtitle != 0) : ?>
<h2><?php echo $module->title; ?></h2>
<?php endif; ?>
<?php echo $module->content; ?>
</div>
}
<?php endif;
}

Wednesday, September 9

Literal... Interpreted... Quotes Matter

Something I never before (but probably should have) read that took hours to figure out:

If the string is enclosed in double-quotes ("), PHP will interpret more escape sequences for special characters. -via php.net
This really matters!!

For example. When you are sending mail, new lines are usually \r\n. If you have your message body in single quotes (') the \r\n will show up in the message!

Even more importantly. When using Pear mime, you have to define eol. Just remember $crlf = '\r\n' WILL NOT WORK!!! it has to be $crlf = "\r\n" If I had read to the bottom of the user comments on the pear page, it would have saved me a lot of time.

And yes, this post is actually about doing Mail_Mime with Pear :D

Resources:
PEAR Mail_Mime
PHP Maniac Examples