diff --git a/docs/changelog/6.x.x.txt b/docs/changelog/6.x.x.txt index 5b15fdbeb..274e60128 100644 --- a/docs/changelog/6.x.x.txt +++ b/docs/changelog/6.x.x.txt @@ -105,7 +105,7 @@ - fix [ 1344665 ] Cache does not remember mimetype - fix [ 1172613 ] Header Tag Not Accessibility Friendly - fix [ 1340839 ] If can't use item in adminConsole don't display it - + - Added SQL Form and error feedback for asset addition (Martin Kamerbeek/Procolix) 6.8.8 - fix [ 1452466 ] File size not set in File asset (Thanks to Eric S) diff --git a/docs/upgrades/templates-6.99.0/sqlform_edit.tmpl b/docs/upgrades/templates-6.99.0/sqlform_edit.tmpl new file mode 100644 index 000000000..72a1fb417 --- /dev/null +++ b/docs/upgrades/templates-6.99.0/sqlform_edit.tmpl @@ -0,0 +1,33 @@ +#SQLFormEditTmpl0000001 +#create +#title:Default SQLForm Edit template +#menuTitle:Default SQLForm Edit template +#url:default_sqlform_edit_template +#namespace:SQLForm/Edit + + +

+
+ +
+ +

+
+ + +

+
+ + +

Some errors occurred

+ +
+ + + + +
diff --git a/docs/upgrades/templates-6.99.0/sqlform_search.tmpl b/docs/upgrades/templates-6.99.0/sqlform_search.tmpl new file mode 100644 index 000000000..c6b363aa7 --- /dev/null +++ b/docs/upgrades/templates-6.99.0/sqlform_search.tmpl @@ -0,0 +1,85 @@ +#SQLFormSearchTmpl00001 +#create +#title:Default SQLForm Search template +#menuTitle:Default SQLForm Search template +#url:default_sqlform_search_template +#namespace:SQLForm/Search + + +

+
+ +
+ + +

+
+ + + +

Search records

+
+ + +Some error(s) occurred: +
    + +
  • +
    +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Last updateLast update bycolor="red"> + + + (asc)(desc)
+ + + + + + + Click here for file + + + + + + +
+ + + +No fields are defined to be shown in the search results. +
+ +
diff --git a/docs/upgrades/upgrade_6.8.8-6.99.0.pl b/docs/upgrades/upgrade_6.8.8-6.99.0.pl index 3cd64cc52..62cea98ad 100644 --- a/docs/upgrades/upgrade_6.8.8-6.99.0.pl +++ b/docs/upgrades/upgrade_6.8.8-6.99.0.pl @@ -17,6 +17,7 @@ use File::Path; use WebGUI::Workflow; use WebGUI::Workflow::Cron; use WebGUI::Group; +use WebGUI::Utility; use WebGUI::Storage; my $toVersion = "6.99.0"; # make this match what version you're going to @@ -48,6 +49,7 @@ updateMatrix(); updateFolder(); addRichEditUpload(); updateArticle(); +installSQLForm(); finish($session); # this line required @@ -927,6 +929,82 @@ EOT $asset->addRevision({template=>$template}); } +sub installSQLForm { + print "\tInstalling SQLForm tables.\n" unless ($quiet); + + # Skip table creation if the SQLForm is already installed. + return "" if WebGUI::Utility::isIn('SQLForm', $session->db->buildArray("show tables")); + + # Create tables; + $session->db->write(<db->write(<db->write(<db->write(<db->write(<db->write("insert into SQLForm_regexes (regexId, name, regex) values ('defaultText', 'Text', '^[\\\\w\\\\d\\\\s]*\$')"); + $session->db->write("insert into SQLForm_regexes (regexId, name, regex) values ('defaultUnsigned', 'Unsigned integer', '^\\\\d+\$')"); + $session->db->write("insert into SQLForm_regexes (regexId, name, regex) values ('defaultSigned', 'Signed integer', '^-?\\\\d+\$')"); + $session->db->write("insert into SQLForm_regexes (regexId, name, regex) values ('defaultFloat', 'Floating point number', '^-?\\\\d+(\\.\\\\d+)?\$')"); +} + + + # ---- DO NOT EDIT BELOW THIS LINE ---- #------------------------------------------------- diff --git a/etc/WebGUI.conf.original b/etc/WebGUI.conf.original index 98f0c7998..49672ea08 100644 --- a/etc/WebGUI.conf.original +++ b/etc/WebGUI.conf.original @@ -181,7 +181,8 @@ "WebGUI::Asset::Wobject::SyndicatedContent", "WebGUI::Asset::Wobject::InOutBoard", "WebGUI::Asset::File::ZipArchive", - "WebGUI::Asset::Wobject::WSClient" + "WebGUI::Asset::Wobject::WSClient", + "WebGUI::Asset::Wobject::SQLForm" ], # Specify the list assets that are used for utility purposes only diff --git a/lib/WebGUI/Asset.pm b/lib/WebGUI/Asset.pm index c7fa019c9..f863ff2fc 100644 --- a/lib/WebGUI/Asset.pm +++ b/lib/WebGUI/Asset.pm @@ -542,6 +542,16 @@ sub getEditForm { }); } $tabform->addTab("properties",$i18n->get("properties")); + + # process errors + my $errors = $self->session->stow->get('editFormErrors'); +print "[$errors]"; + if ($errors) { + $tabform->getTab("properties")->readOnly( + -value=>"

Some error(s) occurred:

  • ".join('
  • ', @$errors).'

', + ) + } + $tabform->getTab("properties")->readOnly( -label=>$i18n->get("asset id"), -value=>$self->get("assetId"), @@ -1523,7 +1533,9 @@ sub prepareView { =head2 processPropertiesFromFormPost ( ) -Updates current Asset with data from Form. +Updates current Asset with data from Form. You can feed back errors by returning an +arrayref containing the error messages. If there is no error you do not have to return +anything. =cut @@ -1906,7 +1918,19 @@ sub www_editSave { return $self->getContainer->www_view; } } - $object->processPropertiesFromFormPost; + + my $error = $object->processPropertiesFromFormPost; + if (ref $error eq 'ARRAY') { + $self->session->stow->set('editFormErrors', $error); + if ($self->session->form->process('assetId') eq 'new') { + $object->purge; + return $self->www_add(); + } else { + $object->purgeRevision; + return $self->www_edit(); + } + } + $object->updateHistory("edited"); if ($self->session->form->process("proceed") eq "manageAssets") { $self->session->asset($object->getParent); @@ -1921,6 +1945,7 @@ sub www_editSave { $self->session->asset($object); return $self->session->asset->$method(); } + $self->session->asset($object->getContainer); return $self->session->asset->www_view; } diff --git a/lib/WebGUI/Asset/Wobject/SQLForm.pm b/lib/WebGUI/Asset/Wobject/SQLForm.pm new file mode 100644 index 000000000..247ae0dce --- /dev/null +++ b/lib/WebGUI/Asset/Wobject/SQLForm.pm @@ -0,0 +1,3844 @@ +package WebGUI::Asset::Wobject::SQLForm; + +=head1 LEGAL + + ------------------------------------------------------------------- + SQLForm is Copyright 2006 Procolix + ------------------------------------------------------------------- + Please read the legal notices (legal.txt) and the license + (license.txt) that came with this distribution before using + this software. + ------------------------------------------------------------------- + http://www.procolix.nl info@procolix.com + ------------------------------------------------------------------- + +=cut + +use strict; +use WebGUI::Asset; +use WebGUI::HTMLForm; +use WebGUI::Form; +use WebGUI::Asset::Wobject; +use WebGUI::Utility; +use WebGUI::DatabaseLink; +use WebGUI::International; +use Storable; +use Tie::IxHash; + +our @ISA = qw(WebGUI::Asset::Wobject); + +#------------------------------------------------------------------- +# This hash contains the allowed database field types. Keys indicate the MySQL name of the field type and +# the values are the the way they are showed in the form. +my ($allowedDbFieldTypes, $allowedFormFieldTypes); +tie %{$allowedDbFieldTypes}, 'Tie::IxHash'; +tie %{$allowedFormFieldTypes}, 'Tie::IxHash'; + + +=head1 Usage of reserved keywords + +It is not allowed to use columnnames that are in the list of reserved keywords. These +keywords are in the @reservedKeywords variable. + +The list with reserved keywords is MySQL specific and is generously copy-pasted from: + http://dev.mysql.com/doc/mysql/en/reserved-words.html + +=cut + +my @reservedKeywords = qw( + ACTION ADD AFTER + AGAINST AGGREGATE ALGORITHM + ALL ALTER ANALYZE + AND ANY AS + ASC ASCII ASENSITIVE + AUTO_INCREMENT AVG AVG_ROW_LENGTH + BACKUP BDB BEFORE + BEGIN BERKELEYDB BETWEEN + BIGINT BINARY BINLOG + BIT BLOB BOOL + BOOLEAN BOTH BTREE + BY BYTE CACHE + CALL CASCADE CASCADED + CASE CHAIN CHANGE + CHANGED CHAR CHARACTER + CHARSET CHECK CHECKSUM + CIPHER CLIENT CLOSE + COLLATE COLLATION COLUMN + COLUMNS COMMENT COMMIT + COMMITTED COMPACT COMPRESSED + CONCURRENT CONDITION CONNECTION + CONSISTENT CONSTRAINT CONTAINS + CONTINUE CONVERT CREATE + CROSS CUBE CURRENT_DATE + CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER + CURSOR DATA DATABASE + DATABASES DATE DATETIME + DAY DAY_HOUR DAY_MICROSECOND + DAY_MINUTE DAY_SECOND DEALLOCATE + DEC DECIMAL DECLARE + DEFAULT DEFINER DELAYED + DELAY_KEY_WRITE DELETE DESC + DESCRIBE DES_KEY_FILE DETERMINISTIC + DIRECTORY DISABLE DISCARD + DISTINCT DISTINCTROW DIV + DO DOUBLE DROP + DUAL DUMPFILE DUPLICATE + DYNAMIC EACH ELSE + ELSEIF ENABLE ENCLOSED + END ENGINE ENGINES + ENUM ERRORS ESCAPE + ESCAPED EVENTS EXECUTE + EXISTS EXIT EXPANSION + EXPLAIN EXTENDED FALSE + FAST FETCH FIELDS + FILE FIRST FIXED + FLOAT FLOAT4 FLOAT8 + FLUSH FOR FORCE + FOREIGN FOUND FRAC_SECOND + FROM FULL FULLTEXT + FUNCTION GEOMETRY GEOMETRYCOLLECTION + GET_FORMAT GLOBAL GOTO + GRANT GRANTS GROUP + HANDLER HASH HAVING + HELP HIGH_PRIORITY HOSTS + HOUR HOUR_MICROSECOND HOUR_MINUTE + HOUR_SECOND IDENTIFIED IF + IGNORE IMPORT IN + INDEX INDEXES INFILE + INNER INNOBASE INNODB + INOUT INSENSITIVE INSERT + INSERT_METHOD INT INT1 + INT2 INT3 INT4 + INT8 INTEGER INTERVAL + INTO INVOKER IO_THREAD + IS ISOLATION ISSUER + ITERATE JOIN KEY + KEYS KILL LABEL + LANGUAGE LAST LEADING + LEAVE LEAVES LEFT + LEVEL LIKE LIMIT + LINES LINESTRING LOAD + LOCAL LOCALTIME LOCALTIMESTAMP + LOCK LOCKS LOGS + LONG LONGBLOB LONGTEXT + LOOP LOW_PRIORITY MASTER + MASTER_CONNECT_RETRY MASTER_HOST MASTER_LOG_FILE + MASTER_LOG_POS MASTER_PASSWORD MASTER_PORT + MASTER_SERVER_ID MASTER_SSL MASTER_SSL_CA + MASTER_SSL_CAPATH MASTER_SSL_CERT MASTER_SSL_CIPHER + MASTER_SSL_KEY MASTER_USER MATCH + MAX_CONNECTIONS_PER_HOUR MAX_QUERIES_PER_HOUR MAX_ROWS + MAX_UPDATES_PER_HOUR MAX_USER_CONNECTIONS MEDIUM + MEDIUMBLOB MEDIUMINT MEDIUMTEXT + MERGE MICROSECOND MIDDLEINT + MIGRATE MINUTE MINUTE_MICROSECOND + MINUTE_SECOND MIN_ROWS MOD + MODE MODIFIES MODIFY + MONTH MULTILINESTRING MULTIPOINT + MULTIPOLYGON MUTEX NAME + NAMES NATIONAL NATURAL + NCHAR NDB NDBCLUSTER + NEW NEXT NO + NONE NOT NO_WRITE_TO_BINLOG + NULL NUMERIC NVARCHAR + OFFSET OLD_PASSWORD ON + ONE ONE_SHOT OPEN + OPTIMIZE OPTION OPTIONALLY + OR ORDER OUT + OUTER OUTFILE PACK_KEYS + PARTIAL PASSWORD PHASE + POINT POLYGON PRECISION + PREPARE PREV PRIMARY + PRIVILEGES PROCEDURE PROCESSLIST + PURGE QUARTER QUERY + QUICK RAID0 RAID_CHUNKS + RAID_CHUNKSIZE RAID_TYPE READ + READS REAL RECOVER + REDUNDANT REFERENCES REGEXP + RELAY_LOG_FILE RELAY_LOG_POS RELAY_THREAD + RELEASE RELOAD RENAME + REPAIR REPEAT REPEATABLE + REPLACE REPLICATION REQUIRE + RESET RESTORE RESTRICT + RESUME RETURN RETURNS + REVOKE RIGHT RLIKE + ROLLBACK ROLLUP ROUTINE + ROW ROWS ROW_FORMAT + RTREE SAVEPOINT SCHEMA + SCHEMAS SECOND SECOND_MICROSECOND + SECURITY SELECT SENSITIVE + SEPARATOR SERIAL SERIALIZABLE + SESSION SET SHARE + SHOW SHUTDOWN SIGNED + SIMPLE SLAVE SMALLINT + SNAPSHOT SOME SONAME + SOUNDS SPATIAL SPECIFIC + SQL SQLEXCEPTION SQLSTATE + SQLWARNING SQL_BIG_RESULT SQL_BUFFER_RESULT + SQL_CACHE SQL_CALC_FOUND_ROWS SQL_NO_CACHE + SQL_SMALL_RESULT SQL_THREAD SQL_TSI_DAY + SQL_TSI_FRAC_SECOND SQL_TSI_HOUR SQL_TSI_MINUTE + SQL_TSI_MONTH SQL_TSI_QUARTER SQL_TSI_SECOND + SQL_TSI_WEEK SQL_TSI_YEAR SSL + START STARTING STATUS + STOP STORAGE STRAIGHT_JOIN + STRING STRIPED SUBJECT + SUPER SUSPEND TABLE + TABLES TABLESPACE TEMPORARY + TEMPTABLE TERMINATED TEXT + THEN TIME TIMESTAMP + TIMESTAMPADD TIMESTAMPDIFF TINYBLOB + TINYINT TINYTEXT TO + TRAILING TRANSACTION TRIGGER + TRIGGERS TRUE TRUNCATE + TYPE TYPES UNCOMMITTED + UNDEFINED UNDO UNICODE + UNION UNIQUE UNKNOWN + UNLOCK UNSIGNED UNTIL + UPDATE USAGE USE + USER USER_RESOURCES USE_FRM + USING UTC_DATE UTC_TIME + UTC_TIMESTAMP VALUE VALUES + VARBINARY VARCHAR VARCHARACTER + VARIABLES VARYING VIEW + WARNINGS WEEK WHEN + WHERE WHILE WITH + WORK WRITE X509 + XA XOR YEAR + YEAR_MONTH ZEROFILL +); + +%{$allowedDbFieldTypes} = ( + # Integer column types + tinyint => { + name => 'tinyint', + maxLength => 255, + maxValue => 127, + minValue => -128, + maxValueUnsigned => 255, + hasSign => 1, + canAutoIncrement => 1, + defaultFormElement => 'integer', + defaultRegEx => 'defaultSigned', + }, + smallint => { + name => 'smallint', + maxLength => 255, + maxValue => 32_767, + minValue => -32_768, + maxValueUnsigned => 65_535, + hasSign => 1, + canAutoIncrement => 1, + defaultFormElement => 'integer', + defaultRegEx => 'defaultSigned', + }, + mediumint => { + name => 'mediumint', + maxLength => 255, + maxValue => 8_388_607, + minValue => -8_388_608, + maxValueUnsigned => 16_777_215, + hasSign => 1, + canAutoIncrement => 1, + defaultFormElement => 'integer', + defaultRegEx => 'defaultSigned', + }, + 'int' => { + name => 'int', + maxLength => 255, + maxValue => 2_147_483_647, + minValue => -2_147_483_648, + maxValueUnsigned => 4_294_967_295, + hasSign => 1, + canAutoIncrement => 1, + defaultFormElement => 'integer', + defaultRegEx => 'defaultSigned', + }, + bigint => { + name => 'bigint', + maxLength => 255, + maxValue => 9_223_372_036_854_775_807, + minValue => -9_223_372_036_854_775_808, + maxValueUnsigned => 18_446_744_073_709_551_615, + hasSign => 1, + canAutoIncrement => 1, + defaultFormElement => 'integer', + defaultRegEx => 'defaultSigned', + }, + + # String column types + char => { + name => 'char', + supportsFulltext => 1, + maxLength => 255, + defaultFormElement => 'text', + defaultRegEx => 'defaultText', + }, + varchar => { + name => 'varchar', + supportsFulltext => 1, + maxLength => 255, + defaultFormElement => 'text', + defaultRegEx => 'defaultText', + }, + tinyblob => { + name => 'tinyblob (tinytext)', + supportsFulltext => 1, + maxLength => 255, + defaultFormElement => 'textarea', + defaultRegEx => 'defaultText', + }, + blob => { + name => 'blob (text)', + supportsFulltext => 1, + maxLength => 65_535, + defaultFormElement => 'textarea', + defaultRegEx => 'defaultText', + }, + mediumblob => { + name => 'mediumblob (mediumtext)', + supportsFulltext => 1, + maxLength => 16_777_215, + defaultFormElement => 'file', + defaultRegEx => '', + }, + longblob => { + name => 'longblob (longtext)', + supportsFulltext => 1, + maxLength => 4_294_967_295, + defaultFormElement => 'file', + defaultRegEx => '', + }, + tinytext => { + name => 'tinytext (tinyblob)', + supportsFulltext => 1, + maxLength => 255, + defaultFormElement => 'textarea', + defaultRegEx => 'defaultText', + }, + text => { + name => 'text (blob)', + supportsFulltext => 1, + maxLength => 65_535, + defaultFormElement => 'textarea', + defaultRegEx => 'defaultText', + }, + mediumtext => { + name => 'mediumtext (mediumblob)', + supportsFulltext => 1, + maxLength => 16_777_215, + defaultFormElement => 'file', + defaultRegEx => '', + }, + longtext => { + name => 'longtext (longblob)', + supportsFulltext => 1, + maxLength => 4_294_967_295, + defaultFormElement => 'file', + defaultRegEx => '', + }, + + # Temporal column types + datetime => { + name => 'datetime', + defaultFormElement => 'dateTime', + }, + timestamp => { + name => 'timestamp', + readOnly => 1, + }, + date => { + name => 'date', + defaultFormElement => 'date', + }, + 'time' => { + name => 'time', + defaultFormElement => 'timeField', + }, + set => { + name => 'set', + supportsFulltext => 0, + maxLength => 65_535, + defaultFormElement => 'selectList', + multipleAllowed => 1, + }, +); + +=head1 Form element definitions + +The $allowedFormFieldTypes hashref contains the allowed WebGUI form elements. Method names are indicated +by the keys of the hash while the values are the screen labels in the form. + +If you want to add addional form elements, you must add them to this hash. The elements you define here +should be implemented in WebGUI::Form::myELement. + +=cut + +%{$allowedFormFieldTypes} = ( + text => { + name => 'Text', + widthParam => 'size', + maxLength => 255, + searchElement => 'text', + type => 'text', + }, + textarea => { + name => 'Text area', + widthParam => 'columns', + heightParam => 'rows', + searchElement => 'text', + type => 'text', + }, + HTMLArea => { + name => 'HTML area', + widthParam => 'columns', + heightParam => 'rows', + searchElement => 'text', + type => 'text', + }, + integer => { + name => 'Integer', + widthParam => 'size', + maxLength => 255, + searchElement => 'integer', + type => 'number', + }, + float => { + name => 'Float', + widthParam => 'size', + maxLength => 255, + searchElement => 'float', + type => 'number', + }, + selectList => { + name => 'Select list', + heightParam => 'size', + hasOptions => 1, + canHaveMultipleValues => 1, + searchElement => 'selectList', + type => 'list', + }, + radioList => { + name => 'Radio list', + hasOptions => 1, + searchElement => 'radioList', +# type => 'list', + }, + checkList => { + name => 'Check list', + hasOptions => 1, + canHaveMultipleValues => 1, + searchElement => 'checkList', + type => 'list', + }, + date => { + name => 'Date', + searchElement => 'date', + type => 'temporal', + }, + timeField => { + name => 'Time', + searchElement => 'timeField', + type => 'temporal', + }, + dateTime => { + name => 'Date/Time combo', + searchElement => 'dateTime', + type => 'temporal', + }, + email => { + name => 'Email address', + searchElement => 'text', + type => 'text', + }, + url => { + name => 'URL', + searchElement => 'text', + type => 'text', + }, + file => { + name => 'File', + }, +); + +# The two hasrefs below are used by the search system +my $types = { + text => {'' => 'Don\'t care', 1 => '=', 100 => 'like', 101 => 'regexp'}, + number => {'' => 'Don\'t care', 1 => '=', 2 => '<', 3 => '>', 4 => '<=', 5 => '>=', 6 => '!=', 10 => 'is between'}, + temporal => {'' => 'Don\'t care', 1 => '=', 2 => '<', 3 => '>', 4 => '<=', 5 => '>=', 6 => '!=', 10 => 'is between'}, + list => {'' => 'Don\'t care', 200 => 'match any', 201 => 'match all', 100 => 'like', 101 => 'regexp'}, +}; + +my $typeFunctions = { + text => 'switchTextField', + number => 'switchNumberField', + temporal => 'switchTemporalField', + list => 'switchListField', +}; + + +#------------------------------------------------------------------- +=head1 _canAlterTable + +Returns a boolean indicating whether the user is allowed to change (alter) the table structure. Ie. manage field etc. + +=cut + +sub _canAlterTable { + my $self = shift; + + return ($self->canEdit || $self->session->user->isInGroup($self->get('alterGroupId'))); +} + +#------------------------------------------------------------------- +=head1 _canEditRecord + +Returns a boolean indicating whether the user is allowed to edit, delete and restore records. + +=cut + +sub _canEditRecord { + my $self = shift; + + return ($self->canEdit || $self->session->user->isInGroup($self->get('submitGroupId'))); +} + +#------------------------------------------------------------------- +=head1 _canPurge + +Returns a boolean indictating whether the user is allowed to purge deleted records. + +=cut + +sub _canPurge { + my $self = shift; + + return $self->_canAlterTable; +} + +#------------------------------------------------------------------- +=head1 _constructColumnType ( fieldProperties ) + +Will construct a MySQL column definition string from the field properties passed as argument. + +=head2 fieldProperties + +A hashref containing the properties of the field for which this column definition is made. Properties +taken into account are: + + * dbFieldType, + * maxFieldLength, + * formPopulationKeys, + * signed + +=cut + +sub _constructColumnType { + my $self = shift; + my $processed = shift; + + # Construct the type specifier + my $type = $processed->{dbFieldType}; + if ($type eq 'varchar' && $allowedDbFieldTypes->{$type}->{maxLength}) { + $type .= '('.$processed->{maxFieldLength}.')' if ($processed->{maxFieldLength}); + } + + if ($type eq 'set') { + my $formPopulationKeys = $self->session->form->process("formPopulationKeys"); + $formPopulationKeys =~ s/\r?\n/\',\'/g; + $type .= '(\''.$formPopulationKeys.'\')'; + } + + if ($allowedDbFieldTypes->{$processed->{dbFieldType}}->{hasSign}) { + if ($processed->{signed}) { + # Explicitly add the signed flag, so we won't have to worry if mysql changes its defaults. + $type .= ' signed'; + } else { + $type .= ' unsigned'; + } + } + + return $type; +} + +#------------------------------------------------------------------- +=head1 _createFieldType ( dbFieldType, formFieldType ) + +Inserts a new field type into the SQLForm_fieldTypes table. + +=head2 dbFieldType + +The column type connected to this field type. + +=head2 formFieldType + +The form element to be used for this field type. + +=cut + +sub _createFieldType { + my $self = shift; + my $dbFieldType = shift; + my $formFieldType = shift; + + my $fieldTypeId = $self->session->id->generate; + + $self->session->db->write('insert into SQLForm_fieldTypes (fieldTypeId, dbFieldType, formFieldType) '. + ' values ('.$self->session->db->quote($fieldTypeId).', '.$self->session->db->quote($dbFieldType).', '.$self->session->db->quote($formFieldType).')'); + + return $fieldTypeId; +} + +#------------------------------------------------------------------- +=head1 _getDbLink + +Returns a WebGUI::DatabaseLink object for the database the SQLForm table is in. + +=cut + +sub _getDbLink { + my $self = shift; + + return WebGUI::DatabaseLink->new($self->session, $self->getValue('databaseLinkId')); +} + +#------------------------------------------------------------------- +=head1 _getFieldProperties ( fieldId ) + +Returns a hashref containing the properties of the field indicated by fieldId. + +=head2 fieldId + +The id of the field of which the properties should be returned. + +=cut + +sub _getFieldProperties { + my ($dbLink, $fieldId, %definition, $properties, @tables, @where, $query, $options, @keys, @values, $numberOfJoins); + my $self = shift; + $fieldId = shift; + + $dbLink = $self->_getDbLink; + %definition = $self->session->db->buildHash("select property, value from SQLForm_fieldDefinitions where fieldId = ".$self->session->db->quote($fieldId)); + + $properties = { %{$allowedFormFieldTypes->{$definition{formFieldType}}}, %{$allowedDbFieldTypes->{$definition{dbFieldType}}} }; + + #### This should be preprocessed in editFieldSave to increase performance #### + # Calculate the number of tables in the join + foreach (keys(%definition)) { + if (m/^table(\d+)$/) { + $numberOfJoins = $1 if ($1 > $numberOfJoins); + } + } + $definition{numberOfJoins} = $numberOfJoins; + + tie %$options, "Tie::IxHash"; + if (exists $definition{formPopulationKeys}) { + @keys = split(/[\r\n]+/, $definition{formPopulationKeys}); + @values = split(/[\r\n]+/, $definition{formPopulationValues}); + + while (my $key = shift(@keys)) { + $options->{$key} = shift(@values); + } + } + + + if ($definition{selectField1} && $definition{selectField2}) { +my $sth = $dbLink->db->unconditionalRead($definition{sqlQuery}." order by ".$definition{selectField2}); + while (my @row = $sth->array) { + $options->{$row[0]} = $row[1]; + } +# $options = $dbLink->db->buildHashRef($definition{sqlQuery}." order by ".$definition{selectField2}); + } + + if (exists $definition{sqlQueryAllOptions}) { + $properties->{allOptions} = $dbLink->db->buildHashRef($definition{sqlQueryAllOptions}); + } else { + $properties->{allOptions} = $options; + } + + $properties->{options} = $options; + $properties->{processedDefaultValue} = WebGUI::Macro::process($self->session, $definition{defaultValue}); + $properties->{fieldId} = $fieldId; + + return {%definition, %$properties}; +} + +#------------------------------------------------------------------- +=head1 _getDatabaseInfo + +Returns a hashref containing all tables and columns including column properties in the database in which the SQLForm +resides. + +=cut + +sub _getDatabaseInfo { + my (@tables, $tableName, $sth, $columnDefinition, $currentColumn, $databaseDefinition); + my $self = shift; + + my $dbLink = $self->_getDbLink; + @tables = $dbLink->db->buildArray("show tables"); + + foreach $tableName (@tables) { + $sth = $dbLink->db->read("describe ".$tableName); + + while ($columnDefinition = $sth->hashRef) { + + $currentColumn = { + name => $columnDefinition->{Field}, + type => $columnDefinition->{Type}, + canBeNull => $columnDefinition->{Null}, + defaultValue => $columnDefinition->{Default}, + # Might need these fields in the future + #extra => $columnDefinition->{Extra}, + #key => $columnDefinition->{Key}, + }; + $databaseDefinition->{$tableName}->{$currentColumn->{name}} = $currentColumn; + } + } + + $dbLink->disconnect; + return $databaseDefinition; +} + +#------------------------------------------------------------------- +=head1 _getManagementLinks + +Returns a string containg all of the management function the user is allowed to use. + +=cut + +sub _getManagementLinks { + my (@links, $i18n); + my $self = shift; + + $i18n = WebGUI::International->new($self->session,'Asset_SQLForm'); + + push(@links, ''.$i18n->get('add record title').'') if ($self->_canEditRecord); + push(@links, ''.$i18n->get('search records title').'') if ($self->canView); + push(@links, ''.$i18n->get('manage fields').'') if ($self->_canAlterTable); + push(@links, ''.$i18n->get('manage regexes').'') if ($self->_canAlterTable); + push(@links, ''.$i18n->get('manage field types').'') if ($self->_canAlterTable); + + return join('·',@links); +} + +#------------------------------------------------------------------- +=head1 _matchField ( string, regexId ) + +Excutes the regex identified by regexId on the string passed as first argument and return a boolean indicating +whether it is a match or not. Will return true if no regex id or a non-existing regex id is passed. + +=head2 string + +The string to be matched. + +=head2 regexId + +The id of the regex to used. + +=cut + +sub _matchField { + my $self = shift; + my $data = shift; + my $regexId = shift; + + return 1 unless ($regexId); + + my ($regex) = $self->session->db->quickArray('select regex from SQLForm_regexes where regexId='.$self->session->db->quote($regexId)); + + return 1 unless ($regex); + + if ($data =~ m/$regex/) { + return 1; + } else { + return 0; + } +} + +#------------------------------------------------------------------- +=head1 _resolveFieldConstraintType ( type ) { + +Translates the numerical value used in field constraint types to a perl operator. + +=head2 type + +The numerical id for the operator. If omitted this method will return a hasref containing +all numerical <-> operator mappings. + +=cut + +sub _resolveFieldConstraintType { + my $self = shift; + my $type = shift; + + my $i18n = WebGUI::International->new($self->session, 'Asset_SQLForm'); + + my $types = {'0' => $i18n->get('none'), 1 => '>', 2 => '>=', 3 => '<', 4 => '<=', 5 => '='}; + + return $types->{$type} if ($type); + return $types; +} + +#------------------------------------------------------------------- +=head1 definition + +The asset definition of the SQLForm. + +=cut + +sub definition { + my $class = shift; + my $session = shift; + my $definition = shift; + + push(@{$definition}, { + tableName=>'SQLForm', + className=>'WebGUI::Asset::Wobject::SQLForm', + icon=>'sqlform.gif', + properties=>{ + formId => { + fieldType => 'text', + defaultValue => undef, + }, + tableName => { + fieldType => 'text', + defaultValue => undef, + }, + maxFileSize => { + fieldType => 'integer', + defaultValue => 1_500_000, + }, + sendMailTo => { + fieldType => 'email', + defaultValue => undef, + }, + showMetaData => { + fieldType => 'yesNo', + defaultValue => 1, + }, + searchTemplateId=> { + fieldType => 'template', + defaultValue => 'SQLFormSearchTmpl00001', + }, + editTemplateId => { + fieldType => 'template', + defaultValue => 'SQLFormEditTmpl0000001', + }, + submitGroupId => { + fieldType => 'group', + defaultValue => undef, + }, + alterGroupId => { + fieldType => 'group', + defaultValue => undef, + }, + databaseLinkId => { + fieldType => 'databaseLink', + defaultValue => 0, + }, + } + }); + return $class->SUPER::definition($session, $definition); +} + +#------------------------------------------------------------------- +=head1 uiLevel + +The uiLevel of the SQLForm asset. It is a power tool so the uiLevel is set to 9. + +=cut + +sub uiLevel { + return 9; +} + +#------------------------------------------------------------------- +=head1 getAdminConsoleWithSubmenu + +Return the adminconsole but adds three submenu items for manage fields/field types/regexes. + +=cut + +sub getAdminConsoleWithSubmenu { + my $self = shift; + my $ac = $self->getAdminConsole; + + my $i18n = WebGUI::International->new($self->session,'Asset_SQLForm'); + + $ac->addSubmenuItem($self->getUrl('func=listFields'), $i18n->get('manage fields')); + $ac->addSubmenuItem($self->getUrl('func=listFieldTypes'), $i18n->get('manage field types')); + $ac->addSubmenuItem($self->getUrl('func=listRegexes'), $i18n->get('manage regexes')); + + return $ac; +} + +#------------------------------------------------------------------- +=head1 getEditForm + +Creates the edit form of the SQLForm asset. + +=cut + +sub getEditForm { + my ($availableDbLinks, $i18n); + my $self = shift; + + $i18n = WebGUI::International->new($self->session,'Asset_SQLForm'); + + $availableDbLinks = WebGUI::DatabaseLink->getList($self->session); + delete($availableDbLinks->{'0'}); + + my $tabform = $self->SUPER::getEditForm; + + unless (keys(%$availableDbLinks)) { + $tabform->getTab('properties')->readOnly( + -value => + '', + ); + + return $tabform; + } + + $tabform->submit({ + id => 'zeSubmitButton', + }); + + $tabform->getTab('properties')->text( + -name => 'tableName', + -label => $i18n->get('gef table name'), + -hoverHelp => $i18n->get('gef table name description'), + -value => $self->get('tableName'), + -extras => 'onkeyup="e = document.getElementById(\'zeSubmitButton\'); ' + .' if (this.value != \'\' ) { e.disabled = false} else {e.disabled = true};"', + ); + $tabform->getTab('properties')->checkbox( + -name => 'importTable', + -label => $i18n->get('gef import table'), + -hoverHelp => $i18n->get('gef import table description'), + -value => [1], + -checked => $self->session->form->process('importTable'), + ); + $tabform->getTab('properties')->selectList( + -name => 'databaseLinkId', + -label => $i18n->get('gef database to use'), + -hoverHelp => $i18n->get('gef database to use description'), + -options => $availableDbLinks, + -value => [$self->get('databaseLinkId') || (keys(%$availableDbLinks))[0]], + -size => 1, + -multiple => 0, + ); + $tabform->getTab('properties')->integer( + -name => 'maxFileSize', + -label => $i18n->get('gef max file size'), + -hoverHelp => $i18n->get('gef max file size description'), + -value => $self->getValue('maxFileSize'), + ); + $tabform->getTab('properties')->text( + -name => 'sendMailTo', + -label => $i18n->get('gef send mail to'), + -hoverHelp => $i18n->get('gef send mail to description'), + -value => $self->getValue('sendMailTo'), + ); + $tabform->getTab('properties')->yesNo( + -name => 'showMetaData', + -label => $i18n->get('gef show meta data'), + -hoverHelp => $i18n->get('gef show meta data description'), + -value => $self->getValue('showMetaData'), + ); + $tabform->getTab('display')->template( + -name => 'editTemplateId', + -label => $i18n->get('gef edit template'), + -hoverHelp => $i18n->get('gef edit template description'), + -value => $self->get('editTemplateId'), + -namespace => 'SQLForm/Edit', + ); + $tabform->getTab('display')->template( + -name => 'searchTemplateId', + -label => $i18n->get('gef search template'), + -hoverHelp => $i18n->get('gef search template description'), + -value => $self->get('searchTemplateId'), + -namespace => 'SQLForm/Search', + ); + $tabform->getTab('security')->group( + -name => 'submitGroupId', + -label => $i18n->get('gef submit group'), + -hoverHelp => $i18n->get('gef submit group description'), + -value => [ $self->get('submitGroupId') ], + ); + + $tabform->getTab('properties')->readOnly( + -value => '', + ) unless ($self->get('tableName')); + + return $tabform; +} + +#------------------------------------------------------------------- +=head1 getIndexerParams + +Should index the data in the table of this SQLForm. Not functional in 6.8.x due to a crippled +search framework. + +=cut + +sub getIndexerParams { + my $self = shift; + my $now = shift; + my $sth = $self->session->db->read("select t1.url, t2.* from asset as t1, SQLForm as t2 where t1.assetId = t2.assetId"); + + my $result = {}; + while (my %row = $sth->hash) { + my $tableName = $row{tableName}; + my $assetId = $row{assetId}; + + if ($row{databaseLinkId}) { + my $dbName; + my %dbInfo = WebGUI::DatabaseLink::get($row{databaseLinkId}); + ($dbName = $dbInfo{DSN}) =~ s/DBI\:\w+\:(\w+)/$1/i; + $tableName = $dbName.'.'.$tableName; + } + my @indexFields = $self->session->db->buildArray("select t2.value from SQLForm_fieldDefinitions as t1, SQLForm_fieldDefinitions as t2 where ". + " t1.fieldId=t2.fieldId and t1.property='useFulltext' and t1.value=1 and t2.property='fieldName' and t1.assetId=".$self->session->db->quote($assetId)); + + my $concatFields = 'concat('. join(",' | ',",@indexFields).')'; + $result->{'SQLForm_'.$tableName} = { + sql => "select + * + from + $tableName + where + __archived = 0 and + __deleted = 0 + ", + fieldsToIndex => \@indexFields, + contentType => 'content', + url => 'my $url=\''.$row{url}.'\'; $self->session->url->gateway($url,\'func=editRecord;rid=\'.$data{__recordId})', + headerShortcut => 'select title from asset where assetId = \''.$assetId.'\'', + bodyShortcut => 'select '.$concatFields.' from '.$tableName.' where __recordId = \'$data{__recordId}\'' + }; + } + + return $result; +} + +#------------------------------------------------------------------- +=head1 getIcon + +Returns the icon of the SQLForm asset. + +=cut + +sub getIcon { + my $self = shift; + my $small = shift; + return $self->session->config->get("extrasURL").'/assets/small/sqlform.gif' if ($small); + return $self->session->config->get("extrasURL").'/assets/sqlform.gif'; +} + +#------------------------------------------------------------------- +=head1 getName + +Return the internationalized name of the SQLForm. + +=cut + +sub getName { + my $self = shift; + my $i18n = WebGUI::International->new($self->session, 'Asset_SQLForm'); + return $i18n->get('assetName'); +} + +#------------------------------------------------------------------- +=head1 processPropertiesFromFormPost + +Processes the data in the edit form of the SQLForm asset. In WebGUI 6.8.x there's no way to feed back any errors on +asset addition. Therefore if something is wrong this method will use die to stop the processing. This will heave the +effect that an SQLForm asset is added to the asset tree, but it won't have any properties saved. You should delete +the 'empty' asset from your asset tree using the asset manager. + +This problem will be solved in 7.0.0. + +=cut + +sub processPropertiesFromFormPost { + my ($tableName, @tables, @usedTables); + my $self = shift; + + my $dbLink = WebGUI::DatabaseLink->new($self->session, $self->session->form->process("databaseLinkId")); + + $tableName = $self->session->form->process("tableName"); + + if ($self->session->form->process("assetId") eq 'new') { + #if table exists and not in SQLForm format, put in SQLFormFormat. + @tables = $dbLink->db->buildArray("show tables"); + + @usedTables = $self->session->db->buildArray("select tableName, state from SQLForm, asset where asset.assetID=SQLForm.assetId and state='published'"); + + if (isIn(lc($tableName), map {lc} @usedTables)) { + return ["The table is already used in an SQLForm."]; + } + elsif ($tableName !~ m/^[\w\d_]+$/i) { + return ["The table name is illegal."]; + } + elsif (isIn(lc($tableName), map {lc} @tables) && !$self->session->form->process('importTable')) { + return ["The table already exists in the database but the import flag has not been set."]; + } + elsif (isIn(lc($tableName), map {lc} @tables)) { #&& !(isIn(lc($tableName), map {lc} @usedTables))) { + # exisiting table + # Write column data to db ----------------------------------------------------------------------------- + my $controlDefined = 0; + my $sth = $dbLink->db->read("describe ".$tableName); + my $processed; + my @columnNames; + my $rank = 0; + while (my $columnDefinition = $sth->hashRef) { + if ($columnDefinition->{Field} =~ m/^__*/ ) { + $controlDefined = 1; + } else { + # clear properties hash + $processed = {}; + + my $type = $columnDefinition->{Type}; + my $set = $columnDefinition->{Type}; + my $length; + $set =~ s/^.*\(//; + $set =~ s/\)$//; + $set =~ s/,/\r\n/g; + $set =~ s/'//g; + $length = $set + 0; + $type =~ s/\(.*\)//; + + my $currentField = $allowedDbFieldTypes->{$type}; + + # Get the fieldTypeId of this column + my ($fieldType) = $self->session->db->quickArray("select fieldTypeId from SQLForm_fieldTypes " + ." where dbFieldType=".$self->session->db->quote($type)." and formFieldType=".$self->session->db->quote($currentField->{defaultFormElement})); + + # Create the field type if it doesn't exist + unless ($fieldType) { + $fieldType = $self->_createFieldType($type, $currentField->{defaultFormElement}); + } + + # Check for + if ($columnDefinition->{Extra} =~ /auto_increment/) { + $processed->{useAutoIncrement} = 1; + my $dropAutoIncrementSQL = "alter table $tableName change column ".$columnDefinition->{Field}. + " ".$columnDefinition->{Field}." ". $columnDefinition->{Type}; + $dropAutoIncrementSQL .= " NOT NULL " if ($columnDefinition->{Null} ne 'YES'); + $dropAutoIncrementSQL .= " default '".$columnDefinition->{Default}."' " if ($columnDefinition->{Default} && ($columnDefinition->{Default} ne 'NULL')); + + $dbLink->db->write($dropAutoIncrementSQL); + } + + $processed->{defaultValue} = $columnDefinition->{Default} if ($columnDefinition->{Default} ne 'NULL'); + $processed->{isRequired} = 1 if ($columnDefinition->{Null} ne 'YES'); + $processed->{formPopulationValues} = $set if ($type =~ m/^set/i); + $processed->{formPopulationKeys} = $set if ($type =~ m/^set/i); + $processed->{dbFieldType} = $type; + $processed->{formFieldType} = $currentField->{defaultFormElement}; + $processed->{fieldType} = $fieldType; + $processed->{maxFieldLength} = $currentField->{maxLength} if ($currentField->{maxLength}); + $processed->{regex} = $currentField->{defaultRegEx} if ($currentField->{defaultRegEx}); + $processed->{fieldName} = $columnDefinition->{Field}; + $processed->{displayName} = $columnDefinition->{Field}; + $processed->{signed} = '1'; + $processed->{showInSearchResults} = '1'; + $processed->{isSearchable} = '1'; + my $fieldId = $self->session->id->generate; + $self->session->db->write('delete from SQLForm_fieldDefinitions where fieldId='.$self->session->db->quote($fieldId)); + foreach (keys(%$processed)) { + $self->session->db->write('insert into SQLForm_fieldDefinitions (fieldId, assetId, property, value) values '. + '('.$self->session->db->quote($fieldId).','.$self->session->db->quote($self->get('assetId')).','.$self->session->db->quote($_).','.$self->session->db->quote($processed->{$_}).')'); + } + $self->session->db->write('insert into SQLForm_fieldOrder (fieldId, assetId, rank) values '. + '('.$self->session->db->quote($fieldId).','.$self->session->db->quote($self->get('assetId')).','.$self->session->db->quote($rank).')'); + $rank++; + + push (@columnNames, $columnDefinition->{Field}); + } + } + + # We can't allow primary keys in the table because of the versioning. + # A composite pk with __recordId and __revision would work but makes no sense because + # __recordId and __revision are always unique and hence the pk. +my %dropKeys; +my $hasPrimaryKey = 0; + my $sth = $dbLink->db->read("show keys from $tableName"); + while (my %row = $sth->hash) { + if ($row{Key_name} eq 'PRIMARY') { + $hasPrimaryKey = 1; + } else { + $dropKeys{$row{Key_name}} = 1; + } + } + + $dbLink->db->write("alter table $tableName drop primary key") if ($hasPrimaryKey); + + foreach (keys(%dropKeys)) { + $dbLink->db->write("alter table $tableName drop index $_ "); + } + + if ($controlDefined == 0){ + # add control fields + $dbLink->db->write("alter table $tableName ". + " add __recordId varchar(22) binary not null,". + " add __creationDate bigint(20),". + " add __createdBy varchar(22) binary,". + " add __initDate bigint(20),". + " add __userId varchar(22) binary,". + " add __deletionDate bigint(20),". + " add __deleted tinyint(1) default 0,". + " add __deletedBy varchar(22),". + " add __archived tinyint(1) default 0,". + " add __revision int(11) not null". + " "); + + # fill status fields + $sth = $dbLink->db->read("select * from $tableName"); + while (my %row = $sth->hash) { + my @where = (); + foreach (@columnNames) { + if (defined $row{$_}) { + push(@where, $_." = ".$self->session->db->quote($row{$_})); + } else { + push(@where, "$_ IS NULL"); + } + } + my $sql = + "update $tableName set ". + "__recordId = ".$self->session->db->quote($self->session->id->generate).", ". + "__creationDate = ".$self->session->db->quote(time).", ". + "__createdBy = ".$self->session->db->quote($self->session->user->userId).", ". + "__initDate = ".$self->session->db->quote(time).", ". + "__userId = ".$self->session->db->quote($self->session->user->userId).", ". + "__archived = 0, ". + "__revision = 1 ". + "where ".join(' and ', @where); + $dbLink->db->write($sql); + #print "$sql\n"; + } + } + + $dbLink->db->write("alter table $tableName add primary key (__recordId, __revision)"); + } + else { + #new table + $dbLink->db->write("create table $tableName (". + " __recordId varchar(22) binary not null,". + " __creationDate bigint(20) not null,". + " __createdBy varchar(22) not null,". + " __initDate bigint(20) not null,". + " __userId varchar(22) not null,". + " __deletionDate bigint(20),". + " __deleted tinyint(1) default 0,". + " __deletedBy varchar(22),". + " __archived tinyint(1) default 0,". + " __revision int(11) not null,". + " primary key (__recordId, __revision)". + ")"); + } + } + else { + if ($self->get('tableName') ne $tableName) { + $dbLink->db->write("rename table ".$self->get('tableName')." to $tableName"); + } + } + + $dbLink->disconnect; + return $self->SUPER::processPropertiesFromFormPost; +} + +#------------------------------------------------------------------- +=head1 purge + +This method purges the Asset completely from the WebGUI instance. + +=cut + +sub purge { + my $self = shift; + + $self->session->db->write("delete from SQLForm_fieldDefinitions where assetId=".$self->session->db->quote($self->getId)); + $self->session->db->write("delete from SQLForm_fieldOrder where assetId=".$self->session->db->quote($self->getId)); + + return $self->SUPER::purge; +} + +#------------------------------------------------------------------- +=head1 view + +The view function of the Asset. + +=cut + +sub view { + my $self = shift; + my ($output, @links); + + + return $output .$self->www_search; +} + +#------------------------------------------------------------------- +=head1 www_deleteFieldType + +This will delete the field type with the id that is passed in a form param called 'ftid'. + +=cut + +sub www_deleteFieldType { + my ($isUsed); + my $self = shift; + + my $i18n = WebGUI::International($self->session, 'Asset_SQLForm'); + + return $self->session->privilege->insufficient unless ($self->_canAlterTable); + + ($isUsed) = $self->session->db->quickArray(' select count(fieldId) '. + ' from SQLForm_fieldDefinitions '. + ' where property="fieldType" and value='.$self->session->db->quote($self->session->form->process("ftid"))); + + if ($isUsed) { + return $self->processStyle($i18n->get('dft cannot delete')." $isUsed ".$i18n->get('sqlforms')."."); + } else { + $self->session->db->write('delete from SQLForm_fieldTypes where fieldTypeId='.$self->session->db->quote($self->session->form->process("ftid"))); + return $self->www_listFieldTypes; + } +} + +#------------------------------------------------------------------- +=head1 www_deleteRecord + +Will put the record with the id given by the form param 'rid', in the trash of the SQLForm. + +=cut + +sub www_deleteRecord { + my $self = shift; + + return $self->session->privilege->insufficient() unless ($self->_canEditRecord); + my $dbLink = $self->_getDbLink; + + $dbLink->db->write("update ".$self->get('tableName')." set ". + " __deleted=1,". + " __deletionDate=".$self->session->db->quote(time).",". + " __deletedBy=".$self->session->db->quote($self->session->user->userId). + " where __recordId=".$self->session->db->quote($self->session->form->process("rid")) + ); + + $dbLink->disconnect; + return $self->www_view; +} + +#------------------------------------------------------------------- +=head1 www_deleteRegex + +Deletes the regex with the id given in the form param 'regexId'. + +=cut + +sub www_deleteRegex { + my $self = shift; + + return $self->session->privilege->insufficient() unless ($self->_canAlterTable); + + unless ($self->session->form->process("regexId") =~ /^default/) { + $self->session->db->write('delete from SQLForm_regexes where regexId='.$self->session->db->quote($self->session->form->process("regexId"))); + } else { + return $self->session->privilege->vitalComponent; + } + + return $self->www_listRegexes; +} + +#------------------------------------------------------------------- +=head1 www_disableField + +Will mark the field indicated by the id given by the form param 'fid' as deleted. This means +that the field is not included in searches and edits. No data is actually purged though. + +=cut + +sub www_disableField { + my $self = shift; + + return $self->session->privilege->insufficient() unless ($self->_canAlterTable); + + $self->session->db->write('delete from SQLForm_fieldDefinitions '. + ' where property="disabled" and assetId='.$self->session->db->quote($self->getId).' and fieldId='.$self->session->db->quote($self->session->form->process("fid"))); + $self->session->db->write('insert into SQLForm_fieldDefinitions '. + ' (assetId, fieldId, property, value) values '. + ' ('.$self->session->db->quote($self->getId).', '.$self->session->db->quote($self->session->form->process("fid")).', "disabled", 1)'); + + return $self->www_listFields; +} + +#------------------------------------------------------------------- +=head1 www_enableField + +Will mark the 'deleted' field identified by the id given in the form param 'fid' as normal again. + +=cut + +sub www_enableField { + my $self = shift; + + $self->session->db->write('delete from SQLForm_fieldDefinitions '. + ' where property="disabled" and assetId='.$self->session->db->quote($self->getId).' and fieldId='.$self->session->db->quote($self->session->form->process("fid"))); + + return $self->www_listFields; +} + +#------------------------------------------------------------------- +=head1 www_editField + +Returns the 'edit field properties' form of the field attached to the id given in form param 'fid'. If fid is set +to 'new' it will add a new field. The form generated relies heavily on three javascript files included in the +/extras/SQLForm directory. + +=cut + +sub www_editField { + my ($databaseDefinition, $fieldId, $properties, $f, $tabForm, $jsDatabaseDef, $table, $jsInitForm, $regexes, + %dbFields, %formFields, $output, $i18n, @jsFieldTypeList); + my $self = shift; + my $errors = shift; + + return $self->session->privilege->insufficient() unless ($self->_canAlterTable); + + $i18n = WebGUI::International->new($self->session,'Asset_SQLForm'); + + my $dbLink = $self->_getDbLink; + + # generate column properties hash in javascript + my $jsDataStruct = "var fieldTypes = new Object;\n"; + $jsDataStruct .= "fieldTypes = {\n"; + foreach my $type (keys(%{$allowedDbFieldTypes})) { + my $jsFieldType = "\t'$type' : {\n"; + $jsFieldType .= join(",\n", map {"\t\t'$_' : '".$allowedDbFieldTypes->{$type}->{$_}."'"} keys(%{$allowedDbFieldTypes->{$type}})); + $jsFieldType .= "\n\t}"; + + push(@jsFieldTypeList, $jsFieldType); + } + + $jsDataStruct.= join(",\n", @jsFieldTypeList)."};"; + $self->session->style->setRawHeadTags(''); + + $fieldId = $self->session->form->process("fid"); + return $self->www_view unless ($fieldId); + + if ($self->session->form->process("func") eq 'editFieldSave') { + $properties = $self->session->form->paramsHashRef; + } elsif ($fieldId eq 'new') { + $properties = { + signed => 1, + useAutoIncrement => 0, + isSearchable => 1, + isReadOnly => 0, + useFulltext => 0, + showInSearchResults => 1, + }; + } else { + $properties = $self->session->db->buildHashRef('select property, value from SQLForm_fieldDefinitions where fieldId = '.$self->session->db->quote($fieldId)); + } + + tie %$regexes, "Tie::IxHash"; + $regexes = $self->session->db->buildHashRef("select regexId, concat(name, ' (', regex, ')') from SQLForm_regexes order by name"); + $regexes->{''} = 'No regex'; + + $databaseDefinition = $self->_getDatabaseInfo; + + tie %dbFields, 'Tie::IxHash'; + tie %formFields, 'Tie::IxHash'; + %dbFields = map {$_ => $allowedDbFieldTypes->{$_}->{name}} keys %{$allowedDbFieldTypes}; + %formFields = map {$_ => $allowedFormFieldTypes->{$_}->{name}} keys %{$allowedFormFieldTypes}; +my %fieldTypes = $self->session->db->buildHash('select fieldTypeId, concat(formFieldType, "/", dbFieldType) from SQLForm_fieldTypes'); + + unless (%fieldTypes) { + return $self->processStyle('There are no field types defined. Please define field types first.
'. + 'To add a field type please go to Manage field types.' + ); + } + +my $tabForm = WebGUI::TabForm->new($self->session, undef, undef, $self->getUrl('func=listFields')); + $tabForm->hidden({ + name => 'func', + value => 'editFieldSave' + }); + $tabForm->hidden({ + name => 'fid', + value => $fieldId + }); + + # Insert warning + unless ($fieldId eq 'new') { +my $message = $i18n->get('change field warning'); + $tabForm->formHeader({ + extras => 'onsubmit="'. + "if (document.getElementById('SQLFormFieldType').value != '".$properties->{fieldType}."' || ". + "document.getElementById('SQLFormMaxFieldLength').value < ".($properties->{maxFieldLength} || '0'). +# " || "."document.getElementById('SQLFormSigned').value != '".$properties->{signed}."'" + ") ". + "return confirm('$message')\"" + }); + } + + # Field definition + $tabForm->addTab('general', 'General Properties'); + $f = $tabForm->getTab('general'); + $f->text( + -name => 'fieldName', + -label => $i18n->get('ef field name'), + -hoverHelp => $i18n->get('ef field name description'), + -value => $properties->{fieldName}, + -maxlength => 64, + ); + $f->text( + -name => 'displayName', + -label => $i18n->get('ef display name'), + -hoverHelp => $i18n->get('ef display name description'), + -value => $properties->{displayName}, + ); + $f->selectList( + -name => 'fieldType', + -label => $i18n->get('ef field type'), + -hoverHelp => $i18n->get('ef field type description'), + -value => [$properties->{fieldType}], + -options=> \%fieldTypes, + -extras => 'onchange="updateFormFields()"', + -id => "SQLFormFieldType", + -multiple => 0, + -size => 1, + ); + $f->trClass('SQLFormSignedRow'); + $f->radioList( + -name => 'signed', + -label => $i18n->get('ef signed'), + -hoverHelp => $i18n->get('ef signed description'), + -value => $properties->{signed} || '0', + -options=> { + '1' => $i18n->get('ef signed label'), + '0' => $i18n->get('ef unsigned label') + }, + -id => 'SQLFormSigned', + ); + $f->trClass('SQLFormAutoIncrementRow'); + $f->yesNo( + -name => 'useAutoIncrement', + -label => $i18n->get('ef autoincrement'), + -hoverHelp => $i18n->get('ef autoincrement description'), + -value => $properties->{useAutoIncrement}, + -id => 'SQLFormAutoIncrement', + ); + $f->trClass(''); + $f->integer( + -name => 'formFieldHeight', + -label => $i18n->get('ef form height'), + -hoverHelp => $i18n->get('ef form height description'), + -value => $properties->{formFieldHeight}, + ); + $f->integer( + -name => 'formFieldWidth', + -label => $i18n->get('ef form width'), + -hoverHelp => $i18n->get('ef form width description'), + -value => $properties->{formFieldWidth}, + ); + $f->trClass('SQLFormMaxFieldLengthRow'); + $f->integer( + -name => 'maxFieldLength', + -label => $i18n->get('ef max field length'), + -hoverHelp => $i18n->get('ef max field length description'), + -value => $properties->{maxFieldLength}, + -id => 'SQLFormMaxFieldLength', + ); + $f->trClass('SQLFormRegexRow'); + $f->selectList( + -name => 'regex', + -label => $i18n->get('ef regex'), + -hoverHelp => $i18n->get('ef regex description'), + -value => [ $properties->{regex} ], + -options=> $regexes, + -id => 'SQLFormRegex', + -size => 1, + -multiple=> 0, + ); + $f->trClass(''); + $f->yesNo( + -name => 'isRequired', + -label => $i18n->get('ef required'), + -hoverHelp => $i18n->get('ef required description'), + -value => $properties->{isRequired}, + ); + $f->trClass('SQLFormReadOnlyRow'); + $f->yesNo( + -name => 'isReadOnly', + -label => $i18n->get('ef read only'), + -hoverHelp => $i18n->get('ef read only description'), + -value => $properties->{isReadOnly}, + -id => 'SQLFormReadOnly', + ); + $f->trClass(''); + $f->text( + -name => 'defaultValue', + -label => $i18n->get('ef default value'), + -hoverHelp => $i18n->get('ef default value description'), + -value => $properties->{defaultValue}, + ); + $f->readOnly( + -label => $i18n->get('ef field constraint'), + -hoverHelp => $i18n->get('ef field constraint description'), + -value => WebGUI::Form::selectList($self->session, { + name => 'fieldConstraintType', + options => {'0' => 'none', 1 => '>', 2 => '>=', 3 => '<', 4 => '<=', 5 => '='}, + value => [ $properties->{fieldConstraintType} || '0' ], + extras => 'onchange="updateFormFields()"', + id => 'SQLFormFieldConstraintType', + size => 1, + multiple=> 0, + }). + WebGUI::Form::selectList($self->session, { + name => 'fieldConstraintTarget', + options => {}, #{'value' => 'Value', 'joinColumn1' => 'Join field 1', joinColumn2 => 'Join field 2'}, + value => [ $properties->{fieldConstraintTarget} ], + extras => 'onchange="updateFormFields()"', + id => 'SQLFormFieldConstraintTarget', + size => 1, + multiple=> 0, + }). + WebGUI::Form::text($self->session, { + name => 'fieldConstraintValue', + value => $properties->{fieldConstraintValue}, + id => 'SQLFormFieldConstraintValue', + }), + ); + + # Search and summary options + $tabForm->addTab('search', 'Search and Summary'); + $f = $tabForm->getTab('search'); + $f->yesNo( + -name => 'isSearchable', + -label => $i18n->get('ef searchable'), + -hoverHelp => $i18n->get('ef searchable description'), + -value => $properties->{isSearchable}, + ); + $f->yesNo( + -name => 'useFulltext', + -label => $i18n->get('ef fulltext'), + -hoverHelp => $i18n->get('ef fulltext description'), + -value => $properties->{useFulltext}, + ); + $f->yesNo( + -name => 'showInSearchResults', + -label => $i18n->get('ef show in search'), + -hoverHelp => $i18n->get('ef show in search description'), + -value => $properties->{showInSearchResults}, + ); + $f->integer( + -name => 'summaryLength', + -label => $i18n->get('ef summary length'), + -hoverHelp => $i18n->get('ef summary length description'), + -value => $properties->{summaryLength}, + ); + + # Form pouplation params + $tabForm->addTab('population', 'Form Population'); + $f = $tabForm->getTab('population'); + $f->textarea( + -name => 'formPopulationKeys', + -label => $i18n->get('ef populate keys'), + -hoverHelp => $i18n->get('ef populate keys description'), + -value => $properties->{formPopulationKeys}, + ); + $f->textarea( + -name => 'formPopulationValues', + -label => $i18n->get('ef populate values'), + -hoverHelp => $i18n->get('ef populate values description'), + -value => $properties->{formPopulationValues}, + ); + + $f->readOnly( + -value => 'Query generation', + ); + # This is quite a special field and is completely built by javascript. The file sqlform.js contains the HTML + # for the row layout. + $f->readOnly( + -label => $i18n->get('ef join selector'), + -hoverHelp => $i18n->get('ef join selector description'), + -value => '
', + ); + $f->readOnly( + -label => $i18n->get('ef join constraint'), + -hoverHelp => $i18n->get('ef join constraint description'), + -value => WebGUI::Form::selectList($self->session, { + name => 'joinConstraintColumn', + options => {}, + id => 'joinConstraintColumn', + size => 1, + multiple=> 0, + }).' = '. + WebGUI::Form::selectList($self->session, { + name => 'joinConstraintField', + options => $self->session->db->buildHashRef('select fieldId, value from SQLForm_fieldDefinitions where property="displayname" and assetId='.$self->session->db->quote($self->getId)), + value => [ $properties->{joinConstraintField} ], + id => 'joinConstraintField', + size => 1, + multiple=> 0, + }), + ); + $f->selectList( + -name => 'selectField1', + -label => $i18n->get('ef join keys'), + -hoverHelp => $i18n->get('ef join keys description'), + -options=> {}, + -id => 'selectField1', + -size => 1, + -multiple=> 0, + ); + $f->selectList( + -name => 'selectField2', + -label => $i18n->get('ef join values'), + -hoverHelp => $i18n->get('ef join values description'), + -options=> {}, + -id => 'selectField2', + -size => 1, + -multiple=> 0, + ); + + # This js file contains code to handle the dynamics of this form. + $self->session->style->setScript($self->session->config->get("extrasURL").'/'.'wobject/SQLForm/SQLFormJoinSelector.js', {type => 'text/javascript'}); + $self->session->style->setScript($self->session->config->get("extrasURL").'/'.'js/at/AjaxRequest.js', {type => 'text/javascript'}); + $self->session->style->setScript($self->session->config->get("extrasURL").'/'.'wobject/SQLForm/SQLFormEditField.js', {type => 'text/javascript'}); + +my $jsDatabases = '[' . join(',', map {"{key : '$_', value : '$_'}"} $dbLink->db->buildArray('show databases')) . ']'; +my $jsInitJoinSelector; +my $js = "\n"; + + $output = '

'.$i18n->get('ef errors occurred').'

  • '.join('
  • ', @{$errors}).'

' if ($errors); + $output .= $tabForm->print . $js; + + $self->getAdminConsole->setHelp("edit field", "Asset_SQLForm"); + return $self->getAdminConsoleWithSubmenu->render($output, $i18n->get('edit field title')); +} + +#------------------------------------------------------------------- +=head1 www_editFieldSave + +Processes and stores the field properties, and will alter the table according to these properties. + +=cut + +sub www_editFieldSave { + my ($databaseDef, $key, $joinNumber, @tables, $processed, $tableName, + $joinAColumnName, $joinATableName, $joinBColumnName, $joinBTableName, + @joinConstraints, @differenceConstraints, $maxAllowedLength, $fieldId, + @error, $properties, $i18n); + my $self = shift; + + return $self->session->privilege->insufficient() unless ($self->_canAlterTable); + + $i18n = WebGUI::International->new($self->session,'Asset_SQLForm'); + + $databaseDef = $self->_getDatabaseInfo; + + # Get the right field id and load properties if applicable ------------------------------------ + if ($self->session->form->process("fid") eq 'new') { + $fieldId = $self->session->id->generate; + } else { + $fieldId = $self->session->form->process("fid"); + $properties = $self->session->db->buildHashRef("select property, value from SQLForm_fieldDefinitions where fieldId=".$self->session->db->quote($fieldId)); + } + + # If no value (or zero) is given for any of these values just discard them and use the WebGUI defaults. + + push (@error, $i18n->get('efs height error')) unless ($self->session->form->process("formFieldHeight") =~ m/^\d*$/); + $processed->{formFieldHeight} = $self->session->form->integer('formFieldHeight') if ($self->session->form->process("formFieldHeight")); + push (@error, $i18n->get('efs width error')) unless ($self->session->form->process("formFieldWidth") =~ m/^\d*$/); + $processed->{formFieldWidth} = $self->session->form->integer('formFieldWidth') if ($self->session->form->process("formFieldWidth")); + + push (@error, $i18n->get('efs populate error')) if (scalar(split(/\n/,$self->session->form->process("formPopulationKeys"))) != scalar(split(/\n/,$self->session->form->process("formPopulationValues")))); + $processed->{formPopulationKeys} = $self->session->form->process("formPopulationKeys"); + $processed->{formPopulationValues} = $self->session->form->process("formPopulationValues"); + + $processed->{isSearchable} = 1 if $self->session->form->process("isSearchable"); + $processed->{showInSearchResults} = 1 if ($self->session->form->process("showInSearchResults")); + + $processed->{summaryLength} = $self->session->form->process("summaryLength") if ($self->session->form->process("summaryLength") =~ m/^\d+$/); + $processed->{useAutoIncrement} = 1 if ($self->session->form->process("useAutoIncrement")); + $processed->{signed} = 1 if ($self->session->form->process("signed")); + $processed->{isRequired} = 1 if ($self->session->form->process("isRequired")); + $processed->{isReadOnly} = 1 if ($self->session->form->process("isReadOnly")); + $processed->{defaultValue} = $self->session->form->process("defaultValue"); + + if ($self->session->form->process("fieldConstraintType") > 0 && defined $self->session->form->process("fieldConstraintTarget")) { + $processed->{fieldConstraintType} = $self->session->form->process("fieldConstraintType"); + $processed->{fieldConstraintTarget} = $self->session->form->process("fieldConstraintTarget"); + if ($processed->{fieldConstraintTarget} eq 'value') { + $processed->{fieldConstraintValue} = $self->session->form->process("fieldConstraintValue"); + push (@error, $i18n->get('efs constraint error')) unless (defined $self->session->form->process("fieldConstraintValue")); + } + if ($processed->{fieldConstraintTarget} eq 'joinColumn1' && !$self->session->form->process("selectField1")) { + push (@error, $i18n->get('efs jf1 error')); + } + if ($processed->{fieldConstraintTarget} eq 'joinColumn2' && !$self->session->form->process("selectField2")) { + push (@error, $i18n->get('efs jf2 error')); + } + + } + + $processed->{joinConstraintColumn} = $self->session->form->process("joinConstraintColumn"); + $processed->{joinConstraintField} = $self->session->form->process("joinConstraintField"); + + # Process the join stuff ----------------------------------------------------------------------- + if ($self->session->form->process("table1") && !($self->session->form->process("selectField1") && $self->session->form->process("selectField2"))) { + push(@error, $i18n->get('efs join populate error')); + } + if ($self->session->form->process("table1")) { + $processed->{selectField1} = $self->session->form->process("selectField1"); + $processed->{selectField2} = $self->session->form->process("selectField2"); + } + + #### Will break if there are more than 9 joins #### +my @columnConstraints; +my %fingerprint; +my $dbLink = $self->_getDbLink; + + foreach $key (sort(keys(%{$self->session->form->paramsHashRef}))) { + if ($key =~ m/^database(\d+)/ && $self->session->form->process($key)) { + $joinNumber = $1; +my $databaseName = $self->session->form->process("database$joinNumber"); + if (isIn($databaseName, $dbLink->db->buildArray('show databases'))) { + $processed->{"database$joinNumber"} = $databaseName; + + $tableName = $self->session->form->process("table$joinNumber"); + if (isIn($tableName, $dbLink->db->buildArray("show tables from $databaseName"))) { + unless ($self->session->form->process("joinFunction$joinNumber") eq 'difference') { + push(@tables, "$databaseName.$tableName as table$joinNumber"); + } + $processed->{"table$joinNumber"} = $tableName; + + +my @columns = $self->dbLink->db->buildArray("describe $databaseName.$tableName"); + if (isIn('__deleted', @columns) && isIn('__archived', @columns)) { + push(@columnConstraints, "table$joinNumber.__deleted=0 and table$joinNumber.__archived=0") if ($joinNumber == 1); + $fingerprint{$joinNumber} = 1; + } + +my $joinAIsSQLForm = 0; + if ($joinNumber > 1) { +my $joinADatabaseName; + if ($self->session->form->process('joinOnA'.$joinNumber) =~ m/^table(\d+)\.(.+)$/) { + $joinAColumnName = $2; + $joinATableName = $self->session->form->process('table'.$1); + $joinADatabaseName = $self->session->form->process('database'.$1); + $joinAIsSQLForm = $fingerprint{$1}; + } else { + push(@error, $i18n->get('efs left join column error').$joinNumber."."); + } + +my $joinBDatabaseName; + if ($self->session->form->process('joinOnB'.$joinNumber) =~ m/^table(\d+)\.(.+)$/) { + $joinBColumnName = $2; + $joinBTableName = $self->session->form->process('table'.$1); + $joinBDatabaseName = $self->session->form->process('database'.$1); + } else { + push(@error, $i18n->get('efs right join column error').$joinNumber."."); + } + if ($joinATableName && $joinBTableName && + isIn($joinAColumnName, $self->dbLink->db->buildArray("describe $joinADatabaseName.$joinATableName")) && + isIn($joinBColumnName, $self->dbLink->db->buildArray("describe $joinBDatabaseName.$joinBTableName"))) { + if ($self->session->form->process("joinFunction$joinNumber") eq 'difference') { +my $subSelect = "select $joinAColumnName from $joinADatabaseName.$joinATableName"; + $subSelect .= " where __deleted=0 and __archived=0" if ($joinAIsSQLForm); + push(@differenceConstraints, $self->session->form->process('joinOnB'.$joinNumber).' not in ('.$subSelect.')'); + } else { + push(@joinConstraints, $self->session->form->process('joinOnA'.$joinNumber) .'='. $self->session->form->process('joinOnB'.$joinNumber)); + push(@columnConstraints, "table$joinNumber.__deleted=0 and table$joinNumber.__archived=0") if ($fingerprint{$joinNumber}); + } + + $processed->{'joinOnA'.$joinNumber} = $self->session->form->process('joinOnA'.$joinNumber); + $processed->{'joinOnB'.$joinNumber} = $self->session->form->process('joinOnB'.$joinNumber); + $processed->{'joinFunction'.$joinNumber} = $self->session->form->process('joinFunction'.$joinNumber); + } else { + push(@error, $i18n->get('efs column name error')." [$joinAColumnName][$joinBColumnName]"); + } + } + } else { + push(@error, $i18n->get('efs table error').' ['.$self->session->form->process($key).'.'.$tableName.']'); + } + } else { + push(@error, $i18n->get('efs database error').' ['.$self->session->form->process("database$joinNumber").']'); + } + + } + } + + # Generate a sqlquery here so we don't have to generate it everytime the form view is called --- + $processed->{sqlQuery} = "select ".$self->session->form->process("selectField1").", ".$self->session->form->process("selectField2"). " from ". + join(', ', @tables); + $processed->{sqlQuery} .= " where " if (@joinConstraints || @differenceConstraints || @columnConstraints); + $processed->{sqlQuery} .= join(' and ', (@joinConstraints, @differenceConstraints, @columnConstraints)); + + # If there are set-differences defined we also need a query without them to be able to show + # the correct values in search results. + if (@differenceConstraints) { + $processed->{sqlQueryAllOptions} = "select ".$self->session->form->process("selectField1").", ".$self->session->form->process("selectField2"). " from ". + join(', ', @tables); + $processed->{sqlQueryAllOptions} .= " where ".join(' and ', @joinConstraints) if (@joinConstraints); + } + + # Process fieldType ---------------------------------------------------------------------------- +my ($dbFieldType, $formFieldType) = $self->session->db->quickArray('select dbFieldType, formFieldType from SQLForm_fieldTypes '. + ' where fieldTypeId='.$self->session->db->quote($self->session->form->process("fieldType"))); + if ($dbFieldType && $formFieldType) { + $processed->{dbFieldType} = $dbFieldType; + $processed->{formFieldType} = $formFieldType; + $processed->{fieldType} = $self->session->form->process("fieldType"); + } else { + push(@error, $i18n->get('efs field type error')); + } + + # Check if fulltext search is allowed ---------------------------------------------------------- + if ($self->session->form->process("useFulltext") && $allowedDbFieldTypes->{$processed->{dbFieldType}}->{supportsFulltext}) { + $processed->{useFulltext} = 1; + } elsif ($self->session->form->process("useFulltext")) { + push (@error, $i18n->get('efs fulltext error')); + } + + # Check whether a correct fieldname has been given --------------------------------------------- + if ($self->session->form->process("fieldName") =~ m/^[a-zA-Z0-9_]+$/) { + if (($self->session->form->process("fieldName") ne $properties->{fieldName}) && + isIn($self->session->form->process("fieldName"), keys(%{$databaseDef->{$self->get('tableName')}}))) + { + push(@error, $i18n->get('efs column name exists error')); + } elsif (isIn(uc($self->session->form->process("fieldName")), @reservedKeywords)) { + push(@error, $i18n->get('efs column name is reserved error')); + } else { + $processed->{fieldName} = $self->session->form->process("fieldName"); + } + } elsif (!$self->session->form->process("useAutoIncrement")) { + push(@error, $i18n->get('efs field name error')); + } + + # Make sure the maxlength is supported by the field types -------------------------------------- + $maxAllowedLength = ($allowedDbFieldTypes->{$processed->{dbFieldType}}->{maxLength} > $allowedFormFieldTypes->{$processed->{formFieldType}}->{maxLength}) ? + $allowedDbFieldTypes->{$processed->{dbFieldType}}->{maxLength} : $allowedFormFieldTypes->{$processed->{formFieldType}}->{maxLength}; + if ($self->session->form->integer('maxFieldLength') <= $maxAllowedLength || !$maxAllowedLength) { + $processed->{maxFieldLength} = $self->session->form->integer('maxFieldLength') || $maxAllowedLength || undef; + } else { + push (@error, "Allow maximum length to large for chosen fields"); + } + + # Check if population params are given in case of a set-field ---------------------------------- + if ($processed->{dbFieldType} eq 'set' and scalar(split(/\r?\n/, $processed->{formPopulationKeys})) == 0) { + push (@error, "You have to enter population key/value pairs if you use the set column type"); + } + + # Process regex + $processed->{regex} = $self->session->form->combo('regex'); + if ($self->session->form->process("regex") eq '_new_') { +my $regexName = $self->session->form->process("regex_name") || 'untitled'; + $self->session->db->write('insert into SQLForm_regexes (name, regex) values ('.$self->session->db->quote($regexName).','.$self->session->db->quote($processed->{regex}).')'); + } + + # Process display name + $processed->{displayName} = $self->session->form->process("displayName") || $processed->{fieldName}; + + # Return errors if necessarry, else write the whole lot to the db. + if (@error) { + return $self->www_editField(\@error); + } else { + # Write data to db ----------------------------------------------------------------------------- + my $dbLink = $self->_getDbLink; + + # Store/update definition + $self->session->db->write('delete from SQLForm_fieldDefinitions where fieldId='.$self->session->db->quote($fieldId)); + foreach (keys(%$processed)) { + $self->session->db->write('insert into SQLForm_fieldDefinitions (fieldId, assetId, property, value) values '. + '('.$self->session->db->quote($fieldId).','.$self->session->db->quote($self->get('assetId')).','.$self->session->db->quote($_).','.$self->session->db->quote($processed->{$_}).')'); + } + + # Set new column at the last position + if ($self->session->form->process("fid") eq 'new') { + my ($rank) = $self->session->db->quickArray('select max(rank)+1 from SQLForm_fieldOrder where assetId ='.$self->session->db->quote($self->getId).' group by assetId'); + $rank ||= '0'; + $self->session->db->write( + " insert into SQLForm_fieldOrder (assetId, fieldId, rank) values " + ." (".$self->session->db->quote($self->getId).", ".$self->session->db->quote($fieldId).", $rank )" + ); + } + + # Construct the type specifier + my $type = $self->_constructColumnType($processed); + + # If id = new create column in table + if ($self->session->form->process("fid") eq 'new') { + $dbLink->db->write('alter table '.$self->get('tableName').' add column '.$processed->{fieldName}.' '.$type); + $dbLink->db->write('alter table '.$self->get('tableName'). + ' add column '.'__'.$processed->{fieldName}.'_mimeType'.' varchar(64)') if ($self->session->form->process("formFieldType") eq 'file'); + } else { + $dbLink->db->write('alter table '.$self->get('tableName').' change column '.$properties->{fieldName}.' '.$processed->{fieldName}.' '.$type); + } + + # Process fulltext columns + if ($processed->{useFulltext} && !$properties->{useFulltext}) { + $dbLink->db->write('alter table '.$self->get('tableName').' add fulltext ('.$processed->{fieldName}.')'); + } elsif (!$processed->{useFulltext} && $properties->{useFulltext}) { + $dbLink->db->write('alter table '.$self->get('tableName').' drop index '.$processed->{fieldName}); + } + + $dbLink->disconnect; + } + + return $self->www_listFields; +} + +#------------------------------------------------------------------- +=head1 www_editFieldType + +Returns the form for editing field types. Only allows editing of field types that are not in use by +any SQLForm asset withing the instance. Pass the field type id through the form param 'ftid'. Pass +ftid=new to add a new field type. + +=cut + +sub www_editFieldType { + my (%dbFields, %formFields, $f, $properties, $i18n); + my $self = shift; + + return $self->session->privilege->insufficient() unless ($self->_canAlterTable); + + tie %dbFields, 'Tie::IxHash'; + tie %formFields, 'Tie::IxHash'; + %dbFields = map {$_ => $allowedDbFieldTypes->{$_}->{name}} keys %{$allowedDbFieldTypes}; + %formFields = map {$_ => $allowedFormFieldTypes->{$_}->{name}} keys %{$allowedFormFieldTypes}; + + $i18n = WebGUI::International->new($self->session,'Asset_SQLForm'); + + if ($self->session->form->process("ftid") eq 'new') { + $properties = {}; + } else { + $properties = $self->session->db->quickHashRef('select * from SQLForm_fieldTypes where fieldTypeId='.$self->session->db->quote($self->session->form->process("ftid"))); + } + + $f = WebGUI::HTMLForm->new($self->session, + -action => $self->getUrl + ); + $f->hidden( + -name => 'func', + -value => 'editFieldTypeSave' + ); + $f->hidden( + -name => 'ftid', + -value => $self->session->form->process("ftid"), + ); + $f->readOnly( + -label => 'fieldTypeId', + -value => $self->session->form->process("ftid"), + ); + $f->selectList( + -name => 'dbFieldType', + -label => $i18n->get('eft db field type'), + -hoverHelp => $i18n->get('eft db field type description'), + -value => [$properties->{dbFieldType}], + -options=> \%dbFields, + -id => 'SQLFormDbFieldType', + -size => 1, + -multiple=> 0, + ); + $f->selectList( + -name => 'formFieldType', + -label => $i18n->get('eft form field type'), + -hoverHelp => $i18n->get('eft form field type description'), + -value => [$properties->{formFieldType}], + -options=> \%formFields, + -id => 'formTypeSelector', + -size => 1, + -multiple=> 0, + ); + $f->readOnly( + -value => + WebGUI::Form::submit($self->session). + WebGUI::Form::button($self->session, { + value => $i18n->get('cancel'), + extras => 'onClick="location.href=\''.$self->getUrl('func=listFieldTypes').'\'"' + }) + ); + + $self->getAdminConsole->setHelp("edit field type", "Asset_SQLForm"); + return $self->getAdminConsoleWithSubmenu->render($f->print, $i18n->get('edit field type title')); +} + +#------------------------------------------------------------------- +=head1 www_editFieldTypeSave + +Saves the field type properties entered in the form returned by www_editFieldType to the database. The form +param 'ftid' is used to pass the field type id. Setting the id to 'new' will create a new field type. + +=cut + +sub www_editFieldTypeSave { + my ($fieldTypeId); + my $self = shift; + + if ($self->session->form->process("ftid") eq 'new') { + $self->_createFieldType($self->session->form->process("dbFieldType"), $self->session->form->process("formFieldType")); + } else { + $self->session->db->write('update SQLForm_fieldTypes '. + ' set dbFieldType='.$self->session->db->quote($self->session->form->process("dbFieldType")).', formFieldType='.$self->session->db->quote($self->session->form->process("formFieldType")). + ' where fieldTypeId='.$self->session->db->quote($self->session->form->process("ftid"))); + } + + return $self->www_listFieldTypes; +} + +#------------------------------------------------------------------- +=head1 _getFieldValue ( field, recordValues, readOnly ) + +Returns the the value for the field represented by the field hashref for the current record, If the record +has no value for this field it will return the default value. The returned value has the correct data type +for the for element that belongs to the field. + +=head2 field + +A hashref containing the field properties of this field. + +=head2 recordValues + +A hasref containg the values of this record. + +=head2 + +A boolean indicating the value should be outputted in read only mode. + +=cut + +sub _getFieldValue { + my ($fieldValue); + my $self = shift; + my $field = shift; + my $recordValues = shift; + my $readOnly = shift; + + $fieldValue = $self->session->form->process($field->{fieldName}) || $recordValues->{$field->{fieldName}} || $field->{processedDefaultValue}; + + if ($fieldValue && !$readOnly) { + $fieldValue = $self->session->datetime->setToEpoch($fieldValue) if (isIn($field->{formFieldType}, qw(date dateTime))); + $fieldValue = $self->session->datetime->timeToSeconds($fieldValue) if ($field->{formFieldType} eq 'timeField'); + } + + #### This might break? #### + if ($field->{canHaveMultipleValues} && !$readOnly) { + $fieldValue = [ $recordValues->{$field->{fieldName}} ]; + $fieldValue = [ $self->session->request->param($field->{fieldName}) ] if (defined $self->session->form->process($field->{fieldName})); + } + + # Handle file uploads + if ($field->{formFieldType} eq 'file') { + unless ($recordValues->{$field->{fieldName}}) { +# $fieldValue .= 'No file uploaded yet'; + } else { + $fieldValue = ''; + if ($recordValues->{'__'.$field->{fieldName}.'_mimeType'} =~ /^image/i) { + $fieldValue .= ''; + } else { + $fieldValue .= WebGUI::Internation::get('click here for file', 'Asset_SQLForm'); + } + $fieldValue .= ''; + } + } + + return $fieldValue; +} + +#------------------------------------------------------------------- +=head1 _getFormElement ( field, recordValues, readOnly ) + +Returns the for element tied to this field. + +=head2 field + +A hashref containing the field properties of this field. + +=head2 recordValues + +A hasref containg the values of this record. + +=head2 + +A boolean indicating the value should be outputted in read only mode. + + +=cut + + +sub _getFormElement { + my ($fieldValue, $readOnly, $fieldParameters, $maxLength, $fieldType, $formElement, $cmd, $i18n); + my $self = shift; + my $field = shift; + my $recordValues = shift; + my $readOnly = shift || !$self->_canEditRecord || $field->{isReadOnly} || $field->{readOnly} || $field->{useAutoIncrement}; + $i18n = WebGUI::International->new($self->session,'Asset_SQLForm'); + + # Get field type and value + $fieldType = $field->{formFieldType}; + $fieldValue = $self->_getFieldValue($field, $recordValues, $readOnly); + + # Resolve value to key in case of read only and key/value pairs + $fieldValue = $field->{allOptions}->{$fieldValue} if ($field->{hasOptions} && $readOnly); + $maxLength = $field->{maxFieldLength} || $allowedDbFieldTypes->{$field->{dbFieldType}}->{maxLength}; + + # Set up form element parameters + $fieldParameters->{name} = $field->{fieldName}; + $fieldParameters->{value} = $fieldValue; + $fieldParameters->{options} = $field->{options} if ($field->{hasOptions}); + $fieldParameters->{multiple} = $field->{multipleAllowed} == 1; + $fieldParameters->{$field->{widthParam}} = $field->{formFieldWidth} if ($field->{formFieldWidth}); + $fieldParameters->{$field->{heightParam}} = $field->{formFieldHeight} if ($field->{formFieldHeight}); + $fieldParameters->{maxlength} = $maxLength; + $fieldParameters->{extras} = 'onkeyup="if (this.value.length > '.$maxLength.') {this.value = this.value.substring(0,'.$maxLength.');}"'; + $fieldParameters->{id} = 'sqlform'.$field->{fieldId}; + + # Construct the form element + if ($readOnly) { + $formElement = $fieldValue; + } else { + # Show file if a file is uploaded + $formElement = $fieldValue.'
' if ($fieldType eq 'file' && $fieldValue); + + # Add form element + $cmd = 'WebGUI::Form::'.$fieldType.'($self->session, $fieldParameters)'; + $formElement = eval($cmd); + $self->session->errorHandler->fatal('Could not instanciate formelement via WebGUI::Form: '.$@) if ($@); + + if ($fieldType eq 'selectList' && !$field->{isRequired}) { + $formElement .= WebGUI::Form::button($self->session, { + value => $i18n->get('clear'), + extras => 'onclick="var a =document.getElementById(\'sqlform'.$field->{fieldId}.'\'); for (i=0; i < a.options.length; i++) { a.options[i].selected = false;};"', + }); + } + + # Add file upload controls if necessary + if ($fieldType eq 'file') { + if ($fieldValue) { + $formElement .= WebGUI::Form::radioList($self->session, { + name => '_'.$field->{fieldName}.'_action', + options => { + 'keep' => $i18n->get('keep'), + 'overwrite' => $i18n->get('overwrite'), + 'delete' => $i18n->get('delete'), + }, + value => 'keep', + }); + } else { + $formElement .= WebGUI::Form::hidden($self->session, { + name => '_'.$field->{fieldName}.'_action', + value => 'overwrite', + }); + } + } + } + + return $formElement; +} + +#------------------------------------------------------------------- +=head1 www_editRecord + +Generates the record edit form for the record with the id given by the form param 'rid'. Setting rid to 'new' will +cause a new record to be added. If the user is not allowed to edit but can view, this method will output in view +mode, which means that only the contents of the record are shown. The view mode is also initiated when the form +param 'viewOnly' is set to a non-zero value. + +=cut + +sub www_editRecord { + my ($recordId, $fieldType, $canEditRecord, @fields, $properties, $f, $field, @fieldParameters, $cmd, $var, @formLoop, $formElement, $numberOfFields, $i18n); + my $self = shift; + my $errors = shift || []; + + return $self->session->privilege->insufficient() unless ($self->canView); + + $i18n = WebGUI::International->new($self->session,'Asset_SQLForm'); + + my $dbLink = $self->_getDbLink; + + @fields = $self->session->db->buildArray( + " select distinct t1.fieldId " + ." from SQLForm_fieldDefinitions as t1, SQLForm_fieldOrder as t2 " + ." where t1.fieldId=t2.fieldId and t1.assetId=t2.assetId and t1.assetId=".$self->session->db->quote($self->getId) + ." order by t2.rank"); + + if ($self->session->form->process("rid") eq 'new') { + $recordId = $self->session->form->process("copyRecordId"); + } else { + $recordId = $self->session->form->process("rid"); + } + + if ($recordId) { + $properties = $dbLink->db->quickHashRef("select * from ".$self->get('tableName'). + " where __archived=0 and __recordId=".$self->session->db->quote($recordId)); + return $i18n->get("invalid record id") unless ($properties->{__recordId}); + } else { + $properties = {}; + } + + $canEditRecord = ($self->_canEditRecord && $properties->{__deleted} == 0) ? 1 : 0; + $canEditRecord = 0 if ($self->session->form->process("viewOnly")); + + $f = WebGUI::HTMLForm->new($self->session, + -action => $self->getUrl, + ); + $f->hidden( + -name => 'func', + -value => 'editRecordSave', + ); + $f->hidden( + -name => 'rid', + -value => $self->session->form->process("rid"), + ); + + foreach (@fields) { + $field = $self->_getFieldProperties($_, $properties); + + # Skip 'deleted' columns. + next if ($field->{disabled} || ($self->session->form->process("rid") eq 'new' && $field->{useAutoIncrement})); + + $numberOfFields++; + + # Add element to preconstructed form + my $formElement = $self->_getFormElement($field, $properties, !$canEditRecord); + $f->readOnly( + -label => $field->{displayName}, + -value => $formElement + ); + + # Add element to the form loop + push(@formLoop, { + 'field.label' => $field->{displayName}, + 'field.formElement' => $formElement, + }); + + $var->{'field.'.$field->{fieldName}.'.formElement'} = $formElement; + $var->{'field.'.$field->{fieldName}.'.label'} = $field->{displayName}; + } + + if ($canEditRecord) { + $f->submit; + push(@formLoop, {'field.formElement' => WebGUI::Form::submit($self->session)}); + } + + $var->{formHeader} = WebGUI::Form::formHeader($self->session). + WebGUI::Form::hidden($self->session, {name=>'func', value=>'editRecordSave'}). + WebGUI::Form::hidden($self->session, {name=>'rid', value=>$self->session->form->process("rid")}); + $var->{formFooter} = WebGUI::Form::formFooter($self->session); + $var->{formLoop} = \@formLoop; + $var->{completeForm} = $f->print; + $var->{errorOccurred} = scalar(@$errors); + $var->{errorLoop} = $errors; + $var->{isNew} = 1 if ($self->session->form->process("rid") eq 'new'); + $var->{'viewHistory.label'} = $i18n->get('view history'); + $var->{'viewHistory.url'} = $self->getUrl('func=viewHistory;rid='.$self->session->form->process("rid")); + $var->{managementLinks} = $self->_getManagementLinks; + + $dbLink->disconnect; + + unless ($numberOfFields) { + return $self->processStyle($i18n->get('no fields defined message').' '.$i18n->get('manage fields title').'.'); + } + + return $self->processStyle($self->processTemplate($var, $self->getValue('editTemplateId'))); +} + +#------------------------------------------------------------------- +=head1 www_editRecordSave + +Will process and save the record data inputted in the form generated by www_editRecord. In errors occur they will +be fed back to www_editRecord. Set the record id using the form param 'rid', and use 'new' as id to add a new +record. + +=cut + +sub www_editRecordSave { + my (@fields, $field, $fieldName, @error, @update, $recordId, $lastRevision, $revision, $previousRecord, $value, $i18n); + my $self = shift; + + return $self->session->privilege->insufficient() unless ($self->_canEditRecord); + + $i18n = WebGUI::International->new($self->session,'Asset_SQLForm'); + + my $dbLink = $self->_getDbLink; + @fields = $self->session->db->buildArray("select distinct fieldId from SQLForm_fieldDefinitions where assetId=".$self->session->db->quote($self->getId)); +my %regexes = $self->session->db->buildHash("select regex, concat(name, ' (', regex, ')') from SQLForm_regexes"); + +my $now = time; +my ($creationDate, $creator); + + if ($self->session->form->process("rid") eq 'new') { + $recordId = $self->session->id->generate; + $creationDate = $now; + $creator = $self->session->user->userId; + + $revision = 0; + } elsif (defined $self->session->form->process("rid")) { + $recordId = $self->session->form->process("rid"); + ($lastRevision) = $dbLink->db->quickArray('select max(__revision) from '.$self->get('tableName').' where __recordId='.$self->session->db->quote($recordId)); + return $i18n->get('invalid record id') unless (defined $lastRevision); + + $revision = $lastRevision + 1; + $previousRecord = $dbLink->db->quickHashRef("select * from ".$self->get('tableName')." where __archived=0 and __recordId=".$self->session->db->quote($recordId)); + $creationDate = $previousRecord->{__creationDate}; + $creator = $previousRecord->{__createdBy}; + } + + # Set the metadata fields + push(@update, "__creationDate=$creationDate"); + push(@update, "__createdBy=".$self->session->db->quote($creator)); + push(@update, "__revision=$revision"); + push(@update, "__initDate = ".$now); + push(@update, "__userId = ".$self->session->db->quote($self->session->user->userId)); + push(@update, "__recordId = ".$self->session->db->quote($recordId)); + push(@update, "__archived = 0"); + + foreach (@fields) { + $field = $self->_getFieldProperties($_); + $fieldName = $field->{fieldName}; + + # Skip 'deleted' columns. + next if $field->{disabled}; + + # Get field constraint +my $fieldConstraint = undef; + if ($field->{fieldConstraintType} && $field->{fieldConstraintTarget} =~ /joinColumn[12]/) { +my $sql = $field->{sqlQuery}; + if ($field->{joinConstraintColumn}) { + $sql =~ s/^select/select $field->{fieldConstraintTarget},/; + if ($sql =~ / where /) { + $sql .= ' and '; + } else { + $sql .= ' where '; + } + $sql .= $field->{joinConstraintColumn} . ' = ' . + $self->session->db->quote($self->session->form->process($self->_getFieldProperties($field->{joinConstraintField})->{fieldName})); + } +my @results = $self->session->db->quickArray($sql); + $fieldConstraint = $results[0]; + } + + # Process autoincrement fields. + if ($field->{useAutoIncrement}) { + if ($revision == 0 || $previousRecord->{$fieldName} eq '') { + ($value) = $dbLink->db->quickArray("select max($fieldName) + 1 from ".$self->get('tableName')); + $value = 0 unless $value; + } else { + $value = $previousRecord->{$fieldName}; + } + push (@update, "$fieldName = ".$self->session->db->quote($value)); + # Process timestamp fields. + } elsif ($field->{dbFieldType} eq 'timestamp') { + push (@update, "$fieldName = now()"); + } elsif ($field->{isReadOnly}) { + push (@update, "$fieldName =".$self->session->db->quote($field->{processedDefaultValue})); + # Process file uploads. + } elsif ($field->{formFieldType} eq 'file') { + if ($self->session->form->process('_'.$fieldName.'_action') eq 'keep') { + push(@update, "$fieldName = ".$self->session->db->quote($previousRecord->{$fieldName})); + push(@update, "__".$fieldName."_mimeType=".$self->session->db->quote($previousRecord->{"__".$fieldName."_mimeType"})); + } elsif ($self->session->form->process('_'.$fieldName.'_action') eq 'overwrite' && $self->session->form->process($fieldName)) { + my $fileHandle = $self->session->request->upload($fieldName); + my $maxFileSize = ($self->get('maxFileSize') > $self->session->setting->get("maxAttachmentSize")) ? + $self->session->setting->get("maxAttachmentSize") : $self->get('maxFileSize'); + push(@error, $i18n->get('ers file too large')) if (-s $fileHandle > $maxFileSize); + my $fileType = $self->session->request->uploadInfo($self->session->form->process($fieldName))->{'Content-Type'}; + my $fileContents; + while (<$fileHandle>) { + $fileContents .= $_; + } + push(@update, "$fieldName = ".$self->session->db->quote($fileContents)); + push(@update, "__".$fieldName."_mimeType=".$self->session->db->quote($fileType)); + } else { + } + # Throw error if field is required and empty. + } elsif ($self->session->form->process($fieldName) eq '' && $field->{isRequired}) { + push(@error, $i18n->get('ers field required').' '.$field->{displayName}) if ($field->{isRequired}); + # Process other fields. + } else { + # Get input in correct format. +my $fieldValue; + if (defined $self->session->form->process($fieldName)) { +my $cmd = '$self->session->form->'.$field->{formFieldType}.'($fieldName)'; + $fieldValue = eval($cmd); #$self->session->form->process($fieldName) + + if ($field->{formFieldType} eq 'dateTime' && $field->{dbFieldType} eq 'datetime') { + $fieldValue = $self->session->form->process($fieldName); + } + if ($field->{formFieldType} eq 'date' && $field->{dbFieldType} eq 'date') { + $fieldValue = $self->session->form->process($fieldName); + } + if ($field->{formFieldType} eq 'timeField' && $field->{dbFieldType} eq 'time') { + $fieldValue = $self->session->form->process($fieldName); + } + } else { + $fieldValue = $field->{processedDefaultValue}; + } + + # Check if input matches its regex + if (_matchField($self, $fieldValue, $field->{regex})) { + push(@update, "$fieldName = ".$self->session->db->quote($fieldValue)); + } else { + push(@error, $i18n->get('ers regex mismatch').' '.$regexes{$field->{regex}}.' '.$field->{displayName}); + } + + # Check if input is of allowed length. + if ($field->{maxLength} && length($fieldValue) > $field->{maxLength}) { + push (@error, $i18n->get('ers too long').' '.$field->{maxLength}.' '.$field->{displayName}); + } + + # Check if input is in compliance with field constraint + if ($field->{fieldConstraintType}) { +my $result = 1; +my $fieldValueCompare = $fieldValue; + if ($field->{formFieldType} eq 'dateTime' || $field->{formFieldType} eq 'date') { + $fieldValueCompare = $self->session->datetime->setToEpoch($self->session->form->process($fieldName)); + $fieldConstraint = $self->session->datetime->setToEpoch($fieldConstraint); + } + if ($field->{formFieldType} eq 'timeField'){ + $fieldValueCompare = $self->session->datetime->timeToSeconds($self->session->form->process($fieldName)); + $fieldConstraint = $self->session->datetime->timeToSeconds($fieldConstraint); + } + +my $cmd = '$result = 0 if ($fieldValueCompare '.$self->_resolveFieldConstraintType($field->{fieldConstraintType}).' $fieldConstraint)'; + eval($cmd); + + push(@error, $i18n->get('ers value not allowed').' '.$field->{displayName}) if $result; + } + + # Check if input is within field range + if ($allowedDbFieldTypes->{$field->{dbFieldType}}->{maxValue}) { + my $maxValue = ($field->{signed}) ? $allowedDbFieldTypes->{$field->{dbFieldType}}->{maxValue} : $allowedDbFieldTypes->{$field->{dbFieldType}}->{maxValueUnsigned}; + my $minValue = ($field->{signed}) ? $allowedDbFieldTypes->{$field->{dbFieldType}}->{minValue} : 0; + if ($self->session->form->process($fieldName) > $maxValue || $self->session->form->process($fieldName) < $minValue) { + push (@error, $i18n->get('ers out of range').' '.$field->{displayName}); + } + } + } + } + + # Return with a list of errors if there are any. + if (@error) { + return $self->www_editRecord([ map {{'error.message'=>$_}} @error ]); + return "
  • ".join('
  • ',@error).'
'; + } + + # In case of no errors write the new values to a new version. + if (@update) { + $dbLink->db->write('update '.$self->get('tableName').' set __archived=1 where __recordId='.$self->session->db->quote($recordId)); + $dbLink->db->write('insert into '.$self->get('tableName').' set '.join(', ', @update)); + } + + $dbLink->disconnect; + + # Send an email notification. + if ($self->get('sendMailTo')) { + my $mail = WebGUI::Mail::Send->create($self->session, { + to => $self->get('sendMailTo'), + subject => $i18n->get('ers change notification'), + }); + $mail->addText($i18n->get('ers change on table').' '.$self->get('tableName'). + ' '.$i18n->get('ers by user').' '.$self->session->user->username."\n". + $i18n->get('ers view url').' '.$self->getUrl('func=editRecord;rid='.$recordId) + ); + $mail->queue; + } + + return $self->www_view; +} + +#------------------------------------------------------------------- +=head1 www_editRegex + +Returns the form for editing regexes. Pass the id of the regex you want to edit in the form param 'regexId'. To +add a new regex pass 'new' for the regex id. + +=cut + +sub www_editRegex { + my ($output, $properties, $f, $i18n); + my $self = shift; + my $errors = shift; + + return $self->session->privilege->insufficient() unless ($self->_canAlterTable); + + $i18n = WebGUI::International->new($self->session,'Asset_SQLForm'); + + if ($errors) { + $output = ''.$i18n->get('er error message').'
  • '.join('
  • ', @$errors).'
'; + } + + if ($self->session->form->process("regexId") eq 'new') { + $properties = {}; + } else { + $properties = $self->session->db->quickHashRef('select * from SQLForm_regexes where regexId='.$self->session->db->quote($self->session->form->process("regexId"))); + } + + $f = WebGUI::HTMLForm->new($self->session, + -action => $self->getUrl + ); + $f->hidden( + -name => 'func', + -value => 'editRegexSave', + ); + $f->hidden( + -name => 'regexId', + -value => $self->session->form->process("regexId"), + ); + $f->readOnly( + -label => 'Id', + -value => $self->session->form->process("regexId"), + ); + $f->text( + -name => 'name', + -label => $i18n->get('er name'), + -hoverHelp => $i18n->get('er name description'), + -value => $self->session->form->process("name") || $properties->{name}, + ); + $f->text( + -name => 'regex', + -label => $i18n->get('er regex'), + -hoverHelp => $i18n->get('er regex description'), + -value => $self->session->form->process("regex")|| $properties->{regex}, + -size => 30, + ); + $f->readOnly( + -value => + WebGUI::Form::submit($self->session). + WebGUI::Form::button($self->session, { + value => $i18n->get('cancel'), + extras => 'onClick="location.href=\''.$self->getUrl('func=listRegexes').'\'"' + }) + ); + + $self->getAdminConsole->setHelp("edit regex", "Asset_SQLForm"); + return $self->getAdminConsoleWithSubmenu->render($f->print, $i18n->get('edit regex title')); +} + +#------------------------------------------------------------------- +=head1 www_editRegexSave + +Saves the regex properties entered in the form generated by www_editRegex to the database. Pass the regex id +in the form param 'regexId'. Set the id to 'new' to add a regex. + +=cut + +sub www_editRegexSave { + my (@error, $regexId, $i18n); + my $self = shift; + + return $self->session->privilege->insufficient() unless ($self->_canAlterTable); + + $i18n = WebGUI::International->new($self->session,'Asset_SQLForm'); + + push(@error, $i18n->get('ers no name')) unless ($self->session->form->process("name")); + push(@error, $i18n->get('ers no regex')) unless ($self->session->form->process("regex")); + + return $i18n->get('er error message').'
  • '.join('
  • ', @error).'
'.$self->www_editRegex if (@error); + + if ($self->session->form->process("regexId") eq 'new') { + $regexId = $self->session->id->generate(); + $self->session->db->write("insert into SQLForm_regexes set ". + " regexId=".$self->session->db->quote($regexId).", name=".$self->session->db->quote($self->session->form->process("name")).", regex=".$self->session->db->quote($self->session->form->process("regex"))); + } elsif ($self->session->form->process("regexId")) { + $regexId = $self->session->form->process("regexId"); + $self->session->db->write("update SQLForm_regexes set ". + "name=".$self->session->db->quote($self->session->form->process("name")).", regex=".$self->session->db->quote($self->session->form->process("regex"))." where regexId=".$self->session->db->quote($self->session->form->process("regexId"))); + } + + return $self->www_listRegexes; +} + +#------------------------------------------------------------------- +=head1 www_listFields + +Shows the list of fields, including edit and delete buttons. + +=cut + +sub www_listFields { + my (@fields, $output, $thisField, $fieldTypesDefined, $i18n); + my $self = shift; + + return $self->session->privilege->insufficient unless ($self->_canAlterTable); + + $i18n = WebGUI::International->new($self->session,'Asset_SQLForm'); + +# $output = '

'.$self->get('title').'

' if $self->get('displayTitle'); + $output .= $self->_getManagementLinks.'
'; + + ($fieldTypesDefined) = $self->session->db->quickArray("select count(*) from SQLForm_fieldTypes"); + unless ($fieldTypesDefined) { + return $self->processStyle($output. + $i18n->get('no field types message').' '.''.$i18n->get('manage field types').'.' + ); + } + + + @fields = $self->session->db->buildArray( + " select fieldId " + ." from SQLForm_fieldOrder " + ." where assetId=".$self->session->db->quote($self->getId) + ." order by rank" + ); + + $output .= ''; + foreach (@fields) { + $thisField = $self->_getFieldProperties($_); + $output .= ''; + $output .= '' unless ($thisField->{disabled}); + $output .= '' if ($thisField->{disabled}); + $output .= ''; + $output .= ''; + $output .= ''; + $output .= ''; + $output .= ''; + } + $output .= '
'.$self->session->icon->delete('func=disableField;fid='.$_).''.'Undelete'.''.$self->session->icon->moveDown('func=moveFieldDown;fid='.$_).''.$self->session->icon->moveUp('func=moveFieldUp;fid='.$_).''.$self->session->icon->edit('func=editField;fid='.$_, $self->get("url")).''.$thisField->{fieldName}." (".$thisField->{displayName}.")".'
'; + + $output .= '
'.$i18n->get('lf add field').''; + + $self->getAdminConsole->setHelp("manage fields", "Asset_SQLForm"); + return $self->getAdminConsoleWithSubmenu->render($output, $i18n->get('manage fields title')); +} + +#------------------------------------------------------------------- +=head1 www_listFieldTypes + +Shows the list of field types. + +=cut + +sub www_listFieldTypes { + my ($sth, $row, $output, $i18n); + my $self = shift; + + return $self->session->privilege->insufficient() unless ($self->_canAlterTable); + + $i18n = WebGUI::International->new($self->session,'Asset_SQLForm'); + +# $output = '

'.$self->get('title').'

' if $self->get('displayTitle'); + $output .= $self->_getManagementLinks.'
'; + + $sth = $self->session->db->read("select * from SQLForm_fieldTypes order by dbFieldType"); + +my $js = "function toggleList(id) {\n" + ."\tvar a = document.getElementById(id);\n" + ."\tif (a.style.display == 'none') {\n" + ."\t\ta.style.display = ''\n" + ."\t} else { \n" + ."\t\ta.style.display = 'none'\n" + ."\t}\n" + ."}"; + +my (@usedTypes, @unusedTypes); + while ($row = $sth->hashRef) { +my $assetsUsing = $self->session->db->read( + ' select distinct t2.url, t2.title, t1.fieldId, t3.value '. + ' from SQLForm_fieldDefinitions as t1, assetData as t2, SQLForm_fieldDefinitions as t3 '. + ' where t1.assetId=t2.assetId and '. + ' t1.fieldId=t3.fieldId and t3.property="fieldName" and '. + ' t1.property="fieldType" and t1.value='.$self->session->db->quote($row->{fieldTypeId})); +my $currentRow = ''; + $currentRow .= ""; + $currentRow .= $self->session->icon->delete('func=deleteFieldType;ftid='.$row->{fieldTypeId}, $self->getUrl, $i18n->get('lft delete confirm message')) unless ($assetsUsing->rows); + $currentRow .= ""; + + $currentRow .= "".$row->{dbFieldType}."".$row->{formFieldType}.""; + if ($assetsUsing->rows) { + $currentRow .= ''; + $currentRow .= '' + .$i18n->get('lft show assets using').''; + $currentRow .= ''.''; + push(@usedTypes, $currentRow); + } else { + $currentRow .= ""; + push(@unusedTypes, $currentRow); + } + } + + $output .= ''; + $output .= ''; + $output .= ''; + $output .= ''; + $output .= join('',@unusedTypes); + $output .= ''; + $output .= ''; + $output .= join('',@usedTypes); + $output .= '

'.$i18n->get('lft unused field types').'

'.$i18n->get('lft db type').''.$i18n->get('lft form type').'

'.$i18n->get('lft used field types').'

'.$i18n->get('lft db type').''.$i18n->get('lft form type').'
'; + $output .= ''.$i18n->get('lft add field type').''; + + $self->getAdminConsole->setHelp("manage field types", "Asset_SQLForm"); + return $self->getAdminConsoleWithSubmenu->render($output,$i18n->get('manage field types title')); +} + +#------------------------------------------------------------------- +=head1 www_listRegexes + +Displays the list of regexes. + +=cut + +sub www_listRegexes { + my ($sth, $row, $output, $i18n); + my $self = shift; + + return $self->session->privilege->insufficient() unless ($self->_canAlterTable); + + $i18n = WebGUI::International->new($self->session,'Asset_SQLForm'); + +# $output = '

'.$self->get('title').'

' if $self->get('displayTitle'); + $output .= $self->_getManagementLinks.'
'; + + $sth = $self->session->db->read("select * from SQLForm_regexes order by name"); + +my $js = "function toggleList(id) {\n" + ."\tvar a = document.getElementById(id);\n" + ."\tif (a.style.display == 'none') {\n" + ."\t\ta.style.display = ''\n" + ."\t} else { \n" + ."\t\ta.style.display = 'none'\n" + ."\t}\n" + ."}"; + +my (@usedTypes, @unusedTypes); + while ($row = $sth->hashRef) { +my $assetsUsing = $self->session->db->read( + ' select distinct t2.url, t2.title, t1.fieldId, t3.value '. + ' from SQLForm_fieldDefinitions as t1, assetData as t2, SQLForm_fieldDefinitions as t3 '. + ' where t1.assetId=t2.assetId and '. + ' t1.fieldId=t3.fieldId and t3.property="fieldName" and '. + ' t1.property="regex" and t1.value='.$self->session->db->quote($row->{regexId})); +my $currentRow = ''; + $currentRow .= ""; + $currentRow .= $self->session->icon->delete('func=deleteRegex;regexId='.$row->{regexId}, $self->getUrl, 'Are you sure?') unless ($assetsUsing->rows); + $currentRow .= ""; + + $currentRow .= "".$row->{name}."".$row->{regex}.""; + if ($assetsUsing->rows) { + $currentRow .= ''; + $currentRow .= '' + .$i18n->get('lr show assets using').''; + $currentRow .= ''.''; + push(@usedTypes, $currentRow); + } else { + $currentRow .= ""; + push(@unusedTypes, $currentRow); + } + } + + $output .= ''; + $output .= ''; + $output .= ''; + $output .= ''; + $output .= join('',@unusedTypes); + $output .= ''; + $output .= ''; + $output .= join('',@usedTypes); + $output .= '

'.$i18n->get('lr unused regexes').'

'.$i18n->get('lr name').''.$i18n->get('lr regex').'

'.$i18n->get('lr used regexes').'

'.$i18n->get('lr name').''.$i18n->get('lr regex').'
'; + $output .= ''.$i18n->get('lr add regex').''; + + $self->getAdminConsole->setHelp("manage regexes", "Asset_SQLForm"); + return $self->getAdminConsoleWithSubmenu->render($output,$i18n->get('manage regexes title')); +} + +#------------------------------------------------------------------- +=head1 www_moveFieldDown + +Moves the field one position to the end in the field ordering. The field id should be passed in the form param 'fid'. + +=cut + +sub www_moveFieldDown { + my (@fieldOrder, $currentField, $i, $nextField); + my $self = shift; + + return $self->session->privilege->insufficient() unless ($self->_canAlterTable); + + @fieldOrder = $self->session->db->buildArray('select fieldId from SQLForm_fieldOrder where assetId = '.$self->session->db->quote($self->getId).' order by rank'); + $currentField = $self->session->form->process("fid"); + + for ($i = 0; $i < scalar(@fieldOrder); $i++) { + if ($fieldOrder[$i] eq $currentField && $i < (scalar(@fieldOrder) - 1)) { + $nextField = $fieldOrder[$i + 1]; + last; + } + } + + if ($nextField) { + $self->session->db->write('update SQLForm_fieldOrder set rank = rank + 1 where assetId='.$self->session->db->quote($self->getId).' and fieldId='.$self->session->db->quote($currentField)); + $self->session->db->write('update SQLForm_fieldOrder set rank = rank - 1 where assetId='.$self->session->db->quote($self->getId).' and fieldId='.$self->session->db->quote($nextField)); + } + + return $self->www_listFields; +} + +#------------------------------------------------------------------- +=head1 www_moveFieldUp + +Moves the field one position to the beginning in the field ordering. The field id should be passed in the form param 'fid'. + +=cut + +sub www_moveFieldUp { + my (@fieldOrder, $currentField, $i, $previousField); + my $self = shift; + + return $self->session->privilege->insufficient() unless ($self->_canAlterTable); + + @fieldOrder = $self->session->db->buildArray('select fieldId from SQLForm_fieldOrder where assetId = '.$self->session->db->quote($self->getId).' order by rank'); + $currentField = $self->session->form->process("fid"); + + for ($i = 0; $i < scalar(@fieldOrder); $i++) { + if ($fieldOrder[$i] eq $currentField) { + $previousField = $fieldOrder[$i - 1]; + last; + } + } + + if ($i > 0 && $previousField) { + $self->session->db->write('update SQLForm_fieldOrder set rank = rank - 1 where assetId='.$self->session->db->quote($self->getId).' and fieldId='.$self->session->db->quote($currentField)); + $self->session->db->write('update SQLForm_fieldOrder set rank = rank + 1 where assetId='.$self->session->db->quote($self->getId).' and fieldId='.$self->session->db->quote($previousField)); + } + + return $self->www_listFields; +} + +#------------------------------------------------------------------- +=head1 www_purgeRecord + +Will purge a record from the record trash. The id of the record must be passed in form param 'rid'. + +=cut + +sub www_purgeRecord { + my (@recordIds, $whereClause, $dbLink); + my $self = shift; + + return $self->session->privilege->insufficient() unless ($self->_canPurge); + + @recordIds = $self->session->request->param('rid'); + $whereClause = join(' or ', map {'__recordId = '.$self->session->db->quote($_)} @recordIds); + + $dbLink = $self->_getDbLink; + $dbLink->db->write("delete from ".$self->get('tableName')." where $whereClause") if ($whereClause); + $dbLink->disconnect; + + return $self->processStyle($self->www_search); +} + +#------------------------------------------------------------------- +=head1 www_purgeTrash + +Purges every record that is in the record trash. + +=cut +sub www_purgeTrash { + my $self = shift; + + return $self->session->privilege->insufficient() unless ($self->_canPurge); + + my $dbLink = $self->_getDbLink; + $dbLink->db->write('delete from '.$self->get('tableName').' where __deleted=1'); + $dbLink->disconnect; + + return $self->processStyle($self->www_search); +} + +#------------------------------------------------------------------- +=head1 www_viewHistory + +Shows the history of a record. The record id should be passed through the form param 'rid'. + +=cut + +sub www_viewHistory { + my $self = shift; + + return $self->session->privilege->insufficient() unless ($self->canView); + + my $dbLink = $self->_getDbLink; + my $output = $self->_getManagementLinks; + my $recordId = $self->session->form->process("rid"); + + my @fields = $self->session->db->buildArray( + " select distinct t1.value ". + " from SQLForm_fieldDefinitions as t1, SQLForm_fieldOrder as t2 ". + " where t1.fieldId=t2.fieldId and t1.assetId=t2.assetId and t1.property='fieldName' and t1.assetId=".$self->session->db->quote($self->getId). + " order by t2.rank" + ); + my @fieldNames = $self->session->db->buildArray( + " select distinct t1.value ". + " from SQLForm_fieldDefinitions as t1, SQLForm_fieldOrder as t2 ". + " where t1.fieldId=t2.fieldId and t1.assetId=t2.assetId and t1.property='displayName' and t1.assetId=".$self->session->db->quote($self->getId). + " order by t2.rank" + ); + + my $sth = $dbLink->db->read("select ". + " t1.__recordId, from_unixtime(t1.__initDate) as __initDate, ". + " t1.__userId, t1.__revision, ". + join(', ', map {"t1.$_"} @fields). + " from ".$self->get('tableName')." as t1". + " where t1.__recordId=".$self->session->db->quote($recordId). + " order by t1.__revision"); + + $output .= '
'; + + $output .= ''; + $output .= ''; + + my %userCache; + + while (my %row = $sth->hash) { + $userCache{$row{__userId}} = WebGUI::User->new($self->session, $row{__userId}) unless (exists $userCache{$row{__userId}}); + my $username = $userCache{$row{__userId}}->username || $row{__userId}; + + $output .= ''; + $output .= ''; + $output .= ''; + $output .= ''; + $output .= ''; + $output .= ''; + } + + $output .= '
RevInit dateUser'.join('', @fieldNames).'
'.$row{__revision}.''.$row{__initDate}.''.$username.''.join('', map {$row{$_}} @fields).'
'; + + $dbLink->disconnect; + return $self->processStyle($output); +} + +#------------------------------------------------------------------- +=head1 www_viewFile + +Returns the file saved in a file upolad field, and sets the mime-type to the correct value. Pass the record id +via form param 'rid' and the field id of the upload field through form param 'fid'. Optionally you can pass the +revision number in form param 'rev'; otherwise the latest revision is used. + +=cut + +sub www_viewFile { + my ($field, $revision); + my $self = shift; + + return $self->session->privilege->insufficient() unless ($self->canView); + + my $dbLink = $self->_getDbLink; + + $field = $self->_getFieldProperties($self->session->form->process("fid")); + + if ($field->{formFieldType} eq 'file') { + if ($self->session->form->process("rev") =~ m/^\d+$/) { + $revision = '__revision='.$self->session->db->quote($self->session->form->process("rev")); + } else { + $revision = '__archived=0'; + } + + my $sql = + 'select '. + ' __'.$field->{fieldName}.'_mimeType, '. + $field->{fieldName}. + ' from '. + $self->get('tableName'). + ' where '. + '__recordId='.$self->session->db->quote($self->session->form->process("rid")).' and '. + $revision; + my ($mimeType, $data) = $dbLink->db->quickArray($sql); + $self->session->http->setMimeType($mimeType); + + return $data; + } + + return "No file found"; +} + +#------------------------------------------------------------------- +=head1 www_restoreRecord + +Restores a record in the record trash. Pass the record id through for param 'rid'. + +=cut + +sub www_restoreRecord { + my ($dbLink, @recordIds, $whereClause); + my $self = shift; + + return $self->session->privilege->insufficient() unless ($self->_canEditRecord); + + @recordIds = $self->session->request->param('rid'); + $whereClause = join(' or ', map {'__recordId = '.$self->session->db->quote($_)} @recordIds); + + $dbLink = $self->_getDbLink; + $dbLink->db->write("update ".$self->get('tableName')." set ". + " __deleted=0,". + " __deletionDate=NULL,". + " __deletedBy=NULL". + " where $whereClause" + ) if ($whereClause); + $dbLink->disconnect; + + return $self->www_view; +} + +#------------------------------------------------------------------- +=head1 www_search + +Generates the normal search form. + +=cut + +sub www_search { + my (%searchableFields, @showFields, $query, $searchInTrash, @searchIn, $f, $output, %fieldProperties, + $useRegex, $sortColumn, $sortAscending, $recordControls, $queryLike, + %row, $sth, @headerLoop, $var, @recordLoop, %searchInTrashOptions, $i18n); + my $self = shift; + my $error = shift; + + return $self->session->privilege->insufficient() unless ($self->canView); + + $i18n = WebGUI::International->new($self->session,'Asset_SQLForm'); + my $dbLink = $self->_getDbLink; + + # Get field properties; + tie %searchableFields, "Tie::IxHash"; +my @fields = $self->session->db->buildArray("select distinct fieldId from SQLForm_fieldOrder where assetId=".$self->session->db->quote($self->getId)." order by rank"); + foreach (@fields) { + $fieldProperties{$_} = $self->_getFieldProperties($_); + unless ($fieldProperties{$_}->{disabled}) { + $searchableFields{$_} = $fieldProperties{$_}->{displayName} if $fieldProperties{$_}->{isSearchable}; + push(@showFields, $_) if $fieldProperties{$_}->{showInSearchResults}; + } + } + + $var->{showFieldsDefined} = 1 if (@showFields); + + # Set up search parameters +# @searchIn = @{Storable::thaw($self->session->scratch->get('SQLForm_'.$self->getId.'searchIn'))} if (defined $self->session->scratch->get('SQLForm_'.$self->getId.'searchIn')); + @searchIn = $self->session->form->checkList('searchIn') if (defined $self->session->form->process("searchIn")); + @searchIn = split(/\n/,$self->session->scratch->get('SQLForm_'.$self->getId.'searchIn')) unless (defined $self->session->form->process("searchIn")); + @searchIn = keys(%searchableFields) unless (@searchIn); + + $query = $self->session->form->process("searchQuery"); + $query = $self->session->scratch->get('SQLForm_'.$self->getId.'query') unless ($query); + + $useRegex = $self->session->form->process("searchMode"); + $useRegex = $self->session->scratch->get('SQLForm_'.$self->getId.'searchMode') unless (defined $self->session->form->process("useRegex")); + $useRegex ||= 'normal'; + + $searchInTrash = $self->session->form->process("searchInTrash"); + $searchInTrash = $self->session->scratch->get('SQLForm_'.$self->getId.'searchInTrash') unless (defined $self->session->form->process("searchInTrash")); + $searchInTrash ||= '0'; + + $sortColumn = $self->session->form->process("sortColumn"); + $sortColumn = $self->session->scratch->get('SQLForm_'.$self->getId.'sortColumn') unless ($sortColumn); + + $sortAscending = $self->session->form->process("sortAscending"); + $sortAscending = $self->session->scratch->get('SQLForm_'.$self->getId.'sortAscending') unless (defined $self->session->form->process("sortAscending")); + + # Save search parameters + $self->session->scratch->set('SQLForm_'.$self->getId.'searchIn', join("\n", @searchIn)) if (@searchIn); + $self->session->scratch->set('SQLForm_'.$self->getId.'query', $query); + $self->session->scratch->set('SQLForm_'.$self->getId.'searchMode', $useRegex); + $self->session->scratch->set('SQLForm_'.$self->getId.'searchInTrash', $searchInTrash); + $self->session->scratch->set('SQLForm_'.$self->getId.'searchType', 'or'); + + + + tie %searchInTrashOptions, "Tie::IxHash"; + %searchInTrashOptions = (0 => 'Only normal', 1 => 'Only trash', 2 => 'Normal and trash'); + + $f = WebGUI::HTMLForm->new($self->session, + -action => $self->getUrl + ); + $f->hidden( + -name => 'func', + -value => 'search', + ); + $f->hidden( + -name => 'searchType', + -value => 'or', + ); + $f->text( + -name => 'searchQuery', + -label => $i18n->get('s query'), + -value => $query, + ); + $f->radioList( + -name => 'searchMode', + -label => $i18n->get('s mode'), + -value => $useRegex , + -options=> {'normal' => 'Normal search', 'regexp' => 'Regex search'}, + ); + $f->checkList( + -name => 'searchIn', + -label => $i18n->get('s search in fields'), + -options=> \%searchableFields, + -value => \@searchIn, + ); + $f->radioList( + -name => 'searchInTrash', + -label => $i18n->get('s location'), + -options=> \%searchInTrashOptions, + -value => $searchInTrash, + ); + $f->submit( + -value => $i18n->get('s search button'), + ); + + $var->{searchForm} = $f->print; + + foreach (@showFields) { + $fieldProperties{$_} = $self->_getFieldProperties($_); + push(@headerLoop, { + 'header.title' => $fieldProperties{$_}->{displayName}, + 'header.sort.url' => $self->getUrl('func=search;sortColumn='.$_.';sortAscending='.($sortAscending ? '0' : '1')), + 'header.sort.onThis' => ($sortColumn eq $_), + 'header.sort.ascending' => $sortAscending, + }); + } + + $var->{'headerLoop'} = \@headerLoop; + + + if (@searchIn && ($query || $searchInTrash)) { +my $sql = $self->_constructSearchQuery(\@searchIn, \@showFields, \%fieldProperties, $query); + + if ($sql) { + # Execute query + $sth = $dbLink->db->unconditionalRead($sql); + + # Handle invalid queries + push(@$error, $i18n->get('s query error').' '. $sth->errorMessage) unless ($sth->errorCode < 1); + + $var->{'searchResults.recordLoop'} = $self->_processSearchQuery($sth, \@showFields, \%fieldProperties); + } + } + + $var->{'superSearch.url'} = $self->getUrl('func=superSearch'); + $var->{'superSearch.label'} = $i18n->get('s advanced search'); + $var->{'normalSearch.url'} = $self->getUrl('func=search'); + $var->{'normalSearch.label'} = $i18n->get('s normal search'); + + $var->{'searchResults.header'} = WebGUI::Form::formHeader($self->session). + WebGUI::Form::hidden($self->session, {name=>'func',value=>'', id=>'SearchResultsAction'}); + $var->{'searchResults.footer'} = WebGUI::Form::formFooter($self->session); + $var->{'searchResults.actionButtons'} = + WebGUI::Form::button($self->session, { + value => $i18n->get('s restore'), + extras => "onclick=\"document.getElementById('SearchResultsAction').value='restoreRecord'; this.form.submit();\"", + }). + WebGUI::Form::button($self->session, { + value => $i18n->get('s purge'), + extras => "onclick=\"document.getElementById('SearchResultsAction').value='purgeRecord'; this.form.submit();\"", + }) if ($searchInTrash); + + $var->{showMetaData} = $self->get('showMetaData'); + $var->{managementLinks} = $self->_getManagementLinks; + $var->{errorOccurred} = defined $error; + $var->{errorLoop} = [ map {{'error.message' => $_}} @$error ]; + + $dbLink->disconnect; + + # Only process style if search is called directly; + return $self->processTemplate($var, $self->getValue('searchTemplateId')) unless ($self->session->form->process("func") eq 'search'); + return $self->processStyle($self->processTemplate($var, $self->getValue('searchTemplateId'))); +} + +#------------------------------------------------------------------- +=head1 www_processAjaxRequest + +Returns an XML string containing database information, depending on the form params passed. If you pass a database +name in form param 'dbName' only, this method will return an XML string containing the tables available within that +database. If you also pass a table name through form param 'tName' an XML string containing the columns in that table +are returned. + +Format of the XML must follow the next convention: + + + + + + ...etc... + + + +=cut + +sub www_processAjaxRequest { + my $self = shift; + + my $dbLink = $self->_getDbLink; + return $self->session->privilege->insufficient unless $self->_canAlterTable; + + $self->session->http->setMimeType('text/xml'); + +my $xml = "\n"; + + if (isIn($self->session->form->process("dbName"), $dbLink->db->buildArray('show databases'))) { + +my @zut; + if ($self->session->form->process("tName") && isIn($self->session->form->process("tName"), $dbLink->db->buildArray('show tables from '.$self->session->form->process("dbName")))) { + @zut = $dbLink->db->buildArray('describe '.$self->session->form->process("dbName").'.'.$self->session->form->process("tName")); + } else { + @zut = $dbLink->db->buildArray('show tables from '.$self->session->form->process("dbName")); + } + + foreach (@zut) { + $xml .= "\t\n"; + } + } + + $xml .= ""; + + return $xml; +} + +#------------------------------------------------------------------- +=head1 _constructSearchForm ( fieldList, fieldProperties ) + +Returns the form for super search. + +=head2 fieldList + +Arrayref containing the field that should be included in the search. + +=head2 fieldProperties + +Hashref containing the properties of the fields that are in the search. + +=cut + +sub _constructSearchForm { + my ($form, $js, %searchInTrashOptions, $i18n); + my $self = shift; + my $fieldList = shift; + my $fieldProperties = shift; + + $i18n = WebGUI::International->new($self->session,'Asset_SQLForm'); + tie %searchInTrashOptions, "Tie::IxHash"; + %searchInTrashOptions = ( + 0 => $i18n->get('_csf only normal'), + 1 => $i18n->get('_csf only trash'), + 2 => $i18n->get('_csf normal and trash') + ); + + $self->session->style->setScript($self->session->config->get("extrasURL").'/'.'wobject/SQLForm/SQLFormSearch.js', {type => 'text/javascript'}); + +my $searchType = $self->session->form->process("searchType") || $self->session->scratch->get('SQLForm_'.$self->getId.'searchType') || 'or'; + +my $searchInTrash = $self->session->form->process("searchInTrash"); + $searchInTrash = $self->session->scratch->get('SQLForm_'.$self->getId.'searchInTrash') unless (defined $self->session->form->process("searchInTrash")); + $searchInTrash ||= '0'; + + $form = WebGUI::Form::formHeader($self->session); + $form .= WebGUI::Form::hidden($self->session, {name => 'func', value => 'superSearch'}); + $form .= WebGUI::Form::hidden($self->session, {name => 'searchQueried', value => 1}); + $form .= ''; + $form .= ''; + $form .= ''; + $form .= ''; + + $self->session->scratch->set('SQLForm_'.$self->getId.'searchType', $searchType); + $self->session->scratch->set('SQLForm_'.$self->getId.'searchInTrash', $searchInTrash); + + foreach (@$fieldList) { + if ($self->session->form->process("searchQueried")) { + $self->session->scratch->delete('SQLForm_'.$self->getId.'---'.$_.'v1'); + $self->session->scratch->delete('SQLForm_'.$self->getId.'---'.$_.'v2'); + $self->session->scratch->delete('SQLForm_'.$self->getId.'---'.$_.'c'); + } + +my $formValue1 = $self->session->form->process($_.'-1') || $self->session->scratch->get('SQLForm_'.$self->getId.'---'.$_.'v1'); +my $formValue2 = $self->session->form->process($_.'-2') || $self->session->scratch->get('SQLForm_'.$self->getId.'---'.$_.'v2'); +my $conditional = $self->session->form->process('_'.$_.'_conditional') || $self->session->scratch->get('SQLForm_'.$self->getId.'---'.$_.'c'); + + $self->session->scratch->set('SQLForm_'.$self->getId.'---'.$_.'v1', $formValue1); + $self->session->scratch->set('SQLForm_'.$self->getId.'---'.$_.'v2', $formValue2); + $self->session->scratch->set('SQLForm_'.$self->getId.'---'.$_.'c', $conditional); + + if ($fieldProperties->{$_}->{type} eq 'list') { + if ($self->session->form->process($_.'-2')) { + $formValue2 = [ $self->session->request->param($_.'-2') ]; + $self->session->scratch->set('SQLForm_'.$self->getId.'---'.$_.'v2', Storable::freeze($formValue2)); + } else { + $formValue2 = eval('Storable::thaw($formValue2)'); + } + } + + $form .= ''; + $form .= ''; + + $form .= ''; + $form .= ''; + $form .= ''; + } + $form .= ''; + $form .= '
'.$i18n->get('s location').''.WebGUI::Form::radioList($self->session, { + name => "searchInTrash", + options => \%searchInTrashOptions, + value => $searchInTrash, + }); + $form .= '
'.$i18n->get('s search type').''.WebGUI::Form::radioList($self->session, { + name => "searchType", + options => {'or' => $i18n->get('or'), 'and' => $i18n->get('and')}, + value => $searchType, + }); + $form .= '
'.$fieldProperties->{$_}->{displayName}.''; + if (exists $types->{$fieldProperties->{$_}->{type}}) { + $form .= WebGUI::Form::selectList($self->session, { + name => '_'.$_.'_conditional', + value => [ $conditional || '' ], + options => $types->{$fieldProperties->{$_}->{type}}, + extras => 'onchange="'.$typeFunctions->{$fieldProperties->{$_}->{type}}.'(this.value, \''.$_.'\')"', + size => 1, + multiple=> 0, + }); + $js .= $typeFunctions->{$fieldProperties->{$_}->{type}}."('".$conditional."', '$_');"; + } + $form .= ''; + + my $parameters = {}; + $parameters->{name} = $_.'-1'; + $parameters->{value} = $formValue1; + $parameters->{options} = $fieldProperties->{$_}->{options} if ($fieldProperties->{$_}->{hasOptions}); + $parameters->{id} = $_.'-1"'; + +my $searchElement = $fieldProperties->{$_}->{searchElement}; + $searchElement = 'text' if ($searchElement eq 'selectList'); +my $cmd = "WebGUI::Form::$searchElement".'($self->session, $parameters)'; + $form .= eval($cmd); + + unless ($fieldProperties->{$_}->{type} eq 'text') { + $searchElement = $fieldProperties->{$_}->{searchElement}; + $parameters->{name} = $_.'-2'; + $parameters->{value} = $formValue2; + $parameters->{size} = undef; + $parameters->{id} = $_.'-2"'; + if ($fieldProperties->{$_}->{type} eq 'list') { + $parameters->{multiple} = 1; + $parameters->{size} = 5; + $parameters->{value} = $formValue2; + } + + $cmd = "WebGUI::Form::$searchElement".'($self->session, $parameters)'; + $form .= eval($cmd); + } + + $form .= '
'.WebGUI::Form::submit($self->session, {value => $i18n->get('s search button')}).'
'; + $form .= WebGUI::Form::formFooter($self->session); + $form .= ''; + + return $form; +} + +#------------------------------------------------------------------- +=head1 _constructSearchQuery ( searchInFields, showFields, fieldProperties ) + +Constructs an SQL query from the search query + +=head2 searchInFields + +Arrayref containing the field id's that should be included in the search. + +=head2 showFields + +List of field id's that should be shown in the results. + +=head2 fieldProperties + +Hashref containing the properties of the fields that are in the search. + +=cut + +sub _constructSearchQuery { + my (@tables, @joinConstraints, $tableCounter, @constraints, $currentField, $conditional, @joinSequence); + my $self = shift; + my $searchInFields = shift; + my $showFields = shift; + my $fieldProperties = shift; + my $passedQuery = shift; + + # This variable should be set to value of the minimum word length for fulltext searches + # as it is set in your MySQL database. Normally this is 3. + my $minimumFulltextLength = 3; + + # Include the table the form writes to. + $tableCounter = 2; + + # Process search fields. + foreach $currentField (@$searchInFields) { + # Set conditional given for this field or to like or regexp mode if in normal search +my $searchMode = $self->session->form->process("searchMode") || $self->session->scratch->get('SQLForm_'.$self->getId.'searchMode'); + if ($searchMode) { + $conditional = 100 if ($searchMode eq 'normal'); + $conditional = 101 if ($searchMode eq 'regexp'); + } else { + $conditional = $self->session->form->process('_'.$currentField.'_conditional') || $self->session->scratch->get('SQLForm_'.$self->getId.'---'.$currentField.'c'); + } + + $tableCounter++; + + if ($conditional ne '') { +my $currentFieldProperties = $fieldProperties->{$currentField}; +my $fieldName = $currentFieldProperties->{fieldName}; +my $fieldType = $currentFieldProperties->{type}; +my $fullFieldName = "t1.$fieldName"; +my $constraint; +my $query = $passedQuery || $self->session->form->process("searchQuery") || $self->session->form->process($currentField.'-1') || $self->session->scratch->get('SQLForm_'.$self->getId.'query'); +my $queryLike; + if ($conditional == 100 || $conditional == 101) { + $query =~ s/\\/\\\\/g; + $query =~ s/'/\\'/g; + + # Search on 'like' + if ($conditional == 100) { + $queryLike = $query; + $queryLike =~ s/%/\\%/g; + $queryLike =~ s/\*/%/g; + $queryLike = "'%".$queryLike."%'"; + } + $query = "'$query'"; + } + +my $formValue1 = $self->session->form->process($currentField.'-1') || $self->session->scratch->get('SQLForm_'.$self->getId.'---'.$currentField.'v1'); +my $formValue2 = $self->session->form->process($currentField.'-2') || $self->session->scratch->get('SQLForm_'.$self->getId.'---'.$currentField.'v2'); + + if ($fieldType eq 'list') { + if ($self->session->form->process($currentField.'-2')) { + $formValue2 = [ $self->session->request->param($currentField.'-2') ]; + } else { + $formValue2 = Storable::thaw($formValue2); + } + } + + if ($conditional == 200 && $formValue2) { + #$constraint = "(".join(' or ', map {"$fullFieldName = ".$self->session->db->quote($_)} $self->session->request->param($currentField.'-2')).")"; + $constraint = "(".join(' or ', map {"$fullFieldName = ".$self->session->db->quote($_)} @$formValue2).")"; + } elsif ($conditional == 201 && $formValue2) { + #$constraint = "(".join(' and ', map {"$fullFieldName = ".$self->session->db->quote($_)} $self->session->request->param($currentField.'-2')).")"; + $constraint = "(".join(' or ', map {"$fullFieldName = ".$self->session->db->quote($_)} @$formValue2).")"; + # Match the joined columns only if type is a list and has joins. + # Else the regular like and regex will handle this. + } elsif ($fieldType eq 'list' && $currentFieldProperties->{numberOfJoins}) { +my $prepend = "t$tableCounter"; + for my $joinCounter (1 .. $currentFieldProperties->{numberOfJoins}) { +my $joinStatement = $currentFieldProperties->{"database$joinCounter"}.'.'. + $currentFieldProperties->{"table$joinCounter"}." as ".$prepend."table$joinCounter"; + + if ($joinCounter > 1) { + $joinStatement .= " on ". + $prepend.$currentFieldProperties->{"joinOnA$joinCounter"}.'='. + $prepend.$currentFieldProperties->{"joinOnB$joinCounter"}; + } else { + $joinStatement .= " on ". + $fullFieldName." = ".$prepend.$currentFieldProperties->{selectField1}; + } + push(@joinSequence, $joinStatement); + } + if ($conditional == 100) { + $constraint .= $prepend.$currentFieldProperties->{selectField2}." like ".$queryLike; + } else { + $constraint .= $prepend.$currentFieldProperties->{selectField2}." regexp($query)"; + } + # 10 = between + } elsif ($conditional == 10) { + $constraint = + "($fullFieldName > ".$self->session->db->quote($formValue1)." and ". + " $fullFieldName <".$self->session->db->quote($formValue2).")"; + # 100 = like + } elsif ($conditional == 100) { + if ($currentFieldProperties->{useFulltext} && length($query) >= $minimumFulltextLength) { + $constraint = "match($fullFieldName) against($query in boolean mode)"; + } else { + $constraint = "$fullFieldName like $queryLike"; + } + # 101 = regexp + } elsif ($conditional == 101) { + $constraint = "$fullFieldName regexp($query)"; + } else { + $constraint = "$fullFieldName ".$types->{$fieldType}->{$conditional}." ".$self->session->db->quote($formValue1); + } + + push(@constraints, $constraint) if $constraint; + } + } + +my $searchInTrash = $self->session->scratch->get('SQLForm_'.$self->getId.'searchInTrash') || $self->session->form->process("searchInTrash") || '0'; + +my $searchType = ($self->session->form->process("searchType") || $self->session->scratch->get('SQLForm_'.$self->getId.'searchType')) eq 'and' ? 'and' : 'or'; + + return undef if (!@constraints); + + # Construct the search query +my $sql = " select t1.__recordId, t1.__deletionDate, t1.__deletedBy, t1.__initDate, t1.__userId, t1.__deleted, t1.__archived, t1.__revision "; + $sql .= ", ".join(", \n", map {"t1.".$fieldProperties->{$_}->{fieldName}} @$showFields)."\n"; + $sql .= " from ".$self->get('tableName').' as t1 '; + $sql .= " left join ".join(" left join \n", @joinSequence)."\n" if (@joinSequence); + $sql .= " where "; + $sql .= "(".join(" $searchType \n", @constraints).")\n" if (@constraints); + $sql .= " and " if (@constraints); + $sql .= " t1.__archived=0 "; + $sql .= " and t1.__deleted=".$self->session->db->quote($searchInTrash) if ($searchInTrash < 2); + +my $sortColumn = $self->session->form->process("sortColumn"); + $sortColumn = $self->session->scratch->get('SQLForm_'.$self->getId.'sortColumn') unless ($sortColumn); + $self->session->scratch->set('SQLForm_'.$self->getId.'sortColumn', $sortColumn); + +my $sortAscending = $self->session->form->process("sortAscending"); + $sortAscending = $self->session->scratch->get('SQLForm_'.$self->getId.'sortAscending') unless (defined $self->session->form->process("sortAscending")); + $self->session->scratch->set('SQLForm_'.$self->getId.'sortAscending', $sortAscending); + + if (isIn($sortColumn, @$showFields)) { + $sql .= " order by ".$fieldProperties->{$sortColumn}->{fieldName}; + $sql .= " desc " unless ($sortAscending); + } + + return $sql; +} + +#------------------------------------------------------------------- +=head1 _processSearchQuery ( sth, showFields, fieldProperties ) + +Processes the results of a search query and returns an arrayref suitable for use as a template loop. + +=head2 sth + +Statement handle of the executed query. + +=head2 showFields + +List of field id's that should be shown in the results. + +=head2 fieldProperties + +Hashref containing the properties of the fields that are in the search. + +=cut + +sub _processSearchQuery { + my $self = shift; + my $sth = shift; + my $showFields = shift; + my $fieldProperties = shift; + + my $i18n = WebGUI::International->new($self->session, 'Asset_SQLForm'); + +my $recordControls; +my $searchInTrash; +my @recordLoop; + + while (my %row = $sth->hash) { + my %record; + my $fieldValues; + if ($self->_canEditRecord) { + if ($row{__deleted}) { + $recordControls = WebGUI::Form::checkbox($self->session, {name=>'rid', value=>$row{__recordId}}); + $recordControls .= ''. + ''; + } else { + $recordControls = $self->session->icon->delete('func=deleteRecord'.';rid='.$row{__recordId},$self->get("url"), + $i18n->get('_psq confirm delete message')); + $recordControls .= $self->session->icon->edit('func=editRecord;rid='.$row{__recordId},$self->get("url")); + $recordControls .= $self->session->icon->copy('func=editRecord;rid=new;copyRecordId='.$row{__recordId},$self->get("url")); + } + $record{'record.controls'} = $recordControls; + } + + $record{'record.controls'} .= ''. + ''; + + if ($searchInTrash) { + $record{'record.deletionDate'} = $self->session->datetime->epochToHuman($row{__deletionDate}); + $record{'record.deletedBy'} = WebGUI::User->new($self->session, $row{__deletedBy})->username; + } else { + $record{'record.updateDate'} = $self->session->datetime->epochToHuman($row{__initDate}); + $record{'record.updatedBy'} = WebGUI::User->new($self->session, $row{__userId})->username; + } + + foreach (@$showFields) { +my $value; + + $fieldProperties->{$_} = $self->_getFieldProperties($_) unless (exists $fieldProperties->{$_}); + + if ($fieldProperties->{$_}->{hasOptions}) { + $value = $fieldProperties->{$_}->{allOptions}->{$row{$fieldProperties->{$_}->{fieldName}}}; + } else { + $value = $row{$fieldProperties->{$_}->{fieldName}}; + } + + $value =~ s/\n/
/g if (1); + + my $props = { + 'record.value' => $value, + }; + + if ($fieldProperties->{$_}->{formFieldType} eq 'file') { + $props->{'record.value.isFile'} = 1; + $props->{'record.value.isImage'} = 1 if ($row{'__'.$fieldProperties->{$_}->{fieldName}.'_mimeType'} =~ m/^image/); + $props->{'record.value.downloadUrl'} = + $self->getUrl('func=viewFile;rid='.$row{__recordId}.';fid='.$_); + } + + push(@$fieldValues, $props); + } + + $record{'record.valueLoop'} = $fieldValues; + + push(@recordLoop, {%record}); + } + + return \@recordLoop; +} + +#------------------------------------------------------------------- +=head1 www_superSearch + +Returns the super search. + +=cut + +sub www_superSearch { + my (@searchableFields, %fieldProperties, $var, @headerLoop, $sortAscending, $sortColumn, $i18n); + my $self = shift; + + $i18n = WebGUI::International->new($self->session,'Asset_SQLForm'); + + $sortColumn = $self->session->form->process("sortColumn"); + $sortColumn = $self->session->scratch->get('SQLForm_'.$self->getId.'sortColumn') unless ($sortColumn); + + $sortAscending = $self->session->form->process("sortAscending"); + $sortAscending = $self->session->scratch->get('SQLForm_'.$self->getId.'sortAscending') unless (defined $self->session->form->process("sortAscending")); + + + $self->session->scratch->delete('SQLForm_'.$self->getId.'searchMode'); + +my @fields = $self->session->db->buildArray("select distinct fieldId from SQLForm_fieldOrder where assetId=".$self->session->db->quote($self->getId)." order by rank"); +my @showFields; + foreach (@fields) { + $fieldProperties{$_} = $self->_getFieldProperties($_); + unless ($fieldProperties{$_}->{disabled}) { + push(@searchableFields, $_) if ($fieldProperties{$_}->{isSearchable}); + push(@showFields, $_) if ($fieldProperties{$_}->{showInSearchResults}); + } + } + + foreach (@showFields) { + $fieldProperties{$_} = $self->_getFieldProperties($_); + push(@headerLoop, { + 'header.title' => $fieldProperties{$_}->{displayName}, + 'header.sort.url' => $self->getUrl('func=superSearch;sortColumn='.$_.';sortAscending='.($sortAscending ? '0' : '1')), + 'header.sort.onThis' => ($sortColumn eq $_), + 'header.sort.ascending' => $sortAscending, + }); + } + + $var->{'headerLoop'} = \@headerLoop; + + + # Construct search form + $var->{searchForm} = $self->_constructSearchForm(\@searchableFields, \%fieldProperties); + + # Build search query +my $sql = $self->_constructSearchQuery(\@searchableFields, \@showFields, \%fieldProperties); + + if ($sql) { + # Retrieve search results +my $dbLink = $self->_getDbLink; +my $sth = $dbLink->db->unconditionalRead($sql); + + # Process search results + $var->{'searchResults.recordLoop'} = $self->_processSearchQuery($sth, \@showFields, \%fieldProperties); + + # Close db connections to prevent memory leaks + $sth->finish; + $dbLink->disconnect; + } + + $var->{'superSearch.url'} = $self->getUrl('func=superSearch'); + $var->{'superSearch.label'} = $i18n->get('s advanced search'); + $var->{'normalSearch.url'} = $self->getUrl('func=search'); + $var->{'normalSearch.label'} = $i18n->get('s normal search'); + + $var->{showFieldsDefined} = 1 if (@showFields); + $var->{'searchResults.header'} = WebGUI::Form::formHeader($self->session). + WebGUI::Form::hidden($self->session, {name=>'func',value=>'', id=>'SearchResultsAction'}); + $var->{'searchResults.footer'} = WebGUI::Form::formFooter($self->session); + $var->{'searchResults.actionButtons'} = + WebGUI::Form::button($self->session, { + value => $i18n->get('s restore'), + extras => "onclick=\"document.getElementById('SearchResultsAction').value='restoreRecord'; this.form.submit();\"", + }). + WebGUI::Form::button($self->session, { + value => $i18n->get('s purge'), + extras => "onclick=\"document.getElementById('SearchResultsAction').value='purgeRecord'; this.form.submit();\"", + }) if ($self->session->form->process("searchInTrash")); + + $var->{showMetaData} = $self->get('showMetaData'); + $var->{managementLinks} = $self->_getManagementLinks; + + return $self->processStyle($self->processTemplate($var, $self->getValue('searchTemplateId'))); +} + +1; diff --git a/lib/WebGUI/Help/Asset_SQLForm.pm b/lib/WebGUI/Help/Asset_SQLForm.pm new file mode 100644 index 000000000..e0c25ab3d --- /dev/null +++ b/lib/WebGUI/Help/Asset_SQLForm.pm @@ -0,0 +1,366 @@ +package WebGUI::Help::Asset_SQLForm; + +our $HELP = { + 'sqlform add/edit ' => { + title => 'edit sqlform', + body => 'sqlform description', + fields => [ + { + title => 'gef table name', + description => 'gef table name description', + namespace => 'Asset_SQLForm', + }, + + { + title => 'gef import table', + description => 'gef import table description', + namespace => 'Asset_SQLForm', + }, + + { + title => 'gef database to use', + description => 'gef database to use description', + namespace => 'Asset_SQLForm', + }, + + { + title => 'gef max file size', + description => 'gef max file size description', + namespace => 'Asset_SQLForm', + }, + + { + title => 'gef send mail to', + description => 'gef send mail to description', + namespace => 'Asset_SQLForm', + }, + + { + title =>'show meta data', + description => 'gef show meta data description', + namespace => 'Asset_SQLForm', + }, + + { + title => 'gef edit template', + description => 'gef edit template description', + namespace => 'Asset_SQLForm', + }, + + { + title => 'gef search template', + description => 'gef search template description', + namespace => 'Asset_SQLForm', + }, + + { + title => 'gef submit group', + description => 'gef submit group description', + namespace => 'Asset_SQLForm', + }, + ], + related => [ + { + tag => 'manage fields', + namespace => 'Asset_SQLForm', + }, + + { + tag => 'manage field types', + namespace => 'Asset_SQLForm' + }, + + { + tag => 'manage regexes', + namespace => 'Asset_SQLForm', + }, + ], + }, + + 'edit field' => { + title => 'edit field title', + body => 'edit field description', + fields => [ + { + title => 'ef field name', + description => 'ef field name description', + namespace => 'Asset_SQLForm', + }, + + { + title => 'ef display name', + description => 'ef display name description', + namespace => 'Asset_SQLForm', + }, + + { + title => 'ef field type', + description => 'ef field type description', + namespace => 'Asset_SQLForm', + }, + + { + title => 'ef signed', + description => 'ef signed description', + namespace => 'Asset_SQLForm', + }, + + { + title => 'ef autoincrement', + description => 'ef autoincrement description', + namespace => 'Asset_SQLForm', + }, + + { + title => 'ef form height', + description => 'ef form height description', + namespace => 'Asset_SQLForm', + }, + + { + title => 'ef max field length', + description => 'ef max field length description', + namespace => 'Asset_SQLForm', + }, + + { + title => 'ef max field length', + description => 'ef max field length description', + namespace => 'Asset_SQLForm', + }, + + { + title => 'ef regex', + description => 'ef regex description', + namespace => 'Asset_SQLForm', + }, + + { + title => 'ef required', + description => 'ef required description', + namespace => 'Asset_SQLForm', + }, + + { + title => 'ef read only', + description => 'ef read only description', + namespace => 'Asset_SQLForm', + }, + + { + title => 'ef default value', + description => 'ef default value description', + namespace => 'Asset_SQLForm', + }, + + { + title => 'ef field constraint', + description => 'ef field constraint description', + namespace => 'Asset_SQLForm', + }, + + { + title => 'ef searchable', + description => 'ef searchable description', + namespace => 'Asset_SQLForm', + }, + + { + title => 'ef fulltext', + description => 'ef fulltext description', + namespace => 'Asset_SQLForm', + }, + + { + title => 'ef show in search', + description => 'ef show in search description', + namespace => 'Asset_SQLForm', + }, + + { + title => 'ef summary length', + description => 'ef summary length description', + namespace => 'Asset_SQLForm', + }, + + { + title => 'ef populate keys', + description => 'ef populate keys description', + namespace => 'Asset_SQLForm', + }, + + { + title => 'ef populate values', + description => 'ef populate values description', + namespace => 'Asset_SQLForm', + }, + + { + title => 'ef join selector', + description => 'ef join selector description', + namespace => 'Asset_SQLForm', + }, + + { + title => 'ef join constraint', + description => 'ef join constraint description', + namespace => 'Asset_SQLForm', + }, + + { + title => 'ef join keys', + description => 'ef join keys description', + namespace => 'Asset_SQLForm', + }, + + { + title => 'ef join values', + description => 'ef join values description', + namespace => 'Asset_SQLForm', + }, + + ], + related => [ + { + tag => 'manage field types', + namespace => 'Asset_SQLForm' + }, + + { + tag => 'manage regexes', + namespace => 'Asset_SQLForm', + }, + ], + }, + + 'edit field type' => { + title => 'edit field type title', + body => 'edit field type description', + fields => [ + { + title => 'eft db field type', + description => 'eft db field type description', + namespace => 'Asset_SQLForm', + }, + { + title => 'eft form field type', + description => 'eft form field type description', + namespace => 'Asset_SQLForm', + }, + ], + related => [ + + { + tag => 'manage fields', + namespace => 'Asset_SQLForm' + }, + ], + }, + + 'edit regex' => { + title =>'edit regex title', + body => 'edit regex description', + fields =>[ + { + title => 'er name', + description => 'er name description', + namespace => 'Asset_SQLForm', + }, + { + title => 'er regex', + description => 'er regex description', + namespace => 'Asset_SQLForm', + }, + ], + related => [ + { + tag => 'manage fields', + namespace => 'Asset_SQLForm' + }, + ], + }, + + 'manage fields' => { + title =>'manage fields title', + body => 'edit field description', + related => [ + { + tag => 'edit field', + namespace => 'Asset_SQLForm', + }, + { + tag => 'manage field types', + namespace => 'Asset_SQLForm' + }, + + { + tag => 'manage regexes', + namespace => 'Asset_SQLForm', + }, + ], + }, + + 'manage field types' => { + title => 'manage field types title', + body => 'edit field type description', + related => [ + { + tag => 'edit field type', + namespace => 'Asset_SQLForm', + }, + { + tag => 'manage fields', + namespace => 'Asset_SQLForm' + }, + ], + }, + + 'manage regexes' => { + title =>'manage regexes title', + body => 'edit regex description', + related => [ + { + tag => 'edit regex', + namespace => 'Asset_SQLForm', + }, + { + tag => 'manage fields', + namespace => 'Asset_SQLForm' + }, + ], + }, + + 'edit record template' => { + title => 'edit template help title', + body => 'edit template help', + related => [ + { + tag => 'sqlform add/edit', + namespace => 'Asset_SQLForm', + }, + { + tag => 'template language', + namespace => 'Asset_SQLForm', + }, + ], + }, + + 'search record template' => { + title => 'search template help title', + body => 'search template help', + related => [ + { + tag => 'sqlform add/edit', + namespace => 'Asset_SQLForm', + }, + { + tag => 'template language', + namespace => 'Asset_SQLForm', + }, + ], + }, + +}; + +1; + diff --git a/lib/WebGUI/i18n/English/Asset_SQLForm.pm b/lib/WebGUI/i18n/English/Asset_SQLForm.pm new file mode 100644 index 000000000..a7ef52c53 --- /dev/null +++ b/lib/WebGUI/i18n/English/Asset_SQLForm.pm @@ -0,0 +1,1322 @@ +package WebGUI::i18n::English::Asset_SQLForm; + +our $I18N = { + 'change field warning' => { + message => q|Changing the following properties can result in permanent loss of data in this field:\n\n +\t - Database field type.\n +\t - Decreasing the Maximum field length.\n +\t - Switching the Sign.\n\n\n +Are you sure to continue?|, + lastUpdated => 0, + }, + + 'ef field name' => { + message => q|Field name (column name)|, + lastUpdated => 0, + }, + + 'ef field name description' => { + message => q|

This sets the name of the column in the database +tied to this field.

|, + lastUpdated => 0, + }, + + 'ef display name' => { + message => q|Display name|, + lastUpdated => 0, + }, + + 'ef display name description' => { + message => q|

Use this property to set the name of this field +that is shown to users.

|, + lastUpdated => 0, + }, + + 'ef field type' => { + message => q|Field type|, + lastUpdated => 0, + }, + + 'ef field type description' => { + message => q|

This property defines the column type of the field +in the database as well as the type of form element that is used for input of +new records. You can only select field type combinations that are defined in the +field type manager. For more information please read the help on Manage field +types, which you can visit using the link in the menu on the right.

+ +

Please note that some other field properties like Auto increment and +Read only will force the field to be readonly and thus render the form +type of no importance. The database field type is very important, though, and +should chosen with proper care.

|, + lastUpdated => 0, + }, + + 'ef signed' => { + message => q|Sign|, + lastUpdated => 0, + }, + + 'ef signed description' => { + message => q|

This property determines wheter this field +interprets number as signed or unsigned. The difference lies in the boundaries +of allowed values.

+ +

This property is available only for numeric fieldtypes like int.

|, + lastUpdated => 0, + }, + + 'ef signed label' => { + message => q|Signed|, + lastUpdated => 0, + }, + + 'ef unsigned label' => { + message => q|Unsigned|, + lastUpdated => 0, + }, + + 'ef autoincrement' => { + message => q|Autoincrement|, + lastUpdated => 0, + }, + + 'ef autoincrement description' => { + message => q|

Setting a field will cause it to assign itself a +value that is the highest value of the field that is already in the database +plus one. In other words each record will have a succesive number for this +field. The field value will increment automatically.

+ +

Enabling autincrement for a field will mean necessarily that the field is +forced read only and will not accept user input. Therefore the choice of +form field type is irrelevant if autoincrement is enabled.

|, + lastUpdated => 0, + }, + + 'ef form height' => { + message => q|Height of form element|, + lastUpdated => 0, + }, + + 'ef form height description' => { + message => q|

This property sets the height of the form +element, if applicable for the chosen form field type. Not all form elements +have a settable height.

|, + lastUpdated => 0, + }, + + 'ef form width' => { + message => q|Width of form element|, + lastUpdated => 0, + }, + + 'ef form width description' => { + message => q|

This property sets the width of the form +element, if applicable for the chosen form field type. Not all form elements +have a settable width.

|, + lastUpdated => 0, + }, + + 'ef max field length' => { + message => q|Maximum field length|, + lastUpdated => 0, + }, + + 'ef max field length description' => { + message => q|

This property defines the number of characters that +the value that is inputted into this field is allowed to have. This property +applies only to form elements that allow a user to actually type. So if you +chose, for instance, a select list a form field type this option will have no +effect.

+ +

Please note that some database type define a limit for the value of this +property.

|, + lastUpdated => 0, + }, + + 'ef regex' => { + message => q|Regex|, + lastUpdated => 0, + }, + + 'ef regex description' => { + message => q|

Regex is short for regular expression. A regex +is used to precisely match data against a specific pattern. The regex property +thus allows you to check the input user assign to this field.

+ +

The list of regexes you can choose from is defined in the Regex +manager of the SQLForm asset. For more information regarding it please read +the help on Manage regexes, which you can access by clicking on the link +in the menu on the right.

|, + lastUpdated => 0, + }, + + 'ef required' => { + message => q|Required|, + lastUpdated => 0, + }, + + 'ef required description' => { + message => q|

Setting this property to yes will force users to +fill in a value for this field when adding a record. If set to no users are +allowed to leave the field empty.

|, + lastUpdated => 0, + }, + + 'ef read only' => { + message => q|Read only|, + lastUpdated => 0, + }, + + 'ef read only description' => { + message => q|

Setting this property to will cause the field to +be read only, meaning that users cannot input a value for it when adding or +editing a record. The value that is stored in this field on record addition is +the value given by the Default value property.

|, + lastUpdated => 0, + }, + + 'ef default value' => { + message => q|Default value|, + lastUpdated => 0, + }, + + 'ef default value description' => { + message => q|

This property can be used to prepopulate the form +element tied to this field on record addition. If the field is set read only the +value of this property will be used to put in the database.

+ +

You can use macro's for this property, to make your default value dynamic. +For instance, if you want a field to default to the username of the person +adding a record, you can use &hat;User(username); in this property.

+ +

If the field is set to autoincrement, the default value property is +neglected.

|, + lastUpdated => 0, + }, + + 'ef field constraint' => { + message => q|Field constraint|, + lastUpdated => 0, + }, + + 'ef field constraint description' => { + message => q|

The field constraint property has a similar use +as the regex. The big diffrence, however, is that you can use the field +constraint to apply a constraint on the actual value that is input by the user +who adds a record, while a regex is used to constrain the form (or the pattern) +of the input.

+ +

There are a number of operators available to define your constraint. If you +set the constraint to another value than none you will be able to select +what you want to compare against. You can use a custom value for comparison, but +if you have defined joins with other tables in the define table(-joins) +you can also match against one of the columns of those tables.

+ +

The field constrained property is ignored if the read only or the +autoincrement property is set.

|, + lastUpdated => 0, + }, + + 'ef searchable' => { + message => q|Searchable|, + lastUpdated => 0, + }, + + 'ef searchable description' => { + message => q|

You can include this field in search queries by +setting to 'yes'. If set to 'no' users will be unable to search on this +field.

|, + lastUpdated => 0, + }, + + 'ef fulltext' => { + message => q|Use fulltext index|, + lastUpdated => 0, + }, + + 'ef fulltext description' => { + message => q|

Fulltext indices are used too speed up search +queries, so setting this property to yes may increase performance of the SQLForm +asset. Adding or editing records, however, will be somewhat slowed down by using +a fulltext index.

+ +

Fulltext indices are only apllicable to certain database field types like +text or longtext but enabling this property on another field type +won't affect the operation of the SQLForm.

|, + lastUpdated => 0, + }, + + 'ef show in search' => { + message => q|Show in search results|, + lastUpdated => 0, + }, + + 'ef show in search description' => { + message => q|

By setting this property to 'yes' this field will +be shown in the list of search results. If you set it to 'no' users will not be +able to see the value of this field in the search results. Users can view the +field when viewing or editing this field regardless of this property +however.

|, + lastUpdated => 0, + }, + + 'ef summary length' => { + message => q|Summary length|, + lastUpdated => 0, + }, + + 'ef summary length description' => { + message => q|

This property determines how much characters of the +value of this field should shown in the search result list. The field value will +be truncated to the number of characters you enter here. Setting this property +to zero will disable this property and cause field value not to be truncated in +the search results list.

|, + lastUpdated => 0, + }, + + 'ef populate keys' => { + message => q|Keys of form element options|, + lastUpdated => 0, + }, + + 'ef populate keys description' => { + message => q|

You can use this property to populate option based +form elements, like select-, radio- and check lists. Each option consists of a +key and a value. Keys are the values that are stored in the database and values +are the text labels that are shown in the form element to identify the +option.

+ +

Use this property to define the keys for the available options. Fill in one +key per line, and make sure that the number of keys here matches the number of +values entered in the Values of form element options property.

+ +

Please note that if a connection to another table is defined in the Define +table(-joins) property, the Keys of form element options property is +neglected.

|, + lastUpdated => 0, + }, + + 'ef populate values' => { + message => q|Values of form element options|, + lastUpdated => 0, + }, + + 'ef populate values description' => { + message => q|

You can use this property to populate option based +form elements, like select-, radio- and check lists. Each option consists of a +key and a value. Keys are the values that are stored in the database and values +are the text labels that are shown in the form element to identify the +option.

+ +

Use this property to define the values for the available options. Fill in one +value per line, and make sure that the number of values here matches the number of +keys entered in the Keys of form element options property.

+ +

Please note that if a connection to another table is defined in the Define +table(-joins) property, the Values of form element options property is +neglected.

|, + lastUpdated => 0, + }, + + 'ef join selector' => { + message => q|Define table(-joins)|, + lastUpdated => 0, + }, + + 'ef join selector description' => { + message => q|

You can connect this field to other tables using +this property. This connection can be used for constraing field values and +setting the keys and values of options of form elements like select list, radio +lists and check lists.

+ +

In order to do so you must select +the database in which the table of your choice resides and, of course, the table +itself. You can add more tables by clicking on the join button that appears +below the table selection. In order to do this you must choose the columns that +connect the tables you have selected and the type of that connection. + +

These columns should identify the rows they are in in exectly the same way so +that the SQLForm knows which record in one table belongs to a record in another. +You can choose from two connection methods: by set-intersection and by +set-difference.

+ +

Suppose we have two tables, A and B, that we want to connect to eachother in +order to get dat out of them into a select list. If you use an intersection the +tables are connected in such a way that only the elements that are in A as well +as B are returned. If you use the difference method, only the rows that are in A +but not B are returned.

|, + lastUpdated => 0, + }, + + 'ef join constraint' => { + message => q|Constraint|, + lastUpdated => 0, + }, + + 'ef join constraint description' => { + message => q|

You can use this property to limit the results from +the definition in the Define table(-joins) property by selecting a +column, a constraint type and a value.

|, + lastUpdated => 0, + }, + + 'ef join keys' => { + message => q|Get keys from column|, + lastUpdated => 0, + }, + + 'ef join keys description' => { + message => q|

Use this property to generate the keys of the +options of option based form elements like select list from the table +definition.

|, + lastUpdated => 0, + }, + + 'ef join values' => { + message => q|Get values from column|, + lastUpdated => 0, + }, + + 'ef join values description' => { + message => q|

Use this property to generate the values of the +options of option based form elements like select list from the table +definition.

|, + lastUpdated => 0, + }, + + + 'ef errors occurred' => { + message => q|Some errors occured:|, + lastUpdated => 0, + }, + + 'efs height error' => { + message => q|Invalid value for Form field height|, + lastUpdated => 0, + }, + + 'efs width error' => { + message => q|Invalid value for Form field width|, + lastUpdated => 0, + }, + + 'efs populate error' => { + message => q|Number of keys and values of form population keys +does not match|, + lastUpdated => 0, + }, + + 'efs constraint error' => { + message => q|You must enter a constraint value|, + lastUpdated => 0, + }, + + 'efs jf1 error' => { + message => q|You cannot select Join field 1 without defining it|, + lastUpdated => 0, + }, + + 'efs jf2 error' => { + message => q|You cannot select Join field 2 without defining it|, + lastUpdated => 0, + }, + + 'efs join populate error' => { + message => q|You should select the key and value columns in the +field population tab|, + lastUpdated => 0, + }, + + 'efs left join column error' => { + message => q|You have to specify the left join column for table|, + lastUpdated => 0, + }, + + 'efs right join column error' => { + message => q|You have to specify the right join column for table|, + lastUpdated => 0, + }, + + 'efs column name error' => { + message => q|Illegal column name in join clause:|, + lastUpdated => 0, + }, + + 'efs table error' => { + message => q|Illegal table selected.|, + lastUpdated => 0, + }, + + 'efs database error' => { + message => q|Illegal database selected.|, + lastUpdated => 0, + }, + + 'efs field type error' => { + message => q|Illegal field type.|, + lastUpdated => 0, + }, + + 'efs fulltext error' => { + message => q|Column type does not support full text search.|, + lastUpdated => 0, + }, + + 'efs column name exists error' => { + message => q|The field name already exists in the table.|, + lastUpdated => 0, + }, + + 'efs column name is reserved error' => { + message => q|The field name is the same as a reserved keyword, +which is not allowed.|, + lastUpdated => 0, + }, + + 'efs field name error' => { + message => q|Illegal field name.|, + lastUpdated => 0, + }, + + 'eft db field type' => { + message => q|Database field type|, + lastUpdated => 0, + }, + + 'eft db field type description' => { + message => q|

This property sets the MySQL column type of the +column in the database that will store the data entered in field with this field +type.

|, + lastUpdated => 0, + }, + + 'eft form field type' => { + message => q|Form element type|, + lastUpdated => 0, + }, + + 'eft form field type description' => { + message => q|

You can select the form element that will be used +to enter data in field with this field type. Please note that some combinations +of form and db types do not make much sense.

|, + lastUpdated => 0, + }, + + 'click here for file' => { + message => q|Click here for file|, + lastUpdated => 0, + }, + + 'keep' => { + message => q|Keep|, + lastUpdated => 0, + }, + + 'overwrite' => { + message => q|Overwrite|, + lastUpdated => 0, + }, + + 'delete' => { + message => q|Delete|, + lastUpdated => 0, + }, + + 'invalid record id' => { + message => q|Not a valid record id.|, + lastUpdated => 0, + }, + + 'view history' => { + message => q|View record history|, + lastUpdated => 0, + }, + 'no fields defined message' => { + message => q|There are no fields defined yet. You can add field +by going to|, + lastUpdated => 0, + }, + + 'manage fields title' => { + message => q|Manage fields|, + lastUpdated => 0, + }, + + 'ers file too large' => { + message => q|File too large|, + lastUpdated => 0, + }, + + 'ers field required' => { + message => q|Field is required:|, + lastUpdated => 0, + }, + + 'ers regex mismatch' => { + message => q|Field does not match its regex:|, + lastUpdated => 0, + }, + + 'ers too long' => { + message => q|Field is too long. Maximum number of characters:|, + lastUpdated => 0, + }, + + 'ers value not allowed' => { + message => q|Value is not allowed for field:|, + lastUpdated => 0, + }, + + 'ers out of range' => { + message => q|The value for this field is out of range:|, + lastUpdated => 0, + }, + + 'er error message' => { + message => q|An error occurred:|, + lastUpdated => 0, + }, + + 'er name' => { + message => q|Name|, + lastUpdated => 0, + }, + + 'er name description' => { + message => q|

Use this property to set the name by which the +regex will be shown on the screen.

|, + lastUpdated => 0, + }, + + 'er regex' => { + message => q|Regex|, + lastUpdated => 0, + }, + + 'er regex description' => { + message => q|

This property defines the actual regular +expression. The regex you enter here should be perl style.

|, + lastUpdated => 0, + }, + + 'ers no name' => { + message => q|Please supply a name for this regex.|, + lastUpdated => 0, + }, + + 'ers no regex' => { + message => q|Please supply a regex.|, + lastUpdated => 0, + }, + + 'no field types message' => { + message => q|In order to add fields to a SQLForm field types +must be defined. Currently there are no field types defined, and therfore it is +not possible ta add fields. Please add at least one field type by going to|, + lastUpdated => 0, + }, + + 'manage field types title' => { + message => q|Manage field types|, + lastUpdated => 0, + }, + + 'lf add field' => { + message => q|Add field|, + lastUpdated => 0, + }, + + 'lft delete confirm message' => { + message => q|Are you sure to delete this field type?|, + lastUpdated => 0, + }, + + 'lft show assets using' => { + message => q|Click here to show SQLForms that use this field type.|, + lastUpdated => 0, + }, + + 'lft in field' => { + message => q|in field|, + lastUpdated => 0, + }, + + 'lft unused field types' => { + message => q|Unused field types|, + lastUpdated => 0, + }, + + 'lft db type' => { + message => q|Database type|, + lastUpdated => 0, + }, + + 'lft form type' => { + message => q|Form element|, + lastUpdated => 0, + }, + + 'lft used field types' => { + message => q|Field types in use|, + lastUpdated => 0, + }, + + 'lft add field type' => { + message => q|Add a new field type|, + lastUpdated => 0, + }, + + 'lr show assets using' => { + message => q|Click here to show SQLForms that use this regular expression.|, + lastUpdated => 0, + }, + + 'lr in field' => { + message => q|in field|, + lastUpdated => 0, + }, + + 'lr unused regexes' => { + message => q|Unused regular expressions|, + lastUpdated => 0, + }, + + 'lr name' => { + message => q|Name|, + lastUpdated => 0, + }, + + 'lr regex' => { + message => q|Regular expression|, + lastUpdated => 0, + }, + + 'lr used regexes' => { + message => q|Regular expressions in use|, + lastUpdated => 0, + }, + + 'lr add regex' => { + message => q|Add a new regular expression|, + lastUpdated => 0, + }, + + 'vh init date' => { + message => q|Init date|, + lastUpdated => 0, + }, + + 'vh user' => { + message => q|User|, + lastUpdated => 0, + }, + + 's query' => { + message => q|Search for|, + lastUpdated => 0, + }, + + 's mode' => { + message => q|Search mode|, + lastUpdated => 0, + }, + + 's type' => { + message => q|Search type|, + lastUpdated => 0, + }, + + 's search in fields' => { + message => q|Search in fields|, + lastUpdated => 0, + }, + + 's location' => { + message => q|Search location|, + lastUpdated => 0, + }, + + 's search button' => { + message => q|Search|, + lastUpdated => 0, + }, + + 's query error' => { + message => q|Your query contains an error:|, + lastUpdated => 0, + }, + + 's advanced search' => { + message => q|Advanced search|, + lastUpdated => 0, + }, + + 's normal search' => { + message => q|Normal search|, + lastUpdated => 0, + }, + + 's restore' => { + message => q|Restore|, + lastUpdated => 0, + }, + + 's purge' => { + message => q|Purge|, + lastUpdated => 0, + }, + + '_csf only normal' => { + message => q|Only normal|, + lastUpdated => 0, + }, + + '_csf only trash' => { + message => q|Only trash|, + lastUpdated => 0, + }, + + '_csf normal and trash' => { + message => q|Normal and trash|, + lastUpdated => 0, + }, + + 'and' => { + message => q|and|, + lastUpdated => 0, + }, + + 'or' => { + message => q|or|, + lastUpdated => 0, + }, + + '_psq confirm delete message' => { + message => q|Are you sure you want to delete this record?|, + lastUpdated => 0, + }, + + 'add record title' => { + message => q|Add record|, + lastUpdated => 0, + }, + + 'search records title' => { + message => q|Search records|, + lastUpdated => 0, + }, + + 'none' => { + message => q|None|, + lastUpdated => 0, + }, + + 'gef no db links' => { + message => q|You can only use this asset if you define databaselinks. Please define databases in the database links.|, + lastUpdated => 0, + }, + + 'gef table name' => { + message => q|Table name|, + lastUpdated => 0, + }, + + 'gef table name description' => { + message => q|

This is the name the table you want to attach +should get in the database, or if you want to attach the SQLForm to an existing +table, the nam of that table.

|, + lastUpdated => 0, + }, + + 'gef database to use' => { + message => q|Database to use|, + lastUpdated => 0, + }, + + 'gef database to use description' => { + message => q|

This property defines the link to the database +where the table should reside or resides in. Database links can be added and +edited in the Databases section of the Admin Console

|, + lastUpdated => 0, + }, + + 'gef max file size' => { + message => q|Maximum file size|, + lastUpdated => 0, + }, + + 'gef max file size description' => { + message => q|

Using this property you can define the maximum +size of files users can upload through the SQLForm. Specify the size in +kilobytes.

+ +

Please note that WebGUI also has a system wide maximum file size setting, +which cannot be overridden by this property. In other words, if you set this +property to a larger value than that of the system wide setting, the maximum +upload size will be the system wide.|, + lastUpdated => 0, + }, + + 'gef send mail to' => { + message => q|Send notification mail to|, + lastUpdated => 0, + }, + + 'gef send mail to description' => { + message => q|

The SQLForm sends a notification email to the +email address specified in this property every time a record is added or edited. +If you do not want to use this feature, simply leave the field blank.

|, + lastUpdated => 0, + }, + + 'gef show metadata' => { + message => q|Show metadata|, + lastUpdated => 0, + }, + + 'gef show metadata description' => { + message => q|

In the SQLForm each record has special meta data +containing the state of the record. If you want some of this this information to +be shown in search results, please set this property to yes.

|, + lastUpdated => 0, + }, + + 'gef edit template' => { + message => q|View/Edit template|, + lastUpdated => 0, + }, + + 'gef edit template description' => { + message => q|

This property sets the template that is used to +layout the record edit or view screen.

|, + lastUpdated => 0, + }, + + 'gef search template' => { + message => q|Search template|, + lastUpdated => 0, + }, + + 'gef search template description' => { + message => q|

This property sets the template that formats the +search results.

|, + lastUpdated => 0, + }, + + 'gef submit group' => { + message => q|Group to submit records|, + lastUpdated => 0, + }, + + 'gef submit group description' => { + message => q|

This is the group of user that can add, edit, +delete and restore but not purge records.

|, + lastUpdated => 0, + }, + + 'assetName' => { + message => q|SQLForm|, + lastUpdated => 0, + }, + + 'edit sqlform' => { + message => q|SQLForm, Add/Edit|, + lastUpdated => 0, + }, + + 'sqlform description' => { + message => q|

The SQLForm asset allows you to dynamically +create data input and storage functionality in your site. All data is put in a +table of your choice in a database of your choice. An arbitrary form element can +be tied to each field and input forms can be built in diffrent ways to ensure +optimal adaptability to your needs.

+ +

The SQLForm features creation of new tables, import of existing tables and +re-importing previously imported tables. Fields can be linked to other fields in +other tables in serveral ways, making it possible to dynamically resolve id's to +values using joins, add constraints to inputted data or connect different +SQLForms together.

+ +

User input can also be checked against regular expressions and manual +constraints that are definable by you. In addition it's possible to add an +autoincrement flag and macros to fields, among other functionality.

+ +

All inputted data is versioned and a two level (delete/purge) trash is build +in. Three privilege layers are available to split access to different +actions.

|, + lastUpdated => 0, + }, + + 'edit field title' => { + message => q|SQLForm, Add/Edit Field|, + lastUpdated => 0, + }, + + 'edit field description' => { + message => q|

Fields are the basis of SQLForms. Each form +consists of one or more fields. Please note that in order to define fields, at +least one field type has to be defined. For information considering field types +please see the Manage field types help section.

|, + lastUpdated => 0, + }, + + 'edit field type title' =>{ + message => q|SQLForm, Add/Edit Field Type|, + lastUpdated => 0, + }, + + 'edit field type description' => { + message => q|

Field types are combinations of a database column +type and a form element for data input. These field types are used to define the +basis of the fields in your SQLForm. Field types can only be deleted if they are +not in use by any SQLForm in the system. Please note that this also includes +SQLForm assets that are in the trash and are not yet purged.

|, + lastUpdated => 0, + }, + + 'edit regex title' => { + message => q|SQLForm, Add/Edit Regex|, + lastUpdated => 0, + }, + + 'edit regex description' => { + message => q|

Regular expressions are strings that represent a +pattern of text. Regexes used in the SQLForm are of the perl-variant. The syntax +of perl-style regular expressions can be found here. To ensure data integrity, +regular expressions can only be deleted if they're not is use by any SQLForm in +the system. Please note this also includes SQLForm that are in the trash and not +have been purged yet.|, + lastUpdated => 0, + }, + + 'manage fields' => { + message => q|Manage fields|, + lastUpdated => 0, + }, + + 'manage fields title' => { + message => q|SQLForm, Manage Fields|, + lastUpdated => 0, + }, + + 'manage field types title' => { + message => q|SQLForm, Manage Field Types|, + lastUpdated => 0, + }, + + 'manage field types' => { + message => q|Manage field types|, + lastUpdated => 0, + }, + + 'manage regexes title' => { + message => q|SQLForm, Manage Regexes|, + lastUpdated => 0, + }, + + 'manage regexes' => { + message => q|Manage regexes|, + lastUpdated => 0, + }, + + 'edit template help title' => { + message => q|SQLForm, Add/Edit Record Template|, + lastUpdated => 0, + }, + + 'edit template help' => { + message => q|

The SQLForm provides you with three +methods to construct record input and edit forms, offering three +levels of flexibility. Please note that more flexibilty has the +downside of increased complexity.

+ +

The three methods you can use are: +completeForm, formloop or manual placement of form +elements using field.<fieldname>.formElement and +field.<fieldname>.label

+ + +

completeForm
+This contains the entire form, complete +and layouted in a WebGUI style table. You don't need to add a +seperate form header, footer or anything else.

+ +

formLoop
+A loop containing each field. Using +this loop will allow you to use a different layout that that of +completeForm. The formLoop loop provides the following +variables:

+ +
+

field.label
+The display name of the field.

+ + +

field.formElement
+The value of the field

+
+ +

Finally there is the option of placing +each seperate field by hand. This allows you to define the order and +place of each form element. Please note, however, that using this +method will not automatically follow changes you make to the SQLForm. +If you add, delete or rename a field you must update the template by +hand. Using this method also implies the use of formHeader and +formFooter. You should use the following two template +variables:

+ + +

field.<fieldname>.formElement
+Contains the form element of the field +<fieldname>. You must substitute <fieldname> with the +field name of the field you intend to place.

+ + +

field.<fieldname>.label
+Contains the display name of the field +<fieldname>. You must substitute <fieldname> with the +field name of the field you intend to place.

+ + +

formHeader
+The header of the the form. If you are +not using the completeForm You must include this variable +before any other form variable. If you do use the completeForm +variable, however, you must not use the formHeader +variable.

+ + +

formFooter
+The footer of the the form. If you are +not using the completeForm You must include this variable +before any other form variable. If you do use the completeForm +variable, however, you must not use the formFooter variable.

+ +

This template also provides some other +variables:

+ + +

errorOccurred
+Conditional indicating whether an error +occurred in the submitted data.

+ + +

errorLoop
+Loop containing the errors.

+ +
+

error.message
+The actual error message.

+
+ +

isNew
+Conditional idicating whether the user +is adding a new record or editing an existing one. True is the record +is new.

+ + +

viewHistory.url
+The url to the history of this record.

+ + +

viewHistory.label
+The label of the link to the history of +this record.

+ + +

managementLinks
+A string of links to all of the +management fucntions.

|, + lastUpdated => 0, + }, + + 'search template help title' => { + message => q|SQLForm, Search Record Template|, + lastUpdated => 0, + }, + + 'search template help' => { + message => q|

The search template of the SQLForm asset +provides you with a way to customize the looks of the search +functionality that the SQLForm offers.

+ +

There are two separate search methods, being normal and advanced search, but +both use the same template.

+ +

The following template variable are available to you:

+ +

showFieldsDefined
+Conditional which returns true if there are field that are defined to be shown. +In other words, this is false if every field is configured not to be displayed +in the search results.

+ +

searchForm
+Contains the form which allows users to search.

+ +

headerLoop
+A loop containing the display names of each field, inclding sort controls. The +following variables are provided within this loop:

+ +
+

header.title
+ The display name of the current field.

+ +

header.sort.url
+ The url that allows you to sort on this field.

+ +

header.sort.onThis
+ Conditional indicating whether the search results are sorted on this + field.

+ +

header.sort.ascending
+ Conditional indicating whether the search results are sorted ascending + or descending.

+
+ +

searchResults.header
+Contains the form header for the batch restore and purge functions in the search +results. You should put this somewhere before the searchResults loop.

+ +

searchResults.footer
+Contains the form footer for the search results batch functions. Put this +template variable somewhere after the searchResults loop.

+ +

searchResults.actionButtons
+Contains the the restore and purge buttons for the batch operations. Put this +variable between searchResults.header and searchResults.footer.

+ +

searchResults.recordLoop
+The loop containg the results of the search query. This should be between +searchResults.header and searchResults.footer. Within this loop the following +variables are available for use:

+ +
+

record.controls
+ Contains the edit/view, delete and purge restore controls for this + record.

+ +

record.deletionDate
+ Contains the date this record was deleted. Only available for records + that are deleted.

+ +

record.deletedBy
+ Contains the username of the person that deleted this record. Only + available for records that are deleted.

+ +

record.updateDate
+ The date of the last time this record has been updated.

+ +

record.updatedBy
+ The username of the person that made the most recent update to this + record.

+ +

record.valueLoop
+ A loop containing the values for each field of this record. This loop + provides the following variables:

+ +
+

record.value
+ The value the record has for this field.

+ +

record.value.isFile
+ Conditional being true if this field contains an uploaded file. + Also returns true if the file is an image.

+ +

record.value.isImage
+ Conditional indicating if the uploaded file is an image.

+ +

record.value.downloadUrl
+ The url to download the uploaded file in this field. Only + available for files and images.

+
+
+ +

superSearch.url
+The url to the advanced search mode.

+ +

superSearch.label
+The internationalized name of the advanced search.

+ +

normalSearch.url
+The url to the normal search mode.

+ +

normalSearch.label
+The internationalized name of the normal search.

+ +

showMetaData
+A conditional indictating whether the show meta data flag is turned on.

+ +

managementLinks
+A collection of links to the admin functions of the SQLForm like manage fields, +as well as links to add record and search record.

+ +

errorOccurred
+Conditional which is true if some error happened while processing the search query.

+ +

errorLoop
+A loop containing the errors that have occurred while processing the search +query. The following variable is available in this loop:

+ +
+

error.message
+ Contains the actual error message.

+
|, + lastUpdated => 0, + }, + + 'dft cannot delete' => { + message => q|This field type cannot by deleted beacause it still is in use by|, + lastUpdated => 0, + }, + + 'sqlforms' => { + message => q|SQLForms|, + lastUpdated => 0, + }, + + 'clear' => { + message => q|Clear|, + lastUpdated => 0, + }, + + 'gef import table' => { + message => q|Import this table|, + lastUpdated => 0, + }, + + 'gef import table description' => { + message => q|

This option is a safety measure against +accidentally importing existing tables. Importing existing tables will alter +the table by removing primary keys and addinng columns. Therefore make sure +that altering the table you want to import will not break other systems. If +you're sure no harm can be done you must select this option to allow importing +the table.

|, + lastUpdated => 0, + }, + + 'ers change notification' => { + message => q|Change notification|, + lastUpdated => 0, + }, + + 'ers change on table' => { + message => q|A change has been made on table|, + lastUpdated => 0, + }, + + 'ers by user' => { + message => q|by user|, + lastUpdated => 0, + }, + + 'ers view url' => { + message => q|You can view this change by clicking on this url:|, + lastUpdated => 0, + }, + + +}; + +1; + diff --git a/www/extras/assets/small/sqlform.gif b/www/extras/assets/small/sqlform.gif new file mode 100644 index 000000000..4e33e18f0 Binary files /dev/null and b/www/extras/assets/small/sqlform.gif differ diff --git a/www/extras/assets/sqlform.gif b/www/extras/assets/sqlform.gif new file mode 100644 index 000000000..4b6737e3d Binary files /dev/null and b/www/extras/assets/sqlform.gif differ diff --git a/www/extras/wobject/SQLForm/SQLFormEditField.js b/www/extras/wobject/SQLForm/SQLFormEditField.js new file mode 100644 index 000000000..f30f62688 --- /dev/null +++ b/www/extras/wobject/SQLForm/SQLFormEditField.js @@ -0,0 +1,91 @@ +function updateFormFields() { +//alert(fieldCombo); + var fieldTypeList = document.getElementById('SQLFormFieldType'); + + if (fieldTypeList.selectedIndex < 0) { + fieldTypeList.selectedIndex = 0; + } + + var re = fieldTypeList.options[fieldTypeList.selectedIndex].text.match(/^[^\/]+\/([^\/]+)$/); + + var fieldType = RegExp.$1; + + var fieldProperties = fieldTypes[fieldType]; + + // Hanlde sign field + if (fieldProperties['hasSign'] == 1) { + enableField('SQLFormSigned'); + } else { + disableField('SQLFormSigned'); + } + + // Handle autoincrement field + if (fieldProperties['canAutoIncrement'] == 1) { + enableField('SQLFormAutoIncrement'); + } else { + disableField('SQLFormAutoIncrement'); + } + + // Handle regex field + if ( + (fieldProperties['canAutoIncrement'] && document.getElementById('autoIncrementField')) || + (document.getElementById('SQLFormReadOnly') == 1) + ) { + disableField('SQLFormRegex'); + } else { + enableField('SQLFormRegex'); + } + + // Handle Field constraints section + if (document.getElementById('SQLFormFieldConstraintType').value > 0) { + enableField('SQLFormFieldConstraintTarget'); + if (document.getElementById('SQLFormFieldConstraintTarget').value == 'value') { + enableField('SQLFormFieldConstraintValue'); + } else { + disableField('SQLFormFieldConstraintValue'); + } + } else { + disableField('SQLFormFieldConstraintTarget'); + disableField('SQLFormFieldConstraintValue'); + } +} + +function enableField(id) { + var e = document.getElementById(id); + + if (e) { + e.disabled = false; + e.style.display = ''; + } + + // also hide row if applicable + var tr = document.getElementsByTagName("tr"); + if (tr == null) return; + for (i=0; i < tr.length; i++) { + if(tr[i].className == id+'Row') { + tr[i].style.display = ''; + } + } + +// document.getElementById(id+'row').style.display = ''; +} + +function disableField(id) { + var e = document.getElementById(id); + + if (e) { + e.disabled = true; + e.style.display = 'none'; + } + + var tr = document.getElementsByTagName("tr"); + if (tr == null) return; + for (i=0; i < tr.length; i++) { + if(tr[i].className == id+'Row') { + tr[i].style.display = 'none'; + } + } + +// document.getElementById(id+'row').style.display = 'none'; +} + diff --git a/www/extras/wobject/SQLForm/SQLFormJoinSelector.js b/www/extras/wobject/SQLForm/SQLFormJoinSelector.js new file mode 100644 index 000000000..b576c5017 --- /dev/null +++ b/www/extras/wobject/SQLForm/SQLFormJoinSelector.js @@ -0,0 +1,375 @@ +// databaseMap is use to store the available db's +var databaseMap = new Object; + +// tableMap is used to cache array of table options. This could lessen the +// number of requests to the server. +var tableMap = new Object; + +// columnMap will hold the columnnames (including db and tablename) for each +// joinselector +var columnMap = new Object; + +var resultList1, resultList2 = null + +function initDatabaseMap(databases) { + databaseMap = databases; +} + +function setResultFields(field1, field2) { + resultList1 = field1; + resultList2 = field2; +} + +//----------------------------------------------------------------------------- +function setAvailableDatabaseOptions(zup) { + zup.options[zup.options.length] = new Option('Please select a database', ''); + for (var i = 0; i < databaseMap.length; i++) { + zup.options[zup.options.length] = new Option(databaseMap[i].key, databaseMap[i].value); + } +} + +// Processes XML +//----------------------------------------------------------------------------- +function processAjaxXml(req, selectList, cache, prepend) { + var options = req.responseXML.getElementsByTagName("Option"); + + if (!prepend) { + prepend = ''; + } + + for (var i = 0; i < options.length; i++) { + var optionKey = prepend + options[i].getElementsByTagName("Key")[0].firstChild.nodeValue; + var optionValue = prepend + options[i].getElementsByTagName("Value")[0].firstChild.nodeValue; + + if (cache) { + cache[cache.length] = { + key : optionKey, + value : optionValue + }; + } + if (selectList) { + selectList.options[selectList.options.length] = new Option(optionKey, optionValue); //currentOption; + } + } +} + +//----------------------------------------------------------------------------- +function addJoinButtonRow(tableId, rowNumber) { + table = document.getElementById(tableId); + + //rowNumber = table.rows.length - 1; + // Create join button + tr = table.insertRow(table.rows.length); + tr.id = 'joinButtonRow'; + td = tr.insertCell(tr.cells.length); + td.innerHTML = ''; + td.colSpan = 7; + document.getElementById('joinButton'+rowNumber).onclick = function() { + hideElement('joinButton'+rowNumber); + table.deleteRow(table.rows.length - 1); // Delete row with button + addSelectorRow(tableId); + } + hideElement('joinButton'+rowNumber); +} + +//----------------------------------------------------------------------------- +function deleteRows(tableId, stopAtRow) { + table = document.getElementById(tableId); + + for (var i = table.rows.length; i > stopAtRow; i--) { + table.deleteRow(table.rows.length - 1); + } + + addJoinButtonRow(tableId, stopAtRow); +} + +//----------------------------------------------------------------------------- +function hideElement(elementId) { + element = document.getElementById(elementId); + + if (element) { + element.style.display = 'none'; + element.disabled = true; + } + +} + +//----------------------------------------------------------------------------- +function unhideElement(elementId) { + element = document.getElementById(elementId); + + if (element) { + element.style.display = ''; + element.disabled = false; + } + +} + +//----------------------------------------------------------------------------- +function hideJoinConstraints(rowNumber) { + hideElement('on'+rowNumber); + hideElement('joinFunction'+rowNumber); + hideElement('joinOnA'+rowNumber); + hideElement('joinOnB'+rowNumber); +} + +//----------------------------------------------------------------------------- +function unhideJoinConstraints(rowNumber) { + unhideElement('on'+rowNumber); + unhideElement('joinFunction'+rowNumber); + unhideElement('joinOnA'+rowNumber); + unhideElement('joinOnB'+rowNumber); +} + +//----------------------------------------------------------------------------- +function addSelectorRow(tableId, formDatabase, formTable, formJoinOnA, formJoinOnB, formJoinFunction) { + var table = document.getElementById(tableId); + var tr, selectName, tds = ''; + + if (!table) { + alert('Fatal error: tableId does not exist.'); + } + + var rowNumber = table.rows.length + 1; + + // Insert a row + tr = table.insertRow(table.rows.length); + + // Table label + tr.insertCell(tr.cells.length).innerHTML = 'table'+rowNumber+''; + + // Create database selector + selectName = 'database'+rowNumber; + td = tr.insertCell(tr.cells.length); + td.innerHTML = ''; + s = document.getElementById(selectName); + setAvailableDatabaseOptions(s); + s.onchange = function() { + setTablesInSelectList(rowNumber, s.value); + deleteRows(tableId, rowNumber); + hideJoinConstraints(rowNumber); + unhideElement('table'+rowNumber); + toggleJoinButton(rowNumber); + }; + if (formDatabase) { + s.value = formDatabase; + } + + // Create table selector + selectName = 'table'+rowNumber; + td = tr.insertCell(tr.cells.length); + td.innerHTML = ''; + document.getElementById(selectName).onchange = function() { + setJoinOnA(rowNumber); + setJoinOnB(rowNumber); + updateFields(rowNumber); + deleteRows(tableId, rowNumber); + if (rowNumber > 1) { + unhideJoinConstraints(rowNumber); + } + toggleJoinButton(rowNumber); + } + if (formDatabase) { + setTablesInSelectList(rowNumber, formDatabase); + document.getElementById(selectName).value = formTable; + } else { + hideElement(selectName); + } + + // Create on word + td = tr.insertCell(tr.cells.length); + td.innerHTML = ' on '; + td.id = 'on'+rowNumber; + + // Create first join selector + selectName = 'joinOnA'+rowNumber; + td = tr.insertCell(tr.cells.length); + td.innerHTML = ''; + document.getElementById(selectName).onchange = function() { + toggleJoinButton(rowNumber); + } + + // Create joinFunction thingy + selectName = 'joinFunction'+rowNumber; + td = tr.insertCell(tr.cells.length); + td.innerHTML = ''; + document.getElementById(selectName).options[0] = new Option('Intersect on', 'intersection'); + document.getElementById(selectName).options[1] = new Option('Difference on', 'difference'); + + // Create second join selector + selectName = 'joinOnB'+rowNumber; + td = tr.insertCell(tr.cells.length); + td.innerHTML = ''; + document.getElementById(selectName).onchange = function() { + toggleJoinButton(rowNumber); + } + + + if (formDatabase && formTable) { + setJoinOnA(rowNumber, formJoinOnA, formDatabase, formTable); + setJoinOnB(rowNumber); + document.getElementById('joinOnA'+rowNumber).value = formJoinOnA; + document.getElementById('joinOnB'+rowNumber).value = formJoinOnB; + document.getElementById('joinFunction'+rowNumber).value = formJoinFunction; + } else { + // Hide the join constraint controls + hideJoinConstraints(rowNumber); + } + if (rowNumber == 1) { + hideJoinConstraints(rowNumber); + } + + // Finally add a row containing a hidden 'join with' button + if (!(formDatabase || formTable || formJoinOnA || formJoinOnB)) { + addJoinButtonRow(tableId, rowNumber); + } +} + +//----------------------------------------------------------------------------- +function setJoinOnA(rowNumber) { + var databaseName = document.getElementById('database'+rowNumber).value; + var tableName = document.getElementById('table'+rowNumber).value; + var prepend = 'table' + rowNumber + '.'; + var s = document.getElementById('joinOnA'+rowNumber); + + s.length = 0; + s.options[s.options.length] = new Option('Please select a column', ''); + columnMap[rowNumber] = [ ]; + + // Do AJAX request + var r = new AjaxRequest; + var params = { + 'parameters' : { + 'func' : 'processAjaxRequest', + 'dbName' : databaseName, + 'tName' : tableName + }, + 'async' : false, + 'onSuccess' : function(req) { processAjaxXml(req, s, columnMap[rowNumber], prepend); } + }; + + r.method = 'POST'; + r.handleArguments(params); + r.process(); + r.onCompleteInternal(); +} + +//----------------------------------------------------------------------------- +function setJoinOnB(rowNumber) { + if (rowNumber > 1) { + var s = document.getElementById('joinOnB'+rowNumber); + s.length = 0; + s.options[s.options.length] = new Option('Please select a column', ''); + + for (var currentRow = 1; currentRow < rowNumber; currentRow++) { + if (currentRow == 1 || document.getElementById('joinFunction'+currentRow).value != 'difference') { + for (var i = 0; i < columnMap[currentRow].length; i++) { + s.options[s.options.length] = new Option(columnMap[currentRow][i].key, columnMap[currentRow][i].value); + } + } + } + } +} + +//----------------------------------------------------------------------------- +function updateFields(rowNumber,value1,value2, ccValue, fcValue) { + var s1 = document.getElementById(resultList1); + s1.length = 0; + s1.options[s1.options.length] = new Option('Please select a column', ''); + + var s2 = document.getElementById(resultList2); + s2.length = 0; + s2.options[s2.options.length] = new Option('Please select a column', ''); + + var cc = document.getElementById('joinConstraintColumn'); + cc.length = 0; + cc.options[cc.options.length] = new Option('Please select a column', ''); + + var fc = document.getElementById('SQLFormFieldConstraintTarget'); + fc.length = 0; + fc.options[fc.options.length] = new Option('Custom value', 'value'); + + for (var currentRow = 1; currentRow <= rowNumber; currentRow++) { + if (columnMap[currentRow] && (currentRow == 1 || document.getElementById('joinFunction'+currentRow).value != 'difference')) { + for (var i = 0; i < columnMap[currentRow].length; i++) { + s1.options[s1.options.length] = new Option(columnMap[currentRow][i].key, columnMap[currentRow][i].value); + s2.options[s2.options.length] = new Option(columnMap[currentRow][i].key, columnMap[currentRow][i].value); + cc.options[cc.options.length] = new Option(columnMap[currentRow][i].key, columnMap[currentRow][i].value); + fc.options[fc.options.length] = new Option(columnMap[currentRow][i].key, columnMap[currentRow][i].value); + } + } + } + if (value1) { + s1.value = value1; + } + if (value2) { + s2.value = value2; + } + if (ccValue) { + cc.value = ccValue; + } + if (fcValue) { + fc.value = fcValue; + } +} + +//----------------------------------------------------------------------------- +function setTablesInSelectList(rowNumber, database) { + var s = document.getElementById('table'+rowNumber); + + if (!s) { + alert('Fatal error: selectList (table'+rowNumber+') does not exist.'); + } + + // Empty select list. + s.length = 0; + s.options[s.options.length] = new Option('Please select a table', ''); + + // If the tables aren't cached in tableMap yet, fetch them using AJAX. + if (!tableMap[database]) { + tableMap[database] = [ ]; + + // Do AJAX request + var r = new AjaxRequest; + var params = { + 'parameters' : { + 'func' : 'processAjaxRequest', + 'dbName' : database + }, + 'async' : false, + 'onSuccess' : function(req) { processAjaxXml(req, s, tableMap[database]); } + }; + + r.method = 'POST'; + r.handleArguments(params); + r.process(); + // Must run this by hand because in sync-mode the internal event handlers are not called. + r.onCompleteInternal(); + // If they are cached then put them in the select list. + } else { + for (var i = 0; i < tableMap[database].length; i++) { + s.options[s.options.length] = new Option(tableMap[database][i].key, tableMap[database][i].value); + } + } +} + +//----------------------------------------------------------------------------- +function toggleJoinButton(rowNumber) { + if (document.getElementById('database'+rowNumber) && document.getElementById('table'+rowNumber)) { + if ( + (document.getElementById('database'+rowNumber).selectedIndex > 0) && + (document.getElementById('table'+rowNumber).selectedIndex > 0) && + ( + (rowNumber == 1) || + ( + (document.getElementById('joinOnA'+rowNumber).selectedIndex > 0) && + (document.getElementById('joinOnB'+rowNumber).selectedIndex > 0) + ) + ) + ) { + unhideElement('joinButton'+rowNumber); + } else { + hideElement('joinButton'+rowNumber); + } + } +} diff --git a/www/extras/wobject/SQLForm/SQLFormSearch.js b/www/extras/wobject/SQLForm/SQLFormSearch.js new file mode 100644 index 000000000..d0cef1569 --- /dev/null +++ b/www/extras/wobject/SQLForm/SQLFormSearch.js @@ -0,0 +1,69 @@ +function switchField(id, switchOn) { + + e = document.getElementById(id); + if (!e) { + alert("Not a valid id: ["+id+"]"); + } + + if (switchOn) { + document.getElementById(id).disabled = false; + document.getElementById(id).style.display = ''; + } else { + document.getElementById(id).disabled = true; + document.getElementById(id).style.display = 'none'; + } +} + +function switchListField(conditional, id) { + if (conditional == '') { + switchField(id+'-1', false); + switchField(id+'-2', false); + } else { + if (conditional == 100 || conditional == 101) { + switchField(id+'-1', true); + switchField(id+'-2', false); + } else { + switchField(id+'-1', false); + switchField(id+'-2', true); + } + } +} + +function switchNumberField(conditional, id) { + if (conditional == '') { + switchField(id+'-1', false); + switchField(id+'-2', false); + } else { + if (conditional == 10) { + switchField(id+'-1', true); + switchField(id+'-2', true); + } else { + switchField(id+'-1', true); + switchField(id+'-2', false); + } + } +} + +function switchTemporalField(conditional, id) { + if (conditional == '') { + switchField(id+'-1', false); + switchField(id+'-2', false); + } else { + if (conditional == 10) { + switchField(id+'-1', true); + switchField(id+'-2', true); + } else { + switchField(id+'-1', true); + switchField(id+'-2', false); + } + } +} + +function switchTextField(conditional, id) { + if (conditional == '') { + switchField(id+'-1', false); + } else { + switchField(id+'-1', true); + } +} + diff --git a/www/extras/wobject/SQLForm/SQLFormViewButton.gif b/www/extras/wobject/SQLForm/SQLFormViewButton.gif new file mode 100644 index 000000000..d3fadddf0 Binary files /dev/null and b/www/extras/wobject/SQLForm/SQLFormViewButton.gif differ