Cchost/developer/Common HOWTO's
Contents
Where your custom code lives
All files with a .PHP extension that reside in the <local_files>/lib directory will be parsed and executed on every page request to a ccHost installation. Where <local_files> is the name of the directory the administrator specified during installation as to where site customizations reside.
Add or remove form fields
Editing the fields of the form
Sometimes you may want to change the fields in a form that already exists in the system (e.g. the upload content form). In order to add form fields, remove them or change some properties on existing one, you first need to register a handler for the CC_EVENT_FORM_FIELDS event and direct it to a PHP function.
This event is triggered every time a form is built, but before the HTML is generated for it. The 'form' parameter is a reference to an object derived from CCForm and 'fields' parameter is a reference to an array that has all the current information about the fields.
Add fields
Put the following into a module with a .PHP extension into the <local_files>/lib directory:
// 1. Register for form_fields event CCEvents::AddHandler(CC_EVENT_FORM_FIELDS, 'form_fields_handler'); // 2. Here's the handler function form_fields_handler( &$form, &$fields ) { // 3. Uncomment these lines to peek into the variables... // CCDebug::Enable(true); // CCDebug::PrintVar($form); // 4. Here is where we check to see if it's a form we care about if( is_subclass_of($form,'CCUploadMediaForm') || is_subclass_of($form,'ccuploadmediaform') ) { /* * 5. Check if the field already in the field list. * * The name of our example field is 'upload_bpm' - you can * name yours whatever you like. * */ if( empty($fields['upload_bpm']) ) { // 6. The field does not exist, let's add it... $fields['upload_bpm'] = array( 'label' => 'Tempo', 'form_tip' => 'Please enter the tempo in beats per minute', 'class' => 'form_input_short', 'formatter' => 'textedit', 'flags' => CCFF_NOUPDATE); } } }
All forms in the system are of a particular class so you can check for the class name or a derivation of that class in order to make sure you have the right. In step 4. above we are only interested in content submit forms.
Always check to see if the field you are adding hasn't already been added and if you are deleting or editing that the field actually exists using the PHP 'empty()' as in step 5. above.
Remove an existing field
If you want to remove a field completely from the forum you use PHP's unset. Replace step 5 above with something like:
if( isset($fields['topic_name']) ) { unset( $fields['topic_name'] ); // this will remove the topic name field from form }
This can have bad side-effects if the system's form POST handling code expects the field to be there. A better option may be to hide the field in the form:
$form->SetHiddenField( 'topic_name' );
Edit existing fields
If you want to change the properties of field (like say, change the Topic Name field from a 'textedit' to a larger 'textarea' when submitting a form message or review) it would look something like this:
if( is_subclass_of($form,'CCTopicForm') || is_subclass_of($form,'cctopicform') ) { /* * First we check to make sure the field exists at all */ if( !empty($fields['topic_name']) ) { // override whatever was there with 'textarea' $fields['topic_name']['formatter'] = 'textarea'; } }
Pre-filling the field with values
Sometimes a record in the system is edited and we have a custom field in the form we would want to pre-populate the value (like when the user selects 'Edit Properties' for an upload). In order to set the value of field before it is displayed to the user, you need to capture the event that is triggered as the form is populating with values:
CCEvents::AddHandler(CC_EVENT_FORM_POPULATE, 'form_populate_handler'); function form_populate_handler(&$form,&$values) { // check to see if it's the kind of form we // care about... if( !is_subclass_of($form,'CCUploadMediaForm') && !is_subclass_of($form,'ccuploadmediaform') ) { return; } // $values might have our value in it (in the case // of an upload "Edit Properties" form if( !empty($values['upload_extra']['bpm']) ) { $value = $values['upload_extra']['bpm']; // Call 'SetFormValue' with our value... $form->SetFormValue( 'upload_bpm', $value ); } }
Validating your form field's user input
If you've added a new field you must validate the user's input and notify them if something causes the value to be rejected. This is phase of the form POST handling where the code extracts the value from PHP's $_POST array and transfers the data to ccHost's field list.
Here's the hook and hanlder for that scenario:
CCEvents::AddHandler(CC_EVENT_FORM_VERIFY, 'form_verify_handler'); function form_verify_handler( &$form, &$retval ) { if( !is_subclass_of($form,'CCUploadMediaForm') && !is_subclass_of($form,'ccuploadmediaform') ) { // this is not a form we care about return; } if( !array_key_exists('upload_bpm', $_POST) ) { // for whatever reason, our field is not in the form _POST return; } // It's always a good idea to 'strip' user' input from a form $bpm = CCUtil::StripText($_POST['upload_bpm']); if( empty($bpm) ) return; $ok = 'YOUR_CODE_TO_VALIDATE_GOES_HERE'; if( $ok ) { // success! Set it into the form $form->SetFormValue('upload_bpm',$bpm ); } else { // wups, stop the form post, return the user and show this error: $form->SetFieldError( 'upload_bpm', "BPM must be between 30 and 200" ); } $retval = $ok; // Make to tell the form system what happened (!!!) return $ok; }
Saving your form field's user input
You probably want to commit the data in a form to disk for use in later sessions. When to do that depends on the operation. For example if the form you are manipulating is the upload submit form, then you should register for the even triggered when everything else in the upload has succeeded and dig your value out of $_POST.
Here's an example we do just that:
CCEvents::AddHandler( CC_EVENT_UPLOAD_DONE, 'upload_done_handler' ); function upload_done_handler( $upload_id, $op ) { if( ($op == CC_UF_NEW_UPLOAD || $op == CC_UF_PROPERTIES_EDIT) && array_key_exists('upload_bpm',$_POST) ) { $uploads =& CCUploads::GetTable(); $uploads->SetExtraField( $upload_id, 'bpm', $_POST['upload_bpm']); } }
Add data per user
Note: this only works in 5.0.1 or later
In order to add data to a user's record you will need their unique identifier number - this is the index key in cc_tbl_user. If you don't have it readily you should have the short user name - this is the user_name field in cc_tbl_user - and convert it to the id number.
$user_id = CCUser::IDFromName($user_name);
If you are looking for the currently logged in user you can call:
$user_id = CCUser::CurrentUser(); // Returns -1 if failed (not logged in)
To save your data to the user's record:
$users =& CCUsers::GetTable(); // note the 's' in CCUsers $users->SetExtraField($user_id,'YOUR_DATA_KEY_GOES_HERE', 'YOUR_VALUE_GOES_HERE' );
To retrieve it at a later time:
$users =& CCUsers::GetTable(); $value = $users->SetExtraField($user_id,'YOUR_DATA_KEY_GOES_HERE');
If you are using SQL you will next expand the 'user_extra' field like this:
$user_row = CCDatabase::QueryRow('SELECT * FROM cc_tbl_user WHERE user_id = ' . $user_id ); $user_extra = unserialize($user_row['user_extra']); $value = $user_extra['YOUR_DATA_KEY_GOES_HERE'];
If you want to remove the data key and it's value completely from the record (e.g. to save space), then use the following method:
$users =& CCUsers::GetTable(); $users->UnsetExtraField($user_id,'YOUR_DATA_KEY_GOES_HERE');
Add data per upload
In order to add data to an upload record you will need their unique identifier number - this is the index key in cc_tbl_uploads.
To save your data to the upload record:
$uploads =& CCUploads::GetTable(); $uploads->SetExtraField($upload_id,'YOUR_DATA_KEY_GOES_HERE', 'YOUR_VALUE_GOES_HERE' );
To retrieve it at a later time:
$uploads =& CCUploads::GetTable(); $value = $uploads->SetExtraField($upload_id,'YOUR_DATA_KEY_GOES_HERE');
If you are using SQL you will next expand the 'upload_extra' field like this:
$upload_row = CCDatabase::QueryRow('SELECT * FROM cc_tbl_uploads WHERE upload_id = ' . $upload_id ); $upload_extra = unserialize($upload_row['upload_extra']); $value = $upload_extra['YOUR_DATA_KEY_GOES_HERE'];
If you want to remove the data key and it's value completely from the record (e.g. to save space), then use the following method:
$uploads =& CCUploads::GetTable(); $uploads->UnsetExtraField($upload_id,'YOUR_DATA_KEY_GOES_HERE');
Using custom data in a template
The typical template will display a record that is in a loop. The custom data is probably going to be in upload_extra or user_extra depending on the type of record you are displaying.
The PHP template version:
foreach( $A['records'] as $R ) { if( !empty($R['upload_extra']['YOUR_DATA_KEY_HERE']) ) { print '<span>' . $R['upload_extra']['YOUR_DATA_KEY_HERE'] . '</span>'; } }
The TPL version:
%loop(records,R)% %if_not_null(#R/upload_extra/YOUR_DATA_KEY_HERE)% <span>%(#R/upload_extra/YOUR_DATA_KEY_HERE)%</span> %end_if% %end_loop%
Note that the upload_extra field is not guaranteed to be available in the template. You have to ask for it specifically in the dataview that is feeding the template.
Saving and retrieving system configuration data
There may be times when you want to save data that is available across sessions that is not associated with any user or upload. You will need the CCConfigs table to do that.
To save settings data into configs:
$myoptions['option1'] = 'OPTION_1_VALUE'; $myoptions['option2'] = 'OPTION_2_VALUE'; $configs =& CCConfigs::GetTable(); $configs->SaveConfig('YOUR_SETTINGS_NAME_HERE', $myoptions );
To retrieve them later:
$configs =& CCConfigs::GetTable(); $myoptions = $configs->GetConfig('YOUR_SETTINGS_NAME_HERE'); $opt_1 = $myoptions['option1'];
Mapping URLs to custom code
In order to map a PHP function to the url some/url:
CCEvents::AddHandler(CC_EVENT_MAP_URLS, 'map_urls_handler'); function map_urls_handler() { CCEvents::MapUrl( ccp( 'some', 'url' ), 'some_url_handler', CC_DONT_CARE_LOGGED_IN ); } function some_url_handler_handler() { }
If the user appends to the URI path (e.g. some/url/arg1/arg2) those will be passed in as arguments, so you should write your handler like this:
function some_url_handler( $arg1='', $arg2='' ) { }
The last parameter in CCEvents::MapUrl determines who is allowed to process the URL. These are valid values:
- CC_MUST_BE_LOGGED_IN Registered users
- CC_ONLY_NOT_LOGGED_IN Anonymous users only
- CC_DONT_CARE_LOGGED_IN Everybody
- CC_ADMIN_ONLY Admins only
- CC_SUPER_ONLY Super admins only
Note: Every time you change the URL map you must perform ?update=1 on your site
If you only have one or a few settings, you can it/them directly onto the admin 'Settings' page. If you have many settings (a whole screen full) things a little more verbose.
Adding an admin option Settings
ccHost will trigger an event when it is gathering options for the admin Settings page. Capture the event and fill in the fields like this:
CCEvents::AddHandler(CC_EVENT_GET_CONFIG_FIELDS, 'config_fields_handler'); function config_fields_handler( { if( $scope == CC_GLOBAL_SCOPE ) { $fields['YOUR_SETTINGS_NAME_HERE'] = array( 'label' => 'My wizbang enabled' 'value' => 'Check here to enable my wizbang feature.', 'formatter' => 'checkbox', // any field here will work 'flags' => CCFF_POPULATE); } }
Adding a whole admin settings form
If you have many settings you want to add you should do so by adding an admin menu item that maps to a custom url that in turn invokes the form:
// Let's make some defines to avoid typos and show the relationship // between key phrases define ( 'URL_TO_WIZBANG_ADMIN', 'admin/wizbang' ); deifne ( 'NAME_OF_WIZBANG_CONFIG', 'wizbang' ); // Register to get notified when the admin menu is being built CCEvents::AddHandler(CC_EVENT_ADMIN_MENU, 'admin_menu_handler'); // This function will be called when the admin menu is about to be // generated. Add your menu item(s) to '$items' function admin_menu_handler( &$items, $scope ) { if( $scope == CC_GLOBAL_SCOPE ) { $items += array( NAME_OF_WIZBANG_CONFIG => array( 'menu_text' => 'My Wizbang Settings', 'menu_group' => 'configure', 'help' => 'Config settings for My Wizbang functionality', 'access' => CC_ADMIN_ONLY, 'weight' => 300, 'action' => ccl( URL_TO_WIZBANG_ADMIN ) ), ); } } CCEvents::AddHandler(CC_EVENT_MAP_URLS, 'map_urls_handler'); function map_urls_handler() { CCEvents::MapUrl( ccp( URL_TO_WIZBANG_ADMIN ), 'admin_mywizbang_handler', CC_ADMIN_ONLY ); } function admin_mywizbang_handler() { require_once('<local_files>/lib/mywizbang_admin_form.inc'); require_once( 'cchost_lib/cc-page.php'); require_once('cchost_lib/cc-admin.php'); // Build up page adornments $title = 'Admin Settings for MyWizBang'; CCPage::SetTitle( $title ); CCAdmin::BreadCrumbs( true, array('url'=>,'text'=>$title)); // Display the form // NOTE: $_POST handling will be handled by ccHost. $form = new CCMyWizBangAdminForm(); CCPage::AddForm( $form->GenerateForm() ); }
In another file <local_files>/lib/mywizbang_admin_form.inc (do NOT use PHP extension) put code that does something like this:
// NB: The name of your form MUST start with 'CC' and end with 'Form' class CCMyWizBangAdminForm : extends CCEditConfigForm { function CCMyWizBangAdminForm() { $this->CCEditConfigForm( NAME_OF_WIZBANG_CONFIG ); // replace these fields with something meaningful: $fields = array( 'option1' => // name of the setting array( 'label' => 'Set Option 1 here', 'form_tip' => 'Check this to enable option 1', 'value' => , 'formatter' => 'textedit', 'flags' => CCFF_POPULATE | CCFF_REQUIRED ), 'option2' => array( 'label' => 'Set Option 2 here', 'form_tip' => 'Check this to enable option 2', 'value' => , 'formatter' => 'checkbox', 'flags' => CCFF_POPULATE ), ); $this->SetModule(__FILE__); $this->AddFormFields($fields); } }