OSB enhancements to date

This commit is contained in:
Deon George
2010-11-30 09:41:08 +11:00
parent 8715a2059b
commit ec6a542bc3
478 changed files with 23423 additions and 9309 deletions

View File

@@ -0,0 +1,4 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
class Block extends lnApp_Block {}
?>

View File

@@ -0,0 +1,4 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
class Breadcrumb extends lnApp_Breadcrumb {}
?>

View File

@@ -0,0 +1,50 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class is for access company informaiton.
*
* @package OSB
* @subpackage Page
* @category Helpers
* @author Deon George
* @copyright (c) 2010 Deon George
* @license http://dev.leenooks.net/license.html
*/
class Company {
public static function instance() {
return new Company;
}
public static function address($ln='<br/>') {
// @todo Company address should be calculated
return implode($ln,array('PO Box 149','Bendigo, VIC 3550'));
}
public static function contacts() {
// @todo Company phone should be calculated
return 'Tel: 03 5410 1135';
}
public static function render() {
echo static::name();
echo static::address();
echo static::contacts();
}
/**
* Return the HTML to render the company address
*/
public function __toString() {
try {
return static::render();
}
// Display the exception message
catch (Exception $e) {
Kohana::exception_handler($e);
return '';
}
}
}
?>

View File

@@ -0,0 +1,4 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
class Config extends lnApp_Config {}
?>

View File

@@ -0,0 +1,4 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
class Controller_Default extends Controller_lnApp_Default {}
?>

View File

@@ -0,0 +1,75 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class provides the default controller for rendering pages.
*
* @package lnApp
* @subpackage Page
* @category Abstract/Controllers
* @author Deon George
* @copyright (c) 2010 Deon George
* @license http://dev.leenooks.net/license.html
*/
abstract class Controller_lnApp_Default extends Controller {
/**
* Controls access to this controller.
* Can be set to a string or an array, for example 'login' or array('login', 'admin')
* Note that in second(array) example, user must have both 'login' AND 'admin' roles set in database
*
* @var boolean is authenticate required with this controller
*/
protected $auth_required = FALSE;
/**
* If redirecting to a login page, which page to redirect to
*/
protected $noauth_redirect = 'login';
/**
* Controls access for separate actions, eg:
* 'adminpanel' => 'admin' will only allow users with the role admin to access action_adminpanel
* 'moderatorpanel' => array('login', 'moderator') will only allow users with the roles login and moderator to access action_moderatorpanel
*
* @var array actions that require a valid user
*/
protected $secure_actions = array();
/**
* Check and see if this controller needs authentication
*
* if $this->auth_required is TRUE, then the user must be logged in only.
* if $this->auth_required is FALSE, AND $this->secure_actions has an array of
* methods set to TRUE, then the user must be logged in AND a member of the
* role.
*
* @return boolean
*/
protected function _auth_required() {
// If our global configurable is disabled, then continue
if (! Kohana::Config('config.method_security'))
return FALSE;
return (($this->auth_required !== FALSE && Auth::instance()->logged_in() === FALSE) ||
(is_array($this->secure_actions) && array_key_exists($this->request->action,$this->secure_actions) &&
Auth::instance()->logged_in($this->secure_actions[$this->request->action]) === FALSE));
}
public function before() {
parent::before();
// Check user auth and role
if ($this->_auth_required()) {
// For AJAX/JSON requests, authorisation is controlled in the method.
if (Request::$is_ajax && $this->request->action === 'json') {
// Nothing required.
// For no AJAX/JSON requests, display an access page
} elseif (Auth::instance()->logged_in(NULL,get_class($this).'|'.__METHOD__)) {
Request::instance()->redirect('login/noaccess');
} else {
Session::instance()->set('afterlogin',Request::instance()->uri());
Request::instance()->redirect($this->noauth_redirect);
}
}
}
}
?>

View File

@@ -0,0 +1,26 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class provides logout capability
*
* @package lnApp
* @subpackage Page/Logout
* @category Controllers
* @author Deon George
* @copyright (c) 2010 Deon George
* @license http://dev.leenooks.net/license.html
* @also [login]
*/
class Controller_lnApp_Logout extends Controller {
public function action_index() {
# If user already signed-in
if (Auth::instance()->logged_in()!= 0) {
Auth::instance()->logout();
Request::instance()->redirect('login');
}
Request::instance()->redirect('welcome/index');
}
}
?>

View File

@@ -0,0 +1,279 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class provides the default template controller for rendering pages.
*
* @package lnApp
* @subpackage Page/Template
* @category Controllers
* @author Deon George
* @copyright (c) 2010 Deon George
* @license http://dev.leenooks.net/license.html
*/
abstract class Controller_lnApp_TemplateDefault extends Controller_Template {
/**
* @var string page template
*/
public $template = 'lnapp/default';
/**
* @var string page media route as per [Route]
*/
protected $mediaroute = 'default/media';
/**
* @var object meta object information as per [meta]
*/
protected $meta;
/**
* Controls access to this controller.
* Can be set to a string or an array, for example 'login' or array('login', 'admin')
* Note that in second(array) example, user must have both 'login' AND 'admin' roles set in database
*
* @var boolean is authenticate required with this controller
*/
protected $auth_required = FALSE;
/**
* If redirecting to a login page, which page to redirect to
*/
protected $noauth_redirect = 'login';
/**
* Controls access for separate actions, eg:
* 'adminpanel' => 'admin' will only allow users with the role admin to access action_adminpanel
* 'moderatorpanel' => array('login', 'moderator') will only allow users with the roles login and moderator to access action_moderatorpanel
*
* @var array actions that require a valid user
*/
protected $secure_actions = array(
'menu' => TRUE,
);
/**
* Check and see if this controller needs authentication
*
* if $this->auth_required is TRUE, then the user must be logged in only.
* if $this->auth_required is FALSE, AND $this->secure_actions has an array of
* methods set to TRUE, then the user must be logged in AND a member of the
* role.
*
* @return boolean
*/
protected function _auth_required() {
// If our global configurable is disabled, then continue
if (! Kohana::Config('config.method_security'))
return FALSE;
return (($this->auth_required !== FALSE && Auth::instance()->logged_in() === FALSE) ||
(is_array($this->secure_actions) && array_key_exists($this->request->action,$this->secure_actions) &&
Auth::instance()->logged_in($this->secure_actions[$this->request->action]) === FALSE));
}
/**
* Loads the template [View] object.
*
* Page information is provided by [meta].
* @uses meta
*/
public function before() {
// Do not template media files
if ($this->request->action === 'media') {
$this->auto_render = FALSE;
return;
}
parent::before();
// Check user auth and role
if ($this->_auth_required()) {
if (Kohana::$is_cli)
throw new Kohana_Exception('Cant run :method, authentication not possible',array(':method'=>$this->request->action));
// If auth is required and the user is logged in, then they dont have access.
// (We have already checked authorisation.)
if (Auth::instance()->logged_in(NULL,get_class($this).'|'.__METHOD__)) {
if (Config::sitemode() == Kohana::DEVELOPMENT)
SystemMessage::add(array(
'title'=>_('Insufficient Access'),
'type'=>'debug',
'body'=>Kohana::debug(array('required'=>$this->auth_required,'action'=>$this->request->action,'user'=>Auth::instance()->get_user()->username)),
));
// @todo Login No Access redirects are not handled in JS?
if (Request::$is_ajax) {
echo _('You dont have enough permissions.');
die();
} else
Request::instance()->redirect('login/noaccess');
} else {
Session::instance()->set('afterlogin',Request::instance()->uri());
Request::instance()->redirect($this->noauth_redirect);
}
}
// For AJAX calls, we dont need to render the complete page.
if (Request::$is_ajax) {
$this->auto_render = FALSE;
return;
}
// Bind our template meta variable
$this->meta = new meta;
View::bind_global('meta',$this->meta);
// Our default style sheet
Style::add(array(
'type'=>'file',
'data'=>'css/default.css',
));
// Our default scripts
// This is in a reverse list, since we push them to the beginging of the scripts to render.
foreach (array('file'=>array(
'js/jquery.cookie.js',
'js/jquery.jstree-1.0rc.js',
'js/jquery-1.4.2.js',
)) as $type => $datas) {
foreach ($datas as $data) {
Script::add(array(
'type'=>$type,
'data'=>$data,
),TRUE);
}
}
// Initialise our content
$this->template->left = '';
$this->template->content = '';
$this->template->right = '';
}
public function after() {
if ($this->auto_render) {
// Application Title
$this->meta->title = 'Application Title';
$this->template->title = '';
// Style Sheets Properties
$this->meta->styles = Style::factory();
// Script Properties
$this->meta->scripts = Script::factory();
// Application logo
$this->template->logo = Config::logo();
// Link images on the header line
$this->template->headimages = $this->_headimages();
// Control Line
$this->template->control = $this->_control();
// System Messages line
$this->template->sysmsg = $this->_sysmsg();
// Left Item
$this->template->left = $this->_left();
// Right Item
$this->template->right = $this->_right();
// Footer
$this->template->footer = $this->_footer();
// For any ajax rendered actions, we'll need to capture the content and put it in the response
} elseif (Request::$is_ajax && isset($this->template->content) && ! $this->request->response) {
// @todo move this formatting to a view?
if ($s = $this->_sysmsg() AND (string)$s) {
$this->request->response = sprintf('<table class="sysmsg"><tr><td>%s</td></tr></table>',$s);
} else
$this->request->response = '';
# In case there any style sheets or scrpits for this render.
$this->request->response .= Style::factory();
# Get the response body
$this->request->response .= sprintf('<table class="content"><tr><td>%s</td></tr></table>',$this->template->content);
}
parent::after();
}
/**
* Default Method to call from the tree menu
*/
public function action_menu() {
$this->template->content = 'See menu on tree';
}
protected function _headimages() {
HeadImages::add(array(
'url'=>'http://dev.leenooks.net',
'img'=>'img/forum-big.png',
'attrs'=>array('onclick'=>"target='_blank';",'title'=>'Link')
));
return HeadImages::factory();
}
/**
* Render our control menu bar
*/
protected function _control() {
return Breadcrumb::factory();
}
protected function _sysmsg() {
return SystemMessage::factory();
}
protected function _left() {
return empty($this->template->left) ? Controller_Tree::js() : $this->template->left;
}
protected function _right() {
return empty($this->template->right) ? '' : $this->template->right;
}
public function _footer() {
return sprintf('&copy; %s',Config::SiteName());
}
/**
* This action will render all the media related files for a page
* @return void
*/
final public function action_media() {
// Generate and check the ETag for this file
$this->request->check_cache(sha1($this->request->uri));
// Get the file path from the request
$file = $this->request->param('file');
// Find the file extension
$ext = pathinfo($file, PATHINFO_EXTENSION);
// Remove the extension from the filename
$file = substr($file, 0, -(strlen($ext) + 1));
// First try and find media files for the site_id
if ($f = Kohana::find_file(sprintf('media/%s',Config::siteid()), $file, $ext)) {
// Send the file content as the response
$this->request->response = file_get_contents($f);
// If not found try a default media file
} elseif ($f = Kohana::find_file('media', $file, $ext)) {
// Send the file content as the response
$this->request->response = file_get_contents($f);
} else {
// Return a 404 status
$this->request->status = 404;
}
// Set the proper headers to allow caching
$this->request->headers['Content-Type'] = File::mime_by_ext($ext);
$this->request->headers['Content-Length'] = filesize($f);
$this->request->headers['Last-Modified'] = date('r', filemtime($f));
}
}
?>

View File

@@ -0,0 +1,110 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class extends renders OSB menu tree.
*
* @package lnApp
* @subpackage Tree
* @category Controllers
* @author Deon George
* @copyright (c) 2010 Open Source Billing
* @license http://dev.osbill.net/license.html
*/
class Controller_lnApp_Tree extends Controller_Default {
// Our tree data
protected $treedata;
/**
* @var string page media route as per [Route]
*/
protected static $mediaroute = 'default/media';
public function after() {
parent::after();
$this->request->headers['Content-Type'] = 'application/json';
$this->request->response = sprintf('[%s]',json_encode($this->treedata));
}
public static function js() {
$mediapath = Route::get(static::$mediaroute);
return '
<div id="tree" class=""></div>
<script type="text/javascript">
$(function () {
$("#tree").jstree({
themes : {
"theme" : "default",
"url" : "'.URL::site($mediapath->uri(array('file'=>'css/jquery.jstree.css'))).'",
},
ui : {
"select_limit" : 1,
"select_node" : false,
},
cookies : {
"save_selected" : false,
},
json_data : {
"correct_state" : "true",
"progressive_render" : "true",
"ajax" : {
"url" : "'.URL::site('/tree/json').'",
"data" : function (n) {
return { id : n.attr ? n.attr("id") : "N_"+0 };
}
}
},
plugins : [ "themes", "json_data", "ui", "cookies" ],
});
// On selection
$("#tree").bind("select_node.jstree", function (e, data) {
if (a = data.rslt.obj.attr(\'id\').indexOf(\'_\')) {
id = data.rslt.obj.attr(\'id\').substr(a+1);
if (href = $("#N_"+id).attr("href"))
$("#ajBODY").load(href, function(r,s,x) {
if (s == "error") {
var msg = "Sorry but there was an error: ";
$("#ajBODY").html(msg + x.status + " " + x.statusText + r);
}
});
else
alert("Unknown: "+id+" HREF:"+href);
}
});
});
</script>';
}
/**
* Draw the Tree Menu
*
* The incoming ID is either a Branch B_x or a Node N_x
* Where X is actually the module.
*
* @param id
*/
public function action_json($id=null) {
if ($this->_auth_required()) {
$this->treedata = array('attr'=>array('id'=>'a_login'),
'data'=>array('title'=>_('Please Login').'...','attr'=>array('id'=>'login','href'=>URL::site('/login'))));
return;
}
$this->treedata = array();
$data = array();
foreach ($data as $branch) {
array_push($this->treedata,array(
'attr'=>array('id'=>sprintf('B_%s',$branch['id'])),
'state'=>$branch['state'],
'data'=>array('title'=>$branch['name']),
'attr'=>array('id'=>sprintf('N_%s',$branch['id']),'href'=>empty($branch['attr_href']) ? URL::site(sprintf('/%s/menu',$branch['name'])) : $branch['attr_href']),
)
);
}
}
}
?>

View File

@@ -0,0 +1,209 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class provides login capability
*
* @package lnApp
* @subpackage Page/Login
* @category Controllers
* @author Deon George
* @copyright (c) 2010 Deon George
* @license http://dev.leenooks.net/license.html
* @also [logout]
*/
class Controller_Login extends Controller_TemplateDefault {
public function action_index() {
// If user already signed-in
if (Auth::instance()->logged_in()!= 0) {
// Redirect to the user account
Request::instance()->redirect('welcome/index');
}
// If there is a post and $_POST is not empty
if ($_POST) {
// Instantiate a new user
$user = ORM::factory('account');
// Check Auth
$status = $user->login($_POST);
// If the post data validates using the rules setup in the user model
if ($status) {
// Redirect to the user account
if ($redir = Session::instance()->get('afterlogin')) {
Session::instance()->delete('afterlogin');
Request::instance()->redirect($redir);
} else
Request::instance()->redirect('welcome/index');
} else {
SystemMessage::add(array(
'title'=>_('Invalid username or password'),
'type'=>'error',
'body'=>_('The username or password was invalid.')
));
}
}
Block::add(array(
'title'=>_('Login to server'),
'body'=>View::factory('login'),
'style'=>array('css/login.css'=>'screen'),
));
$this->template->control = HTML::anchor($this->request->uri(),'Login',array('id'=>'ajxbody'));
$this->template->content = Block::factory();
Script::add(array('type'=>'stdin','data'=>'
$(document).ready(function() {
$("#ajxbody").click(function() {$("#ajBODY").load("'.$this->request->uri().'/"); return false;});
});'
));
}
public function action_register() {
// If user already signed-in
if (Auth::instance()->logged_in()!= 0) {
// Redirect to the user account
Request::instance()->redirect('welcome/index');
}
// Instantiate a new user
$account = ORM::factory('account');
// If there is a post and $_POST is not empty
if ($_POST) {
// Check Auth
$status = $account->values($_POST)->check();
if (! $status) {
foreach ($account->validate()->errors() as $f=>$r) {
// $r[0] has our reason for validation failure
switch ($r[0]) {
// Generic validation reason
default:
SystemMessage::add(array(
'title'=>_('Validation failed'),
'type'=>'error',
'body'=>sprintf(_('The defaults on your submission were not valid for field %s (%s).'),$f,$r[0])
));
}
}
}
$ido = ORM::factory('module')
->where('name','=','account')
->find();
$account->id = $ido->record_id->next_id($ido->id);
// Save the user details
if ($account->save()) {}
}
SystemMessage::add(array(
'title'=>_('Already have an account?'),
'type'=>'info',
'body'=>_('If you already have an account, please login..')
));
Block::add(array(
'title'=>_('Register'),
'body'=>View::factory('bregister')
->set('account',$account)
->set('errors',$account->validate()->errors()),
'style'=>array('css/bregister.css'=>'screen'),
));
$this->template->control = HTML::anchor($this->request->uri(),'Register',array('id'=>'ajxbody'));
$this->template->content = Block::factory();
$this->template->left = HTML::anchor('login','Login').'...';
}
/**
* Enable user password reset
*/
public function action_reset() {
// If user already signed-in
if (Auth::instance()->logged_in()!= 0) {
// Redirect to the user account
Request::instance()->redirect('welcome/index');
}
// If the user posted their details to reset their password
if ($_POST) {
// If the email address is correct, create a method token
if (! empty($_POST['email']) AND ($ao=ORM::factory('account',array('email'=>$_POST['email']))) AND $ao->loaded()) {
$mt = ORM::factory('module_method_token');
// Find out our password reset method id
// @todo move this to a more generic method, so that it can be called by other methods
$mo = ORM::factory('module',array('name'=>'account'));
$mmo = ORM::factory('module_method',array('name'=>'user_resetpassword','module_id'=>$mo->id));
// Check to see if there is already a token, if so, do nothing.
if ($mt->where('account_id','=',$ao->id)->and_where('method_id','=',$mmo->id)->find()) {
if ($mt->date_expire < time()) {
$mt->delete();
$mt->clear();
}
}
if (! $mt->loaded()) {
$mt->account_id = $ao->id;
$mt->method_id = $mmo->id;
$mt->date_expire = time() + 15*3600;
$mt->token = md5(sprintf('%s:%s:%s',$mt->account_id,$mt->method_id,$mt->date_expire));
$mt->save();
// Send our email with the token
$et = EmailTemplate::instance('account_reset_password');
$et->to = array($mt->account->email=>sprintf('%s %s',$mt->account->first_name,$mt->account->last_name));
$et->variables = array(
'SITE'=>URL::base(TRUE,TRUE),
'SITE_ADMIN'=>Config::sitename(),
'SITE_NAME'=>Config::sitename(),
'TOKEN'=>$mt->token,
'USER_NAME'=>sprintf('%s %s',$mt->account->first_name,$mt->account->last_name),
);
$et->send();
}
// Redirect to our password reset, the Auth will validate the token.
} elseif (! empty($_REQUEST['token'])) {
Request::instance()->redirect(sprintf('user/account/resetpassword?token=%s',$_REQUEST['token']));
}
// Show our token screen even if the email was invalid.
if (isset($_POST['email']))
Block::add(array(
'title'=>_('Reset your password'),
'body'=>View::factory('login_reset_sent'),
'style'=>array('css/login.css'=>'screen'),
));
else
Request::instance()->redirect('login');
} else {
Block::add(array(
'title'=>_('Reset your password'),
'body'=>View::factory('login_reset'),
'style'=>array('css/login.css'=>'screen'),
));
}
$this->template->content = Block::factory();
}
public function action_noaccess() {
$this->template->content = '&nbsp;';
SystemMessage::add(array(
'title'=>_('No access to requested resource'),
'type'=>'error',
'body'=>_('You do not have access to the requested resource, please contact your administrator.')
));
}
}
?>

View File

@@ -0,0 +1,4 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
class Controller_Logout extends Controller_lnApp_Logout {}
?>

View File

@@ -0,0 +1,56 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class provides the default template controller for rendering pages.
*
* @package lnApp
* @subpackage Page/Template
* @category Controllers
* @author Deon George
* @copyright (c) 2010 Deon George
* @license http://dev.leenooks.net/license.html
*/
class Controller_TemplateDefault extends Controller_lnApp_TemplateDefault {
/**
* Check and see if this controller needs authentication
*
* if $this->auth_required is TRUE, then the user must be logged in only.
* if $this->auth_required is FALSE, AND $this->secure_actions has an array of
* methods set to TRUE, then the user must be logged in AND a member of the
* role.
*
* @return boolean
*/
protected function _auth_required() {
// If our global configurable is disabled, then continue
if (! Kohana::Config('config.method_security'))
return FALSE;
return (($this->auth_required !== FALSE && Auth::instance()->logged_in(NULL,get_class($this).'|'.__METHOD__) === FALSE) ||
(is_array($this->secure_actions) && array_key_exists($this->request->action,$this->secure_actions) &&
Auth::instance()->logged_in($this->secure_actions[$this->request->action],get_class($this).'|'.__METHOD__) === FALSE));
}
protected function _left() {
if ($this->template->left)
return $this->template->left;
elseif (Auth::instance()->logged_in(NULL,get_class($this).'|'.__METHOD__))
return Controller_Tree::js();
}
protected function _right() {
if ($this->template->right)
return $this->template->right;
else
return $this->_cart();
}
private function _cart() {
if (! Cart::instance()->contents()->reset(FALSE)->count_all())
return '';
return Cart::instance()->cart_block();
}
}
?>

View File

@@ -0,0 +1,90 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class extends renders OSB menu tree.
*
* @package lnApp
* @subpackage Tree
* @category Controllers
* @author Deon George
* @copyright (c) 2010 Open Source Billing
* @license http://dev.osbill.net/license.html
*/
class Controller_Tree extends Controller_lnApp_Tree {
protected $auth_required = TRUE;
/**
* Draw the Tree Menu
*
* The incoming ID is either a Branch B_x or a Node N_x
* Where X is actually the module.
*
* @param id
*/
public function action_json($id=null) {
if ($this->_auth_required()) {
$this->treedata = array('attr'=>array('id'=>'a_login'),
'data'=>array('title'=>_('Please Login').'...','attr'=>array('id'=>'N_login','href'=>URL::site('login'))));
return;
}
// Get the user details
$id = (is_null($id) && isset($_REQUEST['id'])) ? substr($_REQUEST['id'],2) : $id;
$user = Auth::instance()->get_user();
if (! $id) {
$modules = array();
foreach ($user->groups() as $go)
$modules = array_merge($modules,Module_Method::groupmodules($go->id));
ksort($modules);
$data = array();
foreach ($modules as $module => $details)
if (! $details['parent_id'])
array_push($data,
array('id'=>$details['id'],'name'=>$module,'state'=>'closed')
);
} else {
$module = preg_replace('/^N_/','',$id);
$methods = array();
foreach ($user->groups() as $go)
$methods = array_merge($methods,Module_Method::groupmethods($go->id,$module));
ksort($methods);
$data = array();
foreach ($methods as $method => $details) {
if (preg_match('/_/',$method)) {
list($mode,$action) = explode('_',$method);
$url = URL::site(sprintf('/%s/%s/%s',$mode,$details['module'],$action));
} else {
$url = URL::site(sprintf('/%s/%s',$details['module'],$method));
}
array_push($data,array(
'id'=>sprintf('%s_%s',$module,$details['id']),
'name'=>$method,
'state'=>'none',
'attr_id'=>sprintf('%s_%s',$module,$details['id']),
'attr_href'=>(empty($details['page']) ? $url : $details['page'])
));
}
}
$this->treedata = array();
foreach ($data as $branch) {
array_push($this->treedata,array(
'attr'=>array('id'=>sprintf('B_%s',$branch['id'])),
'state'=>$branch['state'],
'data'=>array('title'=>$branch['name']),
'attr'=>array('id'=>sprintf('N_%s',$branch['id']),'href'=>empty($branch['attr_href']) ? URL::site(sprintf('/%s/menu',$branch['name'])) : $branch['attr_href']),
)
);
}
}
}
?>

View File

@@ -1,10 +1,39 @@
<?php defined('SYSPATH') or die('No direct script access.');
<?php defined('SYSPATH') or die('No direct access allowed.');
class Controller_Welcome extends Controller {
/**
* OSB Main home page
*
* @package OSB
* @subpackage Page/Home
* @category Controllers
* @author Deon George
* @copyright (c) 2010 Deon George
* @license http://dev.leenooks.net/license.html
*/
class Controller_Welcome extends Controller_TemplateDefault {
public function action_index() {
$block = new block;
$block->add(array(
'title'=>'Welcome to lnApp (public)!',
'subtitle'=>'Using lnApp',
'body'=>'Sample lnApp application',
'footer'=>'lnApp makes building websites easy! '.time(),
));
public function action_index()
{
$this->request->response = 'hello, world!';
if (Auth::instance()->logged_in()) {
$this->template->control = HTML::anchor('/logout',_('Logout'),array('id'=>'ajxbody'));
} else {
$this->template->control = HTML::anchor('/login',_('Login'),array('id'=>'ajxbody'));
Script::add(array('type'=>'stdin','data'=>'
$(document).ready(function() {
$("#ajxbody").click(function() {$("#ajBODY").load("'.URL::site('/login').'",null,function(x,s,r) {}); return false;});
$("#ajBODY").ajaxSend(function() {$(this).html(\''.sprintf('%s <span class="ajaxmsg">%s<\/span>...',HTML::image('media/img/ajax-progress.gif',array('alt'=>_('Loading Login').'...')),_('Loading Login')).'\');return true;});
});'
));
}
$this->template->content = $block;
}
} // End Welcome
}
?>

View File

@@ -0,0 +1,20 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* MySQL database connection.
*
* Modified for OSB, so that ORM can use enhanced SQL queries. This has been
* achieved by simply removing the identifier backtick.
*
* @package OSB/Modifications
* @subpackage Database
* @category Drivers
* @author Deon George
* @copyright (c) 2010 Open Source Billing
* @license http://dev.osbill.net/license
*/
class Database_MySQL extends Kohana_Database_MySQL {
// MySQL uses a backtick for identifiers
protected $_identifier = '';
}
?>

View File

@@ -0,0 +1,4 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
class HeadImages extends lnApp_HeadImages {}
?>

View File

@@ -0,0 +1,4 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
class HTMLRender extends lnApp_HTMLRender {}
?>

View File

@@ -0,0 +1,81 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class is for rendering HTML body blocks (left, center, right).
*
* It will provide a header, body and footer.
*
* @package lnApp
* @subpackage Page
* @category Helpers
* @author Deon George
* @copyright (c) 2010 Deon George
* @license http://dev.leenooks.net/license.html
* @uses Style
*/
class lnApp_Block extends HTMLRender {
protected static $_data = array();
protected static $_spacer = '<table><tr class="spacer"><td>&nbsp;</td></tr></table>';
protected static $_required_keys = array('body');
/**
* Add a block to be rendered
*
* @param array Block attributes
*/
public static function add($block,$prepend=FALSE) {
parent::add($block);
// Detect any style sheets.
if (! empty($block['style']) && is_array($block['style']))
foreach ($block['style'] as $data=>$media)
Style::add(array(
'type'=>'file',
'data'=>$data,
'media'=>$media,
));
}
/**
* Return an instance of this class
*
* @return Block
*/
public static function factory() {
return new Block;
}
/**
* Render this block
*
* @see HTMLRender::render()
*/
protected function render() {
$output = '';
$styles = array();
$i = 0;
foreach (static::$_data as $value) {
if ($i++)
$output .= static::$_spacer;
$output .= '<table class="block" border="0">';
if (! empty($value['title']))
$output .= sprintf('<tr class="title"><td>%s</td></tr>',$value['title']);
if (! empty($value['subtitle']))
$output .= sprintf('<tr class="subtitle"><td>%s</td></tr>',$value['subtitle']);
$output .= sprintf('<tr class="body"><td>%s</td></tr>',$value['body']);
if (! empty($value['footer']))
$output .= sprintf('<tr class="footer"><td>%s</td></tr>',$value['footer']);
$output .= '</table>';
}
return $output;
}
}
?>

View File

@@ -0,0 +1,64 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class is for rendering a breadcrumb menu.
*
* @package lnApp
* @subpackage Page
* @category Helpers
* @author Deon George
* @copyright (c) 2010 Deon George
* @license http://dev.leenooks.net/license.html
*/
class lnApp_Breadcrumb extends HTMLRender {
protected static $_data = array();
protected static $_spacer = ' &raquo; ';
protected static $_required_keys = array('body');
/**
* Set the breadcrumb path
*
* @param array Block attributes
*/
public static function set($path) {
if (is_string($path))
static::$_data['path'] = explode('/',$path);
else
static::$_data['path'] = $path;
}
/**
* Enable a friendly name to be used for a path
*/
public static function name($path,$name) {
static::$_data['name'][$path] = $name;
}
/**
* Return an instance of this class
*
* @return Breadcrumb
*/
public static function factory() {
return new Breadcrumb;
}
/**
* Render this Breadcrumb
*/
protected function render() {
$output = HTML::anchor('/',_('Home'));
$data = empty(static::$_data['path']) ? explode('/',Request::instance()->uri) : static::$_data['path'];
foreach ($data as $k => $v) {
$output .= static::$_spacer;
$p = join('/',array_slice($data,0,$k+1));
$output .= HTML::anchor($p,empty(static::$_data['name'][$p]) ? ucfirst($v) : static::$_data['name'][$p]);
}
return $output;
}
}
?>

View File

@@ -0,0 +1,129 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class extends the core Kohana class by adding some core application
* specific functions, and configuration.
*
* @package lnApp
* @subpackage Core
* @category Overrides
* @author Deon George
* @copyright (c) 2010 Deon George
* @license http://dev.leenooks.net/license.html
*/
abstract class lnApp_Config extends Kohana {
/**
* Find a list of all database enabled modules
*
* @uses cache
*/
public static function appmodules() {
$cacheable = TRUE;
if (array_key_exists('cache',Kohana::modules())) {
$cache = Cache::instance(static::cachetype());
if ($cacheable AND $cache->get('modules'))
return $cache->get('modules');
} else
$cache = '';
$modules = array();
$module_table = 'module';
if (class_exists('Model_'.ucfirst($module_table))) {
$mo = ORM::factory($module_table)->where('status','=',1)->find_all()->as_array();
foreach ($mo as $o)
$modules[$o->name] = MODPATH.$o->name;
}
if ($cache)
$cache->set('modules',$modules);
return $modules;
}
/**
* Return our site name
*/
public static function site() {
if (! empty($_SERVER['SERVER_NAME']))
return $_SERVER['SERVER_NAME'];
if (! $site = CLI::options('site'))
throw new Kohana_Exception(_('Cant figure out the site, use --site= for CLI'));
return $site['site'];
}
/**
* Work out our site ID for multiehosting
* @todo Change this to query the DB for site number.
*/
public static function siteid() {
$sites = Kohana::config('config.site');
// If we havent been configured for sites
if (is_null($sites) OR ! is_array($sites) OR ! isset($sites[static::site()]))
return 0;
else
return $sites[static::site()];
}
/**
* Work out our site mode (dev,test,prod)
* @todo Change this to query the DB for mode.
*/
public static function sitemode() {
$sites = Kohana::config('config.site_mode');
// If we havent been configured for sites
if (is_null($sites) OR ! is_array($sites) OR ! isset($sites[static::site()]))
return Kohana::PRODUCTION;
else
return $sites[static::site()];
}
public static function sitename() {
return Kohana::config('config.site_name');
}
public static function logo() {
$mediapath = Route::get('default/media');
$logo = $mediapath->uri(array('file'=>'img/logo-small.png'),array('alt'=>static::sitename()));
return HTML::image($logo,array('class'=>'headlogo','alt'=>_('Logo')));
}
/**
* Return our caching mechanism
*/
public static function cachetype() {
return is_null(Kohana::config('config.cache_type')) ? 'file' : Kohana::config('config.cache_type');
}
/**
* Show a date using a site configured format
*/
public static function date($date) {
return date(Kohana::config('config.date_format'),$date);
}
/**
* See if our emails for the template should be sent to configured admin(s)
*
* @param string template - Template to test for
* @return mixed|array - Email to send test emails to
*/
public static function testmail($template) {
$config = Kohana::config('config.email_admin_only');
if (is_null($config) OR ! is_array($config) OR empty($config[$template]))
return FALSE;
else
return $config[$template];
}
}
?>

View File

@@ -0,0 +1,45 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class is for all image icons shown on the page header.
*
* @package lnApp
* @subpackage Page
* @category Helpers
* @author Deon George
* @copyright (c) 2010 Deon George
* @license http://dev.leenooks.net/license.html
*/
class lnApp_HeadImages extends HTMLRender {
protected static $_data = array();
protected static $_spacer = '&nbsp;';
protected static $_required_keys = array('url','img');
/**
* Return an instance of this class
*
* @return HeadImage
*/
public static function factory() {
return new HeadImages;
}
/**
* Render this Header Image
*
* @see HTMLRender::render()
*/
protected function render() {
$output = '';
$mediapath = Route::get(static::$_media_path);
foreach (static::$_data as $value) {
$i = HTML::image($mediapath->uri(array('file'=>$value['img'])),array('alt'=>isset($value['attrs']['title']) ? $value['attrs']['title'] : ''));
$output .= HTML::anchor($value['url'],$i,(isset($value['attrs']) && is_array($value['attrs'])) ? $value['attrs'] : null);
$output .= static::$_spacer;
}
return $output;
}
}
?>

View File

@@ -0,0 +1,94 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class is the base used for common static methods that are used
* for rendering.
*
* @package lnApp
* @subpackage Page
* @category Abstract/Helpers
* @author Deon George
* @copyright (c) 2010 Deon George
* @license http://dev.leenooks.net/license.html
*/
abstract class lnApp_HTMLRender {
protected static $_media_path = 'default/media';
protected static $_required_keys = array();
protected static $_unique_vals = array();
public function __construct() {
if (! isset(static::$_data))
throw new Kohana_Exception(':class is missing important static variables',array(':class'=>get_called_class()));
}
/**
* Add an item to be rendered
*
* @param array Item to be added
*/
public static function add($item,$prepend=FALSE) {
foreach (static::$_required_keys as $key)
if (! isset($item[$key]))
throw new Kohana_Exception('Missing key :key for image',array(':key'=>$key));
// Check for unique keys
if (static::$_unique_vals)
foreach (static::$_unique_vals as $v=>$u)
foreach (static::$_data as $d)
if (isset($d[$u]) && $d['data'] == $item['data'])
return;
if ($prepend)
array_unshift(static::$_data,$item);
else
array_push(static::$_data,$item);
}
/**
* Set the space used between rendering output
*/
public static function setSpacer($spacer) {
static::$_spacer = $spacer;
}
/**
* Set the Kohana Media Path, used to determine where to find additional
* HTML content required for rendering.
*/
public static function setMediaPath($path) {
static::$_media_path = $path;
}
/**
* Factory instance method must be declared by the child class
*/
public static function factory() {
throw new Kohana_Exception(':class is calling :method, when it should have its own method',
array(':class'=>get_called_class(),':method'=>__METHOD__));
}
/**
* Return the HTML to render the header images
*/
public function __toString() {
try {
return static::render();
}
// Display the exception message
catch (Exception $e) {
Kohana::exception_handler($e);
return '';
}
}
/**
* Rendering must be declared by the child class
*/
protected function render() {
throw new Kohana_Exception(':class is calling :method, when it should have its own method',
array(':class'=>get_called_class(),':method'=>__METHOD__));
}
}
?>

View File

@@ -0,0 +1,34 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This is class is for all HTML page attributes.
*
* @package lnApp
* @subpackage Page
* @category Helpers
* @author Deon George
* @copyright (c) 2010 Deon George
* @license http://dev.leenooks.net/license.html
*/
class lnApp_Meta {
private $_data = array();
private $_array_keys = array();
public function __get($key) {
if (in_array($key,$this->_array_keys) && empty($this->_data[$key]))
return array();
if (empty($this->_data[$key]))
return null;
else
return $this->_data[$key];
}
public function __set($key,$value) {
if (in_array($key,$this->_array_keys) && ! is_array($value))
throw new Kohana_Exception('Key :key must be an array',array(':key'=>$key));
$this->_data[$key] = $value;
}
}
?>

View File

@@ -0,0 +1,59 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class is for rendering HTML script tags
*
* @package lnApp
* @subpackage Page
* @category Helpers
* @author Deon George
* @copyright (c) 2010 Deon George
* @license http://dev.leenooks.net/license.html
*/
class lnApp_Script extends HTMLRender {
protected static $_data = array();
protected static $_spacer = "\n";
protected static $_required_keys = array('type','data');
protected static $_unique_vals = array('file'=>'type');
/**
* Return an instance of this class
*
* @return Script
*/
public static function factory() {
return new Script;
}
/**
* Render the script tag
*
* @see HTMLRender::render()
*/
protected function render() {
$foutput = $soutput = '';
$mediapath = Route::get(static::$_media_path);
$i = $j = 0;
foreach (static::$_data as $value) {
switch ($value['type']) {
case 'file':
$foutput .= HTML::script($mediapath->uri(array('file'=>$value['data'])));
if ($i++)
$foutput .= static::$_spacer;
break;
case 'stdin':
$soutput .= sprintf("<script type=\"text/javascript\">//<![CDATA[\n%s\n//]]></script>",$value['data']);
if ($j++)
$soutput .= static::$_spacer;
break;
default:
throw new Kohana_Exception('Unknown style type :type',array(':type'=>$value['type']));
}
}
return $foutput.static::$_spacer.$soutput;
}
}
?>

View File

@@ -0,0 +1,54 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class is for rendering HTML style tags
*
* @package lnApp
* @subpackage Page
* @category Helpers
* @author Deon George
* @copyright (c) 2010 Deon George
* @license http://dev.leenooks.net/license.html
*/
class lnApp_Style extends HTMLRender {
protected static $_data = array();
protected static $_spacer = "\n";
protected static $_required_keys = array('type','data');
/**
* Return an instance of this class
*
* @return Style
*/
public static function factory() {
return new Style;
}
/**
* Render the style tag
*
* @see HTMLRender::render()
*/
protected function render() {
$output = '';
$mediapath = Route::get(static::$_media_path);
$i = 0;
foreach (static::$_data as $value) {
if ($i++)
$output .= static::$_spacer;
switch ($value['type']) {
case 'file':
$output .= HTML::style($mediapath->uri(array('file'=>$value['data'])),
array('media'=>(! empty($value['media'])) ? $value['media'] : 'screen'),TRUE);
break;
default:
throw new Kohana_Exception('Unknown style type :type',array(':type'=>$value['type']));
}
}
return $output;
}
}
?>

View File

@@ -0,0 +1,129 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class is for rendering system information messages.
*
* @package lnApp
* @subpackage SystemMessage
* @category Helpers
* @author Deon George
* @copyright (c) 2010 Deon George
* @license http://dev.leenooks.net/license.html
*/
class lnApp_SystemMessage extends HTMLRender {
protected static $_data = array();
protected static $_spacer = '<table><tr class="spacer"><td>&nbsp;</td></tr></table>';
protected static $_required_keys = array('title','body','type');
/**
* Add a system message to be rendered
*
* @param array System Message attributes
*/
public static function add($msg,$prepend=FALSE) {
if ($msgs = Session::instance()->get('sessionmsgs')) {
static::$_data = $msgs;
}
parent::add($msg);
// Add a gribber popup
Style::add(array(
'type'=>'file',
'data'=>'css/jquery.gritter.css',
'media'=>'screen',
));
Script::add(array(
'type'=>'file',
'data'=>'js/jquery.gritter-1.5.js',
));
Script::add(array(
'type'=>'stdin',
'data'=>sprintf(
'$(document).ready(function() {
$.extend($.gritter.options, {
fade_in_speed: "medium",
fade_out_speed: 2000,
time: "3000",
sticky: false,
});
$.gritter.add({
title: "%s",
text: "%s",
image: "%s",
});});',$msg['title'],$msg['body'],URL::site().static::image($msg['type'],true))));
// Save our messages in our session, so that we get them for redirects
Session::instance()->set('sessionmsgs',static::$_data);
}
/**
* Return an instance of this class
*
* @return SystemMessage
*/
public static function factory() {
return new SystemMessage;
}
/**
* Render an image for the System Message
*/
private static function image($type,$raw=false,$big=false,$alt='') {
$mediapath = Route::get(static::$_media_path);
switch ($type) {
case 'error':
$file = sprintf('img/dialog-error%s.png',$big ? '-big' : '');
break;
case 'info':
$file = sprintf('img/dialog-information%s.png',$big ? '-big' : '');
break;
case 'warning':
$file = sprintf('img/dialog-warning%s.png',$big ? '-big' : '');
break;
case 'debug':
$file = sprintf('img/dialog-question%s.png',$big ? '-big' : '');
break;
default:
throw new Kohana_Exception('Unknown system message type :type',array(':type'=>$value['type']));
}
if ($raw)
return $mediapath->uri(array('file'=>$file));
else
return HTML::image($mediapath->uri(array('file'=>$file)),array('alt'=>$alt ? $alt : '','class'=>'sysicon'));
}
/**
* Render this system message
*
* @see HTMLRender::render()
*/
protected function render() {
$output = '';
$mediapath = Route::get(static::$_media_path);
// Reload our message from the session
if ($msgs = Session::instance()->get('sessionmsgs')) {
Session::instance()->delete('sessionmsgs');
static::$_data = $msgs;
}
$i = 0;
foreach (static::$_data as $value) {
if ($i++)
$output .= static::$_spacer;
$output .= '<table><tr>';
$output .= sprintf('<td class="icon" rowspan="2">%s</td>',static::image($value['type'],false,false,isset($value['alt']) ? $value['alt'] : ''));
$output .= sprintf('<td class="head">%s</td>',$value['title']);
$output .= '</tr><tr>';
$output .= sprintf('<td class="body">%s</td>',$value['body']);
$output .= '</tr></table>';
}
return $output;
}
}
?>

View File

@@ -0,0 +1,4 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
class Meta extends lnApp_Meta {}
?>

View File

@@ -0,0 +1,2 @@
order allow,deny
deny from all

View File

@@ -0,0 +1,34 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* OSB Application Module Model
*
* This module must remain in applications/ as it is used very early in the
* OSB initialisation.
*
* @package OSB
* @subpackage Modules
* @category Models
* @author Deon George
* @copyright (c) 2010 Deon George
* @license http://dev.leenooks.net/license.html
*/
class Model_Module extends ORMOSB {
// Relationships
protected $_has_many = array(
'module_method'=>array(),
);
protected $_has_one = array(
'record_id'=>array()
);
protected $_sorting = array(
'status'=>'DESC',
'name'=>'ASC',
);
protected $_formats = array(
'status'=>array('StaticList_YesNo::display'=>array()),
);
}
?>

View File

@@ -0,0 +1,153 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This class extends Kohana's [ORM] class to create defaults for OSB.
*
* @package OSB
* @subpackage Core
* @category ORM
* @author Deon George
* @copyright (c) 2010 Open Source Billing
* @license http://dev.osbill.net/license.html
*/
abstract class ORMOSB extends ORM {
/**
* @var string Database to connect to
*/
protected $_db = 'default';
protected $_object_formated = array();
protected $_formated = FALSE;
protected $_formats = array();
/**
* @var boolean Database names plural configuration
*/
protected $_table_names_plural = false;
protected $_created_column = array('column'=>'date_orig','format'=>TRUE);
protected $_updated_column = array('column'=>'date_last','format'=>TRUE);
protected $_callbacks = array(
'id'=>array('get_next_id'),
'site_id'=>array('set_site_id'),
);
/**
* Format fields for display purposes
*
* @param string column name
* @return mixed
*/
protected function _format() {
$format = Validate::factory($this->_object);
foreach ($this->_formats as $column => $formats)
$format->filters($column,$formats);
if ($format->check())
foreach ($format as $column => $value)
$this->_object_formated[$column] = $value;
$this->_formated = TRUE;
}
/**
* Return a formated columns, as per the model definition
*/
public function display($column) {
// Trigger a load of the record.
$value = $this->__get($column);
// If some of our fields need to be formated for display purposes.
if ($this->_loaded AND ! $this->_formated AND $this->_formats)
$this->_format();
if (isset($this->_object_formated[$column]))
return $this->_object_formated[$column];
else
return $value;
}
/**
* Our child models should provide an invoice display, this is shown
* on printed invoices.
*/
public function invoice_display() {
throw new Kohana_Exception(':module has not configured an :method, but has made the call',array(':module'=>get_class($this),'method'=>__METHOD__));
}
/**
* Override the _load_result() function so that our site ID is automatically
* added to the SQL query
* @todo This is not picked up by all queries. Need to investigate why
* @todo This is not being done by inserts
*/
protected function _load_result($multiple = FALSE)
{
$this->_db_builder->where($this->_table_name.'.site_id','=',Config::siteid());
return parent::_load_result($multiple);
}
/**
* This function will enhance the [Validate::filter], since it always passes
* the value as the first argument and sometimes functions need that to not
* be the first argument.
*
* Currently this implements:
* [date()][date-ref]
*
* [date-ref]: http://www.php.net/date
*
* This function will throw an exception if called without a function
* defined.
*
* @param mixed $val Value to be processed
* @param string $func Name of function to call
* @param string $arg Other arguments for the function
*/
final public static function _filters($val,$func,$arg) {
switch ($func) {
case 'date':
return date($arg,$val);
default:
throw new Exception(sprintf(_('Unknown function: %s (%s,%s)'),$func,$arg,$val));
}
}
/**
* Get Next record id
*
* @param array Validate object
* @param string Primary Key
*/
public function get_next_id(Validate $array,$field) {
if (! is_null($array[$field]))
return TRUE;
$this->_changed[$field] = $field;
$ido = ORM::factory('module')
->where('name','=',$this->_table_name)
->find();
if (! $ido->loaded())
throw new Kohana_Exception('Problem getting record_id for :table',array(':table'=>$this->_table_name));
$array[$field] = $ido->record_id->next_id($ido->id);
return TRUE;
}
public function set_site_id(Validate $array,$field) {
if (! is_null($array[$field]))
return TRUE;
// @todo This should be a config item
$this->_changed[$field] = $field;
$array[$field] = Config::siteid();
return TRUE;
}
}
?>

View File

@@ -0,0 +1,124 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This is class is for calculating date periods.
*
* @package OSB
* @subpackage Utilities
* @category Helpers
* @author Deon George
* @copyright (c) 2010 Deon George
* @license http://dev.leenooks.net/license.html
*/
class Period {
/**
* Calculate both the start and end dates for a billing period
* and the pro-rata percentage.
*
* See [StaticList_RecurSchedule]
*
* @param StaticList_RecurSchedule Period Type [StaticList_RecurSchedule]
* @param int Starting date if recurring must start on a day of the month
* @param datetime Date to start calculating from, otherwise now() is used
* @param boolean Show dates in 'Y-m-d' format
* @return array
*/
public static function details($type,$weekday=NULL,$start=NULL,$df=FALSE) {
// Our precision for the pro-rata percentage
$precision = 4;
// Make the period consistent, eg: Quarterly = Jan-Mar,Apr-Jun; HalfYearly = Jan-Jun,Jul-Dec
$strict = FALSE;
// Round the time integer to a whole day.
if (is_null($start))
$start = strtotime('today');
else
$start = strtotime(date('Y-m-d',$start));
switch ($type) {
// Weekly
// @todo Make Weekly pro-rata to a day of the week
case 0:
$period_end = $start+(86400*(7-1));
return array('start'=>$start,'date'=>$start,'end'=>$period_end,'prorate'=>1);
# Monthly
case 1:
$inc_months = 1;
break;
# Quarterly
case 2:
# @todo Make this configurable.
$strict = TRUE;
$inc_months = 3;
break;
# Half Yearly
case 3:
# @todo Make this configurable.
$strict = TRUE;
$inc_months = 6;
break;
# Yearly
case 4:
$inc_months = 12;
break;
# Biennial
case 5:
$inc_months = 24;
break;
# Triennial
case 6:
$inc_months = 36;
break;
default:
return FALSE;
}
// If workout a day of week we calculate to.
if (is_null($weekday))
$weekday = date('d',$start);
$used_months = 0;
if ($strict && $type > 0 && $type < 5)
$used_months = $inc_months-(($inc_months-(date('n',$start)%$inc_months))%$inc_months+1);
$d = mktime(0,0,0,date('m',$start)-$used_months,$weekday,date('y',$start));
if ($d <= $start)
$period_start = $d;
else
$period_start = mktime(0,0,0,date('m',$d)-1-$used_months,$weekday,date('y',$d));
$period_end = mktime(0,0,0,date('m',$period_start)+$inc_months,$weekday,date('y',$period_start));
$total_time = $period_end-$period_start;
$remain_time = $period_end-$start;
$used_time = $start-$period_start;
// Change our end date to the day before
$period_end -= 86400;
$return = array(
'start'=>$period_start,
'date'=>$start,
'end'=>$period_end,
'weekday'=>$weekday,
'prorata'=>round($remain_time/$total_time,$precision),
'total_time'=>sprintf('%3.1f',$total_time/86400),
'remain_time'=>sprintf('%3.1f',$remain_time/86400),
'used_time'=>sprintf('%3.1f',$used_time/86400));
// @todo Use the configured data format
if ($df)
foreach (array('start','date','end') as $key)
$return[$key] = date('Y-m-d',$return[$key]);
return $return;
}
}
?>

View File

@@ -0,0 +1,4 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
class Script extends lnApp_Script {}
?>

View File

@@ -0,0 +1,63 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This is class renders standard lists and their values
*
* @package OSB
* @subpackage Utilities
* @category Helpers
* @author Deon George
* @copyright (c) 2010 Deon George
* @license http://dev.leenooks.net/license.html
*/
abstract class StaticList {
// This is our list of items that will be rendered
protected $list = array();
/**
* Each static list type must provide the table function that contains
* the table of list and values.
*/
abstract protected function table();
public static function factory() {
throw new Kohana_Exception(':class is calling :method, when it should have its own method',
array(':class'=>get_called_class(),':method'=>__METHOD__));
}
/**
* Display a static name for a value
*
* @param key $id value to render
* @see _display()
*/
public static function display($value) {
return static::_display($value);
}
// Due to static scope, sometimes we need to call this function from the child class.
protected static function _display($id) {
$table = static::factory()->table();
if (! $table OR empty($table[$id]))
return sprintf('No Value (%s)',$id);
else
return $table[$id];
}
/**
* Renders form input
*
* @param string Form name to render
* @param string Default value to populate in the Form input.
*/
public static function form($name,$default='',$addblank=FALSE) {
$table = static::factory()->table();
if ($addblank)
$table = array_merge(array(''=>'&nbsp;'),$table);
return Form::Select($name,$table,$default);
}
}
?>

View File

@@ -0,0 +1,23 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This is class renders responses and forms based on the values from a module.
*
* @package OSB
* @subpackage Utilities
* @category Helpers
* @author Deon George
* @copyright (c) 2010 Deon George
* @license http://dev.leenooks.net/license.html
*/
class StaticList_Module extends StaticListModule {
protected function table($module=NULL) {
if (is_null($module))
throw new Kohana_Exception('Module is a required attribute.');
}
public static function factory() {
return new StaticList_Module;
}
}
?>

View File

@@ -0,0 +1,30 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This is class renders Price Type responses and forms.
*
* @package OSB
* @subpackage Utilities
* @category Helpers
* @author Deon George
* @copyright (c) 2010 Deon George
* @license http://dev.leenooks.net/license.html
*/
class StaticList_PriceType extends StaticList {
protected function table() {
return array(
0=>_('One-time Charge'),
1=>_('Recurring Membership/Subscription'),
2=>_('Trial for Membership/Subscription')
);
}
public static function factory() {
return new StaticList_PriceType;
}
public static function display($value) {
return static::_display($value);
}
}
?>

View File

@@ -0,0 +1,56 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This is class renders Recurring Schedule Times on responses and forms.
*
* @package OSB
* @subpackage Utilities
* @category Helpers
* @author Deon George
* @copyright (c) 2010 Deon George
* @license http://dev.leenooks.net/license.html
*/
class StaticList_RecurSchedule extends StaticList {
protected function table() {
return array(
0=>_('Weekly'),
1=>_('Monthly'),
2=>_('Quarterly'),
3=>_('Semi-Annually'),
4=>_('Annually'),
5=>_('Two years'),
6=>_('Three Years')
);
}
public static function factory() {
return new StaticList_RecurSchedule;
}
public static function display($value) {
return static::_display($value);
}
/**
* Renders the price display for a product
*
* @uses product
*/
public static function form($name,$product='',$addblank=FALSE) {
if (empty($product))
throw new Kohana_Exception('Product is a required field for :method',array(':method'=>__METHOD__));
$x = '';
$table = static::factory()->table();
foreach ($product->get_price_array() as $term => $price) {
$x[$term] = sprintf('%s %s',Currency::display($price['price_base']),$table[$term]);
if ($price['price_setup'] > 0)
$x[$term] .= sprintf(' + %s %s',Currency::display($price['price_setup']),_('Setup'));
}
return Form::select($name,$x,$product->price_recurr_default);
}
}
?>

View File

@@ -0,0 +1,29 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This is class renders Person Title responses and forms.
*
* @package OSB
* @subpackage Utilities
* @category Helpers
* @author Deon George
* @copyright (c) 2010 Deon George
* @license http://dev.leenooks.net/license.html
*/
class StaticList_Title extends StaticList {
protected function table() {
return array(
'mr'=>_('Mr'),
'ms'=>_('Ms'),
'mrs'=>_('Mrs'),
'miss'=>_('Miss'),
'dr'=>_('Dr'),
'prof'=>_('Prof')
);
}
public static function factory() {
return new StaticList_Title;
}
}
?>

View File

@@ -0,0 +1,29 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This is class renders Yes/No responses and forms.
*
* @package OSB
* @subpackage Utilities
* @category Helpers
* @author Deon George
* @copyright (c) 2010 Deon George
* @license http://dev.leenooks.net/license.html
*/
class StaticList_YesNo extends StaticList {
protected function table() {
return array(
0=>_('No'),
1=>_('Yes'),
);
}
public static function factory() {
return new StaticList_YesNo;
}
public static function display($value) {
return static::_display($value);
}
}
?>

View File

@@ -0,0 +1,83 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* This is class is renders standard values based on DB table row values.
*
* @package OSB
* @subpackage Utilities
* @category Helpers
* @author Deon George
* @copyright (c) 2010 Deon George
* @license http://dev.leenooks.net/license.html
*/
abstract class StaticListModule extends StaticList {
protected static $record = array();
/**
* Display a static name for a value
*/
public static function display($id) {
// Override our argument list as defined in parent
list($table,$key,$skey,$value) = func_get_args();
$db = DB::select($key)->from($table)->where($skey,'=',$value)->execute();
if ($db->count() !== 1)
return sprintf('No Value (%s)',$value);
else
return $db->get($key);
}
/**
* This function is to return the cached value of the current active record
* This is so that a follow up call to get an attribute of a value retrieved
* can reuse the active record values.
* This gets over a limitation where the query to form() to get a default
* no longer exists (or is invalid) and you want other attributes of the
* remaining active record, which may not be the default record.
*/
public static function record($table,$attribute,$skey,$value) {
if (empty(static::$record[$table]))
return static::display($table,$attribute,$skey,$value);
else
return static::$record[$table][$attribute];
}
/**
* Renders form input
*/
public static function form($name,$default='',$addblank=FALSE) {
// Override our argument list as defined in parent
list($name,$table,$default,$key,$value,$where) = func_get_args();
// @todo - our query type should come from our configuration?
$db = DB::select()->from($table);
foreach ($where as $k=>$v) {
list ($op,$v) = explode(':',$v);
$db->where($k,$op,$v);
}
$db = $db->execute();
// If we only have one record, dont make a select list
if ($db->count() == 1) {
static::$record[$table] = $db->as_array();
static::$record[$table] = array_shift(static::$record[$table]);
return Form::hidden($name,$db->get($key)).$db->get($value);
}
// Else we return a select list
$x = array();
foreach ($db as $record) {
$x[$record[$key]] = $record[$value];
// Save our static record, in case we reference this item again.
if ($record[$key] == $default)
static::$record[$table] = $record;
}
return Form::select($name,$x,$default);
}
}
?>

View File

@@ -0,0 +1,4 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
class Style extends lnApp_Style {}
?>

View File

@@ -0,0 +1,4 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
class SystemMessage extends lnApp_SystemMessage {}
?>

View File

@@ -0,0 +1,29 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* Array and variable validation.
*
* @package OSB/Modifications
* @category Classes
* @author Deon George
* @copyright (c) 2010 Deon George
* @license http://dev.leenooks.net/license.html
*/
class Validate extends Kohana_Validate {
/**
* Checks if a field matches the value of another field, if it is set.
* Field is ignored if it is blank.
*
* This function is only invoked anyway when the value is set as per
* $this->_empty_rules
*
* @param string field value
* @param string field name to match
* @return boolean
*/
protected function matches_ifset($value, $match)
{
return ($value === $this[$match]);
}
}
?>