TalkPHP
 
 
Account Login
Latest Articles
» The basic usage of PHPTAL, a XML/XHTML template library for PHP
» Vulnerable methods and the areas they are commonly trusted in.
» Simple way to protect a form from bot
» The Basics On: How Session Stealing Works
» How to keep your forms from double posting data
IRC Channel
IRC Speech Bubble Join the friendly bunch on IRC...
(#TalkPHP on Freenode)

...Also available via a web interface.

See this thread for information on the TalkPHP Free Hugs Initiative™. Subject to availability.
Associates
Associates
CSS Tutorials
Reply
 
LinkBack Thread Tools Search this Thread Display Modes
Old 02-08-2008, 05:38 PM   #1 (permalink)
The Prestige
Upcoming Programmer Inquisitive 
 
Tanax's Avatar
 
Join Date: Sep 2007
Location: Sweden, Stockholm
Posts: 1,080
Thanks: 115
Tanax is on a distinguished road
Default Pagination class

Hello.

I'm releasing my pagination class.
I already wrote it in another topic while helping someone, so I thought I'd just make an own topic for it.

I've improoved it since I posted it in that other topic, and now I've written descriptions of each function.

Also, this *should* work without MySQL, eventhough my example is based on MySQL.

php Code:
<?php

/**
||||||||||||||||||||||||||||||||||||||||||
|||| @author Tanax
|||| @copyright 2008
||||||||||||||||||||||||||||||||||||||||||
**/


    class pagination {
       
        // The total values.
        private $totalPages;
        private $totalResults;
        private $totalPerPage;
       
        // The current values.
        private $currentPage;
       
        // The first result on current page.
        private $firstResult;
       
        /**
         *  Sets the maximum allowed results per page
         *
         *  @param integer $max, default 10
        **/

        public function setMax($max = 10) {
           
            if(is_numeric($max)) {
               
                $this->totalPerPage = $max;
               
            }
           
        }
       
        /**
         *  Generate the first result on the current page
         *
         *  @param integer $page, which page we're currently viewing
         *  @return integer first result
        **/

        public function setPage($page) {
           
            if(is_numeric($page)) {
               
                $this->currentPage = mysql_real_escape_string($page);
                $this->firstResult = (($this->currentPage * $this->totalPerPage) - $this->totalPerPage);
               
                return $this->firstResult;
               
            }
           
        }
       
        /**
         *  Generates how many pages based on the total amount of results
         *
         *  @param array $results, an array of all the results
         *  @return array of pages
        **/

        public function getPages($results) {
           
            $this->totalResults = count($results);
            $totalPages = $this->totalResults / $this->totalPerPage;
            $this->totalPages = ceil($totalPages);
           
            $x = 1;
            $array = array();
           
            while($x <= $this->totalPages) {
               
                $array[] = $x;
                $x++;
               
            }
           
            return $array;
           
        }
       
        /**
         *  Checks if a link is valid
         *
         *  @param integer $pagenr, the number of the page you want to check if it exist
         *  @return true or false
        **/

        public function checkLink($pagenr) {
           
            if($pagenr <= $this->totalPages && $pagenr >= 1) {
               
                return true;
               
            }
           
            return false;
           
        }
       
        /**
         *  Generate the current page number
         *
         *  @return array, [0] = the current page, [1] = total pages
        **/

        public function getCurrentPage() {
           
            $array = array();
            $array[] = $this->currentPage;
            $array[] = $this->totalPages;
           
            return $array;
           
        }
       
    }

?>


Example usage:
php Code:
<?php

/**
||||||||||||||||||||||||||||||||||||||||||
|||| @author Tanax
|||| @copyright 2008
||||||||||||||||||||||||||||||||||||||||||
**/

   
    include('pagination.php');
    $p = $_GET['p'];
    (isset($p)) ? $p : 1;
    $max = 10;
   
    // Create the object and set some basic values.
    $pagination = new pagination();
    $pagination->setMax($max)
   
    // Get the total results, and then calculate how many pages that becomes based on how many results per page.
    $totSql = "SELECT * FROM `table`";
    $totQuery = mysql_query($totSql) or die(mysql_error());
    $totResults = mysql_fetch_array($totQuery);
    $totPages = $pagination->getPages($totResults);
   
    // Set the current page, and get the first result on it.
    $first = $pagination->setPage($p);
   
    // Get the results of the current page.
    $exSql = "SELECT * FROM `table` LIMIT $first, $max";
    $exQuery = mysql_query($exSql) or die(mysql_error());
    $exResults = mysql_fetch_array($exQuery);
   
    // Echo out the current page, and the total amount of pages.
    $page = $pagination->getCurrentPage();
    echo 'Page: '.$page[0].' of '.$page[1];
   
    // Get previous page link, and check if it's valid.
    $prevLink = $p - 1;
    if($pagination->checkLink($prevLink)) {
       
        echo '<a href="example.php?page='.$prevLink.'">Previous Page</a>';
       
    }
   
    // Print all the pages
    foreach($totPages as $pageNumber) {
       
        if($pageNumber == $p) {
           
            // The markup for showing that this is the current page is currently <strong>
            echo '<a href="example.php?page='.$pageNumber.'"><strong>'.$pageNumber.'</strong></a>';
           
        }
       
        else {
           
            echo '<a href="example.php?page='.$pageNumber.'">'.$pageNumber.'</a>';
           
        }
       
    }
   
    // Get the next page link, and check if it's valid.
    $nextLink = $p + 1;
    if($pagination->checkLink($nextLink)) {
       
        echo '<a href="example.php?page='.$nextLink.'">Next Page</a>';
       
    }
   
    foreach($exResults as $news) {
       
        echo $news['news_title'];
        echo '<br /><br />';
       
    }
   

?>


I started working on an example to use this with a gallery, where the image files were in a image dir. But I got stuck.
More specificly I got stuck when I was trying to get the results within the first result on a page, and the last result on a page, based on the value of the max nr of results per page.
Maybe someone else can try to work it out?

Anyways, this is my pagination class.
Any comments?
__________________

Last edited by Tanax : 02-19-2009 at 11:51 PM.
Tanax is offline  
Reply With Quote
The Following 3 Users Say Thank You to Tanax For This Useful Post:
Alan @ CIT (02-08-2008), codefreek (10-31-2008), Y.P.Y (01-22-2009)
Old 02-08-2008, 06:32 PM   #2 (permalink)
Moderateur
RegEx Guru PHP Guru Top Contributor Advanced Programmer 
 
Salathe's Avatar
 
Join Date: Apr 2007
Posts: 1,393
Thanks: 5
Salathe is on a distinguished road
Default

An example which uses a directory of images is given below. The folder structure is such that we have an images folder and within that a thumbs folder. Filenames match between the folders representing a thumbnail and full-sized version of the same image. It's up to you to create the thumbnails however you like.

PHP Code:
<?php

include('pagination.php');
$p = (isset($_GET['p']) AND ctype_digit($_GET['p']) AND (int) $_GET['p'] > 0
   ? (int) 
$_GET['p'
   : 
1;
$max 10;

// Create the object and set some basic values.
$pagination = new pagination();
$pagination->setMax($max);

// Set up some directory variables 
$dir    dirname(__FILE__).'/images/';
$webdir 'images/';
$thdir  'images/thumbs/';

// Grab all of the images 
$images glob($dir.'*.jpg');

// Set up pagination variables
$totPages  $pagination->getPages($images);
$first     $pagination->setPage($p);
$page      $pagination->getCurrentPage();

// Only select the portion of the images array 
// that represents our page
$exResults array_slice($images$first$max);

// Make links spaced a bit
echo '
<style type="text/css">
a { margin: 0 2px 0 0; }
</style>'
;

// Start pagination display
printf('Page: %d of %d | '$page[0], $page[1]);
$pagination->checkLink($p 1) AND printf('<a href="?p=%d">Previous Page</a>'$p 1);
foreach (
$totPages as $pageNumber)
{
    
$tpl = ($pageNumber == $p)
         ? 
'<a href="?p=%1$d">%1$d</a>'
         
'<a href="?p=%1$d"><strong>%1$d</strong></a>';
    
printf($tpl$pageNumber);
}
$pagination->checkLink($p 1) AND printf('<a href="?p=%d">Next Page</a>'$p 1);

// Display current page of images 
echo '<hr><div class="images">';
foreach(
$exResults as $image)
{
    
$image str_replace($dir$webdir$image);
    
$thumb str_replace($webdir$thdir$image);
    
printf('<a href="%s" style="margin: 0 5px 5px 0;"><img src="%s" alt="%s"></a>',
           
$image$thumbbasename($image));
}
echo 
'</div>';
It's just a very quick example essentially ported over from the MySQL example posted above. Don't shoot me if it doesn't work!
Salathe is offline  
Reply With Quote
The Following 3 Users Say Thank You to Salathe For This Useful Post:
Alan @ CIT (02-08-2008), codefreek (10-31-2008), Tanax (02-08-2008)
Old 02-08-2008, 06:48 PM   #3 (permalink)
Alan @ CIT
Member of the Month
The Frequenter
Member of the Month Top Contributor 
 
Alan @ CIT's Avatar
 
Join Date: Apr 2005
Location: South UK
Posts: 483
Thanks: 51
Alan @ CIT is on a distinguished road
Default

Thanks guys, very handy

Alan
Send a message via MSN to Alan @ CIT
Alan @ CIT is offline  
Reply With Quote
Old 02-08-2008, 07:01 PM   #4 (permalink)
The Prestige
Upcoming Programmer Inquisitive 
 
Tanax's Avatar
 
Join Date: Sep 2007
Location: Sweden, Stockholm
Posts: 1,080
Thanks: 115
Tanax is on a distinguished road
Default

Quote:
Originally Posted by Salathe View Post
An example which uses a directory of images is given below. The folder structure is such that we have an images folder and within that a thumbs folder. Filenames match between the folders representing a thumbnail and full-sized version of the same image. It's up to you to create the thumbnails however you like.

PHP Code:
<?php

include('pagination.php');
$p = (isset($_GET['p']) AND ctype_digit($_GET['p']) AND (int) $_GET['p'] > 0
   ? (int) 
$_GET['p'
   : 
1;
$max 10;

// Create the object and set some basic values.
$pagination = new pagination();
$pagination->setMax($max);

// Set up some directory variables 
$dir    dirname(__FILE__).'/images/';
$webdir 'images/';
$thdir  'images/thumbs/';

// Grab all of the images 
$images glob($dir.'*.jpg');

// Set up pagination variables
$totPages  $pagination->getPages($images);
$first     $pagination->setPage($p);
$page      $pagination->getCurrentPage();

// Only select the portion of the images array 
// that represents our page
$exResults array_slice($images$first$max);

// Make links spaced a bit
echo '
<style type="text/css">
a { margin: 0 2px 0 0; }
</style>'
;

// Start pagination display
printf('Page: %d of %d | '$page[0], $page[1]);
$pagination->checkLink($p 1) AND printf('<a href="?p=%d">Previous Page</a>'$p 1);
foreach (
$totPages as $pageNumber)
{
    
$tpl = ($pageNumber == $p)
         ? 
'<a href="?p=%1$d">%1$d</a>'
         
'<a href="?p=%1$d"><strong>%1$d</strong></a>';
    
printf($tpl$pageNumber);
}
$pagination->checkLink($p 1) AND printf('<a href="?p=%d">Next Page</a>'$p 1);

// Display current page of images 
echo '<hr><div class="images">';
foreach(
$exResults as $image)
{
    
$image str_replace($dir$webdir$image);
    
$thumb str_replace($webdir$thdir$image);
    
printf('<a href="%s" style="margin: 0 5px 5px 0;"><img src="%s" alt="%s"></a>',
           
$image$thumbbasename($image));
}
echo 
'</div>';
It's just a very quick example essentially ported over from the MySQL example posted above. Don't shoot me if it doesn't work!
Thanks
And thanks for moving it

Well, I'm glad that it seems to work without MySQL aswell, because that was my intention, to not just limit the class to a MySQL based pagintion
__________________
Tanax is offline  
Reply With Quote
Old 02-11-2008, 03:17 PM   #5 (permalink)
The Contributor
 
Gibou's Avatar
 
Join Date: Nov 2007
Location: France, near Paris
Posts: 53
Thanks: 6
Gibou is on a distinguished road
Default

I just see a bad thing in your class. To use it, you have to keep your mysql connexion opened all the time your page is loading. It's not safe or optimized if the display of your current page is heavy/long :)

Whatever, indeed, it's useful :)
__________________
Wedus project's Website
Send a message via MSN to Gibou
Gibou is offline  
Reply With Quote
Old 10-31-2008, 04:02 PM   #6 (permalink)
The Wanderer
 
Join Date: Oct 2008
Posts: 9
Thanks: 0
kjarli is on a distinguished road
Default

Finally someone that doesn't mix up sql with pagination, good job!
kjarli is offline  
Reply With Quote
Old 10-31-2008, 05:15 PM   #7 (permalink)
Super Moderator
Inquisitive 
 
codefreek's Avatar
 
Join Date: Sep 2007
Location: Near you.
Posts: 791
Thanks: 241
codefreek is on a distinguished road
Default

nice work ;) learned some new things ;) ty!
codefreek is offline  
Reply With Quote
Old 10-31-2008, 08:47 PM   #8 (permalink)
The Prestige
Upcoming Programmer Inquisitive 
 
Tanax's Avatar
 
Join Date: Sep 2007
Location: Sweden, Stockholm
Posts: 1,080
Thanks: 115
Tanax is on a distinguished road
Default

Quote:
Originally Posted by Gibou View Post
I just see a bad thing in your class. To use it, you have to keep your mysql connexion opened all the time your page is loading. It's not safe or optimized if the display of your current page is heavy/long :)

Whatever, indeed, it's useful :)
Yea.. or well, not really. You can close it after each query, it's not like you have to use a cache or anything to keep it working.. or did I misunderstand what you meant?

Thank you

Quote:
Originally Posted by kjarli View Post
Finally someone that doesn't mix up sql with pagination, good job!
Thank you! I thought about that too, and I felt the urge that someone needed to do this.

Thanks again

Quote:
Originally Posted by codefreek View Post
nice work ;) learned some new things ;) ty!
Thank you! Glad you learned something
__________________
Tanax is offline  
Reply With Quote
Old 04-10-2009, 12:29 PM   #9 (permalink)
The Gregarious
 
allworknoplay's Avatar
 
Join Date: Feb 2009
Location: New York
Posts: 645
Thanks: 64
allworknoplay is on a distinguished road
Default

Quote:
Originally Posted by Tanax View Post
Yes, that's an if condition, btw, post in that thread if you have any more questions about that code since it's quite offtopic here.

And that class is quite old, it's not the best. But it definitely get the job done(I've recoded it on localhost so I got a better version though).

Every day we learn something new

That's my job, to learn something new, "almost" everyday, but more importantly absorb and understand.

I won't copy your code but I will use it as a reference along with websavvy's method, so don't worry if it's old. I will never learn OO if I just copy someone's code...

I gotta grind it out like everyone else here did one time!
allworknoplay is offline  
Reply With Quote
Old 04-10-2009, 04:05 PM   #10 (permalink)
The Prestige
Upcoming Programmer Inquisitive 
 
Tanax's Avatar
 
Join Date: Sep 2007
Location: Sweden, Stockholm
Posts: 1,080
Thanks: 115
Tanax is on a distinguished road
Default

Quote:
Originally Posted by allworknoplay View Post
That's my job, to learn something new, "almost" everyday, but more importantly absorb and understand.

I won't copy your code but I will use it as a reference along with websavvy's method, so don't worry if it's old. I will never learn OO if I just copy someone's code...

I gotta grind it out like everyone else here did one time!
Learning new things is always good!

Of course, it's good that you don't just copy paste, it's better to read it through and understand what everything does and why it's there.
__________________
Tanax is offline  
Reply With Quote
Old 04-18-2009, 03:44 AM   #11 (permalink)
The Gregarious
 
allworknoplay's Avatar
 
Join Date: Feb 2009
Location: New York
Posts: 645
Thanks: 64
allworknoplay is on a distinguished road
Default

Tanax, question about a piece of your code.....

What does it mean when you say "return false",
and you also say "return true", but what are you actually
returning?

Code:
public function checkLink($pagenr) {
            
            if($pagenr <= $this->totalPages && $pagenr >= 1) {
                
                return true;
                
            }
            
            return false;
            
        }
Thanks!!
allworknoplay is offline  
Reply With Quote
Old 04-18-2009, 07:33 AM   #12 (permalink)
The Frequenter
Zend Certified 
 
Join Date: Sep 2007
Location: Denmark
Posts: 352
Thanks: 8
Kalle is on a distinguished road
Default

Quote:
Originally Posted by allworknoplay View Post
Tanax, question about a piece of your code.....

What does it mean when you say "return false",
and you also say "return true", but what are you actually
returning?

Code:
public function checkLink($pagenr) {
            
            if($pagenr <= $this->totalPages && $pagenr >= 1) {
                
                return true;
                
            }
            
            return false;
            
        }
Thanks!!
It validates the page number and return a boolean value to check if the page number is valid:

PHP Code:
/* ... */

if($pagination->checkLink(1337))
{
    echo 
'Amazingly enough, 1337 is a valid page number';

__________________
Send a message via MSN to Kalle Send a message via Skype™ to Kalle
Kalle is offline  
Reply With Quote
Old 04-18-2009, 12:28 PM   #13 (permalink)
The Prestige
Upcoming Programmer Inquisitive 
 
Tanax's Avatar
 
Join Date: Sep 2007
Location: Sweden, Stockholm
Posts: 1,080
Thanks: 115
Tanax is on a distinguished road
Default

Like Kalle said, its' function is to validate if the page in the parameter is a valid page in the array of pages.

So I'm returning true if it exists in the array of pages, and false if it does not.
When I then check:
PHP Code:
if($pagination->checkLink(1337))
{
    echo 
'Amazingly enough, 1337 is a valid page number';

it actually checks:
PHP Code:
if($pagination->checkLink(1337) == true// <- meaning if it returned true
{
    echo 
'Amazingly enough, 1337 is a valid page number';
}
// we can run an else statement if we want to do something if it returned false:
else
{
    echo 
'Unfortunately, 1337 is not a valid page number';

__________________
Tanax is offline  
Reply With Quote
Old 04-18-2009, 02:06 PM   #14 (permalink)
The Gregarious
 
allworknoplay's Avatar
 
Join Date: Feb 2009
Location: New York
Posts: 645
Thanks: 64
allworknoplay is on a distinguished road
Default

Ahh I see now.

Thanks again Kalle/Tanax...

Since the return is "true".

Then it just returns the number that you are checking right?

so say 5 is a good number, then you get 5 back?

Since PHP is loosely typed, do you have to do "return false" if the number doesn't check out?

It sounds like good practice to do that anyways, but just wondering.
allworknoplay is offline  
Reply With Quote
Old 04-18-2009, 03:32 PM   #15 (permalink)
how quixotic are you?
 
ETbyrne's Avatar
 
Join Date: Dec 2007
Location: Lapeer, MI
Posts: 445
Thanks: 37
ETbyrne is on a distinguished road
Default

You should change getPages so that you can give it a int value OR an array of data. Like so:

PHP Code:
public function getPages($results) {
   
    if(
is_array($results)){
        
        
$this->totalResults count($results);
    
    }else{
    
        
$this->totalResults $results;
    
    }
    
    
$totalPages $this->totalResults $this->totalPerPage;
    
$this->totalPages ceil($totalPages);
   
    
$x 1;
    
$array = array();
   
    while(
$x <= $this->totalPages) {
       
        
$array[] = $x;
        
$x++;
       
    }
   
    return 
$array;
   

All in all a very useful class Tanax!
__________________
Dingo Web Systems > http://www.dingocode.com
My Website > http://www.evanbot.com
ETbyrne is offline  
Reply With Quote
Old 04-18-2009, 03:46 PM   #16 (permalink)
Moderateur
RegEx Guru PHP Guru Top Contributor Advanced Programmer 
 
Salathe's Avatar
 
Join Date: Apr 2007
Posts: 1,393
Thanks: 5
Salathe is on a distinguished road
Default

Quote:
Originally Posted by ETbyrne View Post
PHP Code:
    $x 1;
    
$array = array();
   
    while(
$x <= $this->totalPages) {
       
        
$array[] = $x;
        
$x++;
       
    }
   
    return 
$array
Maybe I'm missing something, but why not just use return range(1, $this->totalPages) in place of that entire chunk?
Salathe is offline  
Reply With Quote
Old 04-18-2009, 04:04 PM   #17 (permalink)
how quixotic are you?
 
ETbyrne's Avatar
 
Join Date: Dec 2007
Location: Lapeer, MI
Posts: 445
Thanks: 37
ETbyrne is on a distinguished road
Default

Quote:
Maybe I'm missing something, but why not just use return range(1, $this->totalPages) in place of that entire chunk?
Don't look at me!

Also, getCurrentPage() could be reduced to:

PHP Code:
public function getCurrentPage(){
    
    return array(
$this->currentPage,$this->totalPages);
    

__________________
Dingo Web Systems > http://www.dingocode.com
My Website > http://www.evanbot.com
ETbyrne is offline  
Reply With Quote
Old 04-18-2009, 04:42 PM   #18 (permalink)
The Frequenter
Zend Certified 
 
Join Date: Sep 2007
Location: Denmark
Posts: 352
Thanks: 8
Kalle is on a distinguished road
Default

Quote:
Originally Posted by allworknoplay View Post
Ahh I see now.

Thanks again Kalle/Tanax...

Since the return is "true".

Then it just returns the number that you are checking right?

so say 5 is a good number, then you get 5 back?

Since PHP is loosely typed, do you have to do "return false" if the number doesn't check out?

It sounds like good practice to do that anyways, but just wondering.
No thats just because the method is meant for an expression, and true/false are both booleans
__________________
Send a message via MSN to Kalle Send a message via Skype™ to Kalle
Kalle is offline  
Reply With Quote
The Following User Says Thank You to Kalle For This Useful Post:
allworknoplay (04-18-2009)
Old 04-18-2009, 06:37 PM   #19 (permalink)
The Prestige
Upcoming Programmer Inquisitive 
 
Tanax's Avatar
 
Join Date: Sep 2007
Location: Sweden, Stockholm
Posts: 1,080
Thanks: 115
Tanax is on a distinguished road
Default

Quote:
Originally Posted by allworknoplay View Post
Ahh I see now.

Thanks again Kalle/Tanax...

Since the return is "true".

Then it just returns the number that you are checking right?

so say 5 is a good number, then you get 5 back?

Since PHP is loosely typed, do you have to do "return false" if the number doesn't check out?

It sounds like good practice to do that anyways, but just wondering.
No, true and false are just booleans, and never any value such as for example an integer or a string.

Quote:
Originally Posted by ETbyrne View Post
You should change getPages so that you can give it a int value OR an array of data. Like so:

PHP Code:
public function getPages($results) {
   
    if(
is_array($results)){
        
        
$this->totalResults count($results);
    
    }else{
    
        
$this->totalResults $results;
    
    }
    
    
$totalPages $this->totalResults $this->totalPerPage;
    
$this->totalPages ceil($totalPages);
   
    
$x 1;
    
$array = array();
   
    while(
$x <= $this->totalPages) {
       
        
$array[] = $x;
        
$x++;
       
    }
   
    return 
$array;
   

All in all a very useful class Tanax!
Quote:
Originally Posted by ETbyrne View Post
Don't look at me!

Also, getCurrentPage() could be reduced to:

PHP Code:
public function getCurrentPage(){
    
    return array(
$this->currentPage,$this->totalPages);
    

Like I said before, this is a really old piece of code, and I've actually rewritten this whole class, but thanks for the tips! I'll check my new class and see how I solved it in that one.
__________________
Tanax is offline  
Reply With Quote
Old 04-18-2009, 06:49 PM   #20 (permalink)
how quixotic are you?
 
ETbyrne's Avatar
 
Join Date: Dec 2007
Location: Lapeer, MI
Posts: 445
Thanks: 37
ETbyrne is on a distinguished road
Default

Quote:
Like I said before, this is a really old piece of code, and I've actually rewritten this whole class, but thanks for the tips! I'll check my new class and see how I solved it in that one.
Could you perhaps share the new one?
__________________
Dingo Web Systems > http://www.dingocode.com
My Website > http://www.evanbot.com
ETbyrne is offline  
Reply With Quote
Reply



Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are On


All times are GMT. The time now is 02:33 PM.

 
     

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2013, Jelsoft Enterprises Ltd.
Search Engine Optimization by vBSEO 3.1.0
Inactive Reminders By Icora Web Design