Fizz Buzz and Fibbonacci combined
May 11th, 2017I had a job interview recently and I was asked to do FizzBuzz.
The code is very simple to write, however I wanted to see if I could make it easily accept any series of numbers and easily alter its output.
The code I came up within an hour or so is below, but its use comes down to the simple one liners;
// Simple demo of numbers 0-5, default behaviour, no args foreach(NumbersDirector::create() AS $number) echo "$number,"; |
Output: 0, 1, 2, 3, 4, 5
// Fizz Buzz from 1-100 using fizzbuzz factory builder foreach(NumbersDirector::create(new NumericalSeries(array('start'=>1,'end'=>100)),new FizzBuzzBuilder()) AS $number) echo "$number,"; |
Output: 1,2,Fizz,4,Buzz,Fizz,7,8,Fizz,Buzz,11,Fizz,13,14,FizzBuzz,16,17,Fizz,19,Buzz,Fizz,22,23,Fizz,Buzz,26,Fizz,28,29,FizzBuzz,………………..
// Fibo from 0-2000 foreach(NumbersDirector::create(new FibbonacciSeries(array('end'=>2000))) AS $number) echo "$number,"; |
Output: 1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,
// Fibo from 2000-50000000 using fizzbuzz factory builder foreach(NumbersDirector::create(new FibbonacciSeries(array('start'=>2000,'end'=>50000000)),new FizzBuzzBuilder()) AS $number) echo "$number,"; |
Output: Fizz,5998,9997,Buzz,Fizz,41987,67979,109966,FizzBuzz,287911,465856,753767,Fizz,Buzz,3193013,5166403,Fizz,13525819,Buzz,35411054,Fizz,
As you can see the last one is passing in a custom series and modifying the output to FizzBuzz.
It uses several patterns, ReadOnly, Factory, Director, Builder and returns an iterable instance, just a coding exercise really.
Full code below if anyone would care to see:
<?php /* Traits */ Trait Iterable{ protected $_position; protected $_data; public function rewind() { $this->_position = 0; } public function current() { if ($this->valid()){ $data = $this->getData(); return $data[$this->_position]; } } public function key() { return $this->_position; } public function next() { ++$this->_position; } public function valid() { $data = $this->getData(); return isset($data[$this->_position]); } protected function setData(array $data = null){ $this->_data = $data; $this->rewind(); return $this; } public function getData(){ if (!isset($this->_data)){ $this->setData(); } return $this->_data; } public function getStart(){ if (isset($this->_data[0])){ return $this->_data[0]; } return null; } public function getEnd(){ $count = count($this->_data)-1; if ($count>0){ return $this->_data[$count]; } return null; } /* * Required by countable */ public function count(){ return count($this->_data); } } trait Readonly{ public function __construct(array $options = null) { if (is_array($options)) { $this->setOptions($options); } } public function __set($name, $value) { $method = 'set' . $name; if (method_exists($this, $method)) { $this->$method($value); return $this; } throw new \Exception('"'.$name.'" is an invalid property of '.__CLASS__.' assignment of "'.$value.'" failed!'); } public function __get($name) { $method = 'get' . $name; if (method_exists($this, $method)) { return $this->$method(); } throw new \Exception('Invalid '.__CLASS__.' property '.$name); } public function setOptions(array $options) { $methods = get_class_methods($this); foreach ($options as $key => $value) { $method = 'set' . ucfirst($key); if (in_array($method, $methods)) { $this->$method($value); } } return $this; } } /* Interfaces */ Interface SeriesInterface { public function generate(); } Interface BuildersInterface { public function build($entry); } Interface ValueInterface { public function getValue(); public function setValue(); public function __toString(); } /* Abstract classes */ abstract class Series implements SeriesInterface { use Readonly; protected $_start; protected $_end; public function setStart($start = 0){ $this->_start = $start; return $this; } public function getStart(){ if (!isset($this->_start)){ $this->setStart(); } return $this->_start; } public function setEnd($end = 5){ $this->_end = $end; return $this; } public function getEnd(){ if (!isset($this->_end)){ $this->setEnd(); } return $this->_end; } abstract function generate(); } abstract class Value implements ValueInterface { use Readonly; protected $_val; public function getValue(){ if (!isset($this->_val)){ $this->setValue(); } return $this->_val; } public function setValue($val = 0){ if (!is_int($val)){ throw new Exception('Expected integer as value got '.getType($val)); // would normally be the signature } $this->_val = $val; return $this; } abstract public function __toString(); } /* Classes */ class NumbersDirector implements Iterator{ use Iterable; protected function __construct(){} public function create(SeriesInterface $series = null, BuildersInterface $builder = null){ if (is_null($series)){ $series = new NumericalSeries(); } if (is_null($builder)){ $builder = new NumericalBuilder(); } $stack = array(); foreach($series->generate() AS $entry){ $stack[]= $builder->build($entry); } $me = new self(); $me->setData($stack); return $me; } } class NumericalSeries extends Series { public function generate(){ $series = array(); for ($x=$this->start; $x<($this->end + 1); $x++){ $series[] = $x; } return $series; } } class FibbonacciSeries extends Series { /** * Fibonacci using Binet's formula * @link http://mathworld.wolfram.com/BinetsFibonacciNumberFormula.html */ public function binet($n) { $phi = (1 + sqrt(5)) / 2; return (pow($phi, $n) - pow(1 - $phi, $n)) / sqrt(5); } public function setStart($start = 1){ $this->_start = $start; return $this; } public function setEnd($end = 55){ $this->_end = $end; return $this; } public function generate(){ $series = array(); $fib = [$this->start,$this->start - 1]; $next = 0; while($next < $this->end) { $next = array_sum($fib); array_shift($fib); array_push($fib,$next); $series[]=$next; } return $series; } } class NumericalBuilder implements BuildersInterface { public function Build($entry){ $number = new Number(); $number->value = $entry; return $number; } } class FizzBuzzBuilder implements BuildersInterface { public function Build($entry){ /* Factory method */ $class = ""; if ($entry%3==0){ $class.= 'Fizz'; } if ($entry%5==0){ $class.= 'Buzz'; } if (!class_exists($class) || !isset(class_implements($class)['ValueInterface'])){ $class = "Number"; } $number = new $class(); $number->value = $entry; return $number; } } class Number extends Value { public function __toString(){ return (string) $this->getValue(); } } class Buzz extends Value { public function __toString(){ return (string) 'Buzz'; } } class Fizz extends Value { public function __toString(){ return (string) 'Fizz'; } } class FizzBuzz extends Value { public function __toString(){ return (string) 'FizzBuzz'; } } // Simple demo of numbers 0-5, default behaviour, no args foreach(NumbersDirector::create() AS $number) echo "$number,"; echo "<hr />"; // Fizz Buzz from 1-100 using fizzbuzz factory builder foreach(NumbersDirector::create(new NumericalSeries(array('start'=>1,'end'=>100)),new FizzBuzzBuilder()) AS $number) echo "$number,"; echo "<hr />"; // Fibo from 0-2000 foreach(NumbersDirector::create(new FibbonacciSeries(array('end'=>2000))) AS $number) echo "$number,"; echo "<hr />"; // Fibo from 2000-50000000 using fizzbuzz factory builder foreach(NumbersDirector::create(new FibbonacciSeries(array('start'=>2000,'end'=>50000000)),new FizzBuzzBuilder()) AS $number) echo "$number,"; |
WordPress, concrete5, ZF2, PHP, mysql database dump
March 22nd, 2017Here is a very simple script that once uploaded locks onto various architectures and extracts the database using the captured details.
For example, wordpress and concrete5 have been detected here:
When nothing is detected it will still offer the manual entering of the username and password.
It currently supports Concrete5 and WordPress, but I can easily upgrade it to support many more like ZF1, ZF2, Magento, Drupal, Joomla, etc, if there is any interest.
This is handy when you need to quickly copy a database, you simply upload this script to the doc_root and the script will attempt to handle the rest for you.
The script comes with password protection to prevent unauthorised usage, or it will not just be you downloading the database.
The script stores the username and password of those authorised to use it within the code, the password can be entered in plain text, or you can define your own algorithm and store the password encrypted in this way, within the code.
The script can be downloaded from github here and the full source code is below.
/* Adrian Callaghan 21 Mar 2017 Very lightweight database dumper that automatically locks access details provided by other PHP frameworks ****** How to use ****** Enter your usernames and passwords into the constructor in either plain text (insecure if read by someone else) or encypted Example 1: allowing bob access with the password 1234 and joe access with password 5678 - no encryption dbDump::Init(array( 'users' => array( array('username'=>'bob','password'=>'1234'), array('username'=>'joe','password'=>'5678'), ))); Example 2: allowing bob access with the password 1234 and joe access with password 5678 - with encyption method and hashed passwords that must match the method result dbDump::Init(array( 'users' => array( array('username'=>'bob','password'=>'81dc9bdb52d04dc20036dbd8313ed055'), array('username'=>'joe','password'=>'674f3c2c1a8a6f90461e8a66fb5550ba'), ), 'passwordEncryption'=>function($pass){ return md5($pass); } )); */ /****************** Constructor *******************/ dbDump::Init(array( 'users' => array( array('username'=>'bob','password'=>'81dc9bdb52d04dc20036dbd8313ed055'), array('username'=>'joe','password'=>'674f3c2c1a8a6f90461e8a66fb5550ba'), ), 'passwordEncryption'=>function($pass){ return md5($pass); } )); /****************** Class starts... *******************/ final class dbDump{ const APP_STATE = 'state'; const APP_AUTH = 'MY_TOKEN'; const APP_SALT = 'MY_SALT'; private $_users; private $_passwordEncryption; protected function destroySession(){ $this->initSession(); session_destroy(); return $this; } protected function initSession(){ if (session_status() == PHP_SESSION_NONE) { session_start(); } return $this; } protected function getSession($object = true){ $this->initSession(); return $object ? (object) $_SESSION : $_SESSION; } protected function getSessionVar($var = ''){ return isset($this->session->{$var}) ? $this->session->{$var} : false; } protected function setSessionVar($key, $val){ $session = $this->session; $session->{$key} = $val; $this->session = (array) $session; } protected function setSession(array $values){ $this->initSession(); $_SESSION = $values; return $this; } protected function getGet($object = true){ return $object ? (object) $_GET : $_GET; } protected function getGetVar($var = ''){ return isset($this->post->{$var}) ? $this->post->{$var} : false; } protected function isPost(){ return empty($this->getPost(false)) ? false : true; } protected function getPost($object = true){ return $object ? (object) $_POST : $_POST; } protected function getPostVar($var = ''){ return isset($this->post->{$var}) ? $this->post->{$var} : false; } protected function getState(){ return $this->getSessionVar(self::APP_STATE); } protected function setState($state = ''){ $this->setSessionVar(self::APP_STATE, $state); return $this; } protected function is_constant($token) { return $token == T_CONSTANT_ENCAPSED_STRING || $token == T_STRING || $token == T_LNUMBER || $token == T_DNUMBER; } protected function strip($value) { return preg_replace('!^([\'"])(.*)\1$!', '$2', $value); } protected function getDefinitions($php){ $defines = array(); $state = 0; $key = ''; $value = ''; $tokens = token_get_all($php); $token = reset($tokens); while ($token) { // dump($state, $token); if (is_array($token)) { if ($token[0] == T_WHITESPACE || $token[0] == T_COMMENT || $token[0] == T_DOC_COMMENT) { // do nothing } else if ($token[0] == T_STRING && strtolower($token[1]) == 'define') { $state = 1; } else if ($state == 2 && $this->is_constant($token[0])) { $key = $token[1]; $state = 3; } else if ($state == 4 && $this->is_constant($token[0])) { $value = $token[1]; $state = 5; } } else { $symbol = trim($token); if ($symbol == '(' && $state == 1) { $state = 2; } else if ($symbol == ',' && $state == 3) { $state = 4; } else if ($symbol == ')' && $state == 5) { $defines[$this->strip($key)] = $this->strip($value); $state = 0; } } $token = next($tokens); } return $defines; } protected function generateDbForm($fields = null, $forceDisplay = false){ if ($fields===null && !$forceDisplay){ return; } $form = "<form method='post' name='dbForm' class='form-horizontal col-vert-20' role='form'>"; $form.= "<div class='form-group'><label class='col-sm-2 control-label'>Host</label><div class='col-sm-10'><input name='host' type='text' placeholder='Enter host name' required='required' class='form-control' value='".(isset($fields->host) ? $fields->host : '')."'></div></div>"; $form.= "<div class='form-group'><label class='col-sm-2 control-label'>Table</label><div class='col-sm-10'><input name='table' type='text' placeholder='Enter table name' required='required' class='form-control' value='".(isset($fields->table) ? $fields->table : '')."'></div></div>"; $form.= "<div class='form-group'><label class='col-sm-2 control-label'>User</label><div class='col-sm-10'><input name='username' type='text' placeholder='Enter user name' required='required' class='form-control' value='".(isset($fields->username) ? $fields->username : '')."'></div></div>"; $form.= "<div class='form-group'><label class='col-sm-2 control-label'>Password</label><div class='col-sm-10'><input name='password' type='text' placeholder='Enter password' required='required' class='form-control' value='".(isset($fields->password) ? $fields->password : '')."'></div></div>"; $form.= "<div class='form-group'><div class='col-sm-12'><button type='submit' name='button-submit' class='btn btn-success btn-lg pull-right' value=''> <span class='glyphicon glyphicon-download'></span> Download</button></div></div>"; $form.= "</form>"; return $form; } protected function getPlatform($options){ $error = '<div class="pull-right text-warning"><span class="glyphicon glyphicon-warning-sign status"></span></div>'; $notFound = '<div class="pull-right text-danger"><span class="glyphicon glyphicon-remove status"></span></div>'; $found = '<div class="pull-right text-success"><span class="glyphicon glyphicon-ok status"></span></div>'; $out = '<li class="list-group-item">'; $logoW = isset($options->logoW) ? $options->logoW : '213'; $logoH = isset($options->logoH) ? $options->logoH : '120'; $logo = "<img src='".(isset($options->logo) ? $options->logo : "holder.js/{$logoW}x{$logoH}")."' height='{$logoH}' width='{$logoW}' alt='platform logo' longdesc='logo representing the platform option' />"; $dbArgs = new StdClass(); if (isset($options->conf) && file_exists($options->conf)){ if (($fp = fopen($options->conf, "r"))!==false){ $params = (object) $this->getDefinitions(stream_get_contents($fp)); foreach(array('host','table','username','password') AS $option){ $dbArgs->{$option} = isset($options->{$option}) && isset($params->{$options->{$option}}) ? $params->{$options->{$option}} : ''; } $out .= $found.$logo.$this->generateDbForm($dbArgs, isset($options->force) ? $options->force : null); fclose($fp); } else { $out .= $error.$logo.$this->generateDbForm(null, isset($options->force) ? $options->force : null); } } else { $out .= $notFound.$logo.$this->generateDbForm(null, isset($options->force) ? $options->force : null); } $out.= '</li>'; return $out; } protected function getPlatforms(){ $out = '<ul class="list-group">'; $out .= $this->getPlatform((object) array( 'conf' => 'wp-config.php', 'logo' => 'https://s.w.org/about/images/logos/wordpress-logo-32-blue.png', 'logoW' => '32', 'logoH' => '32', 'host' => 'DB_HOST', 'table' => 'DB_NAME', 'username' => 'DB_USER', 'password' => 'DB_PASSWORD' )); $out .= $this->getPlatform((object) array( 'conf' => 'config/site.php', 'logo' => 'https://www.concrete5.org/files/3613/5517/8150/concrete5_Wordmark_200x37.png', 'logoW' => '200', 'logoH' => '37', 'host' => 'DB_SERVER', 'table' => 'DB_DATABASE', 'username' => 'DB_USERNAME', 'password' => 'DB_PASSWORD' )); /*$out .= $this->getPlatform((object) array( 'conf' => 'config/autoload/local.php', 'logo' => 'http://clloh.com/wp-content/uploads/2015/08/zf2-logo-128x128.png', 'logoW' => '128', 'logoH' => '128', 'host' => 'DB_SERVER', 'table' => 'DB_DATABASE', 'username' => 'DB_USERNAME', 'password' => 'DB_PASSWORD' ));*/ $out .= $this->getPlatform((object) array( 'logo' => 'https://www.mysql.com/common/logos/logo-mysql-170x115.png', 'logoW' => '170', 'logoH' => '115', 'force' => true, )); return $out.'</ul>'; } protected function getHeader(){ return '<html><head><title>Restricted area</title><link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"><script src="https://code.jquery.com/jquery-3.2.1.js" integrity="sha256-DZAnKJ/6XZ9si04Hgrsxu/8s717jcIzLy3oi35EouyE=" crossorigin="anonymous"></script><script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/holder/2.9.4/holder.js"></script><META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW"><style>.col-vert-20{margin-top:20px;}.col-vert-100{margin-top:100px;}.status{font-size:30px;}</style></head><body><div class="container-fluid"><div class="row">'; } protected function getFooter(){ $footer = '</div></div>'; if ($this->authentication) { $footer .= '<div class="modal fade" id="confirmationModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel"> <div class="modal-dialog modal-sm" role="document"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> <h4 class="modal-title" id="myModalLabel"><span class="glyphicon glyphicon-lock"></span> Log out request.</h4> </div> <div class="modal-body"> Are you sure you wish to logout '.$this->authentication->username.'? </div> <div class="modal-footer"> <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button> <a type="button" class="btn btn-primary" href="?logout=true">Confirm</a> </div> </div> </div> </div> </body></html>'; } return $footer; } protected function loginForm($inErrorState = false){ return '<div class="col-md-6 well well-large col-md-offset-3 col-vert-100"><h3><span class="glyphicon glyphicon-lock"></span> '.($inErrorState ? 'Access Denied' : 'Secure Area' ).'</h3><form method="post" name="login" class="form-horizontal col-vert-20" role="form"><div class="form-group '.($inErrorState ? 'has-error' : '').'"><label class="col-sm-2 control-label">Username</label><div class=" col-sm-10"><input name="username" type="text" placeholder="Enter username" required="required" class="form-control" value="">'.($inErrorState ? '<ul class="help-block"><li>Invalid username & password combination</li></ul>' : '').'</div></div><div class="form-group '.($inErrorState ? 'has-error' : '').'"><label class="col-sm-2 control-label">Password</label><div class=" col-sm-10"><input name="password" type="password" required="required" placeholder="Password" class="form-control" value="">'.($inErrorState ? '<ul class="help-block"><li>Invalid username & password combination</li></ul>' : '').'</div></div><div class="form-group "><div class=" col-sm-10 col-sm-offset-2"><button type="submit" name="button-submit" class="btn btn-default" value="">Login</button></div></div></form></div>'; } protected function getDownloadOptions($inErrorState = false){ return '<div class="col-md-6 col-md-offset-3 col-vert-100"><div class="panel panel-primary"><div class="panel-heading"><span class="glyphicon glyphicon-wrench"></span> Export options<button class="pull-right btn btn-danger btn-xs" data-toggle="modal" data-target="#confirmationModal">'.$this->authentication->username.' <span class="glyphicon glyphicon-remove-circle"></span></button></div>'.$this->platforms.'</div></div>'; } protected function setUsers(array $users = null){ if ($users==null){ $users = array(); } foreach($users AS $key=>$user){ $users[$key] = (object) $user; } $this->_users = (object) $users; return $this; } protected function getUsers(){ if (!isset($this->_users)){ $this->setUsers(); } return $this->_users; } protected function setPasswordEncryption($function = null){ if ($function==null){ $function = function($val){return $val;}; } $this->_passwordEncryption = $function; return $this; } protected function getPasswordEncryption(){ if (!isset($this->_passwordEncryption)){ $this->setPasswordEncryption(); } return $this->_passwordEncryption; } protected function PasswordEncrypt($pass = ''){ $encryptor = $this->getPasswordEncryption(); return call_user_func($encryptor, $pass); } protected function generateUserToken(\StdClass $user){ return md5((isset($user->username) ? $user->username : uniqid()).self::APP_SALT.(isset($user->password) ? $user->password : uniqid())); } protected function setAuthentication(\StdClass $user){ $this->setSessionVar(self::APP_AUTH, $this->generateUserToken($user)); return $this; } protected function getAuthentication(){ $token = $this->getSessionVar(self::APP_AUTH); foreach($this->users AS $validUser){ $validToken = $this->generateUserToken($validUser); if ($token==$validToken){ return $validUser; } } } protected function authenticateUser(\StdClass $user){ foreach($this->users AS $validUser){ if ($user->username==$validUser->username && $this->passwordEncrypt($user->password)==$validUser->password){ $this->authentication = $validUser; $this->state = 'options'; return $this->authentication; } } } public static function Init(array $options = array()){ static $inst = null; if ($inst === null) { $inst = new dbDump($options); } if (isset($inst->get->logout)){ $inst->destroySession(); header('Location: '.$_SERVER['PHP_SELF']); die; } if (!$inst->authentication){ $inst->state = null; } switch($inst->state){ case 'options': //var_dump($inst->authentication); if ($inst->isPost()){ $hostname = $inst->getPostVar('host'); $table = $inst->getPostVar('table'); $username = $inst->getPostVar('username'); $password = $inst->getPostVar('password'); $command = "mysqldump --add-drop-table --host=$hostname --user=$username --password=$password $table"; header('Content-Description: File Transfer'); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename='.basename($table."_".date("Y-m-d_H-i-s").".sql")); system($command); exit(); } else { echo $inst->header; echo $inst->downloadOptions; echo $inst->footer; } break; default: if ($inst->isPost()){ if ($inst->authenticateUser($inst->post)){ $inst->state = 'options'; header("Refresh:0"); die; } else { echo $inst->header; echo $inst->loginForm(true); echo $inst->footer; } } else { echo $inst->header; echo $inst->loginForm(); echo $inst->footer; } break; } } public function __set($name, $value) { $method = 'set' . $name; if (method_exists($this, $method)) { $this->$method($value); return $this; } throw new \Exception('"'.$name.'" is an invalid property of '.__CLASS__.' assignment failed!'); } public function __get($name) { $method = 'get' . $name; if (method_exists($this, $method)) { return $this->$method(); } throw new \Exception('Invalid '.__CLASS__.' property '.$name); } public function setOptions(array $options) { $methods = get_class_methods($this); foreach ($options as $key => $value) { $method = 'set' . ucfirst($key); if (in_array($method, $methods)) { $this->$method($value); } } return $this; } private function __construct(array $options = null) { // env set_time_limit(20); if (is_array($options)) { $this->setOptions($options); } } private function __clone(){} } |
How to create a ZF2 nested bootstrap dropdown menu
December 15th, 2016To create a nested menu in ZF2 you will need to tell the ZF2 navigation service to use a partial containing code that creates new markup.
Here is a step by step example of how to output a nested menu in bootstraps markup in ZF2.
First you need to tell the navigation service to use a partial to render the menu, find where the navigation is called, typically this is the layout file found in
/module/Application/view/layout/layout.phtml |
And looks something like:
echo $this->navigation('navigation')->menu(); |
To tell ZF2 navigation to use the partial myfile.phtml you simple append to the request setPartial and pass an array of arguments, the first argument contains the name of the partial file and the second the name of the module for example the first argument is ‘myfile.phtml’ and the second ‘default’ which is module Application.
echo $this->navigation('navigation')->menu()->setPartial(array('myfile.phtml','default')); |
Note you would normally organise partials into their own folder to do this just prepend the folder name to the partial name for example ‘->setPartial(array(‘partial/myfile.phtml’,’default’)’
The ul class needs to be declared as ‘nav nav-bar’ which is used by bootstrap. Simply append this to the end as ->setUlClass(‘nav navbar-nav’) so your complete code should now look like
echo $this->navigation('navigation')->menu()->setPartial(array('myfile.phtml','default'))->setUlClass('nav navbar-nav'); |
Now create the partial file for the navigation service to use:
/module/Application/view/myfile.phtml |
The partial will be used to render the navigation and this is where new logic to display the mark up should be contained.
The following example code will create a nested menu, with the correct mark up and classes that bootstrap recognises, add this to the partial created above.
<ul class="<?php echo $this->navigation()->menu()->getUlClass(); ?>"> <?php call_user_func($selfRef = function($pages, $navigation, $level = 0, $id='lvl-%d-pos-%d') use (&$selfRef) { foreach ($pages as $pos=>$page){ $page->setClass('nav-header'); $liClasses = array(); $page->id = sprintf($id, $level, $pos); /* manually check for ACL conditions */ if(!$page->isVisible() || !$navigation->accept($page)) { continue; } switch($level){ case '0': if ($page->isActive()){ $liClasses[] = 'active'; } if($page->hasPages()){ $liClasses[] = 'dropdown'; $page->setClass('dropdown-toggle'); $page->setLabel($page->getLabel().' <span class="caret"></span>'); } break; default: if($page->hasPages()){ $liClasses[] = 'dropdown-submenu'; } break; } ?> <li id="<?php echo $page->id; ?>" <?php echo !empty($liClasses) ? 'class="'.implode(' ',$liClasses).'"' : ''; ?>> <a class="<?php echo $page->getClass(); ?>" href="<?php echo $page->getHref(); ?>" <?php if ($page->hasPages()) echo 'data-toggle="dropdown"'; ?>> <?php echo $page->getLabel(); ?> </a> <?php if ($page->hasPages()) : ?> <ul class="dropdown-menu" role="menu"> <?php $selfRef($page->getPages(), $navigation, $level+1, $id); // recursion ?> </ul> <?php endif; ?> </li> <?php } }, $this->container, $this->navigation() ); ?> </ul> |
The above code contains a simple function that loops through the pages checking if the current iteration has children or not.
If the iteration does not, a page link is rendered with the correct classes for that level. If the iteration does contain children additional markup is created the function then makes a recursive call to itself to render those pages when the call returns it generates closing markup.
When generating the markup the loop is aware of how deep within the menu structure it currently is. By being aware of the levels and positions CSS classes have been able to be applied at specific levels in addition to this extra markup such as carets has been injected into the top layer, indicating the presence of a drop down menu where present.
Happy coding!
1,143 viewsZend framework 2 DomPdf No block-level parent found. Not good. error message
July 25th, 2016This error can be caused by many things, but the most notable is when attempting to reuse DomPdf to render more than one PDF.
This is to do with the way the service manager passes a single instance, and DOMPDF does not clean up after itself properly.
Attempting code like:
$html = $this->getServiceLocator()->get('viewpdfrenderer')->getHtmlRenderer()->render($pdf); $eng = $this->getServiceLocator()->get('viewpdfrenderer')->getEngine(); $eng->load_html($html); $eng->render(); |
Will fail, however despite the answer being very hard to find online the solution is rather simple, simply use the html renderer but request dompdf from the service manager, below I will render multiple PDF’s with different views, save them as files ready to be attached to an email.
// first PDF $pdf = new PdfModel(); $pdf->setTerminal(true); $pdf->setTemplate('template1'); $pdf->setVariables(array( 'foo' => 'bar' )); $html = $this->getServiceLocator()->get('viewpdfrenderer')->getHtmlRenderer()->render($pdf); $dompdf = $this->getServiceLocator()->get('dompdf'); // get fresh instance everytime $dompdf->load_html($html); $dompdf->render(); file_put_contents('file1.pdf', $dompdf->output()); // second pdf $pdf = new PdfModel(); $pdf->setTerminal(true); $pdf->setTemplate('template2'); $pdf->setVariables(array( 'foo' => 'bar' )); $html = $this->getServiceLocator()->get('viewpdfrenderer')->getHtmlRenderer()->render($pdf); $dompdf = $this->getServiceLocator()->get('dompdf'); // get fresh instance everytime $dompdf->load_html($html); $dompdf->render(); file_put_contents('file2.pdf', $dompdf->output()); |
Happy coding!
2,953 viewspassing variables to get_template part in wordpress
July 23rd, 2015When including part of a theme using the wordpress function get_template_part(), you will notice the included template will not have access to any variables previously defined, this is because they are now out of scope.
For example the template foo.php will not have access to $a:
$a =1; get_template_part('foo');
There are two main work a rounds:
1. Global Technique
$a=1; get_template_part('foo');
in foo.php
global $a;
By adding Global $a; into the template foo.php will allow the template to access the variable $a
2. Locate and include
$a=1; include(locate_template('foo.php'));
By using locate_template to generate a path and php’s include it is possible to include the template as though it was a block of code in line with the existing code thus consequently has access to the current scope.
Both of which I am not keen on, the global fix (1) downside is that you will need to declare global on each and every variable you wish to use, and the use of global is bad.
The second, Locate and include (2) downside is that should the template not exist a warning will be displayed, this is because a check before hand is not being performed, this leads to more code to check for the files existence or suppress the error, furthermore the code included has the potential to modify any variables in the existing scope.
My approach is to use an extra function added to the themes functions.php file
$a=1; get_partial('foo',array('a'=>$a));
The function behaves in the same way as get_template_part() but performs all the correct checks before hand thus stopping any errors, then assigns the variables to the scope, includes the template, and finally returns a success of true or false.
The function to add is below (simply copy and paste into functions.php)
function get_partial($template, array $args = array()){ $template = locate_template(preg_replace("/(.+)\.php$/", "$1", $template).'.php'); if (file_exists($template)){ global $posts, $post, $wp_did_header, $wp_query, $wp_rewrite, $wpdb, $wp_version, $wp, $id, $comment, $user_ID; extract($args); include($template); return true; } }
Happy coding!
2,301 viewsScript to monitor internet connection
April 21st, 2015The internet often drops at work, so to compile a csv report of the outage time and for how long I wrote this script, which maybe of use to someone.
Simply create the script, with execution rights (sudo chmod a+x) and call the script with the first argument as a resource e.g google and the second argument as the output filename e.g ./monitor.sh google.co.uk myFile
Code below:
#!/bin/bash resource=$1 outfile=$2 dwnTmp=".dwnLock"; upTmp=".upLock"; # NEED A RESOURCE if [[ -z "$resource" ]]; then echo "You must define a resource as the first parameter e.g google.co.uk"; exit; fi #### # NEED A FILENAME if [[ -z "$outfile" ]]; then echo "You must define an output file as the second parameter"; exit; fi #### ### REMOVE LOCKS if [ -f "$dwnTmp" ]; then rm $dwnTmp; fi if [ -f "$upTmp" ]; then rm $upTmp; fi ################# echo "Logging connectivity of http://$resource/ to $outfile.csv CTRL+C to exit" # LOOP count=0 while [ 1=1 ]; do let count+=1 time=$(date +"%T") timeSec=$(date +%s) duration=0 # log wget -q --tries=1 --timeout=10 --spider "http://$resource/" if [[ $? -eq 0 ]]; then if [ ! -f "$upTmp" ]; then echo $timeSec > $upTmp; if [ -f "$dwnTmp" ]; then duration=$(($timeSec-`cat $dwnTmp`)) rm $dwnTmp; fi echo -ne CHECK $count: AVAIL '\r' echo "$count,$time,Up after being down for $(($duration / 60)) minutes and $(($duration % 60)) seconds" >> "$outfile.csv" fi else if [ ! -f "$dwnTmp" ]; then echo $timeSec > $dwnTmp; if [ -f "$upTmp" ]; then duration=$(($timeSec-`cat $upTmp`)) rm $upTmp; fi echo -ne CHECK $count: ERROR '\r' echo "$count,$time,Down after being up for $(($duration / 60)) minutes and $(($duration % 60)) seconds" >> "$outfile.csv" fi fi done #####2,310 views
Desktop tidy
December 3rd, 2014A cluttered desktop can be a problem, typically people use the desktop becuase they know the location of the files should they have downloaded them from somewhere, and need to reuse them quickly.
I have written a script that runs on Linux that cleans the desktop up on a daily basis, by moving all the files into a folder with an easily readable date name within an archive folder, so that your desktop remains clean and ordered, and you also do not lose your files.
The beauty of this is that you can configure your applications to point at the desktop, safe in the knowledge that all the clutter from today will be gone tomorrow, and placed neatly within a folder with the date so you can find it, when somebody says “I sent it to you Tuesday”
Just run the script below and it will automatically install, any clutter on the Desktop will archive to folder with yesterday’s date found within the Archive folder located on the Desktop.
You can download it here after unzipping, simply install using terminal with:
sudo chmod a+x desktop-tidy-install.sh sudo ./desktop-tidy-install.sh
The source code is below:
#!/bin/bash ######################## # # # Install script # For Desktop tidy, compat with any distro running Gnome 3, tested on fedora 20 # # Adrian callaghan 02-12-2014 # # ######################## ######################## # are you capable? if [[ $(/usr/bin/id -u) -ne 0 ]]; then echo "You must run this install script with super user privileges, use either sudo or become root with su -" exit; fi ######################## # enviroment user=$SUDO_USER; userhome="/home/$user"; scriptPath="/usr/local/bin/dt.sh"; autostartFile="dt.desktop"; desktop="$userhome/Desktop"; archive="$desktop/Archived"; ######################## # reinstall? if [ -f "$scriptPath" ] || [ -f "$autostartPath" ]; then echo "Removing previous install" if [ -f "$scriptPath" ]; then rm "$scriptPath" fi if [ -f "$autostartPath" ]; then rm "$autostartPath" fi fi ######################## # create archive folder if none exists, permissions follow later if [ ! -d "$archive" ]; then mkdir "$archive" fi ######################## # create script echo "#!/bin/bash" > $scriptPath; echo "archive=$archive" >> $scriptPath; echo "desktop=$desktop" >> $scriptPath; echo 'curr_archive="$archive/$(date +"%d-%B-%Y" -d "1 day ago")"; # if has not been archived yet, archive if [ ! -d "$curr_archive" ]; then mkdir "$curr_archive"; for file in $desktop/*; do if [ "$file" != "$archive" ]; then mv "$file" "$curr_archive" fi done fi ' >> $scriptPath; chmod 755 $scriptPath chown "$user:$user" $scriptPath ######################## # Add autostart if [ ! -d "$userhome/.config" ]; then mkdir "$userhome/.config" fi if [ ! -d "$userhome/.config/autostart" ]; then mkdir "$userhome/.config/autostart" fi echo "[Desktop Entry] Name=DeskTidy GenericName=Creates a daily desktop Comment=Allows the user to have a clean desktop daily and keep the all the files in an ordered fashion Exec=$scriptPath Terminal=true Type=Application X-GNOME-Autostart-enabled=true " > "$userhome/.config/autostart/$autostartFile"; chmod 755 "$userhome/.config/autostart/$autostartFile" chown "$user:$user" "$userhome/.config/autostart/$autostartFile" ######################### # Trigger script! # source "$scriptPath" chmod "1775" "-R" "$archive" chown "root:$user" "-R" "$archive" ######################### # FEEDBACK echo "!! Desktop Tidy successfully installed !!" echo "When you login a desktop will be created based on todays date" echo "Should you wish to retrieve an old file, archives can be found at $archive and are named by date"3,480 views
Zend framework 2 restful CMS
August 26th, 2014Based on ZF2 Skeleton and the Album tutorial, this ZF2 application extends this further by migrating fully to Doctrine2 and adding comments for Albums as an association.
The source code is on GitHub and below is how to set the example up
Dynamic modules
There is provision for Dynamic Modules & layouts, seperating the various layers.
Restful service using put/get/post & delete
The default module has routing setup with a top bar navigation, along with a GUI to demonstrate an AJAX RESTful service.
A RESTful service is in place, that is contained within its own module to serve the Albums and comments objects as Create, Update, Delete and List to the default application.
CMS module
A CMS module is in place that uses identity persistance and Zend Auth to authenticate the users, the login forms and validation are all handled.
The CMS users can be created and managed, the CMS also has independent Navigation from the default route, along with CRUD services for the albums & comments services.
Form validation & Flash Messages
Twitter bootstrap has been intergrated into the CMS, so Flash Messages and form validation are all displaying within a bootstrap enviroment.
HOSTING STRUCTURE
- Folder structure is /NAMEOFNEWPROJECT/public/
- Public is the Document-root for the server.
- Public should not be present prior to cloning from GIT, but setup in the host file
INITIAL SETUP
cd ../NAMEOFNEWPROJECT git clone https://github.com/adriancallaghan/cmszend2.git NAMEOFNEWPROJECT (will clone the project into the new project) cd NAMEOFNEWPROJECT mv config/autoload/examplelocal.php config/autoload/local.php nano config/autoload/local.php (set database credientials) php composer.phar self-update php composer.phar install ./vendor/bin/doctrine-module orm:schema-tool:create git remote rm origin git init git add * git commit -m "first commit" git remote add origin https://github.com/NEW-GIT-REPO-LOCATION.git git push -u origin master
UPDATE USING GIT/COMPOSER & DOCTRINE2
git pull php composer.phar install ./vendor/bin/doctrine-module orm:schema-tool:create./vendor/bin/doctrine-module orm:validate-schema
UPDATE DB IF NECESSARY (Caution it can cause the db to flush)
./vendor/bin/doctrine-module orm:schema-tool:update --force
Cms access
You need to add a username, active (true) boolean and (md5) password combination into the cmsuser table in order to be able to login to the cms. The password can be easily md5’d on the cmd line with something like
php -r 'echo md5("SOME PASS");'9,110 views
Bash script to create virtual hosting in Fedora
August 15th, 2014I am often needing to add a script to create virtual hosting, below is one I have written for fedora (that can be altered for any distro) that creates the necessary folder structure, vhost host entries and restarts Apache.
#!/bin/bash ################################# # SETTINGS LOCATIONS WWW_ROOT='/var/www' # no trailing slash DOC_ROOT='httd_docs' # no trailing slash # SETTINGS TEMPLATE FILE INDEX_MSG='HOSTING READY FOR <DN>' # <DN> WILL BE REPLACED WITH THE DOMAIN INDEX_FILE='index.html' # SETTINGS DIRECTORY PERMISSIONS DIR_OWNER='apache' DIR_GROUP='apache' DIR_MOD='770' # APACHE (FEDORA) APACHE_RELOAD='service httpd restart' # cmd to reload/restart apache APACHE_CONF_LOCATION='/etc/httpd/conf/httpd.conf' # no trailing slash APACHE_CONF_TEMPLATE=" # <FQDN> <VirtualHost *:80> ServerAdmin webmaster@<DN> ServerName <FQDN> ServerAlias <DN> DocumentRoot <DN_ROOT> ErrorLog /var/log/httpd/<DN_SLUG>-apache-error_log CustomLog /var/log/httpd/<DN_SLUG>-access_log common </VirtualHost>"; # <DN> WILL BE REPLACED WITH THE DOMAIN, FQDN WITH THE FQDN, DN_SLUG WITH THE DOMAIN SLUG AND DN_ROOT WITH DOCUMENT ROOT # HOSTS FILE HOSTS_FILE='/etc/hosts' ################################# ################################# # FUNCTIONS function notice() { echo "============================================================================================================" echo $1 echo "============================================================================================================" } function slugify(){ echo $* | sed 's/^dl-*//ig' | tr '[:punct:]' '-' | tr '[:upper:]' '[:lower:]' | tr -s '[:blank:]' '[\-*]' } ################################# ################################# # START if [[ $(/usr/bin/id -u) -ne 0 ]]; then notice "You must be root to run this script" else # GET DN AND FQDN notice "Please enter the domain name" read DN DN="${DN/#\www./}" FQDN="www.$DN" # BEGIN host $DN 2>&1 > /dev/null if [ ! $? -eq 0 ]; then echo "$DN is not valid domain" else DN_SLUG=$(slugify "$DN"); if [ -d "$WWW_ROOT/$DN_SLUG" ]; then notice "$DN already exists in $WWW_ROOT/$DN_SLUG" else # MAIN STRUCTURE mkdir "$WWW_ROOT/$DN_SLUG" mkdir "$WWW_ROOT/$DN_SLUG/$DOC_ROOT" echo "${INDEX_MSG//<DN>/$FQDN}" > "$WWW_ROOT/$DN_SLUG/$DOC_ROOT/$INDEX_FILE" # OWNERSHIP chown -R "$DIR_OWNER:$DIR_GROUP" "$WWW_ROOT/$DN_SLUG" chmod -R "$DIR_MOD" "$WWW_ROOT/$DN_SLUG" # QUICK LOOK UP LOCALLY HOSTS_FILE_BK="$(date +$HOSTS_FILE-%d%m%Y_%R.bak)"; cp "$HOSTS_FILE" "$HOSTS_FILE_BK"; if [ ! -f "$HOSTS_FILE_BK" ]; then notice "Failed to create host file backup at $HOSTS_FILE_BK" else echo "# ${FQDN}" >> "$HOSTS_FILE"; echo 127.0.0.1 ${DN} >> "$HOSTS_FILE"; echo 127.0.0.1 ${FQDN} >> ""$HOSTS_FILE""; # APACHE ENTRIES APACHE_TEMPLATE="${APACHE_CONF_TEMPLATE//<DN>/$DN}"; APACHE_TEMPLATE="${APACHE_TEMPLATE//<FQDN>/$FQDN}"; APACHE_TEMPLATE="${APACHE_TEMPLATE//<DN_ROOT>/$WWW_ROOT/$DN_SLUG/$DOC_ROOT/}"; APACHE_TEMPLATE="${APACHE_TEMPLATE//<DN_SLUG>/$DN_SLUG}"; APACHE_CONF_LOCATION_BK="$(date +$APACHE_CONF_LOCATION-%d%m%Y_%R.bak)"; cp "$APACHE_CONF_LOCATION" "$APACHE_CONF_LOCATION_BK"; if [ -f "$APACHE_CONF_LOCATION_BK" ]; then echo "$APACHE_TEMPLATE" >> $APACHE_CONF_LOCATION; $APACHE_RELOAD; notice "$DN created at $WWW_ROOT/$DN_SLUG (apache bkup file found at $APACHE_CONF_LOCATION_BK)" else notice "Failed to create Apache config backup at $APACHE_CONF_LOCATION_BK" fi fi fi fi fi read STOP exit; ################################ |
ZF2 different layouts in modules
June 4th, 2013To enable a different layout in a module, to your module.php for the module define a new layout like so
public function init(ModuleManager $mm) { $mm->getEventManager()->getSharedManager()->attach(__NAMESPACE__, 'dispatch', function($e) { $e->getTarget()->layout('admin/layout'); }); } |
then within your module.config.php view_manager settings define the template
'view_manager' => array( 'template_map' => array( 'admin/layout' => __DIR__ . '/../view/layout/cms.phtml', ), 'template_path_stack' => array( __DIR__ . '/../view', ), ), |
or you can define a direct path in your module.php without any template mapping i.e
public function init(ModuleManager $mm) { $mm->getEventManager()->getSharedManager()->attach(__NAMESPACE__, 'dispatch', function($e) { $e->getTarget()->layout('layout/admin'); }); } |
This will load module/view/layout/admin.phtml when this module is run
16,435 viewsRemote controlling Tivo
October 21st, 2012I was interested in what my ethernet port was doing on my virgin media TIVO box, so I ran some tests and found I could control my TIVO box from my laptop, here are my findings.
First, I went into the Tivo boxes settings on screen and enabled network access.
Next, I logged into my home hub/router to find out what IP address it had been allocated
http://192.168.100.1/ and clicked devices.
Once I had its IP address, (192.168.0.7) I scanned its ports and found the following services:
adrian@adrian-laptop:~$ nmap 192.168.0.7 Starting Nmap 5.00 ( http://nmap.org ) at 2012-10-21 10:37 BST Interesting ports on 192.168.0.7: Not shown: 997 filtered ports PORT STATE SERVICE 443/tcp open https 2190/tcp open unknown 2191/tcp open unknown Nmap done: 1 IP address (1 host up) scanned in 5.50 seconds |
The obvious one that stands out is port 443, so visiting https://192.168.0.7/ shows a web server requesting login, I found that the username is tivo and the password is your media key found in settings > mediakey.
Once logged in, a list of recorded programmes will be displayed but however streaming and downloading are disabled :(.
I tried for sometime to circumvent the streaming restriction using various techniques the closest I came was with VLC and special plugins http://tivo-vlc.sourceforge.net/notes.php#install, but so far I haven’t been able to achieve this.
I looked into what the box is, and it turns out its fundamentally Linux box with a glossy coat, with some features disabled (like streaming something to do with UK copyright laws).
Along the way I found I could telnet to port 31339 on the box
telnet 192.168.0.7 31339 |
which responded with
CH_STATUS 0213 RECORDING |
This is the status of the two tuners, and what they are doing, you can now use commands like:
KEYBOARD (Dunno what this does)
TELEPORT <PLACE>- e.g TIVO, LIVETV, GUIDE, and NOWPLAYING.
SETCH <CHANNEL> - Change channel. If the current tuner is recording a program, it will change the other tuner. If both tuners are recording, the TiVo will respond with "CH_FAILED RECORDING <Title>. Using this command while Tivo is replaying will give "CH_FAILED NO_LIVE".
FORCECH <CHANNEL> - This command will force the current tuner to the tune the desired channel regardless of what it's doing. If a recording is being recorded it will cancel the recording and change the channel without confirmation.
Also a complete set of IRCODE’s exist
IRCODE <COMMAND>
a quick google and I found the following IRCODE commands that seem to work.
UP
DOWN
LEFT
RIGHT
SELECT
TIVO
LIVETV
THUMBSUP
THUMBSDOWN
CHANNELUP
CHANNELDOWN
RECORD
DISPLAY
DIRECTV
NUM0
NUM1
NUM2
NUM3
NUM4
NUM5
NUM6
NUM7
NUM8
NUM9
ENTER
CLEAR
PLAY
PAUSE
SLOW
FORWARD
REVERSE
STANDBY
NOWSHOWING
REPLAY
ADVANCE
DELIMITER
GUIDE
Typing these commands into the telnet session, made the tivo box change channel etc…. cool!
To exit the telnet session press
CTRL + ]
thats right, control and right square bracket!!, then type quit at the prompt
I was able to source a nice GUI that can automate these commands https://github.com/wmcbrine/tivoremote
So, as long as you have Python installed (Mac & linux users by default), just running the script produces a nice GUI that can manage your Tivo box from your laptop.
I also found, if your interested in developing apps for it, its SDK is written for AS3 and can be found here http://developer.tivo.com/
form to database php
October 15th, 2012I have written a collection of classes, that are capable of accepting a submission from a get (REST) or post (form) and output them to an email, the screen or to a database table.
The class can be used to output a CSV, an email or XML (REST).
The code is very straight forward for example create a form.html and add the following.
<form method="post" action="save.php"> <label for="firstname">Firstname</label> <input type="text" name="firstname" id="firstname"><br> <label for="surname">Surname</label> <input type="text" name="surname" id="surname"><br> <input type="submit" value="Submit"> </form> |
This form will submit to save.php, we need to now create this, create the file save.php and add the following
require_once('classes.php'); // load the classes |
This loads the classes, and is required to make this work, next
listener::getInstance(); |
Gets an instance of the listener, the listener has the responsibility of handling the data, it sets a number of values by default, one of which is the default submission type (GET) this needs to be changed to POST, so
listener::getInstance()->method('POST'); |
Next, the listener needs to know which fields are valid fields, otherwise every bit of junk somebody sends to this script will be placed into the database, this is done in the form of an array, with the array key being the fieldname, and the value being whether or not is it required, if a field is defined as required and not codesent, the submission will fail with an error.
array( 'firstname'=>true 'surname'=>true ) |
tell the listener about these fields
listener::getInstance() ->method('POST') ->setFields( array( 'firstname'=>true 'surname'=>true ) ); |
next we have to define, what we wish to do with the submission, in this case, save it to a database, first get the database instance.
database::getInstance(); |
by default, the database will default to certain values, typically it will connect with the username “root”, password “”, database “data”, host “localhost”, table called “submissions” and the field “submitted”.
if you need to change any of these values, it can be done by setting a config of type stdConfig with an array of new parameters.
If you do not decalre say a username, it just defaults, the avaialble fields are: username, password, database, table, field and host
database::getInstance()->setConfig( database::stdConfig( array( 'password'=>'1234' ) ) ); |
The database instance is placed in setAction like this:
listener::getInstance() ->method('POST') ->setFields( array( 'firstname'=>true 'surname'=>true ) ) ->setAction( database::getInstance()->setConfig( database::stdConfig( array( 'password'=>'1234' ) ) ) ); |
Next we need some sort of feedback, about whether or not it was successful, this can be done by asking the instance what happened, for example:
// some visual feedback if (listener::getInstance()->success){ echo 'Thankyou!'; } else { echo 'The following problems were encountered'; print_r(listener::getInstance()->errors); } |
Lets put it all together!, complete code below:
require_once('classes.php'); // load the classes listener::getInstance() ->method('POST') ->setFields( array( 'firstname'=>true 'surname'=>true ) ) ->setAction( database::getInstance()->setConfig( database::stdConfig( array( 'password'=>'1234' ) ) ) ); // some visual feedback if (listener::getInstance()->success){ echo 'Thankyou!'; } else { echo 'The following problems were encountered'; print_r(listener::getInstance()->errors); } |
You will need the class library found in here (right click save as)
Should you require a REST service, a wrapper is included to output the response as xml, for example:
<?php require_once('classes.php'); // load the classes Output::toXml( listener::getInstance() ->setFields( array( 'bob'=>true ) ) ->setAction( database::getInstance()->setConfig( database::stdConfig( array( 'password'=>'1234' ) ) ) ) ); //foreach(listener::getInstance()->errors AS $err) echo $err; |
to get the submissions as a Csv, similiarly:
<?php require_once('classes.php'); // load the classes Output::toCsv( database::getInstance()->setConfig( database::stdConfig(array('password'=>'1234')) )->fetchAll() ); //echo database::getInstance()->error; |
A rest Example can be found here, along with the ability to CSV export
7,600 viewsthickdialog
June 20th, 2012 Jquery dialog can be made to work as a either an ajax form, a dialog box or confirmation box just by use of a class similair to the way in which thickbox used to work (the examples work perfectly outside of wordpress).ThickDialog examples
Simple thickbox style popup
Popup example
code:
<a href="/demos/thickdialog/popup.html" title="A pop up" class="dialog-box">Popup example</a> |
usage: add the class “dialog-box” to make this work
Simple thickbox style confirmation box
Confirm example
code:
<a href="/demos/thickdialog/delete.html" title="Are you sure?" class="dialog-confirm">Confirm example</a> |
usage: add the class “dialog-confirm” to make this work
Simple thickbox style form submission
Form example
code:
<a href="/demos/thickdialog/form.php" title="Please complete" class="dialog-confirm">Form example</a> |
usage: add the class “dialog-form” to make this work
4,999 viewsjquery ui ajax form thickbox
June 20th, 2012 Jquery dialog can be made to work as a ajax form in a similair way to thickbox.The link to the form (such as registration form), can be made to load inside a popup, and loaded using ajax by simply adding a class to the href.
For example this link could perform a registration, just like thickbox used to behave adding the class ‘dialog-form’ (the title is used to title the form popup) loading the content on submission.
<a href="/demos/thickdialog/form.php" class="dialog-form" title="Register">register</a> |
Example: register
In order to do this you will need Jquery, and Jquery-UI and the code below.
Alternatively I have written a library called thickdialog which supports the form, confirmation box and pop ups just by adding a simple class, which is compatible with any framework and can be downloaded from there.
// form pop ups, just give an anchor point the class dialog_form, and the title will be used as the title $(function (){ function submitBind(layout,original_url){ $(layout).find('form').submit(function(event){ /* stop form from submitting normally */ event.preventDefault(); url = $(this).attr('action')=='' ? original_url : $(this).attr('action'); /* Send the data using post and put the results in a div */ $.post( url, $(this).serialize(), function(data) { $('#dialog').empty(); $('#dialog').append(data); $('#dialog').dialog('option', 'position', 'center'); submitBind(layout, original_url); }); }); } $('a.dialog-form').click(function() { var original_url = this.href; var title = this.title; // show a spinner or something via css var dialog = $('<div id="dialog" style="display:none" class="loading">Loading</div>').appendTo('body'); // open the dialog dialog.dialog({ width: dialogBox_width, // add a close listener to prevent adding multiple divs to the document close: function(event, ui) { // remove div with all data and events dialog.remove(); }, modal: true }); // load remote content dialog.load( original_url, function (responseText, textStatus, XMLHttpRequest) { // remove the loading class dialog.removeClass('loading'); dialog.dialog('option', 'position', 'center'); dialog.dialog('option', 'title', title); submitBind(this,original_url); } ); //prevent the browser to follow the link return false; }); }); |
jquery confirmation box thickbox
June 20th, 2012 Jquery dialog can be made to work as a confirmation box in a similair way to thickbox.The link to the action (such as deletion), can be made to display a confirmation box before it completes, by simply adding a class to the href.
For example this link could perform a deletion, just like thickbox used to behave adding the class ‘dialog-confirm’ (the title is used to title the dialog confirm) will request the user to confrim firstly.
<a href="/demos/thickdialog/delete.php" class="dialog-confirm" title="Are you sure about this?">delete</a> |
Example: delete(example works fine outside of wordpress)
In order to do this you will need Jquery, and Jquery-UI and the code below.
Alternatively I have written a library called thickdialog which supports the confirmation box, pop ups and forms just by adding a simple class, which is compatible with any framework and can be downloaded from there.
// confirm box, add the class dialog-confirm and a title and it does the rest $(function (){ $('a.dialog-confirm').click(function() { var url = this.href; var dialog = $('<div id="dialog" style="display:none" title="Are you sure?">'+this.title+'</div>').appendTo('body'); dialog.dialog({ resizable: false, height:confirmBox_height, modal: true, buttons: { "Yes": function() { $(this).dialog( "close" ); window.location.href = url; }, "No": function() { $(this).dialog( "close" ); } } }); dialog.dialog('option', 'position', 'center'); //prevent the browser to follow the link return false; }); }); |
Jquery dialog thickbox
June 20th, 2012 Jquery dialog can be made to work in a similair way to thickbox.Any link to content, can have its content loaded into a jquery dialog box, by simply adding a class to the href.
For example this standard page can be made to behave like a thickbox pop-up by adding the class ‘dialog-box’ (the title is used to title the dialog box).
<a href="/demos/thickdialog/page.php" class="dialog-box" title="popup">popup</a> |
Example: popup (example works fine outside of wordpress)
In order to do this you will need Jquery, and Jquery-UI and the code below.
Alternatively I have written a library called thickdialog which supports the popups, confirmation boxes and forms just by adding a simple class, which is compatible with any framework and can be downloaded from there.
// pop ups, just give an anchor point the class dialog_box, and the title will be used as the title $(function (){ $('a.dialog-box').click(function() { var url = this.href; var title = this.title; // show a spinner or something via css var dialog = $('<div id="dialog" style="display:none" class="loading">Loading</div>').appendTo('body'); // open the dialog dialog.dialog({ width: dialogBox_width, // add a close listener to prevent adding multiple divs to the document close: function(event, ui) { // remove div with all data and events dialog.remove(); }, modal: true }); // load remote content dialog.load( url, {}, // omit this param object to issue a GET request instead a POST request, otherwise you may provide post parameters within the object function (responseText, textStatus, XMLHttpRequest) { // remove the loading class dialog.removeClass('loading'); dialog.dialog('option', 'position', 'center'); dialog.dialog('option', 'title', title); } ); //prevent the browser to follow the link return false; }); }); |
javascript image rotation
January 9th, 2012Very simple class that rotates images on any event, on any target element (with a preloader).
Click here for an example.
To use it yourself (how the example works):
1. include jquery and the class
<script type="text/javascript" src="jquery.js"></script> <script type="text/javascript" src="galleryRotate.js"></script> |
2. Instantiate the class with an array of images
$images = ['1.jpg','2.jpg','3.jpg']; var rotator = new GalleryRotate($images); |
3. bind the objects change method to an event, such as mouseclick, telling it the name of target element (always done once the webpage is ready)
$(document).ready(function(){ // click the image $('#button').click(function(){ return rotator.change('#img'); }); }); |
Once again an example can be found here.
The full source code can be downloaded here.
The class code is very straight forward and below for completeness.
function GalleryRotate ($images){ this.preload = function () { $(this.images).each(function(){ $('<img/>')[0].src = this; }); } this.change = function($element){ $img = this.images[this.imgNo]; if ($element){ $($element).attr("src", $img); } else alert($img); this.imgNo++; if (this.imgNo==this.images.length){ this.imgNo=0; } return false; } this.init = function ($images) { this.images = $images; this.imgNo=0; this.preload(); } this.init($images); } |
adding password authentication for a zend cms or controller
December 13th, 2011This is a very quick way to add password protection to a controller such as a cms controller.
This code intercepts the login within the init() method of your chosen controller, if they are logged in they are free to go, if not they are required to login, the form is rendered and validated against the access model.
The session is obfuscated to deter tampering.
so firstly in your controller init() add
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 | $adminSession = new Zend_Session_Namespace('Admin'); $this->access = new Application_Model_accessControl(); // access control // logged in user $loginMsg = 'Please login'; $loginForm = new Zend_Form; $loginForm ->setMethod('post') ->setAttrib('id','login') ->addElement('text','user',array('label'=>'Username','required'=>true)) ->addElement('password','pass',array('label'=>'Password','required'=>true)) ->addElement('submit','Login'); if ($this->getRequest()->isPost() && $loginForm->isValid($_POST)){ $session = $this->access->validateUser( $loginForm->getValue('user'), $loginForm->getValue('pass') ); if ($session){ $adminSession->loggedInSession = $session; $adminSession->loggedInUser = $loginForm->getValue('user'); } $loginMsg='Access denied'; } if (!$this->access->sessionIsValid($adminSession->loggedInSession) || !$adminSession->loggedInUser){ $this->view->header=$loginMsg; $this->view->content = $loginForm; $this->render('login'); } else { // renew session, will always be the "Now" session, but three are provided, see the model $adminSession->loggedInSession = $this->access->getValidSession(); $this->view->userIdent = $adminSession->loggedInUser; } |
$this->view->userIdent will always contain the logged in user
The next step is to create the model for authentication in models/accessControl.php
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 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | <?php class Application_Model_accessControl { public function __construct(){ // set users, password is the key, because this is less likely to be duplicated $this->setUsers( array( 'password1'=>'admin', 'password2'=>'admin' ) ); // salt for session key $this->setSalt('MySaltGoesHere1234'); } public function validateUser($username, $password){ /* * return session if correct, null otherwise */ $users = $this->getUsers(); if (isset($users[$password]) && $users[$password]==$username){ return $this->getValidSession(); } } public function sessionIsValid($session,$debug=false){ $validSessions = $this->getValidSessions(); if ($debug){ echo "MY KEY: $session<br >KEYS: ".print_r($validSessions,true); } return in_array($session, $validSessions) ? true : false; } public function getValidSessions(){ $sessions = array( $this->getValidSession('past'), $this->getValidSession('now'), $this->getValidSession('future') ); return $sessions; } public function getValidSession($for='now'){ /* * Three sessions are provided, * * past, now and present * * past, person logs in at 11:59 spends ten minutes editing, clicks save at 12:09, past would be the valid session * now, person is logged in during 11:00 - 11:59 * future, is provided encase of time travel, or if the british summer time kicks in while editing, unlikely but more is better than not enough... */ $hour = intval(date('H')); switch($for){ case 'past' : $validSession = ($hour-1) < 0 ? 23 : $hour-1; break; case 'now' : default: $validSession = $hour; break; case 'future' : $validSession = ($hour+1) > 23 ? 0 : $hour+1; break; } /* * obsfucation */ // firstly grab specific details to stop session hijacking $ip = $_SERVER['REMOTE_ADDR']; $browser = $_SERVER['HTTP_USER_AGENT']; // then mix it up and md5 it $validSession = $ip.$validSession.$this->getSalt().$browser; if (!getenv('APPLICATION_ENV')){ $validSession = md5($validSession); } return $validSession; } public function setUsers($users){ $this->users = $users; } public function getUsers(){ return $this->users; } public function setSalt($salt){ $this->salt = $salt; } public function getSalt(){ return $this->salt; } } |
and finally create a view login.phtml (this is just bare bones)
1 2 | <?php echo "<h2>{$this->header}</h2>"; ?> <?php echo $this->content; ?> |
Thats it!
8,572 viewssession_start hangs when it used to work
December 13th, 2011I had a problem with my zend application hanging with a white screen for no reason.
I narrowed it down to when
1 | $adminSession = new Zend_Session_Namespace('Admin'); |
was executed, this is just a session? so debugging further, confirmed that session_start() hangs when it used to work.
There are lots of explanations for this, but before you start modifying ini settings, just try this
1 2 3 | session_write_close(); session_start(); die(print_r($_SESSION,true)); |
I found this resolved the problem completely.
I then just removed this debug-code and went back to developing my zend app.
ckeditor to upload images
September 19th, 2011A few posts on how to do this, and a lot of frustrated people so here is how:
Download both ckeditor and ckfinder.
Decompress them into the root directory.
Include them in your php
include_once ‘ckeditor/ckeditor.php’;
include_once ‘ckfinder/ckfinder.php’;
hopefully no error messages? ok cool…. now simply
1 2 3 4 5 6 7 8 9 10 11 12 13 | <?php $ckeditor = new CKEditor(); $ckeditor->basePath = '/ckeditor/'; $ckeditor->config['filebrowserBrowseUrl'] = '/ckfinder/ckfinder.html'; $ckeditor->config['filebrowserImageBrowseUrl'] = '/ckfinder/ckfinder.html?type=Images'; $ckeditor->config['filebrowserFlashBrowseUrl'] = '/ckfinder/ckfinder.html?type=Flash'; $ckeditor->config['filebrowserUploadUrl'] = '/ckfinder/core/connector/php/connector.php?command=QuickUpload&type=Files'; $ckeditor->config['filebrowserImageUploadUrl'] = '/ckfinder/core/connector/php/connector.php?command=QuickUpload&type=Images'; $ckeditor->config['filebrowserFlashUploadUrl'] = '/ckfinder/core/connector/php/connector.php?command=QuickUpload&type=Flash'; $ckeditor->editor('CKEditor1'); ?> |
To add the field to the form.
Ensure that in ckfinder/userfiles is writable
You also need to alter in ckfinder/userfiles/config.php the function CheckAuthentication(), to return true, take note of the warning!!
ive put it all together in one zip file displaying the submission here
4,131 views