( ! ) Warning: session_start(): open(/var/lib/php/session/sess_i5ujoqfvqfc53f5if54k9hbuh1, O_RDWR) failed: No such file or directory (2) in /var/www/bitweaver/live/users/includes/bit_setup_inc.php on line 82
Call Stack
#TimeMemoryFunctionLocation
10.0000232848{main}( ).../page_history.php:0
20.0001234904require_once( '/var/www/bitweaver/live/kernel/includes/setup_inc.php' ).../page_history.php:16
30.01741909072BitSystem->scanPackages( ).../setup_inc.php:141
40.01922177352BitSystem->loadPackage( ).../BitSystem.php:1183
50.01922180384include_once( '/var/www/bitweaver/live/users/includes/bit_setup_inc.php' ).../BitSystem.php:1109
60.01952584632session_start ( ).../bit_setup_inc.php:82

( ! ) Warning: session_write_close(): open(/var/lib/php/session/sess_i5ujoqfvqfc53f5if54k9hbuh1, O_RDWR) failed: No such file or directory (2) in /var/www/bitweaver/live/kernel/includes/classes/BitSystem.php on line 580
Call Stack
#TimeMemoryFunctionLocation
10.0000232848{main}( ).../page_history.php:0
20.05445262728BitSystem->display( ).../page_history.php:57
30.05525264696BitSystem->preDisplay( ).../BitSystem.php:505
40.05655279320session_write_close ( ).../BitSystem.php:580

( ! ) Warning: session_write_close(): Failed to write session data (files). Please verify that the current setting of session.save_path is correct (/var/lib/php/session) in /var/www/bitweaver/live/kernel/includes/classes/BitSystem.php on line 580
Call Stack
#TimeMemoryFunctionLocation
10.0000232848{main}( ).../page_history.php:0
20.05445262728BitSystem->display( ).../page_history.php:57
30.05525264696BitSystem->preDisplay( ).../BitSystem.php:505
40.05655279320session_write_close ( ).../BitSystem.php:580
- bitweaver
Comparing versions
Version 23Current version
The Bitweaver Framework, consisting of Liberty and Kernel, offers you a lot of functionality. Here we'll try to show how you can make use of that.

Business Objects a la Liberty

Which base class?

If you want to create your own Business Object, you will probably use LibertyAttachable as base class, as it offers most functionality. Use LibertyContent or LibertyBase when you know what you are doing.

LibertyAttachable

Classes derived from LibertyAttachable come with this functionality:
  • They can take title and formatted text
  • They maintain creation date, creator and modification date/modificator information
  • their text content is subject to the full text seach
  • The calendar can display events occurring to LibertyAttachable objects.
  • Upon database access (store, load) other service plugins are triggered

If all you need is a new data type that contains some formatted text, you might be pretty quick in implementation. Customise this sample and continue with your GUI code (List and Detail Views).

<?php
require_once( LIBERTY_PKG_PATH.'LibertyAttachable.php' ); //import base class

define'BITRESOURCE_CONTENT_TYPE_GUID''bitresource' ); // This is used to uniquely identify the object type

class BitResource extends LibertyAttachable {

    
/**
    * During initialisation, be sure to call our base constructors
    **/
    
function BitResource$pContentId=NULL ) {
        
LibertyAttachable::LibertyAttachable();

        
$this->mContentId $pContentId;
        
$this->mContentTypeGuid BITRESOURCE_CONTENT_TYPE_GUID;

        
$parms = array(
            
'content_type_guid' => BITRESOURCE_CONTENT_TYPE_GUID,
            
'content_description' => 'Managed Resource',
            
'handler_class' => 'BitResource',
            
'handler_package' => 'mypackage',
            
'handler_file' => 'BitResource.php',
            
'maintainer_url' => 'mailto:guess@who.com'
        
);
        
$this->registerContentTypeBITRESOURCE_CONTENT_TYPE_GUID$parms ); // register class with Bitweaver
    
}

}
?>


Custom fields

Probably you will want to add some custom fields. This will be some more work, as Liberty won't know about them and we have to handle them ourselves.

First think of a place to store the data. We will have to use our own table in the database. To match the liberty data with our own we should use an identifier. content_id is the one provided by liberty, and it will serve us well now. So create a table with two columns, just as shown. Later you will see how Bitweaver automatically creates the table upon package installation.

<?php
create table mydata 
(
    
content_id integer(4),
    
expires timestamp
)
?>


  • getServicesSQL
  • prepGetList
  • postGetList

History functionality

How can you enable history tracking in Liberty?

LibertyContent

LibertyBase

Sorted Lists

Now let us create a list of resources. Hopefully you already have something in your database so get something to see. As a first, insert this data with your favourite client, or even better with the data pump described below.

Now we will write some code that loads your data from the database. It goes to _index.php_ in your package directory.

<?php
?>


But what? We call a method that does not exist. Unfortunately LibertyAttachable does not come with a method that could easily be there: getList(). And so we will have to create that for ourselves:

<?php
    
function getList( &$pParamHash ) {
        global 
$gBitSystem$gBitUser;

        
// this makes sure parameters used later on are set
        
LibertyContent::prepGetList$pParamHash );

        
$selectSql $joinSql $whereSql '';
        
$bindVars = array();
        
array_push$bindVars$this->mContentTypeGuid );
        
$this->getServicesSql'content_list_sql_function'$selectSql$joinSql$whereSql$bindVars );

        
// this will set $find, $sort_mode, $max_records and $offset
        
extract$pParamHash );

        if( 
is_array$find ) ) {
            
// you can use an array of pages
            
$whereSql .= " AND lc.`title` IN( ".implode',',array_fill0,count$find ),'?' ) )." )";
            
$bindVars array_merge $bindVars$find );
        } elseif( 
is_string$find ) ) {
            
// or a string
            
$whereSql .= " AND UPPER( lc.`title` )like ? ";
            
$bindVars[] = '%' strtoupper$find ). '%';
        }

        
$query "SELECT lc.`content_id`, lc.`title`, lc.`data` $selectSql
            FROM `"
.BIT_DB_PREFIX."liberty_content` lc $joinSql
            WHERE lc.`content_type_guid` = ? 
$whereSql
            ORDER BY "
.$this->mDb->convert_sortmode$sort_mode );
        
$query_cant "select count(*)
                FROM `"
.BIT_DB_PREFIX."eventcal_events` ts INNER JOIN `".BIT_DB_PREFIX."liberty_content` lc ON( lc.`content_id` = ts.`content_id` ) $joinSql
            WHERE lc.`content_type_guid` = ? 
$whereSql";

        
$result $this->mDb->query$query$bindVars$max_records$offset );
        
$ret = array();
        while( 
$res $result->fetchRow() ) {
            
$ret[] = $res;
        }
        
$pParamHash["cant"] = $this->mDb->getOne$query_cant$bindVars );

        
// add all pagination info to pParamHash
        
LibertyContent::postGetList$pParamHash );

        return 
$ret;
    }
?>


  • $gBitSmarty->assign_by_ref(...)
  • $gBitSystem->display(...)

Detail Views

Up to now, the answer is to look at the SamplePackage and guess.

Feedback and Confirmation

Up to now, the answer is to look at the SamplePackage and guess.
  • gBitSystem->setBrowserTitle()
  • gBitSystem->confirmDialog()

Authorisation Required

Up to now, the answer is to look at the SamplePackage and guess.
Up to now, the answer is to look at the SamplePackage and guess.
Layout assigned module titles and parameters

Services

The previous section showed how you can add a nice HTML GUI to your code. Now see what can be done behind the scenes.

Installation and Migration

The Database Schema

Your package contributes to Bitweaver. That also is during database creation. To setup the correct tables, privileges and the such make use of the _admin/schema_inc.php_ script. It is called by the Bitweaver package installer during database creation time. The version shown here creates the table for our custom field in the BitResource class, registers the package and installs some privileges.

<?php
$tables 
= array(
    
'resources' => "
        content_id I4 NOTNULL PRIMARY,
        expires T
    "
,
);

global 
$gBitInstaller;

foreach( 
array_keys$tables ) AS $tableName ) {
    
$gBitInstaller->registerSchemaTableMYPACKAGE_PKG_NAME$tableName$tables[$tableName] );
}

$gBitInstaller->registerPackageInfoMYPACKAGE_PKG_NAME, array(
    
'description' => "What my package is made for",
    
'license' => '<a href="http://www.gnu.org/licenses/licenses.html#LGPL">LGPL</a>',
    
'version' => '0.1',
    
'state' => 'developing',
    
'dependencies' => 'users',
) );

// ### Default UserPermissions
$gBitInstaller->registerUserPermissionsMYPACKAGE_PKG_NAME, array(
    array(
'p_event_create''Can create a resource''registered'MYPACKAGE_PKG_NAME),
    array(
'p_event_admin''Can admin resources''editors'MYPACKAGE_PKG_NAME),
    array(
'p_event_read''Can read resources''registered'MYPACKAGE_PKG_NAME),
) );

?>


  • More about $gBitInstaller

Data Pump

If you want to populate the database with sample data during the installation process, you have to make use of the data pump.

For the easy to use BitResource type, simply create a file named _pump_mypackage_inc.php_ in the bitweaver/install directory with the following content. All it does is fill an array with the data to populate, then loop through that array and feed everything into BitResource::store(). Error/success messages are stored in variables.

This file will be called from the Bitweaver installer after the user agreed to populate the database, and afterwards the installer will display the error or success messages.


<?php
require_once( MYPACKAGE_PKG_PATH.'BitResource.php' );

$resourcesHash = array (
    array(
        
'title' => 'Coffee',
        
'edit' => 'just the classic instant coffe',
),
);

foreach( 
$resourcesHash as $resource ) {
    
$newResource = new BitResource();
    if( 
$newResource->store$resource ) ) {
        
$pumpedData['EventCal'][] = $resource['title'];
    } else {
        
$error $newResource->mErrors;
        
error_log('Error creating mypackage resources: '.$error0);
        
xdebug_debug_zval('error');
        
$gBitSmarty->assign'error',$error );
    }
}

?>

If you have more data or custom fields that need population, simply add them to the array above and you are done.

Data Format

National language characters

What encoding should be used for the PHP file? How shall national language characters (at worst the complete unicode character set) be escaped?

Timestamps

Bitweaver is said to use UTC times within the database. What times are used on timestamps in the installer?
(MySQL does not support timestamps with timezone information. However the timezone used must be set in the database connection. How is that handled?
 
The Bitweaver Framework, consisting of Liberty and Kernel, offers you a lot of functionality. Here we'll try to show how you can make use of that.

Your Own Package

Business Objects a la Liberty

Which base class?

If you want to create your own Business Object, you will probably use LibertyAttachable as base class, as it offers most functionality. Use LibertyContent or LibertyBase when you know what you are doing.

LibertyAttachable

Classes derived from LibertyAttachable come with this functionality (in addition to features already in LibertyContent):
  • They can take title and formatted text
  • They maintain creation date, creator and modification date/modificator information
  • The calendar can display events occurring to LibertyAttachable objects.
  • Upon database access (store, load) other service plugins are triggered

If all you need is a new data type that contains some formatted text, you might be pretty quick in implementation. Customise this sample and continue with your GUI code (List and Detail Views).

<?php
require_once( LIBERTY_PKG_PATH.'LibertyAttachable.php' ); //import base class

define'BITRESOURCE_CONTENT_TYPE_GUID''bitresource' ); // This is used to uniquely identify the object type

class BitResource extends LibertyAttachable {

    
/**
    * During initialisation, be sure to call our base constructors
    **/
    
function BitResource$pContentId=NULL ) {
        
LibertyAttachable::LibertyAttachable();

        
$this->mContentId $pContentId;
        
$this->mContentTypeGuid BITRESOURCE_CONTENT_TYPE_GUID;

        
$parms = array(
            
'content_type_guid' => BITRESOURCE_CONTENT_TYPE_GUID,
            
'content_description' => 'Managed Resource',
            
'handler_class' => 'BitResource',
            
'handler_package' => 'mypackage',
            
'handler_file' => 'BitResource.php',
            
'maintainer_url' => 'mailto:guess@who.com'
        
);
        
$this->registerContentTypeBITRESOURCE_CONTENT_TYPE_GUID$parms ); // register class with Bitweaver
    
}

}
?>


Custom fields

Probably you will want to add some custom fields. This will be some more work, as Liberty won't know about them and we have to handle them ourselves.

First think of a place to store the data. We will have to use our own table in the database. To match the liberty data with our own we should use an identifier. content_id is the one provided by liberty, and it will serve us well now. So create a table with two columns, just as shown. Later you will see how Bitweaver automatically creates the table upon package installation.

<?php
create table mydata 
(
    
content_id integer(4),
    
expires timestamp
)
?>


  • LibertyContent::setIndexData
  • getServicesSQL
  • prepGetList
  • postGetList

History functionality

How can you enable history tracking in Liberty?

LibertyContent

LibertyContent is the base for objects to be treated by Liberty. They come with these features:
  • their title and text content is subject to the full text seach

LibertyBase

Lists, sorted and filtered

Now let us create a list of resources. Hopefully you already have something in your database so get something to see. As a first, insert this data with your favourite client, or even better with the data pump described below.

Now we will write some code that loads your data from the database. It goes to _index.php_ in your package directory.

<?php
?>


But what? We call a method that does not exist. Unfortunately LibertyAttachable does not come with a method that could easily be there: getList(). And so we will have to create that for ourselves:

<?php
function getList( &$pParamHash ) {
    global 
$gBitSystem$gBitUser;

    
LibertyContent::prepGetList$pParamHash );// this makes sure parameters used later on are set

    
$selectSql $joinSql $whereSql '';
    
$bindVars = array();
    
array_push$bindVars$this->mContentTypeGuid );
    
$this->getServicesSql'content_list_sql_function'$selectSql$joinSql$whereSql$bindVars );

    
extract$pParamHash );// this will set $find, $sort_mode, $max_records and $offset

    
if( is_array$find ) ) { // you can use an array of pages
        
$whereSql .= " AND lc.`title` IN( ".implode',',array_fill0,count$find ),'?' ) )." )";
        
$bindVars array_merge $bindVars$find );
    } elseif( 
is_string$find ) ) { // or a string
        
$whereSql .= " AND UPPER( lc.`title` )like ? ";
        
$bindVars[] = '%' strtoupper$find ). '%';
    }

    
$query "SELECT lc.`content_id`, lc.`title`, lc.`data` $selectSql
        FROM `"
.BIT_DB_PREFIX."liberty_content` lc $joinSql
        WHERE lc.`content_type_guid` = ? 
$whereSql
        ORDER BY "
.$this->mDb->convert_sortmode$sort_mode );
    
$query_cant "select count(*)
        FROM `"
.BIT_DB_PREFIX."eventcal_events` ts
            INNER JOIN `"
.BIT_DB_PREFIX."liberty_content` lc ON( lc.`content_id` = ts.`content_id` )
            
$joinSql
        WHERE lc.`content_type_guid` = ? 
$whereSql";

        
$result $this->mDb->query$query$bindVars$max_records$offset );
        
$ret = array();
        while( 
$res $result->fetchRow() ) {
            
$ret[] = $res;
        }
        
$pParamHash["cant"] = $this->mDb->getOne$query_cant$bindVars );

        
// add all pagination info to pParamHash
        
LibertyContent::postGetList$pParamHash );

        return 
$ret;
    }
?>


  • $gBitSmarty->assign_by_ref(...)
  • $gBitSystem->display(...)

Make something visible: The UI

Probably you cannot wait to display somethind on the screen. Up to now we had concentrated on your data objects - the model. Bitweaver works with the MVC pattern. Find out more about this pattern in Model View Controller - MVC, but now we make use of the model, create a simple controller and focus on the view.

Have a look at this picture. It should give you a rough idea of what part of a bitweaver page you will be working on.

In most cases you write ordinary module code that goes into the main area in the middle. Then you can place any HTML/JavaScript/whatever code in your templates but surround it with the tag
, which will look like this:

<?php
<div class="bitmain">
    <!-- 
your HTML here -->
</
dov>
?>

Or you might be creating a module, in which case it is displayed either in the left or right column. In that case surreund your HTML with
as shown:

<?php
<div class="bitmodule">
    <!-- 
your HTML here -->
</
dov>
?>

Don't use tables to control the position as Bitweaver already takes care for the layout (and that can be changed in the admin menu). If you cannot believe it, read Table_vs_Div.

Of course within the
element you can use tables to organise your information.

List Views

  • The Controller
  • The Template (View)

Detail Views

  • The Controller
  • The Template (View)

Feedback and Confirmation

Up to now, the answer is to look at the SamplePackage and guess.
errors, warnings, message text and confirmation dialogs
  • gBitSystem->setBrowserTitle()
  • gBitSystem->confirmDialog()

Authorisation Required

Up to now, the answer is to look at the SamplePackage and guess.
Up to now, the answer is to look at the SamplePackage and guess.
Layout assigned module titles and parameters

Services

The previous section showed how you can add a nice HTML GUI to your code. Now see what can be done behind the scenes.

Installation and Migration

The Database Schema

Your package contributes to Bitweaver. That also is during database creation. To setup the correct tables, privileges and the such make use of the _admin/schema_inc.php_ script. It is called by the Bitweaver package installer during database creation time. The version shown here creates the table for our custom field in the BitResource class, registers the package and installs some privileges.

<?php
$tables 
= array(
    
'resources' => "
        content_id I4 NOTNULL PRIMARY,
        expires T
    "
,
);

global 
$gBitInstaller;

foreach( 
array_keys$tables ) AS $tableName ) {
    
$gBitInstaller->registerSchemaTableMYPACKAGE_PKG_NAME$tableName$tables[$tableName] );
}

$gBitInstaller->registerPackageInfoMYPACKAGE_PKG_NAME, array(
    
'description' => "What my package is made for",
    
'license' => '<a href="http://www.gnu.org/licenses/licenses.html#LGPL">LGPL</a>',
    
'version' => '0.1',
    
'state' => 'developing',
    
'dependencies' => 'users',
) );

// ### Default UserPermissions
$gBitInstaller->registerUserPermissionsMYPACKAGE_PKG_NAME, array(
    array(
'p_event_create''Can create a resource''registered'MYPACKAGE_PKG_NAME),
    array(
'p_event_admin''Can admin resources''editors'MYPACKAGE_PKG_NAME),
    array(
'p_event_read''Can read resources''registered'MYPACKAGE_PKG_NAME),
) );

?>


  • More about $gBitInstaller

Data Pump

If you want to populate the database with sample data during the installation process, you have to make use of the data pump.

For the easy to use BitResource type, simply create a file named _pump_mypackage_inc.php_ in the bitweaver/install directory with the following content. All it does is fill an array with the data to populate, then loop through that array and feed everything into BitResource::store(). Error/success messages are stored in variables.

This file will be called from the Bitweaver installer after the user agreed to populate the database, and afterwards the installer will display the error or success messages.


<?php
require_once( MYPACKAGE_PKG_PATH.'BitResource.php' );

$resourcesHash = array (
    array(
        
'title' => 'Coffee',
        
'edit' => 'just the classic instant coffe',
),
);

foreach( 
$resourcesHash as $resource ) {
    
$newResource = new BitResource();
    if( 
$newResource->store$resource ) ) {
        
$pumpedData['EventCal'][] = $resource['title'];
    } else {
        
$error $newResource->mErrors;
        
error_log('Error creating mypackage resources: '.$error0);
        
xdebug_debug_zval('error');
        
$gBitSmarty->assign'error',$error );
    }
}

?>

If you have more data or custom fields that need population, simply add them to the array above and you are done.

Data Format

National language characters

Short answer: Make sure all your PHP and TPL files are encoded in UTF-8.


Apache, PHP and Bitweaver operate on byte streams. Character sequences are just arrays of bytes. So you need not care what character encoding your national special characters are in - as long as the data is supplied by browsers.

Per default Bitweaver tells the web browser that UTF-8 is used. Therefore any data presented to the browser is assumed to be UTF-8 (even if it is not which results in garbage) and the browser will send user input encoded in UTF-8. That solves the problem almost completely.

There are two items which you will have to look at: Your PHP source and template files. Again, PHP does not assume any encoding on the files, they are just loaded as byte arrays. The PHP code in these files should not contain anything beyond ASCII, but any characters within string literals can. And this data goes untouched through the whole system until it has to be displayed on the screen. As mentioned earlier, the browser will assume all data to be in UTF-8. So you can see garbabe being displayed unless you change the encoding of those characters.

Note: I've seen troubles with trimming down text with special chars. The common example is when article gets automaticly cut after the set number of letters, and the rest is placed to the "read more" part. It says letters, but it actually uses bytes to count. It takes 2 bites to encode most non-Latin1 chars, so after trimming sometimes the last letter is presented with only one bite of 2 necessary, and shows in browser as garbage. So until this is fixed somehow, avoid trimming texts by your php files and templates if you use special chars.

Timestamps

Bitweaver is said to use UTC times within the database. What times are used on timestamps in the installer?
(MySQL does not support timestamps with timezone information. However the timezone used must be set in the database connection. How is that handled?

Page History
Date/CommentUserIPVersion
27 Jan 2007 (17:03 UTC)
dspt213.184.224.341
Current • Source
hiran85.233.40.19239
View • Compare • Difference • Source
hiran85.233.40.19238
View • Compare • Difference • Source
hiran85.233.40.19234
View • Compare • Difference • Source
hiran85.233.40.19233
View • Compare • Difference • Source
hiran85.233.40.19228
View • Compare • Difference • Source
hiran85.233.40.19227
View • Compare • Difference • Source
hiran85.233.40.19226
View • Compare • Difference • Source
hiran85.233.40.19225
View • Compare • Difference • Source
hiran85.233.40.19224
View • Compare • Difference • Source
hiran85.233.40.19223
View • Compare • Difference • Source
hiran85.233.40.19222
View • Compare • Difference • Source
hiran85.233.40.19221
View • Compare • Difference • Source
hiran85.233.40.19220
View • Compare • Difference • Source
hiran85.233.40.19219
View • Compare • Difference • Source
hiran85.233.40.19218
View • Compare • Difference • Source
hiran85.233.40.19217
View • Compare • Difference • Source
hiran85.233.40.19216
View • Compare • Difference • Source
hiran85.233.40.19214
View • Compare • Difference • Source
hiran85.233.40.19212
View • Compare • Difference • Source
hiran85.233.40.19211
View • Compare • Difference • Source
hiran85.233.40.19210
View • Compare • Difference • Source
hiran85.233.40.1928
View • Compare • Difference • Source
hiran85.233.40.1927
View • Compare • Difference • Source
hiran85.233.40.1925
View • Compare • Difference • Source
hiran85.233.40.1924
View • Compare • Difference • Source