M Demo
M demo massively uses JQuery UI tabs and Hijaxing, and is therefore mostly invisble to Google and the likes.
This page is an unclickable collection of its content.
Click M Demo to see the demo.
sql error: Can't read dir of './ohadalon_mdemo/' (errno: 20)
show tables from ohadalon_mdemo
Warning: in_array() [function.in-array]: Wrong datatype for second argument in /home/ohadalon/public_html/M/Mmodel.class.php on line 535
Warning: in_array() [function.in-array]: Wrong datatype for second argument in /home/ohadalon/public_html/M/Mmodel.class.php on line 535
Creating table authors from sql/authors.crtable.sql
sql error: Can't create table 'authors' (errno: 20)
CREATE TABLE `authors` (
`id` int(11) NOT NULL auto_increment,
`first` varchar(255) default NULL,
`last` varchar(255) default NULL,
PRIMARY KEY (`id`)
)
Loading authors from sql/authors.data.sql
sql error: Can't find file: './ohadalon_mdemo/authors.frm' (errno: 20)
INSERT INTO authors (id, first, last) VALUES (1,'Isaac','Asimov');
sql error: Can't find file: './ohadalon_mdemo/authors.frm' (errno: 20)
INSERT INTO authors (id, first, last) VALUES (2,'Robert ','Heinlein');
sql error: Can't find file: './ohadalon_mdemo/authors.frm' (errno: 20)
INSERT INTO authors (id, first, last) VALUES (3,'Stanisław','Lem');
sql error: Can't find file: './ohadalon_mdemo/authors.frm' (errno: 20)
INSERT INTO authors (id, first, last) VALUES (4,'Charles','Dickens');
sql error: Can't find file: './ohadalon_mdemo/authors.frm' (errno: 20)
INSERT INTO authors (id, first, last) VALUES (5,'J. R. R. ','Tolkien');
sql error: Can't find file: './ohadalon_mdemo/authors.frm' (errno: 20)
INSERT INTO authors (id, first, last) VALUES (6,'Roald','Dahl');
sql error: Can't find file: './ohadalon_mdemo/authors.frm' (errno: 20)
INSERT INTO authors (id, first, last) VALUES (7,'Douglas','Adams');
sql error: Can't find file: './ohadalon_mdemo/authors.frm' (errno: 20)
show columns from authors
No columns for authors
/home/ohadalon/public_html/M/Mtable.class.php:70: Mtable:__construct: authors: no auto_incrment column
sql error: Can't find file: './ohadalon_mdemo/authors.frm' (errno: 20)
select * from authors order by last,first
sql error: Can't find file: './ohadalon_mdemo/authors.frm' (errno: 20)
select * from authors order by last,first
Warning: Invalid argument supplied for foreach() in /home/ohadalon/public_html/Mdemo/everything.php on line 29
New Game
See the Source Code
Authors
select * from authors order by last, first
sql error: Can't find file: './ohadalon_mdemo/authors.frm' (errno: 20)
select * from authors order by last, first
Books
select * from books order by title
sql error: Can't find file: './ohadalon_mdemo/books.frm' (errno: 20)
select * from books order by title
Join
select a.*, b.* from authors a, books b where a.id = b.authorId order by a.first, a.last, b.title
sql error: Can't find file: './ohadalon_mdemo/authors.frm' (errno: 20)
select a.*, b.* from authors a, books b where a.id = b.authorId order by a.first, a.last, b.title
Foundation
select a.first, a.last, b.title from authors a, books b where a.id = b.authorId and b.title like '%Foundation%' order by a.first, a.last, b.title
sql error: Can't find file: './ohadalon_mdemo/authors.frm' (errno: 20)
select a.first, a.last, b.title from authors a, books b where a.id = b.authorId and b.title like '%Foundation%' order by a.first, a.last, b.title
The Complete Cartesian Product
select a.*, b.* from authors a, books b order by a.first, a.last, b.title
sql error: Can't find file: './ohadalon_mdemo/authors.frm' (errno: 20)
select a.*, b.* from authors a, books b order by a.first, a.last, b.title
Joins.class.php
<?php
/*------------------------------------------------------------*/
class Joins extends Mcontroller {
/*------------------------------------------------------------*/
public function index() {
$queries = array(
array(
'title' => "Authors",
'sql' => "select * from authors order by last, first",
'exportFileName' => "Authors",
),
array(
'title' => "Books",
'sql' => "select * from books order by title",
'exportFileName' => "Books",
),
array(
'title' => "Join",
'sql' => "select a.*, b.* from authors a, books b where a.id = b.authorId order by a.first, a.last, b.title",
'exportFileName' => "BooksAndAuthors",
),
array(
'title' => "Foundation",
'sql' => "select a.first, a.last, b.title from authors a, books b where a.id = b.authorId and b.title like '%Foundation%' order by a.first, a.last, b.title",
'exportFileName' => "Foundation",
),
array(
'title' => "The Complete Cartesian Product",
'sql' => "select a.*, b.* from authors a, books b order by a.first, a.last, b.title",
'exportFileName' => "Cartesian",
),
);
foreach ( $queries as $query ) {
Mview::msg($query['title'], true);
Mview::msg($query['sql']);
$this->showRows($query['sql'], true, $query['exportFileName']);
echo "<br /><br />\n";
}
$file = "Joins.class.php";
Mview::msg($file);
highlight_file($file);
}
/*------------------------------------------------------------*/
}
/*------------------------------------------------------------*/
|
|
|
|
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
|
|
index.php
<?php
/*------------------------------------------------------------*/
/*
/*------------------------------------------------------------*/
session_start();
/*------------------------------------------------------------*/
require_once("mdemoConfig.php");
require_once("../M/mfiles.php");
require_once("Mdemo.class.php");
/*------------------------------------------------------------*/
/**
* not part of Mdemo
* page hits to these pages are counted for statistical purposed
* no private user information is collected.
*/
$phFile = "../tas/PageHit.class.php";
if ( file_exists($phFile) ) {
require_once($phFile);
$ph = new PageHit;
$ph->hit();
}
/*------------------------------------------------------------*/
global $Mview;
global $Mmodel;
$Mview = new Mview;
$Mview->assign("M", M_URL);
$Mmodel = new Mmodel;
/*------------------------------------------------------------*/
/**
* Mdemo extends Mcontroller and so dispatches URLs from here
* use PATH_INFO=/className/action/otherparts for mod rewrite pretty URLs or just ?className=...&action=...&otherargs
* Mcontroller extended $this->pathParts() returns array of argumnets to pretty URLs
*/
$Mdemo = new Mdemo;
$Mdemo->control();
/*------------------------------------------------------------*/
Mdemo.class.php
<?php
/*------------------------------------------------------------*/
class Mdemo extends Mcontroller {
/*------------------------------------------------------------*/
/**
* allow Mcontroller to do all the URL dispatching
* (this controller is called from index.php)
/*------------------------------------------------------------*/
/**
* if the url has no action specified, than we are just starting.
* show the frame page with the header, footer and the jquery UI tab center setup.
*/
public function index() {
$this->Mview->showTpl("mdemo.tpl", array(
"M" => M_URL,
));
}
/*------------------------------------------------------------*/
}
/*------------------------------------------------------------*/
tpl/mdemo.tpl
<!--
display all header and footer information
and let jquery UI tabs Ajax everything else in the center without ever refreshing
-->
{msuShowTpl file="mdemoHead.tpl"}
{msuShowTpl file="mdemoHeader.tpl"}
<div style="font-size:70%;" class="tabs">
<ul>
<li><a class="noHijax tabLabel" href="?className=Authors&action=listAuthors"><span>Authors & Books</span></a></li>
<li><a class="noHijax tabLabel" href="?className=TicTacToe"><span>Tic Tac Toe</span></a></li>
<li><a class="noHijax tabLabel" href="?className=Joins"><span>Join Tutorial</span></a></li>
<li><a class="noHijax tabLabel" href="?className=Cal"><span>Calendar</span></a></li>
<li><a class="noHijax tabLabel" href="?className=ShowSource"><span>See the Source Code</span></a></li>
<li><a class="noHijax tabLabel" href="?className=Mview&action=showTpl&tpl=admin.tpl"><span>Admin</span></a></li>
</ul>
</div>
{msuShowTpl file="mdemoFooter.tpl"}
{msuShowTpl file="mdemoFoot.tpl"}
Authors.class.php
<?php
/*------------------------------------------------------------*/
class Authors extends Mtable {
/*------------------------------------------------------------*/
/**
* an M scaffold is and extension of the class Mtable which in turn extends Mcontroller
* tell it the name of the table and the default sort order
*/
public function __construct() {
parent::__construct("authors", "last, first");
}
/*------------------------------------------------------------*/
public function listAuthors() {
/**
* use Mmodel to get the data from the database
* and pass the data to Mview to send the ajaxed content
* back to the browser for display
*
*/
$this->Mview->showTpl("authorsList.tpl", array(
"authors" => $this->Mmodel->getRows("select * from authors order by last,first"),
));
}
/*------------------------------------------------------------*/
public function listBooks() {
/**
* more the same
*/
$authorId = $_REQUEST['authorId'];
$this->Mview->showTpl("bookList.tpl", array(
"books" => $this->Mmodel->getRows("select * from books where authorId = $authorId order by title"),
));
}
/*------------------------------------------------------------*/
}
/*------------------------------------------------------------*/
TicTacToe.class.php
<?php
/*------------------------------------------------------------*/
define('YOU', 'X');
define('ME', 'O');
/*------------------------------------------------------------*/
class TicTacToe extends Mcontroller {
/*------------------------------------------------------------*/
private $game;
/*------------------------------------------------------------*/
/**
* the default action is to start a new game.
*
* (this method is called by Mcontroller if no action is
* specified in the url)
*/
public function index() {
$this->newGame();
}
/*------------------------------------------------------------*/
/**
* to start a new game
* empty or re-empty the game array
* by placing null content in all the cells
*/
public function newGame() {
$this->game = array(
array(null, null, null,),
array(null, null, null,),
array(null, null, null,),
);
$_SESSION['game'] = $this->game;
$this->show();
}
/*------------------------------------------------------------*/
/**
* a player's move:
* place the player's chip in the requested spot
* and make the next move
*/
public function play() {
$this->game = $_SESSION['game'];
$x = $_REQUEST['x'];
$y = $_REQUEST['y'];
$this->game[$x][$y] = YOU;
$this->next();
}
/*------------------------------------------------------------*/
/**
* show the source code
*/
public function source() {
$this->menu();
$files = array(
"TicTacToe.class.php",
"tpl/ticTacToe.menu.tpl",
"tpl/ticTacToe.tpl",
);
foreach ( $files as $file ) {
$this->Mview->msg($file);
highlight_file($file);
}
}
/*------------------------------------------------------------*/
/*------------------------------------------------------------*/
/*------------------------------------------------------------*/
/**
* show the board
* if the game is over then game buttons are deactivated
*/
private function show($gameOver = false) {
$this->menu();
$this->Mview->showTpl("ticTacToe.tpl", array(
'game' => $this->game,
'active' => ! $gameOver,
));
}
/*------------------------------------------------------------*/
/**
* put on screen a menu of controls, if its not there already
*/
private function menu() {
static $vistied = false;
if ( $vistied )
return;
$vistied = true;
$this->Mview->showTpl("ticTacToe.menu.tpl");
}
/*------------------------------------------------------------*/
/**
* what to do next
* after the player played.
* check to see if the game is over.
* if it is, tell win/lose result and show
* the final board, with buttons deactivated.
* otherwise, make the move, and again check if the game is over.
*/
private function next() {
$this->menu();
if ( $this->isWinner(YOU)) {
$this->Mview->msg("Contratz. You win. Wanna Play Again?");
$this->show(true);
return;
}
if ( $this->isTie() ) {
$this->Mview->msg("Tie. Wanna Play Again?");
$this->show(true);
return;
}
$this->move();
if ( $this->isWinner(ME)) {
$this->Mview->msg(":-( Wanna Play Again?");
$this->show(true);
return;
}
$_SESSION['game'] = $this->game;
$this->show();
}
/*------------------------------------------------------------*/
/**
* is the given player ('ME' or 'YOU') a winner on the current state of the board
* check each of the rows, columns, and diagonals
*/
private function isWinner($who) {
$m = $this->game;
for($i=0;$i<3;$i++) {
// rows
if ( $this->trioWins($m[$i], $who) )
return(true);
// columns
if ( $this->trioWins(Mutils::arrayColumn($m, $i), $who) )
return(true);
}
// top-left to bottom-right
if ( $this->trioWins(array($m[0][0], $m[1][1], $m[2][2],), $who) )
return(true);
// top-right to bottom-left
if ( $this->trioWins(array($m[0][2], $m[1][1], $m[2][0],), $who) )
return(true);
return(false);
}
/*------------------------------------------------------------*/
/**
* does this array of three values represent a winning for the given player
* it does if all three values equal the argument
*/
private function trioWins($trio, $who) {
for($i=0;$i<3;$i++)
if ( $trio[$i] != $who )
return(false);
return(true);
}
/*------------------------------------------------------------*/
/**
* check if the board represents a tie.
* in fact, do not tell its a tie if there are still moves left
* (so they can be played anyway, even if to futility),
* and since this method is only called after winning combinations were checked,
* it boils down to merely checking to see that all places are filled with chips.
*/
private function isTie() {
$m = $this->game;
for($x=0;$x<3;$x++)
for($y=0;$y<3;$y++)
if ( $m[$x][$y] == null )
return(false);
return(true);
}
/*------------------------------------------------------------*/
/**
* make a move
* this is the game strategy
* first try to see if a win is possible
* otherwise, block any potential win by the player
* otherwise, see if you can place a chip in the center
* otherwise, see if you can place a chip in a corner
* otherwise, see if you can place a chip on a side
* all the play functions return true if a chip was placed and false otherwise
*/
private function move() {
$moveFuncs = array('win', 'block', 'center', 'corner', 'side');
foreach ( $moveFuncs as $func )
if ( $this->$func() )
return;
}
/*------------------------------------------------------------*/
/**
* try to win by completing a trio with the value ME
*/
private function win() {
return($this->complete(ME));
}
/*------------------------------------------------------------*/
/**
* try to block the player by completing the players trio
* in place of the player.
*/
private function block() {
return($this->complete(YOU));
}
/*------------------------------------------------------------*/
/**
* try to complete a trio that already has two chips of the same kind
* if the chips are 'ME', then this is a win attempt
* if the chips are 'YOU' then this is a block attempt
* since if there are two block cases, the game is lost anyway
* it is not important which is being blocked
* the method completeTrio() is called successively with all potential possibilties
* until a completion is found
* in which case the chip is placed in the designated place
* completeTrio is passed a trio in each case
* and the exact position is 'calculated' from the return value
*/
private function complete($who) {
$m = $this->game;
for($i=0;$i<3;$i++) {
// rows
if ( ($idx = $this->completeTrio($m[$i], $who)) >= 0 ) {
$this->game[$i][$idx] = ME;
return(true);
}
// columns
if ( ($idx = $this->completeTrio(Mutils::arrayColumn($m, $i), $who)) >= 0 ) {
$this->game[$idx][$i] = ME;
return(true);
}
}
// top-left to bottom-right
if ( ($idx = $this->completeTrio(array($m[0][0], $m[1][1], $m[2][2],), $who)) >= 0 ) {
$this->game[$idx][$idx] = ME;
return(true);
}
// top-right to bottom-left
if ( ($idx = $this->completeTrio(array($m[0][2], $m[1][1], $m[2][0],), $who)) >= 0 ) {
$this->game[$idx][2-$idx] = ME;
return(true);
}
return(false);
}
/*------------------------------------------------------------*/
/**
* given an array of three values
* tell if it can be completed by placing a chip on the only vacant position.
* return the position (0-2) if so, or -1 if not
*/
private function completeTrio($trio, $who) {
$numComplete = 0;
$ret = null;
for($i=0;$i<3;$i++) {
if ( $trio[$i] != null && $trio[$i] != $who )
return(-1);
if ( $trio[$i] == $who )
$numComplete++;
else
$ret = $i;
}
if ( $numComplete == 2 )
return($ret);
return(-1);
}
/*------------------------------------------------------------*/
/**
* try to place a chip in the center of the baord
*/
private function center() {
return($this->place(1, 1));
}
/*------------------------------------------------------------*/
/**
* try to place a chip in the given position
*/
private function place($x, $y) {
if ( $this->game[$x][$y] != null )
return(false);
$this->game[$x][$y] = ME ;
return(true);
}
/*------------------------------------------------------------*/
/**
* try to place a chip in any corner
* randomize so that the move is a bit less predictable
*/
private function corner() {
$corners = array(
array(0,0),
array(0,2),
array(2,0),
array(2,2),
);
return($this->placeAtRandom($corners));
}
/*------------------------------------------------------------*/
/**
* place in any of the given list of places at random
*/
private function placeAtRandom($places) {
shuffle($places);
foreach ( $places as $place )
if ( $this->place($place[0], $place[1]) )
return(true);
return(false);
}
/*------------------------------------------------------------*/
/**
* try to place a chip in any of the sides
*/
private function side() {
$sides = array(
array(0,1),
array(1,0),
array(1,2),
array(2,1),
);
return($this->placeAtRandom($sides));
}
/*------------------------------------------------------------*/
}
/*------------------------------------------------------------*/
Joins.class.php
<?php
/*------------------------------------------------------------*/
class Joins extends Mcontroller {
/*------------------------------------------------------------*/
public function index() {
$queries = array(
array(
'title' => "Authors",
'sql' => "select * from authors order by last, first",
'exportFileName' => "Authors",
),
array(
'title' => "Books",
'sql' => "select * from books order by title",
'exportFileName' => "Books",
),
array(
'title' => "Join",
'sql' => "select a.*, b.* from authors a, books b where a.id = b.authorId order by a.first, a.last, b.title",
'exportFileName' => "BooksAndAuthors",
),
array(
'title' => "Foundation",
'sql' => "select a.first, a.last, b.title from authors a, books b where a.id = b.authorId and b.title like '%Foundation%' order by a.first, a.last, b.title",
'exportFileName' => "Foundation",
),
array(
'title' => "The Complete Cartesian Product",
'sql' => "select a.*, b.* from authors a, books b order by a.first, a.last, b.title",
'exportFileName' => "Cartesian",
),
);
foreach ( $queries as $query ) {
Mview::msg($query['title'], true);
Mview::msg($query['sql']);
$this->showRows($query['sql'], true, $query['exportFileName']);
echo "<br /><br />\n";
}
$file = "Joins.class.php";
Mview::msg($file);
highlight_file($file);
}
/*------------------------------------------------------------*/
}
/*------------------------------------------------------------*/
Cal.class.php
<?php
/*------------------------------------------------------------*/
class Cal extends Mcontroller {
/*------------------------------------------------------------*/
public function index() {
$this->showCal(date("Y"), date("n"));
}
/*------------------------------------------------------------*/
public function showCal($year = null, $month = null) {
if ( ! $year )
$year = $_REQUEST['year'];
if ( ! $month )
$month = $_REQUEST['month'];
$cal = Mdate::cal($year, $month);
$this->menu($year, $month);
$this->Mview->showTpl("cal.tpl", array(
'cal' => $cal,
'today' => ( date("Y") == $year && date("n") == $month ) ? date("d") : null,
));
}
/*------------------------------------------------------------*/
private function menu($year = null, $month = null) {
if ( $year == null )
$year = date("Y");
if ( $month == null )
$month = date("m");
$this->Mview->showTpl("cal.menu.tpl", array(
'year' => $year,
'month' => $month,
'months' => Mdate::monthLlist(),
));
}
/*------------------------------------------------------------*/
public function nextYear() {
$this->showCal($_REQUEST['year']+1, $_REQUEST['month']);
}
/*------------------------------------------------------------*/
public function prevYear() {
$this->showCal($_REQUEST['year']-1, $_REQUEST['month']);
}
/*------------------------------------------------------------*/
public function nextMonth() {
$this->showCal($_REQUEST['year'] + (($_REQUEST['month'] == 12) ? 1 : 0), ($_REQUEST['month'])%12+1);
}
/*------------------------------------------------------------*/
public function prevMonth() {
$this->showCal($_REQUEST['year'] - (($_REQUEST['month'] == 1) ? 1 : 0), ($_REQUEST['month']+10)%12+1);
}
/*------------------------------------------------------------*/
}
/*------------------------------------------------------------*/
ShowSource.class.php
<?php
/*------------------------------------------------------------*/
class ShowSource extends Mcontroller {
/*------------------------------------------------------------*/
public function index() {
$this->menu();
}
/*------------------------------------------------------------*/
public function fileList() {
$files = array(
"index.php",
"Mdemo.class.php",
"tpl/mdemo.tpl",
"Authors.class.php",
"TicTacToe.class.php",
"Joins.class.php",
"Cal.class.php",
"ShowSource.class.php",
"../M/Mmodel.class.php",
"../M/Mview.class.php",
"../M/Mcontroller.class.php",
);
return($files);
}
/*------------------------------------------------------------*/
public function menu() {
$this->Mview->showTpl("showSource.menu.tpl", array(
'files' => $this->fileList(),
));
}
/*------------------------------------------------------------*/
public function showFile($file = null) {
if ( ! $file ) {
$fileId = $_REQUEST['fileId'];
$files = $this->fileList();
$file = $files[$fileId];
}
echo "<h4>$file</h4>\n";
highlight_file($file);
}
/*------------------------------------------------------------*/
}
/*------------------------------------------------------------*/
../M/Mmodel.class.php
<?php
/*------------------------------------------------------------*/
/**
* Mmodel - mysql convenience utilities
*
* @package M
* @author Ohad Aloni
*/
/*------------------------------------------------------------*/
/**
* Mmodel may be used independently,
* It might use Mview to display some error messages
*/
require_once("Mview.class.php");
/*------------------------------------------------------------*/
/**
* Mmodel - mysql convenience utilities
*
* @package M
* @author Ohad Aloni
*/
class Mmodel {
/*------------------------------------------------------------*/
private $isConnected = false;
/*------------------------------------------------------------*/
private $dbHost = null;
private $dbUser = null;
private $dbPasswd = null;
private $dbName = null;
/*------------------------------*/
private $dbHandle = null;
private $lastSql = null;
private $lastError = null;
private $lastInsertId = null;
/*------------------------------------------------------------*/
public function __construct($user = null, $passwd = null, $dbName = null, $host = null) {
if ( $host )
$this->dbHost = host;
elseif ( defined('M_HOST') )
$this->dbHost = M_HOST;
else
$this->dbHost = 'localhost';
if ( $dbName )
$this->dbName = $dbName;
elseif ( defined('M_DBNAME') && M_DBNAME != 'none' )
$this->dbName = M_DBNAME;
else
$this->dbName = null;
if ( $user )
$this->dbUser = $user;
elseif ( defined('M_USER') )
$this->dbUser = M_USER;
else
$this->dbUser = null;
if ( $passwd )
$this->dbPasswd = $passwd;
elseif ( defined('M_PASSWORD') )
$this->dbPasswd = M_PASSWORD;
else
$this->dbPasswd = null;
if ( ! $this->dbUser || ! $this->dbPasswd ) {
Mview::error("Must define M_USER M_PASSWORD or construct Mmodel with arguments");
return;
}
if ( ! $this->selectHost($this->dbHost, $this->dbUser, $this->dbPasswd, $this->dbName) )
return;
$this->isConnected = true;
}
/*------------------------------*/
public function isConnected() {
return($this->isConnected);
}
/*------------------------------------------------------------*/
public function selectHost($dbHost, $dbUser, $dbPasswd, $dbName) {
$this->dbHost = $dbHost;
$this->dbUser = $dbUser;
$this->dbPasswd = $dbName;
$this->dbName = $dbName;
$this->dbHandle = @mysql_connect($dbHost, $dbUser, $dbPasswd);
if ( ! $this->dbHandle ) {
$error = mysql_error();
$this->lastError = $error;
Mview::error("Could not connect to db: $error");
return(false);
}
$res = $this->query("SET NAMES 'utf8'");
if ( ! $res ) {
}
if ( $dbName ) {
if ( ! $this->selectDb($dbName) ) {
$error = @mysql_error() ;
$this->lastError = $error;
Mview::error("Unable to select db $dbName: $error");
}
}
return(true);
}
/*------------------------------------------------------------*/
/**
* use database
*/
public function selectDB($db) {
$this->dbName = $db;
$ret = @mysql_select_db($this->dbName);
if ( $ret == false ) {
$error = @mysql_error() ;
$this->lastError = $error;
return(false);
}
return(true);
}
/*------------------------------*/
/**
* use database
*/
public function useDB($db) {
return($this->selectDB($db));
}
/*------------------------------------------------------------*/
/**
* id of last insert - same as mysql_insert_id()
*
* @return int
*/
public function insertId() {
return($this->lastInsertId);
}
/*------------------------------------------------------------*/
/**
* make a string usable in quotes of sql statement
* @param string
* @return string
*/
public function str($str) {
if ( ! $str )
return($str);
// if they are already escaped
$ret = str_replace("\\'", "'", $str);
$ret = str_replace("'", "\\'", $ret);
$ret = str_replace("\r\n", "\n", $ret);
$ret = str_replace("\n", "\\n", $ret);
return($ret);
}
/*------------------------------------------------------------*/
/**
* query() is almost synonim to mysql_query().<br />
* It is used for streaming when possibly large data sets are expected.
*
* For queries expecting a single row, item or a relativelly small result set,<br />
* use getRow(), getRows(), getString(), getStrings() or getInt()<br />
* for a complete "round trip" and single-line-coding convenience
*
* @param string the sql query
* @return resource as returned from mysql_query()
*/
public function query($sql) {
$res = null;
try {
$res = @mysql_query($sql);
} catch (Exception $e) {
$msg = $e->getMessage();
Mview::error($msg);
$error = @mysql_error();
$this->lastError = $error;
if ( $error )
Mview::error("sql error: $error");
Mview::error($sql);
return(null);
}
if ( ! $res ) {
$error = @mysql_error();
$this->lastError = $error;
if ( $error )
Mview::error("sql error: $error");
Mview::error($sql);
if ( stristr($error, "MySQL server has gone away") ) {
Mview::error("EXITTING");
exit; // servers will auto restart
}
return(null);
}
return($res);
}
/*------------------------------------------------------------*/
/**
* execute a query, expecting no particular resulting data.
*
* @param string the query
* @param bool used internally (see dbLog())
* @return int number of rows affected by the query
*/
public function _sql($sql, $rememberLastSql = true) {
$res = $this->query($sql);
if ( ! $res ) {
return(null);
}
if ( $rememberLastSql )
$this->lastSql = $sql;
$affected = @mysql_affected_rows();
@mysql_free_result($res);
return($affected);
}
/*------------------------------*/
/**
* execute a query, expecting no particular resulting data.
*
* @param string the query
* @return int number of rows affected by the query
*/
public function sql($sql) {
if ( ! $this->isConnected ) {
Mview::error("Mmodel not connected");
return(null);
}
$ret = $this->_sql($sql);
if ( strstr($sql, 'insert') )
$this->lastInsertId = @mysql_insert_id();
if ( $ret > 0 )
$this->dbLog('', 'sql', 0);
return($ret);
}
/*----------------------------------------*/
/**
* get rows from the database
*
* @param string the query
* @return array array of associative arrays from mysql_fetch_assoc()
*/
public function getRows($sql, $ttl = null) {
$Mmemcache = new Mmemcache;
$memcacheKey = get_class().":4:".__FUNCTION__."('$sql')";
if ( $ttl !== null && ($rows = $Mmemcache->get($memcacheKey)) !== false ) {
return($rows);
}
if ( ! $this->isConnected ) {
Mview::error("Mmodel not connected");
return(null);
}
$res = $this->query($sql);
if ( ! $res ) {
return(null);
}
$ret = array();
while($r = @mysql_fetch_assoc($res))
$ret[] = $r ;
@mysql_free_result($res);
if ( $ttl !== null )
$Mmemcache->set($memcacheKey, $ret, $ttl);
return($ret);
}
/*----------------------------------------*/
/**
* get a single row from the database
*
* @param string the query
* @return array associative array from mysql_fetch_assoc()
*/
public function getRow($sql, $ttl = null) {
if ( ! $this->isConnected ) {
Mview::error("Mmodel not connected");
return(null);
}
$rows = $this->getRows($sql, $ttl);
if ( count($rows) == 0 )
return(null);
return($rows[0]);
}
/*------------------------------*/
/**
* get a row from a table by its id
*
* @param string the table from which the data is fetched
* @param int the id value
* @param string the name of the id field if it is not 'id'
* @return array associative array of data of the row
*/
public function getById($tableName, $id, $idName = "id", $ttl = null) {
return($this->getRow("select * from $tableName where $idName = $id", $ttl));
}
/*----------------------------------------*/
/**
* get a column of data from the database
*
* @param string the query
* @return array an array with the data
*/
public function getStrings($sql, $ttl = null) {
$rows = $this->getRows($sql, $ttl);
if ( $rows === null )
return(null);
$ret = array();
foreach ( $rows as $row )
$ret[] = array_shift($row); // take the value of the first (and only) column, ignoring the index field name
return($ret);
}
/*----------------------------------------*/
/**
* get an item (single row, single column) from the database
*
* @param string the query
* @return string the item data
*/
public function getString($sql, $ttl = null) {
if ( ! $this->isConnected ) {
Mview::error("Mmodel not connected");
return(null);
}
$strings = $this->getStrings($sql, $ttl);
if ( $strings )
return($strings[0]);
else
return(null);
}
/*----------------------------------------*/
/**
* get an int item from the database
*
* @param string the query
* @return int the returned number
*/
public function getInt($sql, $ttl = null) {
if ( ! $this->isConnected ) {
Mview::error("Mmodel not connected");
return(null);
}
if ( ($ret = $this->getString($sql, $ttl)) === null )
return(null);
return((int)$ret);
}
/*------------------------------------------------------------*/
/**
* name of the auto_increment column in table
*
* @param string the name of the table
* @return string name of auto_increment column
*/
public function autoIncrement($tableName) {
$Mmemcache = new Mmemcache;
static $cache = array();
if ( isset($cache[$tableName]) )
return($cache[$tableName]);
$memcacheKey = get_class().":1:".__FUNCTION__."('$tableName')";
if ( ($cache[$tableName] = $Mmemcache->get($memcacheKey)) != null )
return($cache[$tableName]);
$fields = $this->fields($tableName);
if ( ! $fields ) {
$cache[$tableName] = null;
return($cache[$tableName]);
}
foreach ( $fields as $field ) {
if ( $field['isAutoInc'] ) {
$cache[$tableName] = $field['name'];
break;
}
}
if ( ! isset($cache[$tableName]) )
$cache[$tableName] = null;
if ( $cache[$tableName] )
$Mmemcache->set($memcacheKey, $cache[$tableName]);
return($cache[$tableName]);
}
/*------------------------------------------------------------*/
public function typeGroup($ftype) {
if ( strncmp($ftype, 'int', 3) == 0 )
return("int");
if ( $ftype == 'text' )
return("text");
if ( strncmp($ftype, 'varchar', 7) == 0 )
return("text");
if ( in_array($ftype, array('date', 'datetime', 'timestamp')) )
return("date");
return($ftype);
}
/*------------------------------------------------------------*/
/**
* schema information for a table:
*
* @param string the name of the table
* @return array two dimensional array. For each column in the table: name, type, isNull, isPrimary, default, isAutoInc
*/
public function fields($tableName) {
static $cache = array();
if ( isset($cache[$tableName]) )
return($cache[$tableName]);
$columnRows = $this->getRows("show columns from $tableName");
if ( ! $columnRows ) {
Mview::error("No columns for $tableName");
$cache[$tableName] = null ;
return($cache[$tableName]);
}
$fields = array();
foreach ( $columnRows as $col )
$fields[] = array(
'name' => $col['Field'],
'type' => $col['Type'],
'typeGroup' => $this->typeGroup($col['Type']),
'isNull' => $col['Null'] == 'YES' ? true : false,
'isPrimary' => $col['Key'] == 'PRI',
'default' => $col['Default'],
'isAutoInc' => $col['Extra'] == 'auto_increment',
'isKey' => $col['Key'] != null,
);
$cache[$tableName] = $fields ;
return($cache[$tableName]);
}
/*----------------------------------------*/
/**
* schema of one field
*/
public function field($tableName, $fieldName) {
$fields = $this->fields($tableName);
if ( ! $fields )
return(null);
foreach ( $fields as $field )
if ( $field['name'] == $fieldName )
return($field);
return(null);
}
/*----------------------------------------*/
/**
* list fields of a table
*
* @param string the name of the table
* @return array list of field names
*/
public function columns($tableName) {
static $cache = array();
if ( isset($cache[$tableName]) )
return($cache[$tableName]);
$columnRows = $this->getRows("show columns from $tableName");
if ( ! $columnRows ) {
Mview::error("No columns for $tableName");
$cache[$tableName] = null ;
return($cache[$tableName]);
}
$cols = array();
foreach ( $columnRows as $col )
$cols[] = $col['Field'] ;
$cache[$tableName] = $cols ;
return($cache[$tableName]);
}
/*----------------------------------------*/
/**
* does field exist in a table
*
* @param string the name of the table
* @param string the name of the field
* @return bool
*/
public function isColumn($tableName, $column) {
$columns = $this->columns($tableName);
if ( ! $columns )
return(false);
return(in_array($column, $columns));
}
/*----------------------------------------*/
/**
* number of rows in a table
*
* @param string the name of the table
* @return int the number of rows in the table
*/
public function rowNum($t) {
return($this->getInt("select count(*) from $t"));
}
/*----------------------------------------*/
/**
* list of tables in database
*
* @param string the database name (optional - if not the default database)
* @return array list of tables
*/
public function tables($db = null) {
static $cache = null;
if ( ! $db )
$db = $this->dbName;
if ( ! $db )
return(false);
if ( isset($cache[$db]) )
return($cache[$db]);
$cache[$db] = $this->getStrings("show tables from $db", 2*3600);
return($cache[$db]);
}
/*----------------------------------------*/
/**
* list of databases
*/
public function databases() {
static $cache = null;
static $excludes = array('information_schema', 'mysql', 'test',);
$dbHost = $this->dbHost;
if ( isset($cache[$dbHost]) )
return($cache[$dbHost]);
$allDatabases = $this->getStrings("show databases");
$databases = array();
foreach ( $allDatabases as $db )
if ( ! in_array($db, $excludes) )
$databases[] = $db;
$cache[$dbHost] = $databases;
return($cache[$dbHost]);
}
/*----------------------------------------*/
/**
* does table exist
*
* @param string the table name
* @return bool
*/
public function isTable($t) {
$pair = explode('.', $t);
if ( count($pair) == 2 ) {
$dbname = $pair[0];
$tname = $pair[1];
$sql = "select count(*) from information_schema.tables where table_schema = '$dbname' and table_name = '$tname'";
return($this->getInt($sql));
}
$tables = $this->tables();
// with xampp, all table names are lowercase but $t might not be
return(in_array($t, $tables) || in_array(strtolower($t), $tables));
}
/*------------------------------------------------------------*/
/**
* dbInsert() without logging (see dbLog())
*
* @param string table to insert the data to
* @param array associative array with data. Fields not matching columns of the table are silently ignored.
* @return int auto-increment id of the new row
*/
public function _dbInsert($tableName, $data, $rememberLastSql = true, $withId = false) {
if ( ! $this->isConnected ) {
Mview::error("Mmodel not connected");
return(null);
}
if ( ($sql = $this->dbInsertSql($tableName, $data, $withId)) == null )
return(null);
$affected = $this->_sql($sql, $rememberLastSql);
if ( $affected != 1 )
return(null);
$this->lastInsertId = mysql_insert_id();
return($this->lastInsertId);
}
/*------------------------------*/
/**
* insert data to database
*
* @param string table to insert the data to
* @param array associative array with data. Fields not matching columns of the table are silently ignored.
* @return int auto-increment id of the new row
*/
public function dbInsert($tableName, $data, $withId = false) {
if ( ($id = $this->_dbInsert($tableName, $data, true, $withId)) == null )
return(null);
$this->dbLog($tableName, 'insert', $id);
return($id);
}
/*------------------------------------------------------------*/
/**
* update a row of data - raw interface - see also dbUpdate() & dbLog()
*
* @param string table name
* @param int value of id key identifying the row to be updated
* @param array associative array with data. Fields not matching columns of the table are silently ignored.
* @param string the name of the id field if it is not 'id'
* @return int -1 on error, 0 if the query had no effect, 1 if an actual change occured (see mysql_affected_rows())
*/
public function _dbUpdate($tableName, $id, $data, $idName = "id") {
if ( ! $this->isConnected ) {
Mview::error("Mmodel not connected");
return(-1);
}
$cols = $this->columns($tableName);
if ( ! $cols )
return(-1);
$this->ammend($data, $cols);
$origData = $this->getRow("select * from $tableName where $idName = $id");
$pairs = array();
foreach ( $data as $fname => $value ) {
if ( $fname == $idName || ! in_array($fname, $cols) || $this->equiv($origData[$fname], $value) )
continue;
$dataType = $this->dataType($tableName, $fname);
if ( $dataType == 'timestamp' )
continue;
if ( $dataType == 'date' && ($value = Mdate::scan($value)) == null )
continue;
if ( $dataType == 'datetime' && ($value = Mdate::datetimeScan($value)) == null )
continue;
$str = $this->str($value);
if ( $str === 'now()' )
$pairs[] = "$fname = $str";
elseif ( $str === null )
$pairs[] = "$fname = null";
else
$pairs[] = "$fname = '$str'";
}
if ( ! $pairs ) {
// nothing changed - do nothing else
return(0);
}
$pairList = implode(", ", $pairs);
$sql = "update $tableName set $pairList where $idName = $id";
$affected = $this->_sql($sql);
return($affected);
}
/*--------------------*/
/**
* update a row of data
*
* @param string table name
* @param int value of id key identifying the row to be updated
* @param array associative array with data. Fields not matching columns of the table are silently ignored.
* @param string the name of the id field if it is not 'id'
* @return bool true if all is well, (including no-change), false on error
*/
public function dbUpdate($tableName, $id, $data, $idName = "id") {
$affected = $this->_dbUpdate($tableName, $id, $data, $idName = "id");
if ( $affected > 0 )
$this->dbLog($tableName, 'update', $id);
return($affected >= 0);
}
/*----------------------------------------*/
/**
* delete a row (without logging - see dbLog())
*
* @param string table name
* @param int value of id key identifying the row to be updated
* @param string the name of the id field if it is not 'id'
* @return int 1 on success, 0 if nothing was deleted, -1 if an error occured
*/
public function _dbDelete($tableName, $id, $idName = "id") {
if ( ! $this->isConnected ) {
Mview::error("Mmodel not connected");
return(-1);
}
$sql = "delete from $tableName where $idName = $id";
$affected = $this->_sql($sql);
return($affected);
}
/*----------------------------------------*/
/**
* delete a row
*
* @param string table name
* @param int value of id key identifying the row to be updated
* @param string the name of the id field if it is not 'id'
* @return bool true if all is well, (including no-change), false on error
*/
public function dbDelete($tableName, $id, $idName = "id") {
$affected = $this->_dbDelete($tableName, $id, $idName);
if ( $affected > 0 )
$this->dbLog($tableName, 'delete', $id);
return($affected >= 0);
}
/*------------------------------------------------------------*/
/**
* return sql representing the data in the table ( use dumpTable() to stream large tables )
*
* @param string the table name
* @param string single field to sort by or null to avoid sorting
* @return string sql statement(s) to (re-)insert data to the table
*/
public function tableDump($tableName, $orderBy = "id") {
if ( $orderBy && $this->isColumn($tableName, $orderBy) )
$ob = "order by $orderBy";
else
$ob = "";
$ret = "drop table if exists $tableName;\n";
$cr = $this->getRow("show create table $tableName");
$crvalues = array_values($cr);
$ret .= $crvalues[1].";\n";
$rows = $this->getRows("select * from $tableName $ob");
foreach ( $rows as $row )
$ret .= $this->dbInsertSql($tableName, $row, true).";\n";
return($ret);
}
/*------------------------------------------------------------*/
/**
* stream an SQL dump of the table to the standard output
*
* @param string the table name
*/
public function dumpTable($tableName, $select = null) {
$ai = $this->autoIncrement($tableName);
if ( $ai )
$ob = "order by $ai";
else
$ob = "";
$datetime = date("Y-m-d G:i:s.u");
if ( $select ) {
$query = $select;
echo "-- M::dumpTable - $tableName - $select\n";
} else {
$query = "select * from $tableName $ob";
echo "-- M::dumpTable starting - $tableName - $datetime\n";
echo "drop table if exists $tableName;\n";
$cr = $this->getRow("show create table $tableName");
$crvalues = array_values($cr);
echo $crvalues[1].";\n";
}
$res = $this->query($query);
if ( ! $res )
return;
while($row = @mysql_fetch_assoc($res))
echo $this->dbInsertSql($tableName, $row, true).";\n";
if ( ! $select ) {
$datetime = date("Y-m-d G:i:s.u");
echo "-- M::dumpTable done - $tableName - $datetime\n";
echo "\n";
}
}
/*------------------------------------------------------------*/
/**
* the data type of a column in a table
*
* @param string the table name
* @param string the column name
* @return string the data type
*/
public function dataType($tableName, $fieldName) {
static $cache = array();
if ( isset($cache[$tableName][$fieldName]) )
return($cache[$tableName][$fieldName]);
$columnRows = $this->getRows("show columns from $tableName", 2*3600);
foreach ( $columnRows as $col )
$cache[$tableName][$col['Field']] = $col['Type'];
if ( isset($cache[$tableName][$fieldName]) )
return($cache[$tableName][$fieldName]);
return(null);
}
/*------------------------------------------------------------*/
/**
* provide data for jquery autocomplete
*
* the request url is of the form id=$tname-$fname&q=...
* e.g.
* $(".autocomplete", context).autocomplete("?className=Mmodel&action=autocomplete&id=" + $this.id);
*/
public function autoComplete() {
$id = explode('-', @$_REQUEST['id']);
if ( ! $id ) {
@syslog(LOG_ERR, __FILE__.":". __LINE__.": ".get_class().":".__FUNCTION__.": bad id args: ".$_SERVER['QUERY_STRING']);
echo "autoComplete error\n";
return;
}
$cnt = count($id);
if ( $cnt == 2 )
list($tname, $fname) = $id;
elseif ( $cnt == 3 ) {
list($dbname, $tname, $fname) = $id;
if ( ! $this->selectDB($dbname) ) {
@syslog(LOG_ERR, __FILE__.":". __LINE__.": ".get_class().":".__FUNCTION__.": bad id args: ".$_SERVER['QUERY_STRING']);
echo "autoComplete: cannont use $dbname\n";
return;
}
} else {
@syslog(LOG_ERR, __FILE__.":". __LINE__.": ".get_class().":".__FUNCTION__.": bad id args: ".$_SERVER['QUERY_STRING']);
echo "autoComplete error\n";
return;
}
$q = $_REQUEST['q'];
$sql = "select distinct $fname from $tname where $fname like '$q%' order by $fname";
$strings = $this->getStrings($sql);
if ( ! $strings )
return;
$ret = implode("\n", $strings)."\n";
echo $ret;
}
/*------------------------------*/
public function permit() {
// allows automatic Mautocomplete!!
return(true);
}
/*------------------------------------------------------------*/
/**
* update a single item in the database from parameters in $_REQUEST
*
* saveFieldInfo() is used directly from the jQuery jeditable plugin
* as a complete server-side ajax updater/responder
*/
public function saveFieldInfo() {
$elementId = $_REQUEST['id'];
$value = $_REQUEST['value'];
$nameId = explode("-", $elementId);
$tname = $nameId[0];
$fname = $nameId[1];
$id = $nameId[2];
$OldValue = $this->getString("select $fname from $tname where id = $id");
// updating the database requires login credentials
$Msession = new Msession;
if ( ! $Msession->get('loginName') ) {
echo "$OldValue";
exit;
}
$dataType = $this->dataType($tname, $fname);
if ( $dataType == 'date' ) {
if ( ($value = Mdate::scan($value)) == null ) {
echo "$OldValue";
exit;
}
}
if ( $dataType == 'time' ) {
if ( ($value = Mtime::fmt($value)) == null ) {
echo "$OldValue";
exit;
}
}
$sqlValue = $this->str($value);
$sql = "update $tname set $fname = '$sqlValue' where id = $id";
$affected = $this->sql($sql);
$newValue = $this->getString("select $fname from $tname where id = $id");
if ( $dataType == 'date' )
$newValue = Mdate::fmt($newValue);
elseif ( $dataType == 'time' )
$newValue = Mtime::fmt($newValue);
elseif ( $dataType == 'float' || $dataType == 'double' )
$newValue = msuFloatFmt((float)$newValue);
echo $newValue;
}
/*------------------------------------------------------------*/
/**
* log database activity in a table called queryLog
*
* Normally only used as 'private',
* dbInsert(), dbUpdate(), dbDelete() and sql() attempt
* to log activities when successful changes occur,
* while _dbInsert(), _dbUpdate(), _dbDelete() and _sql() do not.
*
* @param string table name
* @param operation performed
* @param int id of the affected row
* @param string the sql statement performing the operation
*/
public function dbLog($tname, $op, $tid, $querySql = null) {
if ( ! $this->isTable('queryLog') )
return;
$Msession = new Msession;
$row = array(
'tname' => $tname,
'op' => $op,
'tid' => $tid,
'querySql' => $querySql ? $querySql : $this->lastSql,
'loginName' => $this->Msession->get('loginName'),
'stamp' => 'now()'
);
$this->_dbInsert('queryLog', $row, false);
}
/*------------------------------------------------------------*/
/**
* a database ready representation of now()
*
* @return string
*/
public function datetimeNow() {
$today = Mdate::dash(Mdate::today());
$now = Mtime::fmt(Mtime::now());
$ret = "$today $now";
return($ret);
}
/*------------------------------*/
/**
* a database ready representation of now() in a given timezone
*
* @param string timezone (see date_default_timezone_set())
* @return string
*/
public function datetimeNowInTZ($tz = null) {
$todayInTZ = Mdate::dash(Mdate::todayInTZ($tz));
$nowInTZ = Mtime::nowInTZ($tz, true);
$ret = "$todayInTZ $nowInTZ";
return($ret);
}
/*------------------------------------------------------------*/
/**
* sql for a sample of rows from table
*
* @param string name of table
*/
public function sampleSql($table) {
if ( ! $this->isTable($table) )
return(null);
$id = $this->autoIncrement($table);
$fieldList = implode(',', $this->columns($table));
$rowNum = $this->rowNum($table);
if ( ! $id ) {
if ( $rowNum > 500 )
$limit = "limit 500";
else
$limit = "";
return("select $fieldList from $table $limit");
}
if ( $rowNum < 100 )
$sql = "select $fieldList from $table order by $id";
elseif ( $rowNum < 1000 )
$sql = "select $fieldList from $table where $id % 10 = 0 order by $id";
elseif ( $rowNum < 10000 )
$sql = "select $fieldList from $table where $id % 100 = 0 order by $id";
elseif ( $rowNum < 100000 )
$sql = "select $fieldList from $table where $id % 1000 = 0 order by $id";
else
$sql = "select $fieldList from $table order by $id desc limit 100";
return($sql);
}
/*------------------------------------------------------------*/
public function name($tname, $fname, $id) {
static $cache = array();
if ( isset($cache[$tname][$fname][$id]) )
return($cache[$tname][$fname][$id]);
if ( ! @$cache[$tname] )
$cache[$tname] = array();
if ( ! @$cache[$tname][$fname] ) {
$cache[$tname][$fname] = array();
$rows = $this->getRows("select id,$fname from $tname", 30*60);
foreach ( $rows as $row )
$cache[$tname][$fname][$row['id']] = $row[$fname];
}
return(@$cache[$tname][$fname][$id]);
}
/*------------------------------*/
public function id($tname, $fname, $name, $make = false) {
static $cache = array();
if ( isset($cache[$tname][$fname][$name]) )
return($cache[$tname][$fname][$name]);
if ( ! @$cache[$tname] )
$cache[$tname] = array();
if ( ! @$cache[$tname][$fname] ) {
$cache[$tname][$fname] = array();
$rows = $this->getRows("select id,$fname from $tname", 30*60);
foreach ( $rows as $row )
$cache[$tname][$fname][$row[$fname]] = $row['id'];
}
if ( @$cache[$tname][$fname][$name] )
return($cache[$tname][$fname][$name]);
if ( ! $make )
return(null);
$id = $this->_dbInsert($tname, array(
$fname => $name,
));
if ( ! $id )
return(null);
$cache[$tname][$fname][$name] = $id;
return($cache[$tname][$fname][$name]);
}
/*------------------------------------------------------------*/
public function lastError() {
return($this->lastError);
}
/*------------------------------------------------------------*/
/*------------------------------------------------------------*/
/*------------------------------------------------------------*/
private function ammend(&$data, $cols, $insert = false) {
$Msession = new Msession;
if ( $insert && in_array('createdOn', $cols) && ! isset($data['createdOn']) )
$data['createdOn'] = date("Y-m-d G:i:s");
if ( $insert && in_array('createdBy', $cols) && ! isset($data['createdBy']) )
$data['createdBy'] = $Msession->get('loginName');
if ( in_array('lastChange', $cols) && ! isset($data['lastChange']) )
$data['lastChange'] = date("Y-m-d G:i:s");
if ( in_array('lastChangeBy', $cols) && ! isset($data['lastChangeBy']) )
$data['lastChangeBy'] = $Msession->get('loginName');
}
/*------------------------------*/
private function dbInsertSql($tableName, $data, $withId = false) {
$cols = $this->columns($tableName);
if ( ! $cols )
return(null);
$row = array();
$idName = $this->autoIncrement($tableName);
foreach ( $data as $fname => $value ) {
if ( ! in_array($fname, $cols) )
continue;
if ( $fname == $idName )
continue;
$dataType = $this->dataType($tableName, $fname);
if ( $dataType == 'timestamp' )
continue;
if ( $dataType == 'date' && ($value = Mdate::scan($value)) == null )
continue;
if ( $dataType == 'datetime' && ($value = Mdate::datetimeScan($value)) == null )
continue;
$str = $value;
if ( $str === null )
continue;
$row[$fname] = $str;
}
$insertData = array();
foreach ( $row as $fname => $value )
$insertData[$fname] = $value;
$this->ammend($insertData, $cols, true);
$fieldList = implode(',', array_keys($insertData));
$valueList = array();
foreach ( $insertData as $value )
if ( $value === 'now()' )
$values[] = 'now()';
else
$values[] = "'".$this->str($value)."'";
$valuesList = implode(",", $values);
$sql = "insert into $tableName ( $fieldList ) values ( $valuesList )";
return($sql);
}
/*------------------------------------------------------------*/
/*
* do fields have equivalent values:
* nulls and empty strings, dates in int or dashed format are equivalent
* updating of equivalent values is skipped
*/
private function equiv($a, $b) {
if ( $a == $b )
return(true);
$alen = strlen($a);
$blen = strlen($b);
// dates
if (
( $alen == 10 || $alen == 8 )
&& ( $alen == 10 || $alen == 8 )
&& str_replace('-', '', $a) == str_replace('-', '', $b)
)
return(true);
// text
if (
str_replace("\r\n", "\n", $a) == str_replace("\r\n", "\n", $b)
)
return(true);
return(false);
}
/*------------------------------------------------------------*/
}
/*------------------------------------------------------------*/
../M/Mview.class.php
<?php
/*------------------------------------------------------------*/
/**
* @package M
* @author Ohad Aloni
*/
/*------------------------------------------------------------*/
/**
* requires Smarty software
*/
if ( ! class_exists("Smarty") )
require_once("Smarty-2.6.9/libs/Smarty.class.php");
/*------------------------------------------------------------*/
require_once("msu.php");
require_once("Mdate.class.php");
/*------------------------------------------------------------*/
/**
* Mview - View class
*
* Mview is an extension of the class Smarty (smarty.php.net)
*
* @package M
* @author Ohad Aloni
*/
class Mview extends Smarty {
/*------------------------------------------------------------*/
// messages and error require no construction with new()
/*------------------------------------------------------------*/
/**
* @var bool
*/
private $templateDirPath = array();
/*------------------------------------------------------------*/
function __construct() {
if ( ! defined('SMARTY_TPL_DIR') )
define('SMARTY_TPL_DIR', 'tpl');
if ( ! defined('SMARTY_RUN_DIR') )
define('SMARTY_RUN_DIR', 'smarty');
$this->use_sub_dirs = false;
$smartyTplDir = SMARTY_TPL_DIR ;
$smartyRunDir = SMARTY_RUN_DIR ;
$this->template_dir = $smartyTplDir;
$this->prependTemplateDir($smartyTplDir);
if ( defined('M_DIR') )
$this->appendTemplateDir(M_DIR."/tpl");
$this->compile_dir = "$smartyRunDir/compile/";
$this->config_dir = "$smartyRunDir/config/";
$this->cache_dir = "$smartyRunDir/cache/";
$this->registerFunction('msuShowTpl');
$this->registerFunction('msuVarDump');
$this->registerFunction('showMsg');
$this->registerFunction('showError');
$this->registerFunction('msuSetTitle');
$this->registerFunction('msuSetStatus');
$this->registerModifier('msuMoneyFmt');
$this->registerModifier('msuFloatFmt');
$this->registerModifier('msuDateFmt');
$this->registerModifier('msuDateTimePickerFmt');
$this->registerModifier('msuIntFmt');
$this->registerModifier('msuTimeFmt');
$this->registerModifier('htmlspecialchars');
$this->registerModifier('undash', 'Mdate');
$this->assign(array(
"yesNo" => array(
0 => 'No',
1 => 'Yes',
),
));
}
/*------------------------------------------------------------*/
private function registerClass($method, $class) {
if ( function_exists($method) )
return(true);
if ( $class ) {
if ( ! class_exists($class) ) {
require_once("$class.class.php");
if ( ! class_exists($class) ) {
self::error("Mview::registerClass: class $class not found");
return(null);
}
}
if ( method_exists($class, $method) )
return($class);
else {
self::error("Mview::registerClass: method $method not found in $class");
return(null);
}
}
if ( method_exists("Mutils", $method) )
return("Mutils");
self::error("Mview::registerClass: method $method not found");
return(null);
}
/*------------------------------*/
/**
* register a smarty plugin function
*
* @param string can be any function or method in Mview or Mutils or the passed class
* @param string
*/
private function registerFunction($method, $class = null) {
$callable = array($this, $method);
if ( is_callable($callable) ) {
$this->register_function($method, $callable);
return;
}
// todo
$class = $this->registerClass($method, $class);
if ( ! $class )
return;
if ( $class === true )
$this->register_function($method, $method, false);
else
$this->register_function($method, array($class, $method,));
}
/*------------------------------------------------------------*/
/**
* register a smarty plugin filter (modifier)
*
* @param string can be any function or method in Mview or Mutils or the passed class
* @param string
*/
public function registerModifier($method, $class = null) {
$class = $this->registerClass($method, $class);
if ( ! $class )
return;
if ( $class === true )
$this->register_modifier($method, $method);
else
$this->register_modifier($method, array($class, $method,));
}
/*------------------------------------------------------------*/
/**
* prepend a template folder to the template search path
*
* for use with {msuShowTpl file=... arg1=...} -
*
* when using include in templates, only the first $smarty->template_dir is recognized
*
*/
public function prependTemplateDir($dir) {
array_unshift($this->templateDirPath, $dir);
}
/*------------------------------*/
/**
* append a template folder to the template search path
*
* for use with {msuShowTpl file=... arg1=...} -
*
* when using include in templates, only the first $smarty->template_dir is recognized
*
*/
public function appendTemplateDir($dir) {
$this->templateDirPath[] = $dir;
}
/*------------------------------------------------------------*/
private function _render($tpl) {
if ( ! is_writable($this->compile_dir) ) {
$pwd = trim(`pwd`);
$this->error("Smarty Compile Dir $pwd/{$this->compile_dir} not writable");
return(false);
}
if ( is_readable($tpl) )
return($this->fetch($tpl));
$cwd = getcwd();
foreach ( $this->templateDirPath as $dir ) {
if ( is_readable("$cwd/$dir/$tpl") )
return($this->fetch("$cwd/$dir/$tpl"));
if ( is_readable("$dir/$tpl") )
return($this->fetch("$dir/$tpl"));
}
if ( isset($this->template_dir) ) {
$td = $this->template_dir;
if ( is_readable("$td/$tpl") )
return($this->fetch($tpl));
}
$showPath = implode(":", $this->templateDirPath);
$this->error("Mview: $tpl not found in $showPath");
return(null);
}
/*------------------------------------------------------------*/
/**
* return a rendered template
*/
public function render($tpl, $args) {
if ( is_array($args) ) {
$this->assign($args);
$this->assign(array('tplArgs' => $args));
}
$rendered = $this->_render($tpl);
if ( is_array($args) ) {
$keys = array_keys($args);
$this->clear_assign($keys);
$this->clear_assign('tplArgs');
}
return($rendered);
}
/*------------------------------------------------------------*/
private static $holdOutput = false;
private static $outputBuffer = "";
/*------------------------------*/
public static function holdOutput() {
self::$holdOutput = true;
}
/*------------------------------*/
public static function flushOutput() {
// msgbuf had better been output by now by app, not usable after this;
$Msession = new Msession;
$msgBuf = $Msession->get('msgBuf');
if ( $msgBuf )
$Msession->set('msgBuf', array()); // sets cookie - must be bfore next...
if ( self::$outputBuffer ) {
echo self::$outputBuffer;
self::$outputBuffer = "";
}
flush();
@ob_flush(); // may have been turned off by ob_implicit_flush()
}
/*------------------------------*/
private static function pushOutput($htmlText) {
if ( self::$holdOutput )
self::$outputBuffer .= $htmlText;
else
echo $htmlText;
}
/*------------------------------*/
/**
* show a template
*
* @param string file name
* @param array list of named arguments
* @param fetch only return the rendered template and do not display if true
*
*/
public function showTpl($tpl = null, $args = null, $fetch = false) {
if ( $tpl == null )
$tpl = @$_REQUEST['tpl'];
$fetched = $this->render($tpl, $args);
if ( ! $fetch )
self::pushOutput($fetched);
return($fetched);
}
/*------------------------------*/
public function permit() {
// allow automatic urls /Mview/showTpl?tpl=
return(true);
}
/*------------------------------------------------------------*/
public static function showRows($rows, $exportFileName = null) {
global $Mview; // need an instance anyway
if ( ! $rows || ! is_array($rows) || count($rows) == 0 ) {
self::msg("No Rows");
return;
}
$columns = array_keys($rows[0]);
if ( ! $Mview )
$Mview = new Mview;
$Mview->showTpl("mShowRows.tpl", array(
'columns' => $columns,
'rows' => $rows,
'exportFileName' => $exportFileName,
));
}
/*------------------------------------------------------------*/
private static $msgBuf = array();
private static $isHold = false;
/*------------------------------*/
/**
* buffered messages
*/
public function messages() {
return(self::$msgBuf);
}
/*------------------------------*/
/**
* buffer requested messages and errors
* do not show anything at least until a further notice by flushMsgs()
*/
public function holdMsgs() {
self::$isHold = true;
}
/*------------------------------*/
/**
* flush messages and errors previously held due to a call to holdMsgs() and stop buffering
*/
function flushMsgs() {
self::$isHold = false ;
foreach ( self::$msgBuf as $msg )
self::message($msg['msg'], $msg['iserror']);
self::$msgBuf = array();
}
/*------------------------------*/
private static function message($msg, $iserror) {
$me = get_class()."::".__FUNCTION__."()";
if ( strstr($msg, "Cannot ") ) {
Mutils::trace();
self::flushOutput();
exit;
}
$isHtml = isset($_SERVER['REMOTE_ADDR']);
if ( $isHtml ) {
$color = $iserror ? "red" : "blue" ;
$msg = htmlspecialchars($msg);
$msg = nl2br($msg);
$text = "<div style=\"color:$color; font-size:small; font-weight: bold;\">$msg</div>\n" ;
}
else {
$pfx = $iserror ? "ERROR: " : "" ;
$text = "$pfx$msg\n" ;
}
$Msession = new Msession;
$sessionMsgBuf = $Msession->get('msgBuf');
if ( ! $sessionMsgBuf )
$sessionMsgBuf = array();
$numMessages = count($sessionMsgBuf);
if ( $numMessages >= 7 ) {
$lastText = $sessionMsgBuf[$numMessages-1];
if ( $lastText != '...' ) {
$sessionMsgBuf[] = '...';
$Msession->set('msgBuf', $sessionMsgBuf);
}
} else {
$sessionMsgBuf[] = $text;
$Msession->set('msgBuf', $sessionMsgBuf);
}
self::pushOutput($text);
}
/*------------------------------*/
/**
* show a msg (or hold it - see holdMsgs())
*
* @param string
*/
public static function msg($msg, $iserror = false) {
if ( self::$isHold )
self::$msgBuf[] = array('msg' => $msg, 'iserror' => $iserror);
else
self::message($msg, $iserror);
}
/*------------------------------*/
/**
* show an error (or hold it - see holdMsgs())
*
* @param string
*/
public static function error($msg) {
self::msg($msg, true);
}
/*------------------------------------------------------------*/
/**
* show a message from a template
*
* {showMsg msg="..."}
*/
public function showMsg($a) { self::msg($a['msg']); }
/*------------------------------*/
/**
* show an error from a template
*
* {showError msg="..."}
*/
public function showError($a) { self::error($a['msg']); }
/*------------------------------------------------------------*/
/**
* a fancier version of print_r helps debugging by showing a title, file and line number
* in a more legible display
*
*
* e.g<br />
* Mview::print_r($_REQUEST, "_REQUEST", __FILE__, __LINE__);
*
* @param mixed
* @param string
* @param string
* @param int
*
*/
public static function print_r($var, $varName = null, $file = null, $line = null, $return = false) {
$isHtml = isset($_SERVER['REMOTE_ADDR']);
$ret = "";
if ( $isHtml )
$ret .= "\n<table border=\"0\"><tr><td align=\"left\"><pre>\n";
if ( $file ) {
$fileParts = explode("/", trim($file, "/"));
$fileName = $fileParts[count($fileParts)-1];
$ret .= "$varName ($fileName: $line)\n--------------------------------------------------------------\n";
}
$ret .= print_r($var, true);
$ret .= "\n";
if ( $isHtml )
$ret .= "\n</pre></td></tr></table>\n";
if ( ! $return )
self::pushOutput($ret);
return($ret);
}
/*------------------------------------------------------------*/
/**
*
* escape quotes for javascript
*
* @param string
* @return string
*/
public function jsStr($str) {
// escape with \ but
// if they are already escaped ...
$ret = str_replace("\\'", "'", $str);
$ret = str_replace("'", "\\'", $ret);
$ret = str_replace("\r\n", "\n", $ret);
$ret = str_replace("\\n", "\n", $ret);
$ret = str_replace("\n", "\\n", $ret);
return($ret);
}
/*------------------------------*/
/**
* execute javascript by echoing it wrapped in html and flushing output buffers for immeadiate execution
*
* @param string
*/
public function js($s) {
echo "<script type=\"text/javascript\"> $s </script>\n" ;
flush();
ob_flush();
}
/*------------------------------*/
/**
* set title of page using javascript
*
* @param string
*/
public function jsTitle($s) {
$title = $this->jsStr($s);
$this->js("document.title = '$title' ; ");
}
/*------------------------------------------------------------*/
public static function setCookie($name, $value, $expires = null) {
if ( $expires < 0 ) {
unset($_COOKIE[$name]);
@setcookie($name, null, time(0) + 7, "/");
return;
}
$defaultExpires = 10*365*24*60*60;
if ( $expires == null )
$expires = $defaultExpires;
if ( $expires <= $defaultExpires )
$expires += time();
if ( @setcookie($name, $value, $expires, "/") ) {
$_COOKIE[$name] = $value;
} else {
/* self::error("Cannot set cookie - output already started"); */
/* Mutils::trace(); */
/* self::flush(); */
/* exit; */
}
}
/*------------------------------------------------------------*/
/**
* include a template
* {msuShowTpl file="abc.tpl" a=... b=... c=...}
*/
public function msuShowTpl($a) {
$tpl = $a['file'];
$b = $a ;
$b['tplArgs'] = $a;
$rendered = $this->showTpl($tpl, $b, true);
// call from a smarty template -
// skip output buffering or output will be reversed.
echo $rendered;
}
/*------------------------------------------------------------*/
}
/*------------------------------------------------------------*/
../M/Mcontroller.class.php
<?php
/*------------------------------------------------------------*/
/**
* @package M
* @author Ohad Aloni
*/
/*------------------------------------------------------------*/
/**
* Mmodel and Mview are accessible from Mcontroller and any class
* that extends Mcontroller
*/
require_once("Mmodel.class.php");
require_once("Mview.class.php");
/*------------------------------------------------------------*/
/**
*
* Mcontroller has several control functions:
* 1. Allow control to flow only if appropriate permission conditions are met.<br />
* 2. Branch URLs of the form className=...&action=... to the corresponding class method.<br />
* 3. Serve as a superclass to be extended with seamless inheritance from Mmoel and Mview.<br />
*
* @package M
*/
/*------------------------------------------------------------*/
class Mcontroller {
/**
* @var controller name of the current controller
*/
protected $controller;
/**
* @var action name of the current action
*/
protected $action;
private $isConstructed = false;
/**
* @var Mmodel access the Mmodel class from this instance
*/
public $Mmodel;
/**
* @var Mview access the Mview class from this instance
*/
public $Mview;
/*------------------------------------------------------------*/
/**
* Mcontroller is typically extended with no arguments
* or created with no arguments.
*
* @param Mmodel force use of this instance
* @param Mview force use of this instance
*
*/
function __construct($Mm = null, $Mv = null) {
global $Mmodel;
global $Mview;
if ( $Mm !== null )
$this->Mmodel = $Mm;
elseif ( isset($Mmodel) && $Mmodel != null )
$this->Mmodel = $Mmodel;
else
$this->Mmodel = new Mmodel();
if ( $Mv !== null )
$this->Mview = $Mv;
elseif ( isset($Mview) && $Mview != null )
$this->Mview = $Mview;
else
$this->Mview = new Mview();
if ( ! $this->Mmodel ) {
$stack = debug_backtrace(false);
Mview::print_r($stack, "stack", __FILE__, __LINE__);
$Mview->flush();
exit;
}
if ( $this->Mmodel->isConnected() )
$this->isConstructed = true;
}
/*------------------------------*/
public function isConstructed() {
return($this->isConstructed);
}
/*------------------------------------------------------------*/
/**
* control() decides which method of which class is executed.
* It is typically called from index.php
* after a control class is extended from Mcontroller:<br />
* $mcc = new MyControlClass;<br />
* $mcc->control();<br />
*
* className denotes the class name and can be presented:
* 1. in the URL as className=...<br />
* 2. as the first part of path info<br />
* 3. as a hidden field in a form.<br />
* the method is denoted by the word 'action' and can be presented:<br />
* 1. in the URL as action=...<br />
* 2. as the second part of path info<br />
* 3. as a hidden field in a form.<br />
*
* The class Abc will be automatically loaded from Abc.class.php
* if it is not already loaded and there is such a file.
*
* if called from within a controller, dispatiching is redirected and args are (re)placed in $_REQUEST<br />
* e.g.<br />
* $this->control("Authors", "listBooks", array('authorId' => 2,))<br />
* will execute as if from a url ?className=Authors&action=listBooks&authorId=2<br />
* this re-dispatches the action directly (without a web redirect)<br /><br />
* so<br />
* $this->control("Authors", "listBooks", array('authorId' => 2,))<br />
* $this->control("Authors", "listBooks", array('authorId' => 3,))<br />
* might have the effect of rendering the results of both actions consecutively on the same page<br />
*
*/
public function control($className = null, $action = null, $args = null) {
/* $this->topUri = $topUri; */
$requestArgs = array();
if ( is_string($args) ) {
$vars = explode('&', $args);
foreach ( $vars as $var ) {
$nv = explode('=', $var);
if ( count($nv) != 2 ) {
$this->Mview->error("$var ???");
continue;
}
list($n, $v) = $nv;
$requestArgs[$n] = $v;
}
} else if ( $args ) {
foreach ( $args as $key => $arg )
$requestArgs[$key] = $arg;
}
$obj = $this->obj($className);
if ( ! $obj )
return(null);
$action = $this->action($action);
if ( $action == null )
$action = "index";
if ( ! is_callable(array($obj, $action)) ) {
$className = get_class($obj);
$this->Mview->error("Mcontroller: Method '$action' not callable in class '$className'");
return(null);
}
$className = get_class($obj);
$this->controller = strtolower($className);
$this->action = strtolower($action);
$obj->controller = strtolower($className);
$obj->action = strtolower($action);
$savedRequestArgs = $this->setRequestArgs($requestArgs);
if ( ! $obj->permit($className, $action) )
return(null);
if ( method_exists($obj, "before") ) // e.g. Mmodel auto-autocomplete does not extend Mcontroller
$obj->before();
$obj->$action();
if ( method_exists($obj, "after") ) // e.g. Mmodel auto-autocomplete does not extend Mcontroller
$obj->after();
$this->revertRequestArgs($requestArgs, $savedRequestArgs);
}
/*------------------------------*/
private function setRequestArgs($requestArgs) {
$savedRequestArgs = array();
foreach ( $requestArgs as $key => $arg ) {
if ( array_key_exists($key, $_REQUEST) )
$savedRequestArgs[$key] = $_REQUEST[$key];
$_REQUEST[$key] = $arg;
}
return($savedRequestArgs);
}
/*------------------------------*/
private function revertRequestArgs($requestArgs, $savedRequestArgs) {
foreach ( $requestArgs as $key => $arg )
unset($_REQUEST[$key]);
foreach ( $savedRequestArgs as $key => $arg )
$_REQUEST[$key] = $arg;
}
/*------------------------------*/
protected function before() {}
protected function after() {}
/*------------------------------------------------------------*/
private function obj($className) {
if ( ($className = $this->className($className)) == null )
return($this);
if ( class_exists($className) ) {
$obj = new $className;
return($obj);
}
$files = Mutils::listDir(".", "php");
// git problems - Fri Dec 14 15:06:35 IST 2012
foreach ( $files as $file ) {
$fileParts = explode(".", $file);
$baseName = reset($fileParts);
if(strtolower($className) != strtolower($baseName) )
continue;
require_once($file);
if ( class_exists($baseName) ) {
$obj = new $baseName;
return($obj);
}
$this->Mview->error("class $baseName no found in $file");
}
/* $this->Mview->error("cannot find class for '$className' in '".implode(",", $files)); */
$this->Mview->error("cannot find class for '$className'");
return(null);
}
/*------------------------------------------------------------*/
public function redirect($url = null) {
if ( $url && substr($url, 0, 4) == "http" ) {
header("Location: $url");
exit;
}
if ( ! $url ) {
$url = $this->controller;
if ( $this->action )
$url .= "/".$this->action;
}
if ( $url == "/" )
$url = "";
$serverName = $_SERVER['SERVER_NAME'];
$isHttps = @$_SERVER['HTTPS'] == "on";
$s = $isHttps ? "s" : "";
$url = trim($url, "/");
header("Location: http$s://$serverName/$url");
exit;
}
/*------------------------------------------------------------*/
/**
* decide whether to allow the execution of a method by the user
*
* permit() is by default suitable for public access.
* It returns true thus allowing everything.<br />
* In secure and controlled systems, permit() is overridden by the extending class
* to check login credentials
*
* @param string
* @param string
* @return bool
*/
protected function permit() {
return(true);
}
/*------------------------------------------------------------*/
/**
* when a URL specifies a controller without an action
* the method index() is called.
*
*/
public function index() {
$className = get_class($this);
$this->Mview->error("$className: method index() not defined");
return(null);
}
/*------------------------------*/
public function defaultAction() { $this->index(); }
public function main() { $this->index(); }
/*------------------------------------------------------------*/
/**
*
* export data to excel - streaming - for larger data sets
*
* @param string the query
* @param string the filename that the browser will offer to 'save as'
*/
public function exportStreamToExcel($sql, $fileName = null) {
if ( ! $fileName && isset($_REQUEST['fileName']) )
$fileName = $_REQUEST['fileName'];
if ( ! $fileName )
$fileName = "M2excel-".date("Ymd");
$isheaded = false;
$res = $this->Mmodel->query($sql);
if ( $res === null )
return;
while ( $row = @mysql_fetch_assoc($res) ) {
if ( ! $isheaded ) {
header("Content-type: application/vnd.ms-excel");
header("Content-Disposition: attachment; filename=$fileName.xls");
$headings = array_keys($row);
$this->Mview->showTpl("excelHeader.tpl", array("headings" => $headings,));
$isheaded = true;
}
$this->Mview->showTpl("excelRow.tpl", array("row" => $row,));
}
if ( $isheaded )
$this->Mview->showTpl("excelFooter.tpl");
else
$this->Mview->error("No Rows");
}
/*------------------------------------------------------------*/
/**
* export data to excel - non-streaming
*
* @param array|string as from Mmodel->getRow, or sql query
* @param string optional 'save as' file name
*/
public function exportToExcel($rows = null, $fileName = null) {
if ( is_string($rows) )
$sql = $rows;
else if ( $rows === null && isset($_REQUEST['sql']) )
$sql = stripslashes($_REQUEST['sql']);
if ( isset($sql) ) {
$this->exportStreamToExcel($sql, $fileName);
return;
}
if ( count($rows) == 0 ) {
$this->Mview->msg("No Rows");
return;
}
$headings = array_keys($rows[0]);
$this->Mview->assign(array("headings" => $headings, "rows" => $rows));
if ( ! $fileName && isset($_REQUEST['fileName']) )
$fileName = $_REQUEST['fileName'];
if ( ! $fileName )
$fileName = "M2excel-".date("Ymd");
$content = $this->Mview->fetch("excel.tpl");
$filesize = strlen($content);
header("Content-type: application/vnd.ms-excel");
header("Content-Disposition: attachment; filename=$fileName.xls");
header("Content-Length: $filesize");
echo $content;
}
/*------------------------------------------------------------*/
/**
*
* show rows on screen
*
* @param string the query
* @param boolean show number of rows on screen
* @param string the filename that the browser will offer to 'save as'
*/
public function showRowsFromSql($sql, $showCount = true, $exportFileName = null) {
if ( ! $exportFileName )
$exportFileName = "M2excel-".date("Ymd");
$res = $this->Mmodel->query($sql);
if ( $res === null )
return;
$numRows = 0 ;
$isheaded = false;
$numRowsSpanId = "numRows".rand(1, 1000000);
$numRowsSSpanId = "numRowsS".rand(1, 1000000);
while ( $row = @mysql_fetch_assoc($res) ) {
$numRows++;
if ( ! $isheaded ) {
$headings = array_keys($row);
$this->Mview->showTpl("mShowRowsHeader.tpl", array(
"sql" => $sql,
'showCount' => $showCount,
'columns' => $headings,
'exportFileName' => $exportFileName,
'numRowsSpanId' => $numRowsSpanId,
'numRowsSSpanId' => $numRowsSSpanId,
));
$isheaded = true;
}
$this->Mview->showTpl("mShowRowsRow.tpl", array("row" => $row,));
}
if ( $isheaded )
$this->Mview->showTpl("mShowRowsFooter.tpl", array(
'numRows' => $numRows,
'numRowsSpanId' => $numRowsSpanId,
'numRowsSSpanId' => $numRowsSSpanId,
));
else
$this->Mview->error("No Rows");
}
/*------------------------------------------------------------*/
/**
/**
* show rows on screen
*
* @param array the rows to be shown
*
* each an associative array with indexes to be used for heading titles
* if rows is a string, it is an sql to fetch the rows and a streaming interface is used
* optional arguments tell wether to show the number of rows
* and set the dfefault file name for exporting
*/
public function showRows($rows, $showCount = false, $exportFileName = null) {
if ( is_string($rows) ) {
$this->showRowsFromSql($rows, $showCount, $exportFileName);
return;
}
if ( ! $rows || ! is_array($rows) || count($rows) == 0 ) {
$this->Mview->msg("No Rows");
return;
}
$headings = array_keys($rows[0]);
$this->Mview->showTpl("mShowRows.tpl", array(
'showCount' => $showCount,
'columns' => $headings,
'rows' => $rows,
'exportFileName' => $exportFileName,
));
}
/*------------------------------------------------------------*/
/**
* show an array on screen - for developing and debugging
*
* @param array
*/
public function showArray($a) {
$this->Mview->showTpl("mShowArray.tpl", array(
'a' => $a,
));
}
/*------------------------------------------------------------*/
public function pathParts() {
if ( isset($_REQUEST['PATH_INFO']) )
$path = $_REQUEST['PATH_INFO'];
elseif ( isset($_SERVER['PATH_INFO']) )
$path = $_SERVER['PATH_INFO'];
else
return(null);
// ignore leading, trailing and duplicate slashes
$pathParts = array();
$parts = explode("/", $path);
foreach ( $parts as $part )
if ( $part != "" )
$pathParts[] = $part;
return($pathParts);
}
/*------------------------------------------------------------*/
protected static $debugLevel = 0;
/*------------------------------*/
public static function debugLevel($level = null) {
$currentLevel = self::$debugLevel;
$requestLevel = @$_REQUEST['debugLevel'];
$envLevel = Mutils::getenv("debugLevel");
$newLevel = 0;
if ( $level != null )
$newLevel = $level;
if ( $currentLevel > $newLevel )
$newLevel = $currentLevel;
if ( $requestLevel > $newLevel )
$newLevel = $requestLevel;
if ( $envLevel > $newLevel )
$newLevel = $envLevel;
if ( self::$debugLevel != $newLevel )
Mutils::setenv("debugLevel", $newLevel);
self::$debugLevel = $newLevel;
return($newLevel);
}
/*------------------------------*/
public function debug($file, $lineNo, $tag, $msg = null, $debugLevelAtLeast = 1) {
$debugLevel = $this->debugLevel();
if ( self::$debugLevel < $debugLevelAtLeast )
return;
$datetime = date("Y-m-d G:i:s");
$fileName = basename($file);
$text = "$datetime: $fileName:$lineNo:$tag";
if ( $msg )
$text .= ": $msg";
$isHttp = @$_SERVER['SERVER_ADDR'] != null;
if ( $isHttp )
$text = htmlspecialchars($text)."<br />";
echo "$text\n";
}
/*------------------------------------------------------------*/
public function space($tag) {
$me = get_class()."::".__FUNCTION__."()";
$space = Perf::space();
Mview::msg("$tag: $space space");
return($space);
}
/*------------------------------------------------------------*/
/*------------------------------------------------------------*/
/*------------------------------------------------------------*/
private function className($className = null) {
if ( $className )
return($className);
if ( isset($_POST['className']) && $_POST['className'] != '' )
return($_POST['className']);
if ( isset($_GET['className']) && $_GET['className'] != '' )
return($_GET['className']);
$pathParts = $this->pathParts();
return(isset($pathParts[0]) ? $pathParts[0] : null);
}
/*------------------------------------------------------------*/
private function action($action = null) {
if ( $action )
return($action);
if ( isset($_POST['action']) && $_POST['action'] != '' )
return($_POST['action']);
if ( isset($_GET['action']) && $_GET['action'] != '' )
return($_GET['action']);
$pathParts = $this->pathParts();
if ( isset($pathParts[1]) )
return($pathParts[1]);
return(null);
}
/*------------------------------------------------------------*/
}
/*------------------------------------------------------------*/