77bdaf2008-12-11Jonas Wallden // $Id: module.pmod,v 1.111 2008/12/11 11:00:06 jonasw Exp $
ef40dc2000-09-01Martin Stjernholm 
d1de942000-08-19Martin Nilsson #include <module.h> #include <roxen.h> // Locale macros //<locale-token project="roxen_config"> LOCALE </locale-token>
2f02b12000-10-12Martin Nilsson #define LOCALE(X,Y) \ ([string](mixed)Locale.translate("roxen_config",roxenp()->locale->get(),X,Y))
d1de942000-08-19Martin Nilsson 
23f13c2002-05-13Jonas Wallden // Increased for each variable, used to index the mappings below. The unique // prefix is needed to avoid clobbering variables after server restart.
fc40392008-08-15Martin Stjernholm protected int unique_vid; protected string unique_prefix = (string) getpid();
d1de942000-08-19Martin Nilsson  // The theory is that most variables (or at least a sizable percentage // of all variables) does not have these members. Thus this saves
edbf2d2000-09-19Per Hedbor // quite a respectable amount of memory, the cost is speed. But // hopefully not all that great a percentage of speed.
fc40392008-08-15Martin Stjernholm protected mapping(string:mixed) changed_values = ([]); protected mapping(string:function(object:void)) changed_callbacks = ([]); protected mapping(string:int) all_flags = ([]); protected mapping(string:string) all_warnings = ([]); protected mapping(string:function(RequestID,object:int))
d1de942000-08-19Martin Nilsson  invisibility_callbacks = set_weak_flag( ([]), 1 );
a7defb2001-08-01Per Hedbor mapping(string:Variable) all_variables = set_weak_flag( ([]), 1 ); mapping get_all_variables() { return all_variables; } Variable get_variables( string v ) { return all_variables[v]; }
67aeb12001-08-17Per Hedbor string get_diff_def_html( Variable v, string button_tag, string def_url, string diff_url, int page ) { if( page ) return v->diff( 2 ); if( v->is_defaulted() || (v->get_flags() & VAR_NO_DEFAULT) ) return ""; string oneliner = v->diff( 0 ), diff_button=""; mapping m; if( !oneliner ) { if( v->diff( 1 ) ) { m = ([ "href":diff_url,"target":"_new", ]); diff_button = Roxen.make_container( "a",m, Roxen.make_container( button_tag, ([]),
9fa11d2001-08-24Martin Nilsson  LOCALE(474,"Show changes")
67aeb12001-08-17Per Hedbor  ) ); } } m = ([ "href":def_url, ]); return diff_button + " " + Roxen.make_container( "a",m, Roxen.make_container( button_tag, ([]),
9fa11d2001-08-24Martin Nilsson  LOCALE(475, "Restore default value" )+
67aeb12001-08-17Per Hedbor  (oneliner||"") ) ); }
a7defb2001-08-01Per Hedbor class Diff {
fc40392008-08-15Martin Stjernholm  private array(string) diff;
a7defb2001-08-01Per Hedbor 
fc40392008-08-15Martin Stjernholm  private
a7defb2001-08-01Per Hedbor  array(string) print_row(array(string) diff_old, array(string) diff_new, int line, int|void start, int|void end) { if(!sizeof(diff_old) && sizeof(diff_new)) // New row. return Array.map(diff_new, lambda(string s) {return "+ " + s;} ); if(sizeof(diff_old) && !sizeof(diff_new)) // Deleted row. return Array.map(diff_old, lambda(string s) {return "- " + s;} ); if(diff_old != diff_new) // Modified row. return Array.map(diff_old, lambda(string s) {return "- " + s;} ) + Array.map(diff_new, lambda(string s) {return "+ " + s;} ); if(start + end < sizeof(diff_old) && (start || end)) { if(start && !end) diff_old = diff_old[.. start - 1]; else { diff_old = diff_old[.. start - 1] + ({ line + sizeof(diff_old) - end }) + diff_old[sizeof(diff_old) - end ..]; } } return Array.map(diff_old, lambda(string|int s) { if(intp(s)) return "Line "+s+":"; return " " + s; } ); } string html(void|int hide_header) { string r = ""; int added, deleted; if(sizeof(diff) && diff[-1] == " ") diff = diff[..sizeof(diff)-2]; foreach(diff, string row) { row = Roxen.html_encode_string(row); row = replace(row, "\t", " "); row = replace(row, " ", "&nbsp;"); switch(row[0]) { case '&': r += "<tt>"+row+"</tt><br>\n"; break; case '+': r += "<tt><font color='darkgreen'>"+row+"</font></tt><br>\n"; added++; break; case '-': r += "<tt><font color='darkred'>"+row+"</font></tt><br>\n"; deleted++; break; case 'L': r += "<i>"+row+"</i><br>\n"; break; } } if (!hide_header) r =
9fa11d2001-08-24Martin Nilsson  "<b>" + LOCALE(476, "Change in content") + "</b><br />\n"+ "<i>"+(added==1? LOCALE(477, "1 line added."): sprintf(LOCALE(478, "%d lines added."), added)) + " " + (deleted==1? LOCALE(479, "1 line deleted."): sprintf(LOCALE(480, "%d lines deleted."), deleted)) +
a7defb2001-08-01Per Hedbor  "</i><p>\n"+ r; return r; } array get() { return diff; } void create(array(string) new, array(string) old, int context) { array(array(string)) diff_old, diff_new; [diff_old, diff_new] = Array.diff(old, new); int line = 1; int diffp = 0; diff = ({ }); for(int i = 0; i < sizeof(diff_old); i++) { if(diff_old[i] != diff_new[i]) { diff += print_row(diff_old[i], diff_new[i], line); diffp = 1; } else if(sizeof(diff_old) > 1) { diff += print_row(diff_old[i], diff_new[i], line, diffp?context:0, sizeof(diff_old) - 1 > i?context:0 ); diffp = 0; } line += sizeof(diff_old[i] - ({ })); } } }
d1de942000-08-19Martin Nilsson 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)
23f13c2002-05-13Jonas Wallden  string _id = unique_prefix + "_" + (string) unique_vid++;
d1de942000-08-19Martin Nilsson  // used for indexing the mappings.
fc40392008-08-15Martin Stjernholm  protected mixed _initial; // default value protected string _path = sprintf("v%s",_id); // used for forms protected LocaleString __name, __doc;
d1de942000-08-19Martin Nilsson 
a7defb2001-08-01Per Hedbor  string diff( int render ) //! Generate a html diff of the difference between the current //! value and the default value. //! //! This method is used by the configuration interface. //! //! The argument @[render] is used to select the operation mode. //! //! render=0 means that you should generate an inline diff. This
67aeb12001-08-17Per Hedbor  //! should be at most 1 line of text with no more than 30 or so //! characters. This is mostly useful for simple variable types such //! as integers or choice-lists.
a7defb2001-08-01Per Hedbor  //! //! If you return 0 when render=0, this function will be called with //! render=1 instead. If you return a non-zero value, you indicate //! that there is a diff available. In this case the function can be //! called again with render=2, in this case you have a full page of //! HTML code to render on. //! //! If you return 0 for render=1 as well, no difference will be //! shown. This is the default. { return 0; }
d1de942000-08-19Martin Nilsson  void destroy() {
dd91482004-02-17Martin Stjernholm  if (global::this) { // clean up... m_delete( all_flags, _id ); m_delete( all_warnings, _id ); m_delete( invisibility_callbacks, _id ); m_delete( changed_values, _id ); }
d1de942000-08-19Martin Nilsson  } 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
65025e2000-10-19Per Hedbor  //!
d1de942000-08-19Martin Nilsson  //! 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,
82f9da2000-09-16Per Hedbor  int initial, int|void variable_in_cfif )
d1de942000-08-19Martin Nilsson  //! 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
82f9da2000-09-16Per Hedbor  //! //! If variable_in_cfif is true, the variable is in a module //! that is added to the configuration interface itself.
d1de942000-08-19Martin Nilsson  { int flags = get_flags(); function cb;
e51c7b2000-12-08Martin Nilsson  if( flags & VAR_INVISIBLE ) return 0;
82f9da2000-09-16Per Hedbor  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( (flags & VAR_NOT_CFIF) && variable_in_cfif ) return 0;
d1de942000-08-19Martin Nilsson  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 ); }
c6fd2e2000-09-03Per Hedbor  void add_changed_callback( function(Variable:void) cb ) //! Add a new callback to be called when the variable is changed. //! If set_changed_callback is called, callbacks added with this function //! are overridden. { mixed oc = get_changed_callback( ); if( arrayp( oc ) ) oc += ({ cb }); else oc = ({ oc, cb }) - ({ 0 }); changed_callbacks[ _id ] = oc; }
d1de942000-08-19Martin Nilsson  function(RequestID,Variable:int) get_invisibility_check_callback() //! Return the current invisibility check callback { return invisibility_callbacks[_id]; }
79ca872000-11-24Per Hedbor  LocaleString doc( )
d1de942000-08-19Martin Nilsson  //! Return the documentation for this variable (locale dependant). //! //! The default implementation queries the locale object in roxen //! to get the documentation. { return __doc || ""; }
d321602005-02-04Martin Stjernholm  void set_doc (LocaleString doc) //! Set the (locale dependent) documentation for this variable. { __doc = doc; }
79ca872000-11-24Per Hedbor  LocaleString name( )
d1de942000-08-19Martin Nilsson  //! Return the name of this variable (locale dependant). //! //! The default implementation queries the locale object in roxen //! to get the documentation. {
55a8662000-11-20Per Hedbor  return __name || LOCALE(326,"unnamed")+" "+_id;
d1de942000-08-19Martin Nilsson  }
d321602005-02-04Martin Stjernholm  void set_name (LocaleString name) //! Set the (locale dependent) name for this variable. { __name = name; }
79ca872000-11-24Per Hedbor  LocaleString type_hint( )
d1de942000-08-19Martin Nilsson  //! 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; }
bbaf612005-02-04Martin Stjernholm  void set_default_value (mixed to) //! Change the default value. If the variable was previously set to //! the old default value, this will also change its actual value. { _initial = to; }
d1de942000-08-19Martin Nilsson  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 ); }
b8fd5c2000-09-28Per Hedbor  void add_warning( string to ) //! Like set_warning, but adds to the current warning, if any. {
a882cd2000-09-28Per Hedbor  if(to) set_warning( (get_warnings()||"") + to );
b8fd5c2000-09-28Per Hedbor  }
d1de942000-08-19Martin Nilsson  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
3976532001-06-13Martin Stjernholm  //! its default value.
d1de942000-08-19Martin Nilsson  //! //! 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. {
67b85e2001-11-12Martin Stjernholm  string err; mixed e2;
d1de942000-08-19Martin Nilsson  if( e2 = catch( [err,to] = verify_set( to )) ) { if( stringp( e2 ) ) {
b8fd5c2000-09-28Per Hedbor  add_warning( e2 );
d1de942000-08-19Martin Nilsson  return ([])[0]; } throw( e2 ); }
b8fd5c2000-09-28Per Hedbor  add_warning( err );
d1de942000-08-19Martin Nilsson  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() )
5d8bab2002-01-29Martin Stjernholm  get_changed_callback()( this_object() );
d1de942000-08-19Martin Nilsson  return 1; } else { m_delete( changed_values, _id ); if( get_changed_callback() )
5d8bab2002-01-29Martin Stjernholm  get_changed_callback()( this_object() );
d1de942000-08-19Martin Nilsson  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()); }
b8fd5c2000-09-28Per Hedbor  array(string|mixed) verify_set_from_form( mixed new_value ) //! Like verify_set, but only called when the variables are set //! from a form. { return ({ 0, new_value }); }
d1de942000-08-19Martin Nilsson  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; }
39adcf2001-06-27Martin Nilsson  mixed transform_from_form( string what, mapping|void v )
d1de942000-08-19Martin Nilsson  //! Given a form value, return what should be set. //! Used by the default set_from_form implementation. { return what; }
bacec92001-06-13Martin Nilsson  int(0..1) set_from_form( RequestID id, void|int(0..1) force )
d1de942000-08-19Martin Nilsson  //! Set this variable from the form variable in id->Variables, //! if any are available. The default implementation simply sets
d9ab8c2001-05-22Martin Nilsson  //! the variable to the string in the form variables. @[force] //! forces the variable to be set even if the variable already //! has the new value, forcing possible warnings to be added.
bacec92001-06-13Martin Nilsson  //! Returns 1 if the variable was changed, otherwise 0.
d1de942000-08-19Martin Nilsson  //! //! Other side effects: Might create warnings to be shown to the //! user (see get_warnings)
b8fd5c2000-09-28Per Hedbor  //! //! Calls verify_set_from_form and verify_set
d1de942000-08-19Martin Nilsson  {
b8fd5c2000-09-28Per Hedbor  mixed val;
d9ab8c2001-05-22Martin Nilsson  if( sizeof( val = get_form_vars(id)) && val[""])
b8fd5c2000-09-28Per Hedbor  {
e8668d2001-08-28Martin Nilsson  set_warning(0);
39adcf2001-06-27Martin Nilsson  val = transform_from_form( val[""], val );
cc31712001-05-24Martin Nilsson  if( !force && val == query() )
bacec92001-06-13Martin Nilsson  return 0;
b8fd5c2000-09-28Per Hedbor  array b; mixed q = catch( b = verify_set_from_form( val ) ); if( q || sizeof( b ) != 2 ) { if( q ) add_warning( q ); else
cd87a22000-11-27Martin Nilsson  add_warning( "Internal error: Illegal sized array " "from verify_set_from_form\n" );
bacec92001-06-13Martin Nilsson  return 0;
b8fd5c2000-09-28Per Hedbor  }
a882cd2000-09-28Per Hedbor  if( b ) {
e8668d2001-08-28Martin Nilsson  add_warning( b[0] );
a882cd2000-09-28Per Hedbor  set( b[1] );
bacec92001-06-13Martin Nilsson  return 1;
a882cd2000-09-28Per Hedbor  }
b8fd5c2000-09-28Per Hedbor  }
d1de942000-08-19Martin Nilsson  } 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. {
a7defb2001-08-01Per Hedbor  m_delete( all_variables, _path );
d1de942000-08-19Martin Nilsson  _path = to;
a7defb2001-08-01Per Hedbor  all_variables[ to ] = this_object();
d1de942000-08-19Martin Nilsson  }
a968342000-08-25Martin Nilsson  string render_form( RequestID id, void|mapping additional_args );
d1de942000-08-19Martin Nilsson  //! 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. {
88c8672000-09-19Per Hedbor  mixed v = query(); if( arrayp(v) ) v = map(v,lambda(mixed v){return(string)v;})*", " ; return Roxen.html_encode_string( (string)v );
d1de942000-08-19Martin Nilsson  }
fc40392008-08-15Martin Stjernholm  protected string _sprintf( int i )
d1de942000-08-19Martin Nilsson  { if( i == 'O' )
8fec792000-09-04Per Hedbor  return sprintf( "Variable.%s(%s)",type,(string)name());
d1de942000-08-19Martin Nilsson  }
fc40392008-08-15Martin Stjernholm  protected void create(mixed default_value, void|int flags, void|LocaleString std_name, void|LocaleString std_doc)
d1de942000-08-19Martin Nilsson  //! 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;
a7defb2001-08-01Per Hedbor  all_variables[ path() ] = this_object();
d1de942000-08-19Martin Nilsson  } }
2c76162004-10-20Martin Stjernholm class NoLimit { string _sprintf (int flag) { switch (flag) { case 's': return "n/a"; case 'O': return this == no_limit ? "no_limit" : "<bogus no_limit clone>"; default: return 0; } } } NoLimit no_limit = NoLimit(); //! @[no_limit] is used as value in a limit setting to signify that it //! isn't applicable, to allow open ended intervals in e.g. @[Float] //! and @[Int]. //! //! @note //! Always use the @[no_limit] object, never instance another one from //! the @[NoLimit] class.
d1de942000-08-19Martin Nilsson  // ===================================================================== // Float // ===================================================================== class Float //! Float variable, with optional range checks, and adjustable precision. { inherit Variable; constant type = "Float";
fc40392008-08-15Martin Stjernholm  protected float|NoLimit _max = no_limit, _min = no_limit; protected int _prec = 2; protected int _may_be_empty = 0; protected int(0..1) _is_empty = 0;
d1de942000-08-19Martin Nilsson 
fc40392008-08-15Martin Stjernholm  protected string _format( float|NoLimit m )
d1de942000-08-19Martin Nilsson  {
2c76162004-10-20Martin Stjernholm  if (m == no_limit) return "n/a";
d1de942000-08-19Martin Nilsson  if( !_prec ) return sprintf( "%d", (int)m ); return sprintf( "%1."+_prec+"f", m ); }
a7defb2001-08-01Per Hedbor  string diff( int render ) { if(!render) return "("+_format(default_value())+")"; }
2c76162004-10-20Martin Stjernholm  void set_range(float|NoLimit minimum, float|NoLimit maximum ) //! Set the range of the variable. //! //! As a compatibility measure, the range check is removed if //! maximum < minimum. {
4e98962006-02-17Martin Stjernholm  if (maximum != no_limit && minimum != no_limit && maximum < minimum)
2c76162004-10-20Martin Stjernholm  _max = _min = no_limit; else { _max = maximum; _min = minimum; }
d1de942000-08-19Martin Nilsson  } 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 ) {
b03a3b2007-01-12 Erik Dahl  if (new_value == (float)0 && _is_empty) return ({ 0, new_value });
d1de942000-08-19Martin Nilsson  string warn;
2c76162004-10-20Martin Stjernholm  if(_max != no_limit && new_value > _max)
d1de942000-08-19Martin Nilsson  {
55a8662000-11-20Per Hedbor  warn = sprintf(LOCALE(328,"Value is bigger than %s, adjusted"),
9f97972000-11-20Per Hedbor  _format(_max) );
a968342000-08-25Martin Nilsson  new_value = _max; }
2c76162004-10-20Martin Stjernholm  else if(_min != no_limit && new_value < _min)
a968342000-08-25Martin Nilsson  {
55a8662000-11-20Per Hedbor  warn = sprintf(LOCALE(329,"Value is less than %s, adjusted"),
9f97972000-11-20Per Hedbor  _format(_min) );
a968342000-08-25Martin Nilsson  new_value = _min;
d1de942000-08-19Martin Nilsson  } return ({ warn, new_value }); }
e8668d2001-08-28Martin Nilsson  float transform_from_form( mixed what )
d1de942000-08-19Martin Nilsson  {
b03a3b2007-01-12 Erik Dahl  if (!sizeof(what) && _may_be_empty) { _is_empty = 1; return (float)0; }
e8668d2001-08-28Martin Nilsson  string junk; if(!sizeof(what)) { add_warning(LOCALE(80, "No data entered.\n")); return _min; } sscanf(what, "%f%s", what, junk); if(!junk) { add_warning(LOCALE(81, "Data is not a float.\n")); return _min; } if(sizeof(junk)) add_warning(sprintf(LOCALE(82, "Found the string %O trailing after the float.\n"), junk));
d1de942000-08-19Martin Nilsson  return (float)what; } string render_view( RequestID id ) { return Roxen.html_encode_string( _format(query()) ); }
a968342000-08-25Martin Nilsson  string render_form( RequestID id, void|mapping additional_args )
d1de942000-08-19Martin Nilsson  { int size = 15;
2c76162004-10-20Martin Stjernholm  if( _max != no_limit && _min != no_limit )
d1de942000-08-19Martin Nilsson  size = max( strlen(_format(_max)), strlen(_format(_min)) )+2;
b03a3b2007-01-12 Erik Dahl  string value; if (_may_be_empty && (float)query() == (float)0) value = ""; else value = query()==""? "" : _format( (float)query() );
fc1a0c2008-01-29Mathias Södermark  additional_args = additional_args || ([]); additional_args->type="text";
b03a3b2007-01-12 Erik Dahl  return input(path(), value, size, additional_args); } void may_be_empty(int(0..1) state) //! Decides if an empty variable also is valid. { _may_be_empty = state;
d1de942000-08-19Martin Nilsson  } } // ===================================================================== // Int // ===================================================================== class Int //! Integer variable, with optional range checks { inherit Variable; constant type = "Int";
fc40392008-08-15Martin Stjernholm  protected int|NoLimit _max = no_limit, _min = no_limit;
d1de942000-08-19Martin Nilsson 
fc40392008-08-15Martin Stjernholm  protected int(0..1) _may_be_empty = 0; protected int(0..1) _is_empty = 0;
b03a3b2007-01-12 Erik Dahl 
2c76162004-10-20Martin Stjernholm  void set_range(int|NoLimit minimum, int|NoLimit maximum ) //! Set the range of the variable. //! //! As a compatibility measure, the range check is removed if //! maximum < minimum. {
4e98962006-02-17Martin Stjernholm  if (maximum != no_limit && minimum != no_limit && maximum < minimum)
2c76162004-10-20Martin Stjernholm  _max = _min = no_limit; else { _max = maximum; _min = minimum; }
d1de942000-08-19Martin Nilsson  }
a7defb2001-08-01Per Hedbor  string diff( int render ) { if(!render) return "("+default_value()+")"; }
6fd6832000-11-27Per Hedbor  array(string|int) verify_set( mixed new_value )
d1de942000-08-19Martin Nilsson  {
b03a3b2007-01-12 Erik Dahl  if (new_value == 0 && _is_empty) return ({ 0, new_value });
d1de942000-08-19Martin Nilsson  string warn;
6fd6832000-11-27Per Hedbor  if(!intp( new_value ) )
e05d532000-11-27Martin Nilsson  return ({ sprintf(LOCALE(152,"%O is not an integer"),new_value),
6fd6832000-11-27Per Hedbor  query() });
2c76162004-10-20Martin Stjernholm  if( _max != no_limit && new_value > _max)
d1de942000-08-19Martin Nilsson  {
55a8662000-11-20Per Hedbor  warn = sprintf(LOCALE(328,"Value is bigger than %s, adjusted"),
9f97972000-11-20Per Hedbor  (string)_max );
a968342000-08-25Martin Nilsson  new_value = _max; }
2c76162004-10-20Martin Stjernholm  else if( _min != no_limit && new_value < _min)
a968342000-08-25Martin Nilsson  {
55a8662000-11-20Per Hedbor  warn = sprintf(LOCALE(329,"Value is less than %s, adjusted"),
9f97972000-11-20Per Hedbor  (string)_min );
a968342000-08-25Martin Nilsson  new_value = _min;
d1de942000-08-19Martin Nilsson  } return ({ warn, new_value }); }
6fd6832000-11-27Per Hedbor  int transform_from_form( mixed what )
d1de942000-08-19Martin Nilsson  {
b03a3b2007-01-12 Erik Dahl  if (!sizeof(what) && _may_be_empty) { _is_empty = 1; return 0; }
e8668d2001-08-28Martin Nilsson  string junk; if(!sizeof(what)) { add_warning(LOCALE(80, "No data entered.\n")); return _min; } sscanf( what, "%d%s", what, junk ); if(!junk) { add_warning(LOCALE(83, "Data is not an integer\n")); return _min; } if(sizeof(junk)) add_warning(sprintf(LOCALE(84, "Found the string %O trailing after the integer.\n"), junk));
6fd6832000-11-27Per Hedbor  return what;
d1de942000-08-19Martin Nilsson  }
a968342000-08-25Martin Nilsson  string render_form( RequestID id, void|mapping additional_args )
d1de942000-08-19Martin Nilsson  { int size = 10;
2c76162004-10-20Martin Stjernholm  if( _min != no_limit && _max != no_limit )
d1de942000-08-19Martin Nilsson  size = max( strlen((string)_max), strlen((string)_min) )+2;
b03a3b2007-01-12 Erik Dahl  string value = (query() == 0 && _is_empty)? "" : (string)query();
fc1a0c2008-01-29Mathias Södermark  additional_args = additional_args || ([]); additional_args->type="text";
b03a3b2007-01-12 Erik Dahl  return input(path(), value, size, additional_args); } void may_be_empty(int(0..1) state) //! Decides if an empty variable also is valid. { _may_be_empty = state;
d1de942000-08-19Martin Nilsson  } }
d0876c2008-09-25Martin Stjernholm class TmpInt //! @[Int] that doesn't get saved. { inherit Int; void save() {} }
d1de942000-08-19Martin Nilsson  // ===================================================================== // String // ===================================================================== class String //! String variable { inherit Variable; constant type = "String";
c85b8f2001-06-14Johan Schön  int width = 40;
d1de942000-08-19Martin Nilsson  //! The width of the input field. Used by overriding classes.
613baf2000-09-28Per Hedbor 
a7defb2001-08-01Per Hedbor  string diff( int render ) { if(!render) return "("+Roxen.html_encode_string( default_value() )+")"; }
bacec92001-06-13Martin Nilsson  array(string) verify_set_from_form( mixed new )
613baf2000-09-28Per Hedbor  {
bacec92001-06-13Martin Nilsson  return ({ 0, [string]new-"\r" });
613baf2000-09-28Per Hedbor  }
a968342000-08-25Martin Nilsson  string render_form( RequestID id, void|mapping additional_args )
d1de942000-08-19Martin Nilsson  {
0167ce2007-10-17Jonas Wallden  additional_args = additional_args || ([]);
5460082007-10-17Mathias Södermark  additional_args->type="text";
a968342000-08-25Martin Nilsson  return input(path(), (string)query(), width, additional_args);
d1de942000-08-19Martin Nilsson  } } // ===================================================================== // Text // ===================================================================== class Text //! Text (multi-line string) variable { inherit String; constant type = "Text";
7263fd2001-08-09Martin Nilsson 
61d55c2008-09-26Martin Stjernholm  int cols = 56;
d1de942000-08-19Martin Nilsson  //! The width of the textarea
7263fd2001-08-09Martin Nilsson 
c85b8f2001-06-14Johan Schön  int rows = 10;
d1de942000-08-19Martin Nilsson  //! The height of the textarea
a7defb2001-08-01Per Hedbor  string diff( int render ) { switch( render ) { case 0: return 0; case 1: return ""; case 2: array lines_orig = default_value()/"\n"; array lines_new = query()/"\n"; Diff diff = Diff( lines_new, lines_orig, 2 ); if( sizeof(diff->get()) ) return diff->html(); else
9fa11d2001-08-24Martin Nilsson  return "<i>"+LOCALE(481,"No difference\n" )+"</i>";
a7defb2001-08-01Per Hedbor  } } array(string) verify_set_from_form( mixed new ) { return ({ 0, [string]new-"\r" }); }
a968342000-08-25Martin Nilsson  string render_form( RequestID id, void|mapping additional_args )
d1de942000-08-19Martin Nilsson  {
ec6f242006-05-05Marcus Wellhardh  return "<textarea cols='"+cols+"' rows='"+rows+"' name='"+path()+"' wrap='off'>"
d1de942000-08-19Martin Nilsson  + Roxen.html_encode_string( query() || "" ) + "</textarea>"; }
a7defb2001-08-01Per Hedbor 
fc40392008-08-15Martin Stjernholm  protected void create(mixed default_value, void|int flags, void|LocaleString std_name, void|LocaleString std_doc)
a7defb2001-08-01Per Hedbor  //! 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) { if( strlen( default_value ) && default_value[0] == '\n' ) // This is enforced by both netscape and IE... So let's just conform. default_value = default_value[1..]; ::create( default_value, flags, std_name, std_doc ); }
d1de942000-08-19Martin Nilsson } // ===================================================================== // Password // ===================================================================== class Password //! Password variable (uses crypt) { inherit String;
c85b8f2001-06-14Johan Schön  int width = 20;
d1de942000-08-19Martin Nilsson  constant type = "Password";
42fb352001-06-14Martin Nilsson  int(0..1) set_from_form( RequestID id )
d1de942000-08-19Martin Nilsson  { mapping val; if( sizeof( val = get_form_vars(id)) &&
42fb352001-06-14Martin Nilsson  val[""] && strlen(val[""]) ) {
d1de942000-08-19Martin Nilsson  set( crypt( val[""] ) );
42fb352001-06-14Martin Nilsson  return 1; } return 0;
d1de942000-08-19Martin Nilsson  } string render_view( RequestID id ) { return "******"; }
a968342000-08-25Martin Nilsson  string render_form( RequestID id, void|mapping additional_args )
d1de942000-08-19Martin Nilsson  {
a968342000-08-25Martin Nilsson  additional_args = additional_args || ([]); additional_args->type="password";
2ffb482000-11-19Stefan Wallström  return input(path(), "", 30, additional_args);
d1de942000-08-19Martin Nilsson  } } class File //! A filename { inherit String; constant type = "File";
c85b8f2001-06-14Johan Schön  int width = 50;
d1de942000-08-19Martin Nilsson  string read( ) //! Read the file as a string. { return Stdio.read_bytes( query() ); }
1f4a6c2000-08-28Per Hedbor  Stat stat()
d1de942000-08-19Martin Nilsson  //! Stat the file { return file_stat( query() ); }
be36312000-08-23Per Hedbor  #ifdef __NT__ array verify_set( string value ) { return ::verify_set( replace( value, "\\", "/" ) ); } #endif
d1de942000-08-19Martin Nilsson } class Location //! A location in the virtual filesystem { inherit String; constant type = "Location";
c85b8f2001-06-14Johan Schön  int width = 50;
81251c2000-11-20Per Hedbor  array verify_set( string value ) { if( !strlen( value ) || !((<'~','/'>)[value[-1]]) ) return ({
55a8662000-11-20Per Hedbor  LOCALE(330,"You most likely want an ending '/' on this variable"),
81251c2000-11-20Per Hedbor  value }); return ::verify_set( value ); }
d1de942000-08-19Martin Nilsson } class URL //! A URL. { inherit String; constant type = "URL";
c85b8f2001-06-14Johan Schön  int width = 50;
d1de942000-08-19Martin Nilsson 
b8fd5c2000-09-28Per Hedbor  array verify_set_from_form( string new_value )
d1de942000-08-19Martin Nilsson  {
bc0dec2001-08-05Martin Nilsson  return verify_port( new_value );
d1de942000-08-19Martin Nilsson  } } class Directory //! A Directory. { inherit String; constant type = "Directory";
c85b8f2001-06-14Johan Schön  int width = 50;
d1de942000-08-19Martin Nilsson  array verify_set( string value ) {
be36312000-08-23Per Hedbor #ifdef __NT__ value = replace( value, "\\", "/" ); #endif if( strlen(value) && value[-1] != '/' ) value += "/";
f637572000-08-22Per Hedbor  if( !strlen( value ) ) return ::verify_set( value );
d1de942000-08-19Martin Nilsson  if( !(r_file_stat( value ) && (r_file_stat( value )[ ST_SIZE ] == -2 )))
55a8662000-11-20Per Hedbor  return ({sprintf(LOCALE(331,"%s is not a directory"),value)+"\n",value});
d1de942000-08-19Martin Nilsson  return ::verify_set( value ); }
1f4a6c2000-08-28Per Hedbor  Stat stat()
d1de942000-08-19Martin Nilsson  //! 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;
fc40392008-08-15Martin Stjernholm  protected array _list = ({}); protected mapping _table = ([]);
d1de942000-08-19Martin Nilsson 
a7defb2001-08-01Per Hedbor  string diff( int render ) { if(!render) return "("+_title( default_value() )+")"; }
d1de942000-08-19Martin Nilsson  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; }
fc40392008-08-15Martin Stjernholm  protected string _name( mixed what )
d1de942000-08-19Martin Nilsson  //! Get the name used as value for an element gotten from the //! get_choice_list() function. { return (string)what; }
fc40392008-08-15Martin Stjernholm  protected string _title( mixed what )
d1de942000-08-19Martin Nilsson  //! 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; }
a968342000-08-25Martin Nilsson  string render_form( RequestID id, void|mapping additional_args )
d1de942000-08-19Martin Nilsson  {
134f6f2002-02-26Marcus Wellhardh  string autosubmit = ""; if(additional_args && additional_args->autosubmit) autosubmit = " autosubmit='autosubmit' onChange='javascript:submit();'"; string res = "<select name='"+path()+"'"+autosubmit+">\n";
ef40dc2000-09-01Martin Stjernholm  string current = _name (query()); int selected = 0;
d1de942000-08-19Martin Nilsson  foreach( get_choice_list(), mixed elem ) { mapping m = ([]); m->value = _name( elem );
ef40dc2000-09-01Martin Stjernholm  if( equal( m->value, current ) ) {
d1de942000-08-19Martin Nilsson  m->selected="selected";
ef40dc2000-09-01Martin Stjernholm  selected = 1; }
d1de942000-08-19Martin Nilsson  res += " "+Roxen.make_container( "option", m, _title( elem ) )+"\n"; }
ef40dc2000-09-01Martin Stjernholm  if (!selected) // Make an entry for the current value if it's not in the list, // so no other value appears to be selected, and to ensure that // the value doesn't change as a side-effect by another change.
c6fd2e2000-09-03Per Hedbor  res += " " + Roxen.make_container (
62b1c52007-10-05Martin Jonsson  "option", (["value":current, "selected": "selected"]), sprintf(LOCALE(332,"(keep stale value %s)"),current));
d1de942000-08-19Martin Nilsson  return res + "</select>"; }
7263fd2001-08-09Martin Nilsson 
fc40392008-08-15Martin Stjernholm  protected void create( mixed default_value, array|mapping choices, void|int _flags, void|LocaleString std_name, void|LocaleString std_doc )
d1de942000-08-19Martin Nilsson  //! 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 );
b0e7e22005-12-17Jonas Wallden  set_choice_list( sort(indices(choices)) );
d1de942000-08-19Martin Nilsson  } 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";
fc40392008-08-15Martin Stjernholm  protected int _prec = 3;
d1de942000-08-19Martin Nilsson  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; }
fc40392008-08-15Martin Stjernholm  protected string _title( mixed what )
d1de942000-08-19Martin Nilsson  { 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(); }
7263fd2001-08-09Martin Nilsson 
fc40392008-08-15Martin Stjernholm  protected void create(mixed default_value, void|int flags, void|LocaleString std_name, void|LocaleString std_doc)
d1de942000-08-19Martin Nilsson  //! 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 ); } }
2ef68b2001-10-04Per Hedbor class TableChoice { inherit StringChoice; constant type = "TableChoice"; Variable db; array(string) get_choice_list( ) { return sort(DBManager.db_tables( db->query() )); } void create( string default_value, void|int flags, void|LocaleString std_name, void|LocaleString std_doc, Variable _dbchoice ) { ::create( default_value, ({}), flags, std_name, std_doc ); db = _dbchoice; } }
5bfe942001-06-16Martin Nilsson class DatabaseChoice //! Select a database from all available databases. { inherit StringChoice; constant type = "DatabaseChoice";
630b482001-08-01Per Hedbor  function(void:void|object) config = lambda() { return 0; };
5bfe942001-06-16Martin Nilsson  DatabaseChoice set_configuration_pointer( function(void:object) configuration ) //! Provide a function that returns a configuration object, //! that will be used for authentication against the database //! manager. Typically called as
dd03b22006-04-20Henrik Grubbström (Grubba)  //! @expr{set_configuration_pointer(my_configuration)@}.
5bfe942001-06-16Martin Nilsson  { config = configuration; return this_object(); } array get_choice_list( ) { return ({ " none" }) + sort(DBManager.list( config() )); }
110a252001-06-18Martin Nilsson 
fc40392008-08-15Martin Stjernholm  protected void create(string default_value, void|int flags, void|LocaleString std_name, void|LocaleString std_doc)
110a252001-06-18Martin Nilsson  { ::create( default_value, ({}), flags, std_name, std_doc ); }
5bfe942001-06-16Martin Nilsson }
9836772001-09-21Per Hedbor class AuthMethodChoice { inherit StringChoice; constant type = "AuthMethodChoice";
fc40392008-08-15Martin Stjernholm  protected Configuration config;
9836772001-09-21Per Hedbor  array get_choice_list( ) { return ({ " all" }) + sort( config->auth_modules()->name ); }
fc40392008-08-15Martin Stjernholm  protected void create( string default_value, int flags, string std_name, string std_doc, Configuration c )
9836772001-09-21Per Hedbor  { config = c; ::create( default_value, ({}), flags, std_name, std_doc ); } } class UserDBChoice { inherit StringChoice; constant type = "UserDBChoice";
fc40392008-08-15Martin Stjernholm  protected Configuration config;
9836772001-09-21Per Hedbor  array get_choice_list( ) { return ({ " all" }) + sort( config->user_databases()->name ); }
fc40392008-08-15Martin Stjernholm  protected void create( string default_value, int flags, string std_name, string std_doc, Configuration c )
9836772001-09-21Per Hedbor  { config = c; ::create( default_value, ({}), flags, std_name, std_doc ); } }
ec7ab12006-11-30Henrik Grubbström (Grubba) // FIXME: Consider making a ModuleChoice as well. //! Select a module that provides the specified interface. class ProviderChoice { inherit StringChoice; constant type = "ProviderChoice";
fc40392008-08-15Martin Stjernholm  protected Configuration conf; protected string provides; protected string default_id; protected string local_id = ""; protected int isset;
ec7ab12006-11-30Henrik Grubbström (Grubba)  int low_set(RoxenModule to) {
ee8d502007-01-22Henrik Grubbström (Grubba)  RoxenModule old = changed_values[_id]; if (to == old) return 0; if (!old) { if (local_id != "") { old = transform_from_form(local_id); } if (!old) { old = default_value(); if (old) local_id = _name(old); } changed_values[_id] = to; if (to == old) return 0; } changed_values[_id] = to;
ec7ab12006-11-30Henrik Grubbström (Grubba)  local_id = _name(to);
ee8d502007-01-22Henrik Grubbström (Grubba)  if( get_changed_callback() ) get_changed_callback()( this_object() ); return 1;
ec7ab12006-11-30Henrik Grubbström (Grubba)  }
151c842007-01-10Henrik Grubbström (Grubba)  // NOTE: Will be called with a string at module init! int set(string|RoxenModule to) { if (stringp(to)) { local_id = to; to = transform_from_form(to);
62b1c52007-10-05Martin Jonsson  isset = 1;
151c842007-01-10Henrik Grubbström (Grubba)  } return ::set(to); }
ec7ab12006-11-30Henrik Grubbström (Grubba)  RoxenModule query() {
62b1c52007-10-05Martin Jonsson  RoxenModule res = changed_values[_id];
2df07b2006-12-06Henrik Grubbström (Grubba)  if (!res) {
7ae70c2006-12-06Henrik Grubbström (Grubba)  if (local_id != "") {
2df07b2006-12-06Henrik Grubbström (Grubba)  // The module might have been reloaded. // Try locating it again. res = transform_from_form(local_id);
7ae70c2006-12-06Henrik Grubbström (Grubba)  if (res) low_set(res);
62b1c52007-10-05Martin Jonsson  } else if(!isset) { res = default_value(); if(res) { set(res); }
2df07b2006-12-06Henrik Grubbström (Grubba)  }
ec7ab12006-11-30Henrik Grubbström (Grubba)  } return res; } array get_choice_list() {
ee8d502007-01-22Henrik Grubbström (Grubba)  array res = conf->get_providers(provides); sort(map(res, _title), res); return res;
ec7ab12006-11-30Henrik Grubbström (Grubba)  }
fc40392008-08-15Martin Stjernholm  protected string _name(RoxenModule val)
ec7ab12006-11-30Henrik Grubbström (Grubba)  { return val?val->module_local_id():""; }
fc40392008-08-15Martin Stjernholm  protected string _title(RoxenModule val)
ec7ab12006-11-30Henrik Grubbström (Grubba)  {
7ae70c2006-12-06Henrik Grubbström (Grubba)  return val?val->module_name:"";
ec7ab12006-11-30Henrik Grubbström (Grubba)  } RoxenModule transform_from_form(string local_id, mapping|void v) { return conf->find_module(local_id); } RoxenModule default_value() { if (default_id) { return transform_from_form(default_id); } else {
62b1c52007-10-05Martin Jonsson  array(RoxenModule) providers = conf->get_providers(provides);
ec7ab12006-11-30Henrik Grubbström (Grubba)  if (sizeof(providers)) { return providers[0]; }
2df07b2006-12-06Henrik Grubbström (Grubba)  return UNDEFINED;
ec7ab12006-11-30Henrik Grubbström (Grubba)  } }
da85d72006-12-07Henrik Grubbström (Grubba)  array(string|mixed) verify_set( mixed new_value ) {
5fe8672007-01-22Henrik Grubbström (Grubba)  if (!new_value) { new_value = query(); }
da85d72006-12-07Henrik Grubbström (Grubba)  if (!new_value) { return ({ "Not configured", 0 }); } return ({ 0, new_value }); }
ec7ab12006-11-30Henrik Grubbström (Grubba)  //! @param default_id //! The @[RoxenModule.module_local_id] of the default value. //! @param provides //! The provider string to match modules against. //! @param conf //! The current configuration.
fc40392008-08-15Martin Stjernholm  protected void create(string default_id, int flags, string std_name, string std_doc, string provides, Configuration conf)
ec7ab12006-11-30Henrik Grubbström (Grubba)  { this_program::provides = provides; this_program::default_id = default_id; this_program::conf = conf; ::create(0, ({}), flags, std_name, std_doc); } }
d1de942000-08-19Martin Nilsson  // ===================================================================== // List baseclass // ===================================================================== class List //! Many of one type types {
0a19152000-11-11Per Hedbor  inherit Variable;
d1de942000-08-19Martin Nilsson  constant type="List";
c85b8f2001-06-14Johan Schön  int width = 40;
d1de942000-08-19Martin Nilsson 
5f70162001-11-01Henrik Grubbström (Grubba)  array(string|array(string)) verify_set(mixed to) { if (stringp(to)) { // Backward compatibility junk... return ({ "Compatibility: " "Converted from TYPE_STRING to TYPE_STRING_LIST.\n", (to-" ")/"," }); } return ::verify_set(to); }
d1de942000-08-19Martin Nilsson  string transform_to_form( mixed what ) //! Override this function to do the value->form mapping for //! individual elements in the array. { return (string)what; }
d21c1f2001-02-02Per Hedbor  mixed transform_from_form( string what,mapping v )
d1de942000-08-19Martin Nilsson  { return what; }
fc40392008-08-15Martin Stjernholm  protected int _current_count = time()*100+(gethrtime()/10000);
42fb352001-06-14Martin Nilsson  int(0..1) set_from_form(RequestID id)
d1de942000-08-19Martin Nilsson  {
f637572000-08-22Per Hedbor  int rn, do_goto;
98ab172003-03-05Mattias Andersson  array l = copy_value(query());
d1de942000-08-19Martin Nilsson  mapping vl = get_form_vars(id); // first do the assign... if( (int)vl[".count"] != _current_count )
42fb352001-06-14Martin Nilsson  return 0;
d1de942000-08-19Martin Nilsson  _current_count++;
e8668d2001-08-28Martin Nilsson  set_warning(0);
d1de942000-08-19Martin Nilsson  foreach( indices( vl ), string vv )
d21c1f2001-02-02Per Hedbor  if( sscanf( vv, ".set.%d", rn ) && (vv == ".set."+rn) )
d1de942000-08-19Martin Nilsson  {
74dd852004-06-09Henrik Grubbström (Grubba)  if ((rn >= 0) && (rn < sizeof(l))) { m_delete( id->variables, path()+vv ); l[rn] = transform_from_form( vl[vv], vl ); m_delete( vl, vv ); } else { report_debug("set_from_form(%O): vv:%O sizeof(l):%d\n", id, vv, sizeof(l)); }
d1de942000-08-19Martin Nilsson  } // then the move... foreach( indices(vl), string vv ) if( sscanf( vv, ".up.%d.x%*s", rn ) == 2 ) {
f637572000-08-22Per Hedbor  do_goto = 1;
d1de942000-08-19Martin Nilsson  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 ) {
f637572000-08-22Per Hedbor  do_goto = 1;
d1de942000-08-19Martin Nilsson  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"] ) {
f637572000-08-22Per Hedbor  do_goto = 1;
d1de942000-08-19Martin Nilsson  m_delete( id->variables, path()+".new.x" );
d21c1f2001-02-02Per Hedbor  l += ({ transform_from_form( "",vl ) });
d1de942000-08-19Martin Nilsson  } // .. and delete .. foreach( indices(vl), string vv ) if( sscanf( vv, ".delete.%d.x%*s", rn )==2 ) {
f637572000-08-22Per Hedbor  do_goto = 1;
d1de942000-08-19Martin Nilsson  m_delete( id->variables, path()+vv ); l = l[..rn-1] + l[rn+1..]; }
0a19152000-11-11Per Hedbor  array b; mixed q = catch( b = verify_set_from_form( l ) ); if( q || sizeof( b ) != 2 ) { if( q )
e8668d2001-08-28Martin Nilsson  add_warning( q );
0a19152000-11-11Per Hedbor  else
e8668d2001-08-28Martin Nilsson  add_warning( "Internal error: Illegal sized array "
cd87a22000-11-27Martin Nilsson  "from verify_set_from_form\n" );
42fb352001-06-14Martin Nilsson  return 0;
0a19152000-11-11Per Hedbor  }
42fb352001-06-14Martin Nilsson  int ret;
0a19152000-11-11Per Hedbor  if( b ) {
e8668d2001-08-28Martin Nilsson  add_warning( b[0] );
0a19152000-11-11Per Hedbor  set( b[1] );
42fb352001-06-14Martin Nilsson  ret = 1;
0a19152000-11-11Per Hedbor  }
42fb352001-06-14Martin Nilsson 
4329da2001-01-29Per Hedbor  if( do_goto && !id->misc->do_not_goto ) { RequestID nid = id; while( nid->misc->orig ) nid = id->misc->orig; string section = RXML.get_var("section", "var"); string query = nid->query; if( !query ) query = ""; else query += "&";
552cd92003-10-23Jonas Wallden  // The URL will get a fragment identifier below and since some // broken browsers (MSIE) incorrectly includes the fragment in // the last variable value we'll place section before random. query += (section ? ("section=" + section + "&") : "") + "random=" + random(4949494);
22f4c22008-05-09Martin Stjernholm  string url = Roxen.http_encode_invalids (nid->not_query + (nid->misc->path_info || "") + "?" + query + "#" + path()); nid->set_response_header ("Location", url);
4329da2001-01-29Per Hedbor  if( nid->misc->defines ) nid->misc->defines[ " _error" ] = 302; else if( id->misc->defines ) id->misc->defines[ " _error" ] = 302; }
42fb352001-06-14Martin Nilsson  return ret;
d1de942000-08-19Martin Nilsson  }
d21c1f2001-02-02Per Hedbor  string render_row(string prefix, mixed val, int width) { return input( prefix, val, width ); }
a968342000-08-25Martin Nilsson  string render_form( RequestID id, void|mapping additional_args )
d1de942000-08-19Martin Nilsson  { string prefix = path()+"."; int i;
f637572000-08-22Per Hedbor  string res = "<a name='"+path()+"'>\n</a><table>\n" "<input type='hidden' name='"+prefix+"count' value='"+_current_count+"' />\n";
d1de942000-08-19Martin Nilsson 
d21c1f2001-02-02Per Hedbor  foreach( map(query(), transform_to_form), mixed val )
d1de942000-08-19Martin Nilsson  {
d21c1f2001-02-02Per Hedbor  res += "<tr>\n<td><font size='-1'>"+ render_row(prefix+"set."+i, val, width) + "</font></td>\n";
d1de942000-08-19Martin Nilsson #define BUTTON(X,Y) ("<submit-gbutton2 name='"+X+"'>"+Y+"</submit-gbutton2>")
e69c012000-08-31Martin Nilsson #define REORDER(X,Y) ("<submit-gbutton2 name='"+X+"' icon-src='"+Y+"'></submit-gbutton2>")
80cd682003-11-17Anders Johansson #define DIMBUTTON(X) ("<disabled-gbutton icon-src='"+X+"'></disabled-gbutton>")
d1de942000-08-19Martin Nilsson  if( i ) res += "\n<td>"+
e69c012000-08-31Martin Nilsson  REORDER(prefix+"up."+i, "/internal-roxen-up")+
d1de942000-08-19Martin Nilsson  "</td>"; else
80cd682003-11-17Anders Johansson  res += "\n<td>"+DIMBUTTON("/internal-roxen-up")+"</td>";
d1de942000-08-19Martin Nilsson  if( i != sizeof( query())- 1 ) res += "\n<td>"+
e69c012000-08-31Martin Nilsson  REORDER(prefix+"down."+i, "/internal-roxen-down")
d1de942000-08-19Martin Nilsson  +"</td>"; else
80cd682003-11-17Anders Johansson  res += "\n<td>"+DIMBUTTON("/internal-roxen-down")+"</td>";
d1de942000-08-19Martin Nilsson  res += "\n<td>"+
99a7452000-08-22Andreas Lange  BUTTON(prefix+"delete."+i, LOCALE(227, "Delete") )
d1de942000-08-19Martin Nilsson  +"</td>"; "</tr>"; i++; } res +=
f637572000-08-22Per Hedbor  "\n<tr><td colspan='2'>"+
99a7452000-08-22Andreas Lange  BUTTON(prefix+"new", LOCALE(297, "New row") )+
f637572000-08-22Per Hedbor  "</td></tr></table>\n\n";
d1de942000-08-19Martin Nilsson  return res; } } // ===================================================================== // List subclasses // ===================================================================== class DirectoryList //! A list of directories { inherit List; constant type="DirectoryList"; array verify_set( array(string) value ) { string warn = "";
f637572000-08-22Per Hedbor  foreach( value, string vi ) { if(!strlen(vi)) // empty continue; if( !(r_file_stat( vi ) && (r_file_stat( vi )[ ST_SIZE ] == -2 )))
55a8662000-11-20Per Hedbor  warn += sprintf(LOCALE(331,"%s is not a directory"),vi)+"\n";
f637572000-08-22Per Hedbor  if( strlen(vi) && vi[-1] != '/' ) value = replace( value, vi, vi+"/" );
843ccc2000-08-25Per Hedbor  }
be36312000-08-23Per Hedbor #ifdef __NT__
843ccc2000-08-25Per Hedbor  value = map( value, replace, "\\", "/" );
be36312000-08-23Per Hedbor #endif
d1de942000-08-19Martin Nilsson  if( strlen( warn ) ) return ({ warn, value });
f637572000-08-22Per Hedbor 
d1de942000-08-19Martin Nilsson  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";
c85b8f2001-06-14Johan Schön  int width=20;
d1de942000-08-19Martin Nilsson  string transform_to_form(int what) { return (string)what; }
d21c1f2001-02-02Per Hedbor  int transform_from_form(string what,mapping v) { return (int)what; }
d1de942000-08-19Martin Nilsson } class FloatList //! A list of floating point numbers { inherit List;
dd03b22006-04-20Henrik Grubbström (Grubba)  constant type="FloatList";
c85b8f2001-06-14Johan Schön  int width=20;
d1de942000-08-19Martin Nilsson 
fc40392008-08-15Martin Stjernholm  protected int _prec = 3;
d1de942000-08-19Martin Nilsson  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); }
d21c1f2001-02-02Per Hedbor  float transform_from_form(string what,mapping v) { return (float)what; }
d1de942000-08-19Martin Nilsson } class URLList //! A list of URLs { inherit List;
a968342000-08-25Martin Nilsson  constant type="URLList";
d1de942000-08-19Martin Nilsson 
b8fd5c2000-09-28Per Hedbor  array verify_set_from_form( array(string) new_value )
d1de942000-08-19Martin Nilsson  { string warn = ""; array res = ({}); foreach( new_value, string vv ) { string tmp1, tmp2;
bc0dec2001-08-05Martin Nilsson  [tmp1,tmp2] = verify_port( vv );
d1de942000-08-19Martin Nilsson  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";
d21c1f2001-02-02Per Hedbor  string render_row( string prefix, mixed val, int width ) { string res = "<input type=hidden name='"+prefix+"' value='"+prefix+"' />";
b98c022001-07-31Per Hedbor  Standards.URI split = Standards.URI( val );
d21c1f2001-02-02Per Hedbor  res += "<select name='"+prefix+"prot'>";
71e8722005-02-11Martin Stjernholm  int default_port;
d21c1f2001-02-02Per Hedbor  foreach( sort(indices( roxenp()->protocols )), string p ) {
71e8722005-02-11Martin Stjernholm  if( p == split->scheme ) {
d21c1f2001-02-02Per Hedbor  res += "<option selected='t'>"+p+"</option>";
71e8722005-02-11Martin Stjernholm  default_port = roxenp()->protocols[p]->default_port; }
d21c1f2001-02-02Per Hedbor  else res += "<option>"+p+"</option>"; } res += "</select>";
a497e32001-11-05Ambrose Li  res += "://<input type=text name='"+prefix+"host' value='"+
b98c022001-07-31Per Hedbor  Roxen.html_encode_string(split->host)+"' />";
80cd682003-11-17Anders Johansson  res += ":<input type=text size=5 name='"+prefix+"port' value='"+
71e8722005-02-11Martin Stjernholm  (split->port == default_port ? "" : split->port) +"' />";
d21c1f2001-02-02Per Hedbor 
a497e32001-11-05Ambrose Li  res += "/<input type=text name='"+prefix+"path' value='"+
21182a2001-10-05Per Hedbor  Roxen.html_encode_string(split->path[1..])+"' /><br />"; mapping opts = ([]); string a,b; foreach( (split->fragment||"")/";", string x ) { sscanf( x, "%s=%s", a, b ); opts[a]=b; }
a497e32001-11-05Ambrose Li  res += "IP#: <input size=15 type=text name='"+prefix+"ip' value='"+
21182a2001-10-05Per Hedbor  Roxen.html_encode_string(opts->ip||"")+"' /> ";
9c3df92001-11-21Henrik Grubbström (Grubba)  res += LOCALE(510,"Bind this port: ");
63ef1c2002-04-19Anders Johansson  res += "<select name='"+prefix+"nobind'>";
21182a2001-10-05Per Hedbor  if( (int)opts->nobind ) { res +=
63ef1c2002-04-19Anders Johansson  ("<option value='0'>"+LOCALE("yes","Yes")+"</option>" "<option selected='t' value='1'>"+LOCALE("no","No")+"</option>");
21182a2001-10-05Per Hedbor  } else { res +=
63ef1c2002-04-19Anders Johansson  ("<option selected='t' value='0'>"+LOCALE("yes","Yes")+"</option>" "<option value='1'>"+LOCALE("no","No")+"</option>");
21182a2001-10-05Per Hedbor  } res += "</select>";
d21c1f2001-02-02Per Hedbor  return res; } string transform_from_form( string v, mapping va ) { if( v == "" ) return "http://*/"; v = v[strlen(path())..];
21182a2001-10-05Per Hedbor  if( strlen( va[v+"path"] ) && va[v+"path"][-1] != '/' ) va[v+"path"]+="/";
77bdaf2008-12-11Jonas Wallden  // Handle IPv6 addresses string host = va[v + "host"]; if (has_value(host, ":") && !has_prefix(host, "[")) host = "[" + host + "]";
21182a2001-10-05Per Hedbor 
77bdaf2008-12-11Jonas Wallden  return (string)Standards.URI(va[v+"prot"]+"://"+ host +
71e8722005-02-11Martin Stjernholm  (va[v+"port"] && sizeof (va[v+"port"]) ? ":"+ va[v+"port"] : "") +"/"+va[v+"path"]+"#"
21182a2001-10-05Per Hedbor  // all options below this point "ip="+va[v+"ip"]+";" "nobind="+va[v+"nobind"]+";" );
d21c1f2001-02-02Per Hedbor  }
b8fd5c2000-09-28Per Hedbor  array verify_set_from_form( array(string) new_value )
d1de942000-08-19Martin Nilsson  { string warn = ""; array res = ({}); foreach( new_value, string vv ) { string tmp1, tmp2;
bc0dec2001-08-05Martin Nilsson  [tmp1,tmp2] = verify_port( vv );
d1de942000-08-19Martin Nilsson  if( tmp1 ) warn += tmp1; res += ({ tmp2 }); } if( !strlen( warn ) ) warn = ""; return ({ warn, res }); } } class FileList //! A list of filenames. { inherit List; constant type="FileList";
be36312000-08-23Per Hedbor  #ifdef __NT__
c6fe1f2003-02-17Tomas Nilsson  array(string|array(string)) verify_set(mixed value )
be36312000-08-23Per Hedbor  {
c6fe1f2003-02-17Tomas Nilsson  // Backward compatibility junk... if (stringp(value)) return ::verify_set( replace(value, "\\", "/") );
be36312000-08-23Per Hedbor  return ::verify_set( map( value, replace, "\\", "/" ) ); } #endif
d1de942000-08-19Martin Nilsson } // ===================================================================== // Flag // ===================================================================== class Flag //! A on/off toggle. { inherit Variable; constant type = "Flag"; int transform_from_form( string what ) { return (int)what; }
a968342000-08-25Martin Nilsson  string render_form( RequestID id, void|mapping additional_args )
d1de942000-08-19Martin Nilsson  { 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 // =================================================================
fc40392008-08-15Martin Stjernholm protected array(string) verify_port( string port )
d1de942000-08-19Martin Nilsson { if(!strlen(port)) return ({ 0, port });
9f97972000-11-20Per Hedbor 
d1de942000-08-19Martin Nilsson  string warning=""; if( (int)port ) {
55a8662000-11-20Per Hedbor  warning += sprintf(LOCALE(333,"Assuming http://*:%[0]d/ for %[0]d")+"\n",
9f97972000-11-20Per Hedbor  (int)port);
d1de942000-08-19Martin Nilsson  port = "http://*:"+port+"/"; } string protocol, host, path; if(!strlen( port ) )
55a8662000-11-20Per Hedbor  return ({ LOCALE(334,"Empty URL field")+"\n", port });
d1de942000-08-19Martin Nilsson  if(sscanf( port, "%[^:]://%[^/]%s", protocol, host, path ) != 3)
55a8662000-11-20Per Hedbor  return ({ sprintf(LOCALE(335,"%s does not conform to URL syntax")+"\n",port),
9f97972000-11-20Per Hedbor  port });
d1de942000-08-19Martin Nilsson 
21182a2001-10-05Per Hedbor // if( path == "" || path[-1] != '/' ) // { // warning += sprintf(LOCALE(336,"Added / to the end of %s")+"\n",port); // path += "/"; // }
d1de942000-08-19Martin Nilsson  if( protocol != lower_case( protocol ) ) {
55a8662000-11-20Per Hedbor  warning += sprintf(LOCALE(338,"Changed %s to %s"),
9f97972000-11-20Per Hedbor  protocol, lower_case( protocol ))+"\n";
edbf2d2000-09-19Per Hedbor  protocol = lower_case( protocol ); }
c50bf62000-09-28Per Hedbor #if constant(SSL.sslfile)
bc0dec2001-08-05Martin Nilsson  // All is A-OK
edbf2d2000-09-19Per Hedbor #else
bc0dec2001-08-05Martin Nilsson  if( (protocol == "https" || protocol == "ftps") ) warning += LOCALE(339,"SSL support not available in this Pike version.")+"\n"+ sprintf(LOCALE(340,"Please use %s instead."), protocol[..strlen(protocol)-2])+"\n";
edbf2d2000-09-19Per Hedbor #endif
9f23122000-09-21Per Hedbor  int pno;
77bdaf2008-12-11Jonas Wallden  int default_pno = (roxenp()->protocols[lower_case(protocol)] || ([ ]) )->default_port; if (has_value(host, "[")) { // IPv6 address Standards.URI uri = Standards.URI(port); pno = uri->port; port = (string) uri; } else { if (sscanf(host, "%s:%d", host, pno) == 2) if (pno != default_pno) host = host + ":" + pno; port = protocol+"://"+host+path; } if (default_pno && (pno == default_pno)) warning += sprintf(LOCALE(341, "Removed the default port number " "(%d) from %s"), pno, port) + "\n";
edbf2d2000-09-19Per Hedbor  if( !roxenp()->protocols[ protocol ] )
55a8662000-11-20Per Hedbor  warning += sprintf(LOCALE(342,"Warning: The protocol %s is not known "
9f97972000-11-20Per Hedbor  "by roxen"),protocol)+"\n";
d1de942000-08-19Martin Nilsson  return ({ (strlen(warning)?warning:0), port }); }
a968342000-08-25Martin Nilsson 
ae70232000-12-02Martin Nilsson string input(string name, string value, int size, void|mapping(string:string) args, void|int noxml)
a968342000-08-25Martin Nilsson { if(!args) args=([]); else args+=([]); args->name=name;
ae70232000-12-02Martin Nilsson  if(value) args->value=value; if(!args->size && size)
f4cf462000-11-20Martin Nilsson  args->size=(string)size;
a968342000-08-25Martin Nilsson  string render="<input"; foreach(indices(args), string attr) { render+=" "+attr+"="; if(!has_value(args[attr], "\"")) render+="\""+args[attr]+"\""; else if(!has_value(args[attr], "'")) render+="'"+args[attr]+"'";
03f1692002-09-11Martin Stjernholm  else render+="'"+replace(args[attr], "'", "&#39;")+"'";
a968342000-08-25Martin Nilsson  } if(noxml) return render+">"; return render+" />"; }