2003-12-27 11:21:00 +00:00
< ? php
/*
$Id $
This code is part of LDAP Account Manager ( http :// www . sourceforge . net / projects / lam )
Copyright ( C ) 2003 Tilo Lutz
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
2004-06-08 18:54:37 +00:00
class quota extends baseModule {
2004-06-13 19:58:58 +00:00
2004-07-02 18:01:44 +00:00
/**
* Creates a new quota object .
*/
function quota ( $scope ) {
// call parent constructor
parent :: baseModule ( $scope );
}
2004-09-26 13:48:52 +00:00
/** this functin fills the error message array with messages
**/
function load_Messages () {
// error messages for input checks
$this -> messages [ 'softblock' ][ 0 ] = array ( 'ERROR' , _ ( 'Block soft quota' ), _ ( 'Block soft quota contains invalid characters. Only natural numbers are allowed' ));
$this -> messages [ 'hardblock' ][ 0 ] = array ( 'ERROR' , _ ( 'Block hard quota' ), _ ( 'Block hard quota contains invalid characters. Only natural numbers are allowed' ));
$this -> messages [ 'softinode' ][ 0 ] = array ( 'ERROR' , _ ( 'Inode soft quota' ), _ ( 'Inode soft quota contains invalid characters. Only natural numbers are allowed' ));
$this -> messages [ 'hardinode' ][ 0 ] = array ( 'ERROR' , _ ( 'Inode hard quota' ), _ ( 'Inode hard quota contains invalid characters. Only natural numbers are allowed' ));
$this -> messages [ 'block_cmp' ][ 0 ] = array ( 'ERROR' , _ ( 'Block quota' ), _ ( 'Block soft quota must be smaller than block hard quota' ));
$this -> messages [ 'inode_cmp' ][ 0 ] = array ( 'ERROR' , _ ( 'Inode quota' ), _ ( 'Inode soft quota must be smaller than inode hard quota' ));
}
2004-06-13 19:58:58 +00:00
/**
* Returns meta data that is interpreted by parent class
*
* @ return array array with meta data
*/
function get_metaData () {
$return = array ();
// manages user and group accounts
$return [ " account_types " ] = array ( " user " , " group " );
2004-06-14 16:05:36 +00:00
// alias name
$return [ " alias " ] = _ ( 'Quota' );
2004-06-20 17:32:02 +00:00
if ( $this -> get_scope () == 'group' ) {
// module dependencies
$return [ 'dependencies' ] = array ( 'depends' => array ( 'posixGroup' ), 'conflicts' => array ());
}
if ( $this -> get_scope () == 'user' ) {
// module dependencies
$return [ 'dependencies' ] = array ( 'depends' => array ( 'posixAccount' ), 'conflicts' => array ());
}
2004-08-17 15:16:17 +00:00
// available PDF fields
$return [ 'PDF_fields' ] = array ( 'quotas' );
2004-06-13 19:58:58 +00:00
return $return ;
}
2003-12-27 11:21:00 +00:00
// Constructor
2004-06-08 18:54:37 +00:00
function init ( $base ) {
2004-09-01 20:53:06 +00:00
// call parent init
parent :: init ( $base );
2003-12-27 11:21:00 +00:00
// Get basic quotas for new account
$output_array = $_SESSION [ $this -> base ] -> lamdaemon ( array ( " + quota get " . $_SESSION [ $this -> base ] -> type ));
// process quotas
if ( is_array ( $output_array )) {
$all_quota = explode ( ':' , $output_array [ 0 ]);
for ( $j = 0 ; $j < sizeof ( $all_quota ) - 1 ; $j ++ ) {
$single_quota = explode ( ',' , $all_quota [ $j ]);
// unset not existing quotas from account-object
// collect all existing mountpoints in array
$real_quotas [] = $single_quota [ 0 ];
for ( $k = 0 ; $k < sizeof ( $single_quota ); $k ++ )
$this -> quota [ $j ][ $k ] = $single_quota [ $k ];
if ( $this -> quota [ $j ][ 4 ] < time ()) $this -> quota [ $j ][ 4 ] = '' ;
else $this -> quota [ $j ][ 4 ] = strval ( intval (( $this -> quota [ $j ][ 4 ] - time ()) / 3600 )) . ' ' . _ ( 'hours' );
if ( $this -> quota [ $j ][ 8 ] < time ()) $this -> quota [ $j ][ 8 ] = '' ;
else $this -> quota [ $j ][ 8 ] = strval ( intval (( $this -> quota [ $j ][ 8 ] - time ()) / 3600 )) . ' ' . _ ( 'hours' );
}
$j = 0 ;
while ( isset ( $this -> quota [ $j ][ 0 ]))
// remove invalid quotas
if ( ! in_array ( $this -> quota [ $j ][ 0 ], $real_quotas )) unset ( $this -> quota [ $j ]);
else $j ++ ;
// Beautify array, repair index
if ( is_array ( $this -> quota )) $this -> quota = array_values ( $this -> quota );
}
}
// Variables
var $quota ;
function module_ready () {
2004-09-12 10:27:14 +00:00
if ( ! isset ( $_SESSION [ 'config' ] -> scriptPath )) return $false ;
2003-12-27 11:21:00 +00:00
if ( $_SESSION [ $this -> base ] -> type == 'user' && $_SESSION [ $this -> base ] -> module [ 'posixAccount' ] -> attributes [ 'uid' ][ 0 ] == '' ) return false ;
if ( $_SESSION [ $this -> base ] -> type == 'group' && $_SESSION [ $this -> base ] -> module [ 'posixGroup' ] -> attributes [ 'cn' ][ 0 ] == '' ) return false ;
return true ;
}
2004-02-09 18:11:01 +00:00
/* This functions return true
* if all needed settings are done
*/
function module_complete () {
if ( ! $this -> module_ready ()) return false ;
return true ;
}
2003-12-30 15:36:30 +00:00
/* This function returns a list of all html - pages in module
* This is usefull for mass upload and pdf - files
* because lam can walk trough all pages itself and do some
* error checkings
2003-12-27 11:21:00 +00:00
*/
2003-12-30 15:36:30 +00:00
function pages () {
return array ( 'attributes' );
2003-12-27 11:21:00 +00:00
}
2004-01-27 19:07:31 +00:00
/*
*/
2004-09-09 07:10:14 +00:00
function get_help ( $id , $scope ) {
2004-01-27 19:07:31 +00:00
switch ( $id ) {
case " description " :
return array ( " ext " => " FALSE " , " Headline " => _ ( " Description " ),
" Text " => _ ( " Host Description. " ));
break ;
}
return false ;
}
2003-12-30 15:36:30 +00:00
/* This function returns all ldap attributes
* which are part of quota and returns
* also their values .
*/
function get_attributes () {
return $this -> quota ;
}
2003-12-27 11:21:00 +00:00
/* This function loads all attributes into the object
* $attr is an array as it ' s retured from ldap_get_attributes
*/
function load_attributes ( $attr ) {
// Load name
if ( $_SESSION [ $this -> base ] -> type == 'user' ) $id = $attr [ 'uid' ][ 0 ];
if ( $_SESSION [ $this -> base ] -> type == 'group' ) $id = $attr [ 'cn' ][ 0 ];
// Get quotas
$output_array = $_SESSION [ $this -> base ] -> lamdaemon ( array ( " $id quota get " . $_SESSION [ $this -> base ] -> type ));
// process quotas
if ( is_array ( $output_array )) {
$all_quota = explode ( ':' , $output_array [ 0 ]);
for ( $j = 0 ; $j < sizeof ( $all_quota ) - 1 ; $j ++ ) {
$single_quota = explode ( ',' , $all_quota [ $j ]);
// unset not existing quotas from account-object
// collect all existing mountpoints in array
$real_quotas [] = $single_quota [ 0 ];
for ( $k = 0 ; $k < sizeof ( $single_quota ); $k ++ )
$this -> quota [ $j ][ $k ] = $single_quota [ $k ];
if ( $this -> quota [ $j ][ 4 ] < time ()) $this -> quota [ $j ][ 4 ] = '' ;
else $this -> quota [ $j ][ 4 ] = strval ( intval (( $this -> quota [ $j ][ 4 ] - time ()) / 3600 )) . ' ' . _ ( 'hours' );
if ( $this -> quota [ $j ][ 8 ] < time ()) $this -> quota [ $j ][ 8 ] = '' ;
else $this -> quota [ $j ][ 8 ] = strval ( intval (( $this -> quota [ $j ][ 8 ] - time ()) / 3600 )) . ' ' . _ ( 'hours' );
}
$j = 0 ;
while ( isset ( $this -> quota [ $j ][ 0 ]))
// remove invalid quotas
if ( ! in_array ( $this -> quota [ $j ][ 0 ], $real_quotas )) unset ( $this -> quota [ $j ]);
else $j ++ ;
// Beautify array, repair index
if ( is_array ( $this -> quota )) $this -> quota = array_values ( $this -> quota );
}
return 0 ;
}
/* This function returns an array with 3 entries :
* array ( DN1 ( 'add' => array ( $attr ), 'remove' => array ( $attr ), 'modify' => array ( $attr )), DN2 .... )
* DN is the DN to change . It may be possible to change several DNs ,
* e . g . create a new user and add him to some groups via attribute memberUid
* add are attributes which have to be added to ldap entry
* remove are attributes which have to be removed from ldap entry
* modify are attributes which have to been modified in ldap entry
*/
function save_attributes () {
/* Check wich quotas have changed
* Because we can not send an array to lamdaemon . pl we have to put all
* values in a string . ':' sepraeates the first array , ',' the second
*
* $values -> quota [][] First array is an index for every chare with active quotas
* second array Contains values for every share :
* mountpoint , used blocks , soft block limit , hard block limit , grace block period , used inodes ,
* soft inode limit , hard inode limit , grace inode period
*/
$i = 0 ;
while ( $this -> quota [ $i ][ 0 ]) {
$quotastring = $quotastring . $this -> quota [ $i ][ 0 ] . ',' . $this -> quota [ $i ][ 2 ] . ',' . $this -> quota [ $i ][ 3 ]
. ',' . $this -> quota [ $i ][ 6 ] . ',' . $this -> quota [ $i ][ 7 ] . ':' ;
$i ++ ;
}
if ( $_SESSION [ $this -> base ] -> type == 'user' ) $id = $_SESSION [ $this -> base ] -> module [ 'posixAccount' ] -> attributes [ 'uid' ][ 0 ];
if ( $_SESSION [ $this -> base ] -> type == 'group' ) $id = $_SESSION [ $this -> base ] -> module [ 'posixGroup' ] -> attributes [ 'cn' ][ 0 ];
$return [ $_SESSION [ $this -> base ] -> dn ][ 'lamdaemon' ][ 'command' ][] = $id . " quota set " . $_SESSION [ $this -> base ] -> type . " $quotastring\n " ;
return $return ;
}
2003-12-30 15:36:30 +00:00
function delete_attributes ( $post ) {
2003-12-27 11:21:00 +00:00
$i = 0 ;
while ( $this -> quota [ $i ][ 0 ]) {
$quotastring = $quotastring . $this -> quota [ $i ][ 0 ] . ',0,0,0,0:' ;
$i ++ ;
}
if ( $_SESSION [ $this -> base ] -> type == 'user' ) $id = $_SESSION [ $this -> base ] -> module [ 'posixAccount' ] -> attributes [ 'uid' ][ 0 ];
if ( $_SESSION [ $this -> base ] -> type == 'group' ) $id = $_SESSION [ $this -> base ] -> module [ 'posixGroup' ] -> attributes [ 'cn' ][ 0 ];
2004-01-10 11:47:48 +00:00
$return [ $_SESSION [ $this -> base ] -> dn_orig ][ 'lamdaemon' ][ 'command' ][] = $id . " quota set " . $_SESSION [ $this -> base ] -> type . " $quotastring\n " ;
2003-12-27 11:21:00 +00:00
return $return ;
}
2003-12-30 15:36:30 +00:00
/* Write variables into object and do some regexp checks
2003-12-27 11:21:00 +00:00
*/
2004-01-27 19:07:31 +00:00
function proccess_attributes ( $post , $profile = false ) {
2003-12-30 15:36:30 +00:00
// Write all general values into $account_new
$i = 0 ;
// loop for every mointpoint with quotas
while ( $this -> quota [ $i ][ 0 ]) {
$this -> quota [ $i ][ 2 ] = $post [ $i . '_2' ];
$this -> quota [ $i ][ 3 ] = $post [ $i . '_3' ];
$this -> quota [ $i ][ 6 ] = $post [ $i . '_6' ];
$this -> quota [ $i ][ 7 ] = $post [ $i . '_7' ];
// Check if values are OK and set automatic values. if not error-variable will be set
2004-09-26 13:48:52 +00:00
if ( ! get_preg ( $this -> quota [ $i ][ 2 ], 'digit' ))
$errors [ $this -> quota [ $i ][ 2 ]][] = $this -> messages [ 'softblock' ][ 0 ];
if ( ! get_preg ( $this -> quota [ $i ][ 3 ], 'digit' ))
$errors [ $this -> quota [ $i ][ 3 ]][] = $this -> messages [ 'hardblock' ][ 0 ];
if ( ! get_preg ( $this -> quota [ $i ][ 6 ], 'digit' ))
$errors [ $this -> quota [ $i ][ 6 ]][] = $this -> messages [ 'softinode' ][ 0 ];
if ( ! get_preg ( $this -> quota [ $i ][ 7 ], 'digit' ))
$errors [ $this -> quota [ $i ][ 7 ]][] = $this -> messages [ 'hardinode' ][ 0 ];
2003-12-30 15:36:30 +00:00
if ( intval ( $this -> quota [ $i ][ 2 ]) > intval ( $this -> quota [ $i ][ 3 ]))
2004-09-26 13:48:52 +00:00
$errors [ $this -> quota [ $i ][ 2 ]][] = $this -> messages [ 'block_cmp' ][ 0 ];
2003-12-30 15:36:30 +00:00
if ( intval ( $this -> quota [ $i ][ 6 ]) > intval ( $this -> quota [ $i ][ 7 ]))
2004-09-26 13:48:52 +00:00
$errors [ $this -> quota [ $i ][ 6 ]][] = $this -> messages [ 'inode_cmp' ][ 0 ];
2003-12-30 15:36:30 +00:00
$i ++ ;
}
// Return error-messages
if ( is_array ( $errors )) return $errors ;
// Go to additional group page when no error did ocour and button was pressed
return 0 ;
2003-12-27 11:21:00 +00:00
}
/* This function will create the html - page
* to show a page with all attributes .
* It will output a complete html - table
*/
2004-01-27 19:07:31 +00:00
function display_html_attributes ( $post , $profile = false ) {
$return [] = array ( 0 => array ( 'kind' => 'text' , 'text' => _ ( 'Mountpoint' ) ),
1 => array ( 'kind' => 'text' , 'text' => _ ( 'Used blocks' ) ),
2 => array ( 'kind' => 'text' , 'text' => _ ( 'Soft block limit' ) ),
3 => array ( 'kind' => 'text' , 'text' => _ ( 'Hard block limit' ) ),
4 => array ( 'kind' => 'text' , 'text' => _ ( 'Grace block period' ) ),
5 => array ( 'kind' => 'text' , 'text' => _ ( 'Used inodes' ) ),
6 => array ( 'kind' => 'text' , 'text' => _ ( 'Soft inode limit' ) ),
7 => array ( 'kind' => 'text' , 'text' => _ ( 'Hard inode limit' ) ),
8 => array ( 'kind' => 'text' , 'text' => _ ( 'Grace inode period' ) ));
$return [] = array ( 0 => array ( 'kind' => 'help' , 'value' => 'Mountpoint' ),
1 => array ( 'kind' => 'help' , 'value' => 'UsedBlocks' ),
2 => array ( 'kind' => 'help' , 'value' => 'SoftBlockLimit' ),
3 => array ( 'kind' => 'help' , 'value' => 'HardBlockLimit' ),
4 => array ( 'kind' => 'help' , 'value' => 'GraceBlockPeriod' ),
5 => array ( 'kind' => 'help' , 'value' => 'UsedInodes' ),
6 => array ( 'kind' => 'help' , 'value' => 'SoftInodeLimit' ),
7 => array ( 'kind' => 'help' , 'value' => 'HardInodeLimit' ),
8 => array ( 'kind' => 'help' , 'value' => 'GraceInodePeriod' ));
2003-12-27 11:21:00 +00:00
$i = 0 ;
// loop for every mointpoint with enabled quotas
while ( $this -> quota [ $i ][ 0 ]) {
2004-01-27 19:07:31 +00:00
$return [] = array ( 0 => array ( 'kind' => 'text' , 'text' => $this -> quota [ $i ][ 0 ] ),
1 => array ( 'kind' => 'text' , 'text' => $this -> quota [ $i ][ 1 ] ),
2 => array ( 'kind' => 'input' , 'name' => $i . '_2' , 'type' => 'text' , 'size' => '12' , 'maxlength' => '20' , 'value' => $this -> quota [ $i ][ 2 ]),
3 => array ( 'kind' => 'input' , 'name' => $i . '_3' , 'type' => 'text' , 'size' => '12' , 'maxlength' => '20' , 'value' => $this -> quota [ $i ][ 3 ]),
4 => array ( 'kind' => 'text' , 'text' => $this -> quota [ $i ][ 4 ] ),
5 => array ( 'kind' => 'text' , 'text' => $this -> quota [ $i ][ 5 ] ),
6 => array ( 'kind' => 'input' , 'name' => $i . '_6' , 'type' => 'text' , 'size' => '12' , 'maxlength' => '20' , 'value' => $this -> quota [ $i ][ 6 ]),
7 => array ( 'kind' => 'input' , 'name' => $i . '_7' , 'type' => 'text' , 'size' => '12' , 'maxlength' => '20' , 'value' => $this -> quota [ $i ][ 7 ]),
8 => array ( 'kind' => 'text' , 'text' => $this -> quota [ $i ][ 8 ] ));
2003-12-27 11:21:00 +00:00
$i ++ ;
}
2004-01-27 19:07:31 +00:00
return $return ;
2003-12-27 11:21:00 +00:00
}
2004-01-10 11:47:48 +00:00
function display_html_delete ( $post ) {
return 0 ;
}
2004-09-19 08:34:14 +00:00
/**
* Returns a list of elements for the account profiles .
*
* @ return profile elements
*/
2004-04-03 14:47:33 +00:00
function get_profileOptions () {
2004-06-23 19:10:22 +00:00
// Get quotas
$quotas = $_SESSION [ $this -> base ] -> lamdaemon ( array ( " + quota get " . $this -> get_scope ()));
$dirs = split ( " : " , $quotas [ 0 ]);
array_pop ( $dirs ); // remove empty element at the end
for ( $i = 0 ; $i < sizeof ( $dirs ); $i ++ ) {
$dirs [ $i ] = split ( " , " , $dirs [ $i ]);
$dirs [ $i ] = $dirs [ $i ][ 0 ];
}
2004-03-09 12:03:39 +00:00
$return = array ();
2004-06-23 19:10:22 +00:00
if ( sizeof ( $dirs ) < 1 ) return $return ; // stop if no quota directories were found
$return [] = array (
0 => array ( 'kind' => 'text' , 'text' => '<b>' . _ ( 'Mountpoint' ) . ' </b>' , 'align' => 'center' ),
1 => array ( 'kind' => 'text' , 'text' => '<b>' . _ ( 'Soft block limit' ) . ' </b>' , 'align' => 'center' ),
2 => array ( 'kind' => 'text' , 'text' => '<b>' . _ ( 'Hard block limit' ) . ' </b>' , 'align' => 'center' ),
3 => array ( 'kind' => 'text' , 'text' => '<b>' . _ ( 'Soft inode limit' ) . ' </b>' , 'align' => 'center' ),
4 => array ( 'kind' => 'text' , 'text' => '<b>' . _ ( 'Hard inode limit' ) . ' </b>' , 'align' => 'center' ),
);
$return [] = array (
0 => array ( 'kind' => 'help' , 'value' => 'TODO' , 'align' => 'center' ),
1 => array ( 'kind' => 'help' , 'value' => 'TODO' , 'align' => 'center' ),
2 => array ( 'kind' => 'help' , 'value' => 'TODO' , 'align' => 'center' ),
3 => array ( 'kind' => 'help' , 'value' => 'TODO' , 'align' => 'center' ),
4 => array ( 'kind' => 'help' , 'value' => 'TODO' , 'align' => 'center' ),
);
for ( $i = 0 ; $i < sizeof ( $dirs ); $i ++ ) {
$return [] = array (
0 => array ( 'kind' => 'text' , 'text' => $dirs [ $i ], 'align' => 'left' ),
1 => array ( 'kind' => 'input' , 'type' => 'text' , 'text' => " " , 'align' => 'center' , 'size' => '12' , 'name' => " quota_softblock_ $i " ),
2004-07-02 18:01:44 +00:00
2 => array ( 'kind' => 'input' , 'type' => 'text' , 'text' => " " , 'align' => 'center' , 'size' => '12' , 'name' => " quota_hardblock_ $i " ),
3 => array ( 'kind' => 'input' , 'type' => 'text' , 'text' => " " , 'align' => 'center' , 'size' => '12' , 'name' => " quota_softinode_ $i " ),
4 => array ( 'kind' => 'input' , 'type' => 'text' , 'text' => " " , 'align' => 'center' , 'size' => '12' , 'name' => " quota_hardinode_ $i " ),
2004-06-23 19:10:22 +00:00
);
}
2004-03-09 12:03:39 +00:00
return $return ;
2003-12-27 11:21:00 +00:00
}
2004-09-19 08:34:14 +00:00
/**
* Checks input values of account profiles .
*
* @ param array $options a hash array ( name => value ) containing the options
* @ return array list of error messages ( array ( type , title , text )) to generate StatusMessages , if any
*/
2004-07-01 15:54:33 +00:00
function check_profileOptions ( $options ) {
2004-07-02 18:01:44 +00:00
$return = array ();
$i = 0 ;
while ( isset ( $options [ " quota_softblock_ $i " ])) {
2004-09-26 13:48:52 +00:00
if ( ! get_preg ( $options [ " quota_softblock_ $i " ][ 0 ], 'digit' )) $return [] = $this -> messages [ 'softblock' ][ 0 ];
if ( ! get_preg ( $options [ " quota_hardblock_ $i " ][ 0 ], 'digit' )) $return [] = $this -> messages [ 'hardblock' ][ 0 ];
if ( ! get_preg ( $options [ " quota_softinode_ $i " ][ 0 ], 'digit' )) $return [] = $this -> messages [ 'softinode' ][ 0 ];
if ( ! get_preg ( $options [ " quota_hardinode_ $i " ][ 0 ], 'digit' )) $return [] = $this -> messages [ 'hardinode' ][ 0 ];
if ( intval ( $options [ " quota_softblock_ $i " ][ 0 ]) > $options [ " quota_hardblock_ $i " ][ 0 ]) $return [] = $this -> messages [ 'block_cmp' ][ 0 ];
if ( intval ( $options [ " quota_softinode_ $i " ][ 0 ]) > $options [ " quota_hardinode_ $i " ][ 0 ]) $return [] = $this -> messages [ 'inode_cmp' ][ 0 ];
2004-07-02 18:01:44 +00:00
$i ++ ;
}
return $return ;
2004-03-14 17:33:05 +00:00
}
2004-05-24 21:39:57 +00:00
2004-08-17 15:16:17 +00:00
/*
* ( non - PHPDoc )
* @ see baseModule #get_pdfEntries
*/
2004-06-03 14:26:48 +00:00
function get_pdfEntries ( $account_type = " user " ) {
2004-05-29 19:20:28 +00:00
return array ( 'quota_quotas' => array ( '<block><key>' . _ ( 'User quota' ) . '</key><td width="20%"><b>' . _ ( 'Mountpoint' ) . '</b></td><td width="20%"><b>' . _ ( 'Soft block' ) . '</b></td><td width="20%"><b>' . _ ( 'Soft inode' ) . '</b></td><td width="20%"><b>' . _ ( 'Hard block' ) . '</b></td><td width="20%"><b>' . _ ( 'Hard inode' ) . '</b></td></block>' ));
2004-05-24 21:39:57 +00:00
}
2004-03-14 17:33:05 +00:00
2004-03-09 12:03:39 +00:00
}
2003-12-30 15:36:30 +00:00
?>