Roxen.git/
server/
etc/
modules/
Variable.pmod/
module.pmod
Branch:
Tag:
Non-build tags
All tags
No tags
2000-08-19
2000-08-19 00:33:00 by Martin Nilsson <mani@lysator.liu.se>
d1de9445dacc519a230f6038e8dd2b46d9d02c31 (
1138
lines) (+
1138
/-
0
)
[
Show
|
Annotate
]
Branch:
5.2
The old Variable.pmod file
Rev: server/etc/modules/Variable.pmod/module.pmod:1.1
1:
+
#include <module.h>
+
#include <roxen.h>
+
static inherit "html";
-
+
// Locale macros
+
static inline string getloclang() {
+
return roxenp()->locale->get();
+
}
+
+
//<locale-token project="roxen_config"> LOCALE </locale-token>
+
+
#if constant(Locale.DeferredLocale)
+
#define LOCALE(X,Y) \
+
([string](mixed)Locale.DeferredLocale("roxen_config",getloclang,X,Y))
+
#else /* !Locale.DeferredLocale */
+
#define LOCALE(X,Y) \
+
([string](mixed)RoxenLocale.DeferredLocale("roxen_config",getloclang,X,Y))
+
#endif /* Locale.DeferredLocale */
+
+
// Increased for each variable, used to index the mappings below.
+
static int unique_vid;
+
+
// The theory is that most variables (or at least a sizable percentage
+
// of all variables) does not have these members. Thus this saves
+
// quite a respectable amount of memory, the cost is speed. But not
+
// all that great a percentage of speed.
+
static mapping(int:mixed) changed_values = ([]);
+
static mapping(int:function(object:void)) changed_callbacks = ([]);
+
static mapping(int:int) all_flags = ([]);
+
static mapping(int:string) all_warnings = ([]);
+
static mapping(int:function(RequestID,object:int))
+
invisibility_callbacks = set_weak_flag( ([]), 1 );
+
+
class Variable
+
//! The basic variable type in Roxen. All other variable types should
+
//! inherit this class.
+
{
+
constant is_variable = 1;
+
+
constant type = "Basic";
+
//! Mostly used for debug (sprintf( "%O", variable_obj ) uses it)
+
+
static int _id = unique_vid++;
+
// used for indexing the mappings.
+
+
static mixed _initial; // default value
+
static string _path; // used for forms
+
static string|object __name, __doc;
+
+
void destroy()
+
{
+
// clean up...
+
m_delete( all_flags, _id );
+
m_delete( all_warnings, _id );
+
m_delete( invisibility_callbacks, _id );
+
m_delete( changed_values, _id );
+
}
+
+
string get_warnings()
+
//! Returns the current warnings, if any.
+
{
+
return all_warnings[ _id ];
+
}
+
+
int get_flags()
+
//! Returns the 'flags' field for this variable.
+
//! Flags is a bitwise or of one or more of
+
//!
+
//! VAR_EXPERT Only for experts
+
//! VAR_MORE Only visible when more-mode is on (default on)
+
//! VAR_DEVELOPER Only visible when devel-mode is on (default on)
+
//! VAR_INITIAL Should be configured initially.
+
{
+
return all_flags[_id];
+
}
+
+
void set_flags( int flags )
+
//! Set the flags for this variable.
+
//! Flags is a bitwise or of one or more of
+
//!
+
//! VAR_EXPERT Only for experts
+
//! VAR_MORE Only visible when more-mode is on (default on)
+
//! VAR_DEVELOPER Only visible when devel-mode is on (default on)
+
//! VAR_INITIAL Should be configured initially.
+
{
+
if(!flags )
+
m_delete( all_flags, _id );
+
else
+
all_flags[_id] = flags;
+
}
+
+
int check_visibility( RequestID id,
+
int more_mode,
+
int expert_mode,
+
int devel_mode,
+
int initial )
+
//! Return 1 if this variable should be visible in the
+
//! configuration interface. The default implementation check the
+
//! 'flags' field, and the invisibility callback, if any. See
+
//! get_flags, set_flags and set_invisibibility_check_callback
+
{
+
int flags = get_flags();
+
function cb;
+
if( initial && !(flags & VAR_INITIAL) ) return 0;
+
if( (flags & VAR_EXPERT) && !expert_mode ) return 0;
+
if( (flags & VAR_MORE) && !more_mode ) return 0;
+
if( (flags & VAR_DEVELOPER) && !devel_mode ) return 0;
+
if( (cb = get_invisibility_check_callback() ) &&
+
cb( id, this_object() ) )
+
return 0;
+
return 1;
+
}
+
+
void set_invisibility_check_callback( function(RequestID,Variable:int) cb )
+
//! If the function passed as argument returns 1, the variable
+
//! will not be visible in the configuration interface.
+
//!
+
//! Pass 0 to remove the invisibility callback.
+
{
+
if( functionp( cb ) )
+
invisibility_callbacks[ _id ] = cb;
+
else
+
m_delete( invisibility_callbacks, _id );
+
}
+
+
function(Variable:void) get_changed_callback( )
+
//! Return the callback set with set_changed_callback
+
{
+
return changed_callbacks[ _id ];
+
}
+
+
void set_changed_callback( function(Variable:void) cb )
+
//! The function passed as an argument will be called
+
//! when the variable value is changed.
+
//!
+
//! Pass 0 to remove the callback.
+
{
+
if( functionp( cb ) )
+
changed_callbacks[ _id ] = cb;
+
else
+
m_delete( changed_callbacks, _id );
+
}
+
+
function(RequestID,Variable:int) get_invisibility_check_callback()
+
//! Return the current invisibility check callback
+
{
+
return invisibility_callbacks[_id];
+
}
+
+
string doc( )
+
//! Return the documentation for this variable (locale dependant).
+
//!
+
//! The default implementation queries the locale object in roxen
+
//! to get the documentation.
+
{
+
return __doc || "";
+
}
+
+
string name( )
+
//! Return the name of this variable (locale dependant).
+
//!
+
//! The default implementation queries the locale object in roxen
+
//! to get the documentation.
+
{
+
return __name || "unnamed "+_id;
+
}
+
+
string type_hint( )
+
//! Return the type hint for this variable.
+
//! Type hints are generic documentation for this variable type,
+
//! and is the same for all instances of the type.
+
{
+
}
+
+
mixed default_value()
+
//! The default (initial) value for this variable.
+
{
+
return _initial;
+
}
+
+
void set_warning( string to )
+
//! Set the warning shown in the configuration interface
+
{
+
if( to && strlen(to) )
+
all_warnings[ _id ] = to;
+
else
+
m_delete( all_warnings, _id );
+
}
+
+
int set( mixed to )
+
//! Set the variable to a new value.
+
//! If this function returns true, the set was successful.
+
//! Otherwise 0 is returned. 0 is also returned if the variable was
+
//! not changed by the set. 1 is returned if the variable was
+
//! changed, and -1 is returned if the variable was changed back to
+
//! it's default value.
+
//!
+
//! If verify_set() threw a string, ([])[0] is returned, that is,
+
//! 0 with zero_type set.
+
//!
+
//! If verify_set() threw an exception, the exception is thrown.
+
{
+
string err, e2;
+
if( e2 = catch( [err,to] = verify_set( to )) )
+
{
+
if( stringp( e2 ) )
+
{
+
set_warning( e2 );
+
return ([])[0];
+
}
+
throw( e2 );
+
}
+
set_warning( err );
+
return low_set( to );
+
}
+
+
int low_set( mixed to )
+
//! Forced set. No checking is done whatsoever.
+
//! 1 is returned if the variable was changed, -1 is returned if
+
//! the variable was changed back to it's default value and 0
+
//! otherwise.
+
{
+
if( equal( to, query() ) )
+
return 0;
+
+
if( !equal(to, default_value() ) )
+
{
+
changed_values[ _id ] = to;
+
if( get_changed_callback() )
+
catch( get_changed_callback()( this_object() ) );
+
return 1;
+
}
+
else
+
{
+
m_delete( changed_values, _id );
+
if( get_changed_callback() )
+
catch( get_changed_callback()( this_object() ) );
+
return -1;
+
}
+
}
+
+
mixed query()
+
//! Returns the current value for this variable.
+
{
+
mixed v;
+
if( !zero_type( v = changed_values[ _id ] ) )
+
return v;
+
return default_value();
+
}
+
+
int is_defaulted()
+
//! Return true if this variable is set to it's default value.
+
{
+
return zero_type( changed_values[ _id ] ) ||
+
equal(changed_values[ _id ], default_value());
+
}
+
+
array(string|mixed) verify_set( mixed new_value )
+
//! Return ({ error, new_value }) for the variable, or throw a string.
+
//!
+
//! If error != 0, it should contain a warning or error message.
+
//! If new_value is modified, it will be used instead of the
+
//! supplied value.
+
//!
+
//! If a string is thrown, it will be used as a error message from
+
//! set, and the variable will not be changed.
+
{
+
return ({ 0, new_value });
+
}
+
+
mapping(string:string) get_form_vars( RequestID id )
+
//! Return all form variables preficed with path().
+
{
+
string p = path();
+
array names = glob( p+"*", indices(id->variables) );
+
mapping res = ([ ]);
+
foreach( sort(names), string n )
+
res[ n[strlen(p).. ] ] = id->variables[ n ];
+
return res;
+
}
+
+
mixed transform_from_form( string what )
+
//! Given a form value, return what should be set.
+
//! Used by the default set_from_form implementation.
+
{
+
return what;
+
}
+
+
void set_from_form( RequestID id )
+
//! Set this variable from the form variable in id->Variables,
+
//! if any are available. The default implementation simply sets
+
//! the variable to the string in the form variables.
+
//!
+
//! Other side effects: Might create warnings to be shown to the
+
//! user (see get_warnings)
+
{
+
mapping val;
+
if( sizeof( val = get_form_vars(id)) && val[""] &&
+
transform_from_form( val[""] ) != query() )
+
set( transform_from_form( val[""] ));
+
}
+
+
string path()
+
//! A unique identifier for this variable.
+
//! Should be used to prefix form variable names.
+
//!
+
//! Unless this variable was created by defvar(), the path is set
+
//! by the configuration interface the first time the variable is
+
//! to be shown in a form. This function can thus return 0. If it
+
//! does, and you still have to show the form, call set_path( )
+
//! with a unique string.
+
{
+
return _path;
+
}
+
+
void set_path( string to )
+
//! Set the path. Not normally called from user-level code.
+
//!
+
//! This function must be called at least once before render_form
+
//! can be called (at least if more than one variable is to be
+
//! shown on the same page). This is normally done by the
+
//! configuration interface.
+
{
+
_path = to;
+
}
+
+
string render_form( RequestID id );
+
//! Return a (HTML) form to change this variable. The name of all <input>
+
//! or similar variables should be prefixed with the value returned
+
//! from the path() function.
+
+
string render_view( RequestID id )
+
//! Return a 'view only' version of this variable.
+
{
+
return Roxen.html_encode_string( (string)query() );
+
}
+
+
static string _sprintf( int i )
+
{
+
if( i == 'O' )
+
return sprintf( "Variables.%s(%s) [%O]", type,
+
(string)name(),
+
query() );
+
}
+
+
static void create(mixed default_value,int flags,
+
string|object std_name,string|object std_doc)
+
//! Constructor.
+
//! Flags is a bitwise or of one or more of
+
//!
+
//! VAR_EXPERT Only for experts
+
//! VAR_MORE Only visible when more-mode is on (default on)
+
//! VAR_DEVELOPER Only visible when devel-mode is on (default on)
+
//! VAR_INITIAL Should be configured initially.
+
//!
+
//! The std_name and std_doc is the name and documentation string
+
//! for the default locale (always english)
+
{
+
set_flags( flags );
+
_initial = default_value;
+
__name = std_name;
+
__doc = std_doc;
+
}
+
}
+
+
+
+
+
// =====================================================================
+
// Float
+
// =====================================================================
+
+
class Float
+
//! Float variable, with optional range checks, and adjustable precision.
+
{
+
inherit Variable;
+
constant type = "Float";
+
static float _max, _min;
+
static int _prec = 2, mm_set;
+
+
static string _format( float m )
+
{
+
if( !_prec )
+
return sprintf( "%d", (int)m );
+
return sprintf( "%1."+_prec+"f", m );
+
}
+
+
void set_range(float minimum, float maximum )
+
//! Set the range of the variable, if minimum and maximum are both
+
//! 0.0 (the default), the range check is removed.
+
{
+
if( minimum == maximum )
+
mm_set = 0;
+
else
+
mm_set = 1;
+
_max = maximum;
+
_min = minimum;
+
}
+
+
void set_precision( int prec )
+
//! Set the number of _decimals_ shown to the user.
+
//! If prec is 3, and the float is 1, 1.000 will be shown.
+
//! Default is 2.
+
{
+
_prec = prec;
+
}
+
+
array(string|float) verify_set( float new_value )
+
{
+
string warn;
+
if( mm_set )
+
{
+
if( new_value > _max )
+
{
+
warn = sprintf("Value is bigger than %s, adjusted", _format(_max) );
+
new_value = _max;
+
}
+
else if( new_value < _min )
+
{
+
warn = sprintf("Value is less than %s, adjusted", _format(_min) );
+
new_value = _min;
+
}
+
}
+
return ({ warn, new_value });
+
}
+
+
float transform_from_form( string what )
+
{
+
return (float)what;
+
}
+
+
string render_view( RequestID id )
+
{
+
return Roxen.html_encode_string( _format(query()) );
+
}
+
+
string render_form( RequestID id )
+
{
+
int size = 15;
+
if( mm_set )
+
size = max( strlen(_format(_max)), strlen(_format(_min)) )+2;
+
return input(path(), _format(query()), size);
+
}
+
}
+
+
+
+
+
// =====================================================================
+
// Int
+
// =====================================================================
+
+
class Int
+
//! Integer variable, with optional range checks
+
{
+
inherit Variable;
+
constant type = "Int";
+
static int _max, _min, mm_set;
+
+
void set_range(int minimum, int maximum )
+
//! Set the range of the variable, if minimum and maximum are both
+
//! 0 (the default), the range check is removed.
+
{
+
if( minimum == maximum )
+
mm_set = 0;
+
else
+
mm_set = 1;
+
_max = maximum;
+
_min = minimum;
+
}
+
+
array(string|int) verify_set( int new_value )
+
{
+
string warn;
+
if( mm_set )
+
{
+
if( new_value > _max )
+
{
+
warn = sprintf("Value is bigger than %d, adjusted", _max );
+
new_value = _max;
+
}
+
else if( new_value < _min )
+
{
+
warn = sprintf("Value is less than %d, adjusted", _min );
+
new_value = _min;
+
}
+
}
+
return ({ warn, new_value });
+
}
+
+
int transform_from_form( string what )
+
{
+
return (int)what;
+
}
+
+
string render_form( RequestID id )
+
{
+
int size = 10;
+
if( mm_set )
+
size = max( strlen((string)_max), strlen((string)_min) )+2;
+
return input(path(), (string)query(), size);
+
}
+
}
+
+
+
// =====================================================================
+
// String
+
// =====================================================================
+
+
class String
+
//! String variable
+
{
+
inherit Variable;
+
constant type = "String";
+
constant width = 40;
+
//! The width of the input field. Used by overriding classes.
+
string render_form( RequestID id )
+
{
+
return input(path(), (string)query(), width);
+
}
+
}
+
+
// =====================================================================
+
// Text
+
// =====================================================================
+
class Text
+
//! Text (multi-line string) variable
+
{
+
inherit String;
+
constant type = "Text";
+
constant cols = 60;
+
//! The width of the textarea
+
constant rows = 10;
+
//! The height of the textarea
+
string render_form( RequestID id )
+
{
+
return "<textarea cols='"+cols+"' rows='"+rows+"' name='"+path()+"'>"
+
+ Roxen.html_encode_string( query() || "" ) +
+
"</textarea>";
+
}
+
}
+
+
+
+
// =====================================================================
+
// Password
+
// =====================================================================
+
class Password
+
//! Password variable (uses crypt)
+
{
+
inherit String;
+
constant width = 20;
+
constant type = "Password";
+
+
void set_from_form( RequestID id )
+
{
+
mapping val;
+
if( sizeof( val = get_form_vars(id)) &&
+
val[""] && strlen(val[""]) )
+
set( crypt( val[""] ) );
+
}
+
+
string render_view( RequestID id )
+
{
+
return "******";
+
}
+
+
string render_form( RequestID id )
+
{
+
return "<input name=\""+path()+"\" type=\"password\" size=\"30\">";
+
}
+
}
+
+
class File
+
//! A filename
+
{
+
inherit String;
+
constant type = "File";
+
constant width = 50;
+
+
string read( )
+
//! Read the file as a string.
+
{
+
return Stdio.read_bytes( query() );
+
}
+
+
array stat()
+
//! Stat the file
+
{
+
return file_stat( query() );
+
}
+
}
+
+
class Location
+
//! A location in the virtual filesystem
+
{
+
inherit String;
+
constant type = "Location";
+
constant width = 50;
+
}
+
+
class URL
+
//! A URL.
+
{
+
inherit String;
+
constant type = "URL";
+
constant width = 50;
+
+
array verify_set( string new_value )
+
{
+
return verify_port( new_value, 1 );
+
}
+
}
+
+
class Directory
+
//! A Directory.
+
{
+
inherit String;
+
constant type = "Directory";
+
constant width = 50;
+
+
array verify_set( string value )
+
{
+
if( !(r_file_stat( value ) && (r_file_stat( value )[ ST_SIZE ] == -2 )))
+
return ({value+" is not a directory", value });
+
return ::verify_set( value );
+
}
+
+
array stat()
+
//! Stat the directory
+
{
+
return file_stat( query() );
+
}
+
+
array get( )
+
//! Return a listing of all files in the directory
+
{
+
return get_dir( query() );
+
}
+
}
+
+
+
+
// =====================================================================
+
// MultipleChoice (one of many) baseclass
+
// =====================================================================
+
+
class MultipleChoice
+
//! Base class for multiple-choice (one of many) variables.
+
{
+
inherit Variable;
+
static array _list = ({});
+
static mapping _table = ([]);
+
+
void set_choice_list( array to )
+
//! Set the list of choices.
+
{
+
_list = to;
+
}
+
+
array get_choice_list( )
+
//! Get the list of choices. Used by this class as well.
+
//! You can overload this function if you want a dynamic list.
+
{
+
return _list;
+
}
+
+
void set_translation_table( mapping to )
+
//! Set the lookup table.
+
{
+
_table = to;
+
}
+
+
mapping get_translation_table( )
+
//! Get the lookup table. Used by this class as well.
+
//! You can overload this function if you want a dynamic table.
+
{
+
return _table;
+
}
+
+
static string _name( mixed what )
+
//! Get the name used as value for an element gotten from the
+
//! get_choice_list() function.
+
{
+
return (string)what;
+
}
+
+
static string _title( mixed what )
+
//! Get the title used as description (shown to the user) for an
+
//! element gotten from the get_choice_list() function.
+
{
+
if( mapping tt = get_translation_table() )
+
return tt[ what ] || (string)what;
+
return (string)what;
+
}
+
+
string render_form( RequestID id )
+
{
+
string res = "<select name='"+path()+"'>\n";
+
foreach( get_choice_list(), mixed elem )
+
{
+
mapping m = ([]);
+
m->value = _name( elem );
+
if( m->value == query() )
+
m->selected="selected";
+
res += " "+Roxen.make_container( "option", m, _title( elem ) )+"\n";
+
}
+
return res + "</select>";
+
}
+
static void create( mixed default_value, array|mapping choices,
+
int _flags, string std_name, string std_doc )
+
//! Constructor.
+
//!
+
//! Choices is the list of possible choices, can be set with
+
//! set_choice_list at any time.
+
//!
+
//! Flags is a bitwise or of one or more of
+
//!
+
//! VAR_EXPERT Only for experts
+
//! VAR_MORE Only visible when more-mode is on (default on)
+
//! VAR_DEVELOPER Only visible when devel-mode is on (default on)
+
//! VAR_INITIAL Should be configured initially.
+
//!
+
//! The std_name and std_doc is the name and documentation string
+
//! for the default locale (always english)
+
{
+
::create( default_value, _flags, std_name, std_doc );
+
if( mappingp( choices ) ) {
+
set_translation_table( choices );
+
set_choice_list( indices(choices) );
+
} else
+
set_choice_list( choices );
+
}
+
}
+
+
+
// =====================================================================
+
// MultipleChoice subclasses
+
// =====================================================================
+
+
class StringChoice
+
//! Select one of many strings.
+
{
+
inherit MultipleChoice;
+
constant type = "StringChoice";
+
}
+
+
+
class IntChoice
+
//! Select one of many integers.
+
{
+
inherit MultipleChoice;
+
constant type = "IntChoice";
+
int transform_from_form( string what )
+
{
+
return (int)what;
+
}
+
}
+
+
class FloatChoice
+
//! Select one of many floating point (real) numbers.
+
{
+
inherit MultipleChoice;
+
constant type = "FloatChoice";
+
static int _prec = 3;
+
+
void set_precision( int prec )
+
//! Set the number of _decimals_ shown to the user.
+
//! If prec is 3, and the float is 1, 1.000 will be shown.
+
//! Default is 2.
+
{
+
_prec = prec;
+
}
+
+
static string _title( mixed what )
+
{
+
if( !_prec )
+
return sprintf( "%d", (int)what );
+
return sprintf( "%1."+_prec+"f", what );
+
}
+
+
int transform_from_form( string what )
+
{
+
array q = get_choice_list();
+
mapping a = mkmapping( map( q, _name ), q );
+
return a[what] || (float)what; // Do we want this fallback?
+
}
+
}
+
+
class FontChoice
+
//! Select a font from the list of available fonts
+
{
+
inherit StringChoice;
+
constant type = "FontChoice";
+
void set_choice_list()
+
{
+
}
+
array get_choice_list()
+
{
+
return roxenp()->fonts->available_fonts();
+
}
+
static void create(mixed default_value,int flags,
+
string std_name,string std_doc)
+
//! Constructor.
+
//! Flags is a bitwise or of one or more of
+
//!
+
//! VAR_EXPERT Only for experts
+
//! VAR_MORE Only visible when more-mode is on (default on)
+
//! VAR_DEVELOPER Only visible when devel-mode is on (default on)
+
//! VAR_INITIAL Should be configured initially.
+
//!
+
//! The std_name and std_doc is the name and documentation string
+
//! for the default locale (always english)
+
{
+
::create( default_value, 0, flags,std_name, std_doc );
+
}
+
}
+
+
+
// =====================================================================
+
// List baseclass
+
// =====================================================================
+
class List
+
//! Many of one type types
+
{
+
inherit String;
+
constant type="List";
+
constant width = 40;
+
+
string transform_to_form( mixed what )
+
//! Override this function to do the value->form mapping for
+
//! individual elements in the array.
+
{
+
return (string)what;
+
}
+
+
mixed transform_from_form( string what )
+
{
+
return what;
+
}
+
+
static int _current_count = time()*100+(gethrtime()/10000);
+
void set_from_form(RequestID id)
+
{
+
int rn;
+
array l = query();
+
mapping vl = get_form_vars(id);
+
// first do the assign...
+
+
if( (int)vl[".count"] != _current_count )
+
return;
+
_current_count++;
+
+
foreach( indices( vl ), string vv )
+
if( sscanf( vv, ".set.%d", rn ) )
+
{
+
m_delete( id->variables, path()+vv );
+
l[rn] = transform_from_form( vl[vv] );
+
m_delete( vl, vv );
+
}
+
// then the move...
+
foreach( indices(vl), string vv )
+
if( sscanf( vv, ".up.%d.x%*s", rn ) == 2 )
+
{
+
m_delete( id->variables, path()+vv );
+
m_delete( vl, vv );
+
l = l[..rn-2] + l[rn..rn] + l[rn-1..rn-1] + l[rn+1..];
+
}
+
else if( sscanf( vv, ".down.%d.x%*s", rn )==2 )
+
{
+
m_delete( id->variables, path()+vv );
+
l = l[..rn-1] + l[rn+1..rn+1] + l[rn..rn] + l[rn+2..];
+
}
+
// then the possible add.
+
if( vl[".new.x"] )
+
{
+
m_delete( id->variables, path()+".new.x" );
+
l += ({ transform_from_form( "" ) });
+
}
+
+
// .. and delete ..
+
foreach( indices(vl), string vv )
+
if( sscanf( vv, ".delete.%d.x%*s", rn )==2 )
+
{
+
m_delete( id->variables, path()+vv );
+
l = l[..rn-1] + l[rn+1..];
+
}
+
set( l ); // We are done. :-)
+
}
+
+
string render_form( RequestID id )
+
{
+
string prefix = path()+".";
+
int i;
+
+
_current_count++;
+
+
string res = "<table>\n"
+
"<input type='hidden' name='"+prefix+"count' value='"+_current_count+"' />";
+
+
foreach( map(query(), transform_to_form), string val )
+
{
+
res += "<tr><td><font size='-1'>"+ input( prefix+"set."+i, val, width) + "</font></td>";
+
+
#define BUTTON(X,Y) ("<submit-gbutton2 name='"+X+"'>"+Y+"</submit-gbutton2>")
+
if( i )
+
res += "\n<td>"+
+
BUTTON(prefix+"up."+i, "^")+
+
"</td>";
+
else
+
res += "<td></td>";
+
if( i != sizeof( query())- 1 )
+
res += "\n<td>"+
+
BUTTON(prefix+"down."+i, "v")
+
+"</td>";
+
else
+
res += "<td></td>";
+
res += "\n<td>"+
+
BUTTON(prefix+"delete."+i, LOCALE("", "Delete") )
+
+"</td>";
+
"</tr>";
+
i++;
+
}
+
res +=
+
"<tr><td colspan='2'>"+
+
BUTTON(prefix+"new", LOCALE("", "New row") )+
+
"</td></tr></table>\n";
+
+
return res;
+
}
+
}
+
+
+
// =====================================================================
+
// List subclasses
+
// =====================================================================
+
class DirectoryList
+
//! A list of directories
+
{
+
inherit List;
+
constant type="DirectoryList";
+
+
array verify_set( array(string) value )
+
{
+
string warn = "";
+
foreach( value, string value )
+
if( !(r_file_stat( value ) && (r_file_stat( value )[ ST_SIZE ] == -2 )))
+
warn += value+" is not a directory\n";
+
if( strlen( warn ) )
+
return ({ warn, value });
+
return ::verify_set( value );
+
}
+
}
+
+
class StringList
+
//! A list of strings
+
{
+
inherit List;
+
constant type="StringList";
+
}
+
+
class IntList
+
//! A list of integers
+
{
+
inherit List;
+
constant type="IntList";
+
constant width=20;
+
+
string transform_to_form(int what) { return (string)what; }
+
int transform_from_form(string what) { return (int)what; }
+
}
+
+
class FloatList
+
//! A list of floating point numbers
+
{
+
inherit List;
+
constant type="DirectorYList";
+
constant width=20;
+
+
static int _prec = 3;
+
+
void set_precision( int prec )
+
//! Set the number of _decimals_ shown to the user.
+
//! If prec is 3, and the float is 1, 1.000 will be shown.
+
//! Default is 2.
+
{
+
_prec = prec;
+
}
+
+
string transform_to_form(int what)
+
{
+
return sprintf("%1."+_prec+"f", what);
+
}
+
float transform_from_form(string what) { return (float)what; }
+
}
+
+
class URLList
+
//! A list of URLs
+
{
+
inherit List;
+
constant type="UrlList";
+
+
array verify_set( array(string) new_value )
+
{
+
string warn = "";
+
array res = ({});
+
foreach( new_value, string vv )
+
{
+
string tmp1, tmp2;
+
[tmp1,tmp2] = verify_port( vv, 1 );
+
if( tmp1 )
+
warn += tmp1;
+
res += ({ tmp2 });
+
}
+
if( !strlen( warn ) )
+
warn = 0;
+
return ({ warn, res });
+
}
+
}
+
+
class PortList
+
//! A list of Port URLs
+
{
+
inherit List;
+
constant type="PortList";
+
+
array verify_set( array(string) new_value )
+
{
+
string warn = "";
+
array res = ({});
+
foreach( new_value, string vv )
+
{
+
string tmp1, tmp2;
+
[tmp1,tmp2] = verify_port( vv, 0 );
+
if( tmp1 )
+
warn += tmp1;
+
res += ({ tmp2 });
+
}
+
if( !strlen( warn ) )
+
warn = "";
+
return ({ warn, res });
+
}
+
}
+
+
+
class FileList
+
//! A list of filenames.
+
{
+
inherit List;
+
constant type="FileList";
+
}
+
+
+
// =====================================================================
+
// Flag
+
// =====================================================================
+
+
class Flag
+
//! A on/off toggle.
+
{
+
inherit Variable;
+
constant type = "Flag";
+
+
int transform_from_form( string what )
+
{
+
return (int)what;
+
}
+
+
string render_form( RequestID id )
+
{
+
string res = "<select name=\""+path()+"\"> ";
+
if(query())
+
res += "<option value=\"1\" selected=\"selected\">" +
+
LOCALE("yes", "Yes")+ "</option>\n"
+
"<option value=\"0\">" +LOCALE("no", "No")+ "</option>\n";
+
else
+
res += "<option value=\"1\">" +LOCALE("yes", "Yes")+ "</option>\n"
+
"<option value=\"0\" selected>" +LOCALE("no", "No")+ "</option>\n";
+
return res+"</select>";
+
}
+
}
+
+
+
+
+
// =================================================================
+
// Utility functions used in multiple variable classes above
+
// =================================================================
+
+
array(string) verify_port( string port, int nofhttp )
+
{
+
if(!strlen(port))
+
return ({ 0, port });
+
string warning="";
+
if( (int)port )
+
{
+
warning += "Assuming http://*:"+port+"/ for "+port+"\n";
+
port = "http://*:"+port+"/";
+
}
+
string protocol, host, path;
+
+
if(!strlen( port ) )
+
return ({ "Empty URL field", port });
+
+
if(sscanf( port, "%[^:]://%[^/]%s", protocol, host, path ) != 3)
+
return ({""+port+" does not conform to URL syntax\n", port });
+
+
if( path == "" )
+
{
+
warning += "Added / to the end of "+port+"\n";
+
host += "/";
+
}
+
int pno;
+
if( sscanf( host, "%s:%d", host, pno ) == 2)
+
{
+
if( roxenp()->protocols[ lower_case( protocol ) ]
+
&& (pno == roxenp()->protocols[ lower_case( protocol ) ]->default_port ))
+
warning += "Removed the "
+
"default port number ("+pno+") from "+port+"\n";
+
else
+
host = host+":"+pno;
+
}
+
if( nofhttp && protocol == "fhttp" )
+
{
+
warning += "Changed " + protocol + " to http\n";
+
protocol = "http";
+
}
+
if( protocol != lower_case( protocol ) )
+
{
+
warning += "Changed "+protocol+" to "+ lower_case( protocol )+"\n";
+
}
+
+
port = lower_case( protocol )+"://"+host+path;
+
+
if( !roxenp()->protocols[ lower_case( protocol ) ] )
+
warning += "Warning: The protocol "+lower_case(protocol)+" is unknown\n";
+
return ({ (strlen(warning)?warning:0), port });
+
}
Newline at end of file added.