' 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 = 'getUrl).'">';
+ if ($recordValues->{'__'.$field->{fieldName}.'_mimeType'} =~ /^image/i) {
+ $fieldValue .= 'getUrl).'" />';
+ } 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 .= '
';
+ $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 .= '
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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
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.
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.
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.
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.
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.
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.
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