6.0.0-beta1
7/24/25

[#1395] Add preference data type support for floating point numbers
Summary Add preference data type support for floating point numbers
Queue Horde Base
Queue Version 3.0.3
Type Enhancement
State Rejected
Priority 1. Low
Owners
Requester scott (at) realorganized (dot) com
Created 02/17/2005 (7462 days ago)
Due
Updated 10/23/2005 (7214 days ago)
Assigned 02/17/2005 (7462 days ago)
Resolved 10/23/2005 (7214 days ago)
Milestone
Patch No

History
10/23/2005 12:11:34 AM Jan Schneider Comment #9
State ⇒ Rejected
Reply to this comment
No feedback.
05/29/2005 04:35:41 AM Chuck Hagenbuch Comment #8
Taken from Horde DevelopersHorde Developers
Reply to this comment
Still no feedback. Unassigning for now. Scott, please see 
http://wiki.horde.org/AddingPages for wiki info, and please upload the 
diffs - this ticket is way too long and old to be comprehensible with 
inline changes.
03/06/2005 05:29:45 PM Jan Schneider Comment #7 Reply to this comment
How about the unified diff to UI.php?
02/18/2005 05:55:34 PM scott (at) realorganized (dot) com Comment #6 Reply to this comment
I have some text for the wiki, however I can't figure out how to 
create a new page.



Here is the text for your review:



Preferences are an important infrastructure capability of the Horde 
Framework.  Each user has their own preference settings.  These 
settings are preserved from session to session and therefore are a 
useful way to retain long-term preference settings for the user.   
Since preferences are unique to each user, it would not be appropriate 
to use preferences to specify general server settings for the entire 
Horde server.  You should use the Horde Configuration capability for 
that.  Preferences are saved across multiple sessions.  If you would 
like to store state information for a particular session, use the PHP 
session capability.



Horde preferences can be specific to your application or can be shared 
amongst multiple applications.  Generally, your preference settings 
would be local and not shared.  However, if you had a suite of 
applications which each used a particular preference you would 
designate that preference as shared.



Horde preferences can be locked.  What this means is that the user 
would not be able to view, or edit this preference from the setting 
screens.  However, locked preferences can be changed via programatic 
interface.  For example, if you allowed users to modify their sort 
order via a control within your application, then there would be no 
need to display this information in the settings screens.  This 
preference setting would be considered locked.  Another use for a 
locked preference is to store a preference for a setting that you have 
not yet decided to expose to the user.  Your code can operate on this 
setting, but the user is not allowed to view or change it.



To initialize your preferences, you would place your initial 
preference settings in the prefs.php file located here: 
yourapplication/config/prefs.php

Each preference setting is called a property and is initialized with 
code similar to that shown below:



$_prefs['yourpropertyname'] = array(

     'value' => '28',

     'locked' => false,

     'shared' => false,

     'type' => 'floatnumber',

     'desc' => _("% Housing-to-Income Ratio")

);



The 'value' element is the default value for your property.  'locked' 
and 'shared' are as we have discussed above.  'type' is the way you 
would like your data to be presented to the user when they view your 
preferences panel.  'desc' is a description string that is displayed 
in your preferences panel describing the property.  Notice the double 
quotes and enclosure using _( ).  This is used for 
internationalization support.



If you have many preferences, you can organize them into preference 
groups.  These are logical groups of one or more properties organized 
into individual preference penal.  When the user views the settings 
for your application, they would see an initial screen of all the 
individual preference panels.  The user then selects an individual 
panel (group).



To create a preference group, you would add code like this to your 
prefs.php file:



$prefGroups['calculator'] = array(

     'column' => _("Calculator Settings"),

     'label' => _("Calculator Defaults"),

     'desc' => _("Change default calculator settings."),

     'members' => array('yorproperty', 'anotherproperty', ... )

);



Add a section like that shown above for each preference panel.



Information about possible values for each of these arrays can be 
found here: 'horde/config/prefs.php'



The predefined types provided are fairly complete and can provide 
basic support for user preferences.  However, there will always be a 
need to create some level of customization of individual properties.



To create your own custom property handler, set your 'type' => 'special'.

Then add a file to 
'yourapplication/templates/prefs/yourpropertyname.inc' containng the 
html code generation code for the control.

Add a function handler into the file 'yourapplication/lib/prefs.php'

The function handler is of the form 'handle_yourpropertyname($updated)'



One limitation of custom property handlers is that they are specific 
to an individual property.  If you have several properties that need 
the same custom control, you cannot reuse the handler.  For cases 
where you would like to reuse your handler, you can create your own 
custom type hander.



To create your own custom type handler:

Assign your own custom type to 'type' => yourcustomtype

Place a file inside your application at 
'templates/prefs/yourcustomtype.inc' which contains the html for the 
control.

Inside of the lib/prefs.php in your application, add a function 
'handle_yourcustotype'



Warning: If you have a shared preference, custom UI gadgets will not 
be available to other applications.
02/18/2005 04:20:08 PM scott (at) realorganized (dot) com Comment #5 Reply to this comment
To integrate both feature requests you would do the following:

1. Apply the changes described in the comment on 02/17/05 to ui.php.   
This change includes both the floating point field as well as the 
ability to create new types.

2. Add the attached file floatnumber.inc included and described in the 
note on 2/16/2005.  You can ignore the change to ui.php in that note 
as it is in the 2/17/05 note.



I'll add the Wiki documentation today.
02/18/2005 09:55:31 AM Jan Schneider Comment #4
State ⇒ Feedback
Reply to this comment
Please upload a unified diff for the changes you made to Prefs/UI.php.

And I think the steps necessary to create such custom prefs (as well 
as how to use pref in general) should better be documented in the wiki 
than in the source code.
02/18/2005 02:23:30 AM scott (at) realorganized (dot) com Comment #3 Reply to this comment
Here is a sample handler function which would go into the 
application's pref.php file for each type that a UI widget is created 
for.  The $pref variable is the individual preference for which the UI 
widget is being handled for.



function handle_largefloat($updated, $pref)

{

     global $prefs, $notification;

     $num = Util::getPost($pref);

     if (floatval($num) != $num) {

         $notification->push(_("This value must be a number."), 'horde.error');

     } else {

         $updated = $updated | $prefs->setValue($pref, $num);

     }

     return $updated;

}



The .inc file with the html code is identical to the existing help 
controls with the exception that the file name uses the type rather 
than the property name.
02/18/2005 02:15:36 AM scott (at) realorganized (dot) com Comment #2 Reply to this comment
I have a second related feature request.  Currently, you can create UI 
gadgets for specific properties within your application. While useful, 
this has the disadvantage that the UI gadgets are one-of-a-kind and 
therefore do not lend themselves to multiple use of the preerences UI 
gadget that you create.  For example, if I created an RGB control, it 
would be great if I could simply define a type 'rgbcolor' in such a 
way that I could use this gadget and handler for any properties that I 
like.  I think this promotes reusability and have a particular need 
for this in the application that I am developing as I have several 
dozen preference settings with some which are similar to one another.   
Instructions for use are in a comment in the code.



Towards this end, I propose the following patch.  It requires two 
quite small changes to the horde/lib/horde/prefs/ui.php file.  I have 
marked the changes with SDS.



     function generateUI($group = null)

     {

         global $browser, $conf, $prefs, $prefGroups, $_prefs, $registry, $app;



         /* Check if any options are actually available. */

         if (is_null($prefGroups)) {

             $GLOBALS['notification']->push(_("There are no options 
available."), 'horde.message');

         }



         /* Show the header. */

         Prefs_UI::generateHeader($group);



         /* Assign variables to hold select lists. */

         if (!$prefs->isLocked('language')) {

             $GLOBALS['language_options'] = &$GLOBALS['nls']['languages'];

         }



         if (!empty($group) && Prefs_UI::groupIsEditable($group)) {

             foreach ($prefGroups[$group]['members'] as $pref) {

                 if (!$prefs->isLocked($pref)) {

                     /* Get the help link. */

                     if (!empty($_prefs[$pref]['help'])) {

                         $helplink = 
Help::link(!empty($_prefs[$pref]['shared']) ? 'horde' : 
$registry->getApp(), $_prefs[$pref]['help']);

                     } else {

                         $helplink = null;

                     }



                     switch ($_prefs[$pref]['type']) {

                     case 'implicit':

                         break;



                     case 'special':

                         require $registry->get('templates', 
!empty($_prefs[$pref]['shared']) ? 'horde' : $registry->getApp()) . 
"/prefs/$pref.inc";

                         break;



                     case 'link':        // SDS 2/17/2005

                     case 'select':

                     case 'text':

                     case 'textarea':

                     case 'password':

                     case 'enum':

                     case 'multienum':

                     case 'number':

                     case 'floatnumber':

                     case 'checkbox':

                         require $registry->get('templates', 'horde') 
. '/prefs/' . $_prefs[$pref]['type'] . '.inc';

                         break;



                     default:        // SDS 2/17/2005

                             /* Individual applications can create their own 
class of UI handler

                          * just invent your own type.  Then place a 
file inside your application

                          * at templates/prefs/yourcustomtype.inc 
which contains your html for the control.

                          * Inside of the lib/prefs.php in your 
application, add a function

                          * handle_yourcustotype and you are done.  If 
you have a shared

                          * preference, this UI gadget will not be 
available to other applications.

                          * Differs from 'special' in that you can 
re-use same UI widget

                          * in your application's preferences. */

                         $custType = $_prefs[$pref]['type'];

                         require $registry->get('templates', 
$registry->getApp()) . "/prefs/$custType.inc";

                         break;

                     }

                 }

             }

             require $registry->get('templates', 'horde') . '/prefs/end.inc';

         } else {

             $columns = array();

             if (is_array($prefGroups)) {

                 foreach ($prefGroups as $group => $gvals) {

                     if (Prefs_UI::groupIsEditable($group)) {

                         $col = $gvals['column'];

                         unset($gvals['column']);

                         $columns[$col][$group] = $gvals;

                     }

                 }

                 $span = round(100 / count($columns));

             } else {

                 $span = 100;

             }



             require $registry->get('templates', 'horde') . 
'/prefs/overview.inc';

         }

     }



     function handleForm(&$group, &$save)

     {

         global $prefs, $prefGroups, $_prefs, $notification, $registry;



         $updated = false;



         /* Run through the action handlers */

         if (Util::getPost('actionID') == 'update_prefs') {

             if (isset($group) && Prefs_UI::groupIsEditable($group)) {

                 $updated = false;



                 foreach ($prefGroups[$group]['members'] as $pref) {

                     if (!$prefs->isLocked($pref) ||

                         ($_prefs[$pref]['type'] == 'special')) {

                         switch ($_prefs[$pref]['type']) {



                         /* These either aren't set or are set in other

                          * parts of the UI. */

                         case 'implicit':

                         case 'link':

                             break;



                         case 'select':

                         case 'text':

                         case 'textarea':

                         case 'password':

                             $updated = $updated | 
$save->setValue($pref, Util::getPost($pref));

                             break;



                         case 'enum':

                             $val = Util::getPost($pref);

                             if (isset($_prefs[$pref]['enum'][$val])) {

                                 $updated = $updated | 
$save->setValue($pref, $val);

                             } else {

                                 $notification->push(_("An illegal 
value was specified."), 'horde.error');

                             }

                             break;



                         case 'multienum':

                             $vals = Util::getPost($pref);

                             $set = array();

                             $invalid = false;

                             if (is_array($vals)) {

                                 foreach ($vals as $val) {

                                     if (isset($_prefs[$pref]['enum'][$val])) {

                                         $set[] = $val;

                                     } else {

                                         $invalid = true;

                                         continue;

                                     }

                                 }

                             }



                             if ($invalid) {

                                 $notification->push(_("An illegal 
value was specified."), 'horde.error');

                             } else {

                                 $updated = $updated | 
$save->setValue($pref, @serialize($set));

                             }

                             break;



                         case 'number':

                             $num = Util::getPost($pref);

                             if (intval($num) != $num) {

                                 $notification->push(_("This value 
must be a number."), 'horde.error');

                             } elseif ($num == 0) {

                                 $notification->push(_("This number 
must be at least one."), 'horde.error');

                             } else {

                                 $updated = $updated | 
$save->setValue($pref, $num);

                             }

                             break;



                        case 'floatnumber':        // SDS 2/16/2005

                             $num = Util::getPost($pref);

                             if (floatval($num) != $num) {

                                 $notification->push(_("This value 
must be a number."), 'horde.error');

                             } else {

                                 $updated = $updated | 
$save->setValue($pref, $num);

                             }

                             break;



                         case 'checkbox':

                             $val = Util::getPost($pref);

                             $updated = $updated | 
$save->setValue($pref, isset($val) ? 1 : 0);

                             break;



                         case 'special':

                             /* Code for special elements must be

                              * written specifically for each

                              * application. */

                             if (function_exists('handle_' . $pref)) {

                                 $updated = $updated | 
call_user_func('handle_' . $pref, $updated);

                             }

                             break;



                         default :        // SDS 2/17/2004

                             /* Individual applications can create 
their own class of UI handler

                              * just use the special 'type' that you create

                              * differs from 'special' in that you can 
use same UI widget

                              * in several places in your 
application's preferences . */

                             if (function_exists('handle_' . 
$_prefs[$pref]['type'])) {

                                 $updated = $updated | 
call_user_func('handle_' . $_prefs[$pref]['type'], $updated, $pref);

                             }

                             break;



                         }

                     }

                 }



                 if ($updated) {

                     if (function_exists('prefs_callback')) {

                         prefs_callback();

                     }

                     $notification->push(_("Your options have been 
updated."), 'horde.message');

                     $group = null;

                 }

             }

         }



         return $updated;

     }



     /**

      * Generate the UI for the preferences interface, either for a

      * specific group, or the group selection interface.

      *

      * @access public

      *

      * @param optional string $group  The group to generate the UI for.

      */

     function generateUI($group = null)

     {

         global $browser, $conf, $prefs, $prefGroups, $_prefs, $registry, $app;



         /* Check if any options are actually available. */

         if (is_null($prefGroups)) {

             $GLOBALS['notification']->push(_("There are no options 
available."), 'horde.message');

         }



         /* Show the header. */

         Prefs_UI::generateHeader($group);



         /* Assign variables to hold select lists. */

         if (!$prefs->isLocked('language')) {

             $GLOBALS['language_options'] = &$GLOBALS['nls']['languages'];

         }



         if (!empty($group) && Prefs_UI::groupIsEditable($group)) {

             foreach ($prefGroups[$group]['members'] as $pref) {

                 if (!$prefs->isLocked($pref)) {

                     /* Get the help link. */

                     if (!empty($_prefs[$pref]['help'])) {

                         $helplink = 
Help::link(!empty($_prefs[$pref]['shared']) ? 'horde' : 
$registry->getApp(), $_prefs[$pref]['help']);

                     } else {

                         $helplink = null;

                     }



                     switch ($_prefs[$pref]['type']) {

                     case 'implicit':

                         break;



                     case 'special':

                         require $registry->get('templates', 
!empty($_prefs[$pref]['shared']) ? 'horde' : $registry->getApp()) . 
"/prefs/$pref.inc";

                         break;



                     case 'link':        // SDS 2/17/2005

                     case 'select':

                     case 'text':

                     case 'textarea':

                     case 'password':

                     case 'enum':

                     case 'multienum':

                     case 'number':

                     case 'floatnumber':

                     case 'checkbox':

                         require $registry->get('templates', 'horde') 
. '/prefs/' . $_prefs[$pref]['type'] . '.inc';

                         break;



                     default:        // SDS 2/17/2005

                             /* Individual applications can create their own 
class of UI handler

                          * just invent your own type.  Then place a 
file inside your application

                          * at templates/prefs/yourcustotype.inc which 
contains your html for the control

                          * inside of the lib/prefs.php in your 
application, add a function

                          * handle_yourcustotype an you are done.   
Remember - if you have a shared

                          * preference, your UI will not be available 
to other applications.

                          * Differs from 'special' in that you can 
re-use same UI widget

                          * in your application's preferences. */

                         $custType = $_prefs[$pref]['type'];

                         require $registry->get('templates', 
$registry->getApp()) . "/prefs/$custType.inc";

                         break;

                     }

                 }

             }

             require $registry->get('templates', 'horde') . '/prefs/end.inc';

         } else {

             $columns = array();

             if (is_array($prefGroups)) {

                 foreach ($prefGroups as $group => $gvals) {

                     if (Prefs_UI::groupIsEditable($group)) {

                         $col = $gvals['column'];

                         unset($gvals['column']);

                         $columns[$col][$group] = $gvals;

                     }

                 }

                 $span = round(100 / count($columns));

             } else {

                 $span = 100;

             }



             require $registry->get('templates', 'horde') . 
'/prefs/overview.inc';

         }

     }






02/17/2005 11:17:05 AM Jan Schneider Assigned to Horde DevelopersHorde Developers
State ⇒ Assigned
 
02/17/2005 03:08:22 AM scott (at) realorganized (dot) com Comment #1
Priority ⇒ 1. Low
Type ⇒ Enhancement
Summary ⇒ Add preference data type support for floating point numbers
Queue ⇒ Horde Base
New Attachment: floatnumber.inc Download
State ⇒ New
Reply to this comment
I am developing a real estate module for horde and have a need to be 
able to handle preferences for several floating point numbers.  There 
is support for integers and strings but not floating point.  Since I 
need to use this for several fields, it would be non-ideal to create a 
hook for each of these fields.



The change required is quite simple.  Add the following code to

horde/lib/Prefs/ui.php right after the case 'number' element.





                        case 'floatnumber':        // SDS 2/16/2005

                             $num = Util::getPost($pref);

                             if (floatval($num) != $num) {

                                 $notification->push(_("This value 
must be a number."), 'horde.error');

                             } else {

                                 $updated = $updated | 
$save->setValue($pref, $num);

                             }

                             break;



Additionally, a file must be added to horde/templates/prefs/ for that 
particular data type.  I have included the appropriate file in 
attachment.



thanks,



Scott.








Saved Queries