summaryrefslogtreecommitdiff
path: root/FS/FS/part_virtual_field.pm
diff options
context:
space:
mode:
authorlevinse <levinse>2011-07-22 18:59:27 +0000
committerlevinse <levinse>2011-07-22 18:59:27 +0000
commitc405e80203f323a83b447d6fc899dbba32d52f2a (patch)
treeaa7cc7ad31c43eb012ad3d9b787ee0a51bb6b4ac /FS/FS/part_virtual_field.pm
parent99e8e2006117bd4b97ebb1daf897cc257265dc3f (diff)
custom fields, RT11714
Diffstat (limited to 'FS/FS/part_virtual_field.pm')
-rwxr-xr-xFS/FS/part_virtual_field.pm224
1 files changed, 52 insertions, 172 deletions
diff --git a/FS/FS/part_virtual_field.pm b/FS/FS/part_virtual_field.pm
index f5a4161..4e6d2e4 100755
--- a/FS/FS/part_virtual_field.pm
+++ b/FS/FS/part_virtual_field.pm
@@ -29,11 +29,9 @@ FS::part_virtual_field - Object methods for part_virtual_field records
=head1 DESCRIPTION
-An FS::part_virtual_field object represents the definition of a virtual field
+An FS::part_virtual_field object represents the definition of a custom field
(see the BACKGROUND section). FS::part_virtual_field contains the name and
-base table of the field, as well as validation rules and UI hints about the
-display of the field. The actual data is stored in FS::virtual_field; see
-its manpage for details.
+base table of the field.
FS::part_virtual_field inherits from FS::Record. The following fields are
currently supported:
@@ -46,75 +44,12 @@ currently supported:
=item dbtable - table for which this virtual field is defined
-=item check_block - Perl code to validate/normalize data
-
-=item list_source - Perl code to generate a list of values (UI hint)
-
=item length - expected length of the value (UI hint)
=item label - descriptive label for the field (UI hint)
-=item sequence - sort key (UI hint; unimplemented)
-
=back
-=head1 BACKGROUND
-
-"Form is none other than emptiness,
- and emptiness is none other than form."
--- Heart Sutra
-
-The virtual field mechanism allows site admins to make trivial changes to
-the Freeside database schema without modifying the code. Specifically, the
-user can add custom-defined 'fields' to the set of data tracked by Freeside
-about objects such as customers and services. These fields are not associated
-with any logic in the core Freeside system, but may be referenced in peripheral
-code such as exports, price calculations, or alternate interfaces, or may just
-be stored in the database for future reference.
-
-This system was originally devised for svc_broadband, which (by necessity)
-comprises such a wide range of access technologies that no static set of fields
-could contain all the information needed by the exports. In an appalling
-display of False Laziness, a parallel mechanism was implemented for the
-router table, to store properties such as passwords to configure routers.
-
-The original system treated svc_broadband custom fields (sb_fields) as records
-in a completely separate table. Any code that accessed or manipulated these
-fields had to be aware that they were I<not> fields in svc_broadband, but
-records in sb_field. For example, code that inserted a svc_broadband with
-several custom fields had to create an FS::svc_broadband object, call its
-insert() method, and then create several FS::sb_field objects and call I<their>
-insert() methods.
-
-This created a problem for exports. The insert method on any FS::svc_Common
-object (including svc_broadband) automatically triggers exports after the
-record has been inserted. However, at this point, the sb_fields had not yet
-been inserted, so the export could not rely on their presence, which was the
-original purpose of sb_fields.
-
-Hence the new system. Virtual fields are appended to the field list of every
-record at the FS::Record level, whether the object is created ex nihilo with
-new() or fetched with qsearch(). The fields() method now returns a list of
-both real and virtual fields. The insert(), replace(), and delete() methods
-now update both the base table and the virtual fields, in a single transaction.
-
-A new method is provided, virtual_fields(), which gives only the virtual
-fields. UI code that dynamically generates form widgets to edit virtual field
-data should use this to figure out what fields are defined. (See below.)
-
-Subclasses may override virtual_fields() to restrict the set of virtual
-fields available. Some discipline and sanity on the part of the programmer
-are required; in particular, this function should probably not depend on any
-fields in the record other than the primary key, since the others may change
-after the object is instantiated. (Making it depend on I<virtual> fields is
-just asking for pain.) One use of this is seen in FS::svc_Common; another
-possibility is field-level access control based on FS::UID::getotaker().
-
-As a trivial case, a subclass may opt out of supporting virtual fields with
-the following code:
-
-sub virtual_fields { () }
-
=head1 METHODS
=over 4
@@ -128,87 +63,13 @@ Create a new record. To add the record to the database, see "insert".
sub table { 'part_virtual_field'; }
sub virtual_fields { () }
-=item insert
-
-Adds this record to the database. If there is an error, returns the error,
-otherwise returns false.
-
-=item delete
-
-Deletes this record from the database. If there is an error, returns the
-error, otherwise returns false.
-
-=item replace OLD_RECORD
-
-Replaces OLD_RECORD with this one in the database. If there is an error,
-returns the error, otherwise returns false.
-
-=item check
-
-If there is an error, returns the error, otherwise returns false.
-Called by the insert and replace methods.
-
-=back
-
-=cut
-
-sub check {
- my $self = shift;
-
- my $error = $self->ut_text('name') ||
- $self->ut_text('dbtable') ||
- $self->ut_number('length')
- ;
- return $error if $error;
-
- # Make sure it's a real table with a numeric primary key
- my ($table, $pkey);
- if($table = dbdef->table($self->dbtable)) {
- if($pkey = $table->primary_key) {
- if($table->column($pkey)->type =~ /int/i) {
- # this is what it should be
- } else {
- $error = "$table.$pkey is not an integer";
- }
- } else {
- $error = "$table does not have a single-field primary key";
- }
- } else {
- $error = "$table does not exist in the schema";
- }
- return $error if $error;
-
- # Possibly some sanity checks for check_block and list_source?
-
- $self->SUPER::check;
-}
-
-=item list
-
-Evaluates list_source.
-
-=cut
-
-sub list {
- my $self = shift;
- return () unless $self->list_source;
-
- my @opts = eval($self->list_source);
- if($@) {
- warn $@;
- return ();
- } else {
- return @opts;
- }
-}
-
=item widget UI_TYPE MODE [ VALUE ]
Generates UI code for a widget suitable for editing/viewing the field, based on
list_source and length.
-The only UI_TYPE currently supported is 'HTML', and the only MODE is 'view'.
-Others will be added later.
+The only UI_TYPE currently supported is 'HTML', and possible MODEs are 'view'
+and 'edit'.
In HTML, all widgets are assumed to be table rows. View widgets look like
<TR><TD ALIGN="right">Label</TD><TD BGCOLOR="#ffffff">Value</TD></TR>
@@ -235,22 +96,12 @@ sub widget {
} elsif ($mode eq 'edit') {
$text = q!<TR><TD ALIGN="right">! . $label .
q!</TD><TD>!;
- if ($self->list_source) {
- $text .= q!<SELECT NAME="! . $self->name .
- q!" SIZE=1>! . "\n";
- foreach ($self->list) {
- $text .= q!<OPTION VALUE="! . $_ . q!"!;
- $text .= ' SELECTED' if ($_ eq $value);
- $text .= '>' . $_ . '</OPTION>' . "\n";
- }
- } else {
$text .= q!<INPUT NAME="! . $self->name .
q!" VALUE="! . escapeHTML($value) . q!"!;
if ($self->length) {
$text .= q! SIZE="! . $self->length . q!"!;
}
$text .= '>';
- }
$text .= q!</TD></TR>! . "\n";
} else {
return '';
@@ -261,38 +112,67 @@ sub widget {
return $text;
}
-=head1 NOTES
-=head2 Semantics of check_block:
+=item insert
-This has been changed from the sb_field implementation to make check_blocks
-simpler and more natural to Perl programmers who work on things other than
-Freeside.
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
-The check_block is eval'd with the (proposed) new value of the field in $_,
-and the object to be updated in $self. Its return value is ignored. The
-check_block may change the value of $_ to override the proposed value, or
-call die() (with an appropriate error message) to reject the update entirely;
-the error string will be returned as the output of the check() method.
+=item delete
-This makes check_blocks like
+Deletes this record from the database. If there is an error, returns the
+error, otherwise returns false.
-C<s/foo/bar/>
+=item replace OLD_RECORD
-do what you expect.
+Replaces OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
-The check_block is expected NOT to do anything freaky to $self, like modifying
-other fields or calling $self->check(). You have been warned.
+=item check
-(FIXME: Rewrite some of the warnings from part_sb_field and insert here.)
+If there is an error, returns the error, otherwise returns false.
+Called by the insert and replace methods.
-=head1 BUGS
+=back
-None. It's absolutely falwless.
+=cut
+
+sub check {
+ my $self = shift;
+
+ my $error = $self->ut_text('name') ||
+ $self->ut_text('dbtable') ||
+ $self->ut_number('length')
+ ;
+ return $error if $error;
+
+ # Make sure it's a real table with a numeric primary key
+ my ($table, $pkey);
+ if($table = dbdef->table($self->dbtable)) {
+ if($pkey = $table->primary_key) {
+ if($table->column($pkey)->type =~ /int/i) {
+ # this is what it should be
+ } else {
+ $error = "$table.$pkey is not an integer";
+ }
+ } else {
+ $error = "$table does not have a single-field primary key";
+ }
+ } else {
+ $error = "$table does not exist in the schema";
+ }
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=head1 NOTES
+
+=head1 BUGS
=head1 SEE ALSO
-L<FS::Record>, L<FS::virtual_field>
+L<FS::Record>
=cut