From 07a40788bba3a168f76691e97d410d10f083cc7b Mon Sep 17 00:00:00 2001 From: Doug Bell Date: Mon, 28 May 2007 21:35:34 +0000 Subject: [PATCH] add: User profile data table is now a flat table. --- docs/changelog/7.x.x.txt | 2 + docs/gotcha.txt | 11 ++ docs/upgrades/upgrade_7.3.19-7.4.0.pl | 121 +++++++++++++ lib/WebGUI/Asset/Wobject/InOutBoard.pm | 42 ++--- lib/WebGUI/Asset/Wobject/ProjectManager.pm | 9 +- lib/WebGUI/Auth/WebGUI.pm | 5 +- lib/WebGUI/Form/Asset.pm | 3 + lib/WebGUI/Form/Button.pm | 3 + lib/WebGUI/Form/Captcha.pm | 3 + lib/WebGUI/Form/Codearea.pm | 5 +- lib/WebGUI/Form/Color.pm | 3 + lib/WebGUI/Form/Combo.pm | 5 +- lib/WebGUI/Form/Control.pm | 13 +- lib/WebGUI/Form/DatabaseLink.pm | 3 + lib/WebGUI/Form/Date.pm | 3 + lib/WebGUI/Form/DateTime.pm | 3 + lib/WebGUI/Form/File.pm | 5 +- lib/WebGUI/Form/FilterContent.pm | 3 + lib/WebGUI/Form/Float.pm | 3 + lib/WebGUI/Form/Group.pm | 3 + lib/WebGUI/Form/HTMLArea.pm | 3 + lib/WebGUI/Form/HiddenList.pm | 3 + lib/WebGUI/Form/Image.pm | 7 +- lib/WebGUI/Form/IntSlider.pm | 3 + lib/WebGUI/Form/Integer.pm | 3 + lib/WebGUI/Form/Interval.pm | 3 + lib/WebGUI/Form/LdapLink.pm | 5 +- lib/WebGUI/Form/List.pm | 3 + lib/WebGUI/Form/SelectBox.pm | 3 + lib/WebGUI/Form/SelectList.pm | 15 +- lib/WebGUI/Form/SelectSlider.pm | 3 + lib/WebGUI/Form/Template.pm | 3 + lib/WebGUI/Form/Textarea.pm | 3 + lib/WebGUI/Form/TimeField.pm | 3 + lib/WebGUI/Form/User.pm | 3 + lib/WebGUI/Form/Workflow.pm | 5 +- lib/WebGUI/Form/YesNo.pm | 3 + lib/WebGUI/Operation/Profile.pm | 12 +- lib/WebGUI/Operation/User.pm | 7 +- lib/WebGUI/ProfileField.pm | 196 ++++++++++++++++----- lib/WebGUI/User.pm | 123 +++++++------ 41 files changed, 493 insertions(+), 164 deletions(-) diff --git a/docs/changelog/7.x.x.txt b/docs/changelog/7.x.x.txt index 178ad926f..007b3ed84 100644 --- a/docs/changelog/7.x.x.txt +++ b/docs/changelog/7.x.x.txt @@ -5,6 +5,8 @@ http://www.plainblack.com/rfe/request-for-enhancement/search-asset-feedback.-i.e.-no-results-found#kH5X_YA17ZxbRvi6gx5KRA - RFE: JS confirmation Operation/DatabaseLink.pm http://www.plainblack.com/rfe/request-for-enhancement/-1493348--js-confirmation-operation/databaselink/re--1493348--js-confirmation-operation/databaselink.pm#OUb5zN8bltGdPG_2LJZMGQ + - add: User profile data table is now a flat table, one column for each + field. diff --git a/docs/gotcha.txt b/docs/gotcha.txt index 7f6608c49..bfb06a652 100644 --- a/docs/gotcha.txt +++ b/docs/gotcha.txt @@ -7,6 +7,17 @@ upgrading from one version to the next, or even between multiple versions. Be sure to heed the warnings contained herein as they will save you many hours of grief. +7.4.0 +-------------------------------------------------------------------- + + * The userProfileData table has been completely re-done. Now, each + user profile field has its own column in the userProfileData table. + + Any applications that you may have that makes raw SQL queries against + the userProfileData table will need to be updated to reflect these + changes. + + 7.3.16 -------------------------------------------------------------------- diff --git a/docs/upgrades/upgrade_7.3.19-7.4.0.pl b/docs/upgrades/upgrade_7.3.19-7.4.0.pl index e427b5fe7..56cb412e5 100644 --- a/docs/upgrades/upgrade_7.3.19-7.4.0.pl +++ b/docs/upgrades/upgrade_7.3.19-7.4.0.pl @@ -21,6 +21,8 @@ my $quiet; # this line required my $session = start(); # this line required # upgrade functions go here +fixProfileDataWithoutFields($session); +buildNewUserProfileTable($session); finish($session); # this line required @@ -32,6 +34,125 @@ finish($session); # this line required # # and here's our code #} +#---------------------------------------------------------------------------- + +sub fixProfileDataWithoutFields { + my $session = shift; + my $db = $session->db; + + use WebGUI::ProfileField; + + print "\tFixing profile data without entries in userProfileField table..." unless $quiet; + + for my $fieldName (qw{ firstDayOfWeek language timeZone uiLevel }) { + next if WebGUI::ProfileField->new($session, $fieldName); + $db->write( + q{INSERT INTO userProfileField (fieldName, label, visible, fieldType, protected, editable) + VALUES (?,?,0,"ReadOnly",1,0)}, + [$fieldName, $fieldName] + ); + } + + print "OK!\n" unless $quiet; +} + + +#---------------------------------------------------------------------------- + +sub buildNewUserProfileTable { + my $session = shift; + my $db = $session->db; + print "\tBuilding new user profile table. This may take a while...\n" unless $quiet; + + use WebGUI::ProfileField; + use List::Util qw( first ); + + print "\t\tCreating structure..." unless $quiet; + # Create a new temporary table + $db->write(q{ + CREATE TABLE tmp_userProfileData ( + userId VARCHAR(22) BINARY NOT NULL, + PRIMARY KEY (userId) + ) + }); + + # Loop through the current fields and add them to the new table + my @profileFields; + my $sth = $db->read(q{SELECT fieldName, fieldType FROM userProfileField}); + while (my %fieldData = $sth->hash) { + push @profileFields, $fieldData{fieldName}; + my $fieldType = 'WebGUI::Form::'.ucfirst $fieldData{fieldType}; + my $fieldName = $db->dbh->quote_identifier($fieldData{fieldName}); + eval "use $fieldType;"; + my $dataType = $fieldType->new($session)->get("dbDataType"); + + $db->write( + "ALTER TABLE tmp_userProfileData ADD COLUMN ($fieldName $dataType)" + ); + } + print " OK!\n" unless $quiet; + + # Find fields that were not in the userProfileField database. + print "\t\tLooking for profile fields not defined in User Profiling... \n" unless $quiet; + my @dataFields = $db->buildArray("SELECT fieldName FROM userProfileData GROUP BY fieldName"); + for my $dataField (@dataFields) { + if (!first { $_ eq $dataField } @profileFields) { + print "\t\t\tCreating invisible, read-only profile field '$dataField'\n" unless $quiet; + + my $fieldType = 'WebGUI::Form::ReadOnly'; + my $fieldName = $db->dbh->quote_identifier($dataField); + eval "use $fieldType;"; + my $dataType = $fieldType->new($session)->get("dbDataType"); + + $db->write( + "ALTER TABLE tmp_userProfileData ADD COLUMN ($fieldName $dataType)" + ); + + # Create the profile field + WebGUI::ProfileField->create($session, $dataField, { + label => $dataField, + fieldType => "ReadOnly", + visible => 0, + protected => 1, + }); + } + } + print "\t\t... Done!\n"; + + print "\t\tMigrating data to temporary table... " unless $quiet; + # Loop over the old table and put them in the new table + $sth = $db->read(q{SELECT userId FROM users}); + while (my $user = $sth->hashRef) { + # Get all of this user's profile data + my %profile + = $db->buildHash( + "SELECT fieldName, fieldData FROM userProfileData WHERE userId=?", + [$user->{userId}] + ); + + # Write to the temp table + my $sql + = q{INSERT INTO tmp_userProfileData } + . q{(userId,} . join(",", map { $db->dbh->quote_identifier($_) } keys %profile) . q{)} + . q{VALUES (?,} . join(",",("?")x values %profile) . q{)} + ; + $db->write($sql, [$user->{userId},values %profile]); + } + $sth->finish; + print "OK!\n" unless $quiet; + + # Delete the old table + print "\t\tExchanging old data with new... "; + $db->write("drop table userProfileData"); + + # Rename the new table + $db->write("rename table tmp_userProfileData to userProfileData"); + print "OK!\n" unless $quiet; + + print "\t\t... Done!\n" unless $quiet; +} + + # ---- DO NOT EDIT BELOW THIS LINE ---- diff --git a/lib/WebGUI/Asset/Wobject/InOutBoard.pm b/lib/WebGUI/Asset/Wobject/InOutBoard.pm index 109234ea7..8519b8a79 100644 --- a/lib/WebGUI/Asset/Wobject/InOutBoard.pm +++ b/lib/WebGUI/Asset/Wobject/InOutBoard.pm @@ -31,14 +31,10 @@ sub _fetchNames { my $self = shift; my @userIds = @_; my %nameHash; - my $sql = "select users.username, -users.userId, -a.fieldData as firstName, -b.fieldData as lastName -from users -left join userProfileData a on users.userId=a.userId and a.fieldName='firstName' -left join userProfileData b on users.userId=b.userId and b.fieldName='lastName' -where users.userId=?"; + my $sql = "SELECT users.username, users.userId, firstName, lastName +FROM users +LEFT JOIN userProfileData ON users.userId=userProfileData.userId +WHERE users.userId=?"; my $sth = $self->session->db->prepare($sql); foreach my $userId (@userIds) { $sth->execute([ $userId ]); @@ -51,7 +47,7 @@ where users.userId=?"; #------------------------------------------------------------------- sub _fetchDepartments { my $self = shift; - return $self->session->db->buildArray("select fieldData from userProfileData where fieldName='department' GROUP by fieldData"); + return $self->session->db->buildArray("SELECT department FROM userProfileData GROUP BY department"); } @@ -244,19 +240,17 @@ sub view { my $sql = "select users.username, users.userId, -a.fieldData as firstName, +firstName, InOutBoard_status.message, -b.fieldData as lastName, +lastName, InOutBoard_status.status, InOutBoard_status.dateStamp, -c.fieldData as department, +department, groupings.groupId from users left join groupings on groupings.userId=users.userId left join InOutBoard on groupings.groupId=InOutBoard.inOutGroup -left join userProfileData a on users.userId=a.userId and a.fieldName='firstName' -left join userProfileData b on users.userId=b.userId and b.fieldName='lastName' -left join userProfileData c on users.userId=c.userId and c.fieldName='department' +left join userProfileData on users.userId=userProfileData.userId left join InOutBoard_status on users.userId=InOutBoard_status.userId and InOutBoard_status.assetId=".$self->session->db->quote($self->getId())." where users.userId<>'1' and InOutBoard.inOutGroup=".$self->session->db->quote($self->get("inOutGroup"))." group by userId @@ -312,14 +306,12 @@ sub www_selectDelegates { "select users.username, users.userId, - a.fieldData as firstName, - b.fieldData as lastName + firstName, + lastName from users left join groupings on users.userId=groupings.userId left join InOutBoard on groupings.groupId=InOutBoard.inOutGroup - left join userProfileData a on users.userId=a.userId and a.fieldName='firstName' - left join userProfileData b on users.userId=b.userId and b.fieldName='lastName' - left join userProfileData c on users.userId=c.userId and c.fieldName='department' + left join userProfileData on users.userId=userProfileData.userId left join InOutBoard_status on users.userId=InOutBoard_status.userId and InOutBoard_status.assetId=? where users.userId<>'1' @@ -465,19 +457,17 @@ sub www_viewReport { my $sql = "select users.username, users.userId, -a.fieldData as firstName, +firstName, InOutBoard_statusLog.message, -b.fieldData as lastName, +lastName, InOutBoard_statusLog.status, InOutBoard_statusLog.dateStamp, InOutBoard_statusLog.createdBy, -c.fieldData as department, +department, groupings.groupId from users left join groupings on groupings.userId=users.userId -left join userProfileData a on users.userId=a.userId and a.fieldName='firstName' -left join userProfileData b on users.userId=b.userId and b.fieldName='lastName' -left join userProfileData c on users.userId=c.userId and c.fieldName='department' +left join userProfileData on users.userId=userProfileData.userId left join InOutBoard_statusLog on users.userId=InOutBoard_statusLog.userId and InOutBoard_statusLog.assetId=".$self->session->db->quote($self->getId())." where users.userId<>'1' and groupings.groupId=".$self->session->db->quote($self->getValue("inOutGroup"))." and diff --git a/lib/WebGUI/Asset/Wobject/ProjectManager.pm b/lib/WebGUI/Asset/Wobject/ProjectManager.pm index 54c8e428d..4a1e94108 100644 --- a/lib/WebGUI/Asset/Wobject/ProjectManager.pm +++ b/lib/WebGUI/Asset/Wobject/ProjectManager.pm @@ -270,13 +270,10 @@ sub _userSearchQuery { my $query = <<"SQL"; SELECT 'user' AS resourceKind, users.userId AS resourceId FROM users - LEFT JOIN userProfileData AS lastName ON users.userId = lastName.userId - AND lastName.fieldName = 'lastName' - LEFT JOIN userProfileData AS firstName ON users.userId = firstName.userId - AND firstName.fieldName = 'firstName' - WHERE (LOWER(lastName.fieldData) LIKE ? OR LOWER(firstName.fieldData) LIKE ? + LEFT JOIN userProfileData ON users.userId = userProfileData.userId + WHERE (LOWER(lastName) LIKE ? OR LOWER(firstName) LIKE ? OR LOWER(users.username) LIKE ?) AND (users.userId NOT IN $excludePlaceholders) - ORDER BY lastName.fieldData, firstName.fieldData + ORDER BY lastName, firstName SQL my @placeholders = (($searchPattern) x 3, @exclude); return ($query, \@placeholders); diff --git a/lib/WebGUI/Auth/WebGUI.pm b/lib/WebGUI/Auth/WebGUI.pm index 270e6a46f..391ba5046 100644 --- a/lib/WebGUI/Auth/WebGUI.pm +++ b/lib/WebGUI/Auth/WebGUI.pm @@ -651,10 +651,9 @@ sub recoverPasswordFinish { my @fieldNames = keys %fieldValues; my @fieldValues = values %fieldValues; - my $joins = join(' ', map{"INNER JOIN userProfileData AS p$_ ON u.userId = p$_.userId AND p$_.fieldName = ".$self->session->db->quote($fieldNames[$_])} (0..$#fieldNames)); - my $wheres = join(' ', map{"AND p$_.fieldData = ?"} (0..$#fieldNames)); + my $wheres = join(' ', map{"AND upd.$_ = ?"} (0..$#fieldNames)); $wheres .= ' AND u.username = ?' if defined $username; - my $sql = "SELECT u.userId FROM users AS u $joins WHERE u.authMethod = 'WebGUI' $wheres"; + my $sql = "SELECT u.userId FROM users AS u JOIN userProfileData AS upd ON u.userId=upd.userId WHERE u.authMethod = 'WebGUI' $wheres"; my @userIds = $self->session->db->buildArray($sql, [@fieldValues, (defined($username)? ($username) : ())]); if (@userIds == 0) { diff --git a/lib/WebGUI/Form/Asset.pm b/lib/WebGUI/Form/Asset.pm index 3d72327af..cbf3e158e 100644 --- a/lib/WebGUI/Form/Asset.pm +++ b/lib/WebGUI/Form/Asset.pm @@ -81,6 +81,9 @@ sub definition { class=>{ defaultValue=> undef }, + dbDataType => { + defaultValue => "VARCHAR(22) BINARY", + }, }); return $class->SUPER::definition($session, $definition); } diff --git a/lib/WebGUI/Form/Button.pm b/lib/WebGUI/Form/Button.pm index 36901963a..f47328756 100644 --- a/lib/WebGUI/Form/Button.pm +++ b/lib/WebGUI/Form/Button.pm @@ -64,6 +64,9 @@ sub definition { defaultValue=>{ defaultValue=>$i18n->get(62) }, + dbDataType => { + defaultValue => "VARCHAR(255)", + }, }); return $class->SUPER::definition($session, $definition); } diff --git a/lib/WebGUI/Form/Captcha.pm b/lib/WebGUI/Form/Captcha.pm index 7b17a299d..686ba63a5 100644 --- a/lib/WebGUI/Form/Captcha.pm +++ b/lib/WebGUI/Form/Captcha.pm @@ -72,6 +72,9 @@ sub definition { profileEnabled=>{ defaultValue=>0 }, + dbDataType => { + defaultValue => "VARCHAR(6)", + }, }); return $class->SUPER::definition($session, $definition); } diff --git a/lib/WebGUI/Form/Codearea.pm b/lib/WebGUI/Form/Codearea.pm index 4e4faae1e..9e95f6356 100644 --- a/lib/WebGUI/Form/Codearea.pm +++ b/lib/WebGUI/Form/Codearea.pm @@ -85,7 +85,10 @@ sub definition { }, profileEnabled=>{ defaultValue=>1 - } + }, + dbDataType => { + defaultValue => "TEXT", + }, }); return $class->SUPER::definition($session, $definition); } diff --git a/lib/WebGUI/Form/Color.pm b/lib/WebGUI/Form/Color.pm index d91eb1342..e66961ef5 100644 --- a/lib/WebGUI/Form/Color.pm +++ b/lib/WebGUI/Form/Color.pm @@ -53,6 +53,9 @@ sub definition { formName=>{ defaultValue=>$i18n->get("color") }, + dbDataType => { + defaultValue => "VARCHAR(7)", + }, }); return $class->SUPER::definition($session, $definition); } diff --git a/lib/WebGUI/Form/Combo.pm b/lib/WebGUI/Form/Combo.pm index 65a50d453..e6e91e1d0 100644 --- a/lib/WebGUI/Form/Combo.pm +++ b/lib/WebGUI/Form/Combo.pm @@ -68,7 +68,10 @@ sub definition { }, profileEnabled=>{ defaultValue=>1 - } + }, + dbDataType => { + defaultValue => "TEXT", + }, }); return $class->SUPER::definition($session, $definition); } diff --git a/lib/WebGUI/Form/Control.pm b/lib/WebGUI/Form/Control.pm index 775d6169f..af1c4f2a3 100644 --- a/lib/WebGUI/Form/Control.pm +++ b/lib/WebGUI/Form/Control.pm @@ -159,6 +159,12 @@ A text string that will pop up when the user hovers over the label when toHtmlWi Flag that tells the User Profile system that this is a valid form element in a User Profile +=head4 dbDataType + +The SQL data type for this form element. Fields created using this form control +will create a column with this data type. If undef, will not create a database +column. Defaults to "VARCHAR(255)". + =cut sub definition { @@ -216,8 +222,11 @@ sub definition { }, profileEnabled=>{ defaultValue=>0 - }, - }); + }, + dbDataType => { + defaultValue => "VARCHAR(255)", + }, + }); return $definition; } diff --git a/lib/WebGUI/Form/DatabaseLink.pm b/lib/WebGUI/Form/DatabaseLink.pm index b9a115526..f244f5865 100644 --- a/lib/WebGUI/Form/DatabaseLink.pm +++ b/lib/WebGUI/Form/DatabaseLink.pm @@ -93,6 +93,9 @@ sub definition { hoverHelp=>{ defaultValue=>$i18n->get('1075 description') }, + dbDataType => { + defaultValue => "VARCHAR(22) BINARY", + }, }); return $class->SUPER::definition($session, $definition); } diff --git a/lib/WebGUI/Form/Date.pm b/lib/WebGUI/Form/Date.pm index 2ca7299fd..08293b641 100644 --- a/lib/WebGUI/Form/Date.pm +++ b/lib/WebGUI/Form/Date.pm @@ -101,6 +101,9 @@ sub definition { profileEnabled=>{ defaultValue=>1 }, + dbDataType => { + defaultValue => "BIGINT", + }, }); return $class->SUPER::definition($session, $definition); } diff --git a/lib/WebGUI/Form/DateTime.pm b/lib/WebGUI/Form/DateTime.pm index 625ea2325..03ed71efb 100644 --- a/lib/WebGUI/Form/DateTime.pm +++ b/lib/WebGUI/Form/DateTime.pm @@ -96,6 +96,9 @@ sub definition { profileEnabled=>{ defaultValue=>1 }, + dbDataType => { + defaultValue => "BIGINT", + }, }); return $class->SUPER::definition($session, $definition); } diff --git a/lib/WebGUI/Form/File.pm b/lib/WebGUI/Form/File.pm index cb65a1057..a06645010 100644 --- a/lib/WebGUI/Form/File.pm +++ b/lib/WebGUI/Form/File.pm @@ -87,7 +87,10 @@ sub definition { }, deleteFileUrl=>{ defaultValue=>undef - } + }, + dbDataType => { + defaultValue => "VARCHAR(22) BINARY", + }, }); return $class->SUPER::definition($session, $definition); } diff --git a/lib/WebGUI/Form/FilterContent.pm b/lib/WebGUI/Form/FilterContent.pm index 7d12fd64d..4db48aed3 100644 --- a/lib/WebGUI/Form/FilterContent.pm +++ b/lib/WebGUI/Form/FilterContent.pm @@ -79,6 +79,9 @@ sub definition { defaultValue=>{ defaultValue=>"most", }, + dbDataType => { + defaultValue => "VARCHAR(16)", + }, }); return $class->SUPER::definition($session, $definition); } diff --git a/lib/WebGUI/Form/Float.pm b/lib/WebGUI/Form/Float.pm index 7b4f4b814..6190c4453 100644 --- a/lib/WebGUI/Form/Float.pm +++ b/lib/WebGUI/Form/Float.pm @@ -85,6 +85,9 @@ sub definition { profileEnabled=>{ defaultValue=>1 }, + dbDataType => { + defaultValue => "DOUBLE", + }, }); return $class->SUPER::definition($session, $definition); } diff --git a/lib/WebGUI/Form/Group.pm b/lib/WebGUI/Form/Group.pm index 4bf09a43a..e69d8356b 100644 --- a/lib/WebGUI/Form/Group.pm +++ b/lib/WebGUI/Form/Group.pm @@ -93,6 +93,9 @@ sub definition { profileEnabled=>{ defaultValue=>1 }, + dbDataType => { + defaultValue => "VARCHAR(22) BINARY", + }, }); return $class->SUPER::definition($session, $definition); } diff --git a/lib/WebGUI/Form/HTMLArea.pm b/lib/WebGUI/Form/HTMLArea.pm index 21c00e286..37a09d70a 100644 --- a/lib/WebGUI/Form/HTMLArea.pm +++ b/lib/WebGUI/Form/HTMLArea.pm @@ -95,6 +95,9 @@ sub definition { profileEnabled=>{ defaultValue=>1 }, + dbDataType => { + defaultValue => "LONGTEXT", + }, }); return $class->SUPER::definition($session, $definition); } diff --git a/lib/WebGUI/Form/HiddenList.pm b/lib/WebGUI/Form/HiddenList.pm index 847f977b9..323a993f7 100644 --- a/lib/WebGUI/Form/HiddenList.pm +++ b/lib/WebGUI/Form/HiddenList.pm @@ -72,6 +72,9 @@ sub definition { profileEnabled=>{ defaultValue=>1 }, + dbDataType => { + defaultValue => "TEXT", + }, }); return $class->SUPER::definition($session, $definition); } diff --git a/lib/WebGUI/Form/Image.pm b/lib/WebGUI/Form/Image.pm index 747fa596f..f6e74bdf4 100644 --- a/lib/WebGUI/Form/Image.pm +++ b/lib/WebGUI/Form/Image.pm @@ -81,8 +81,11 @@ sub definition { forceImageOnly=>{ defaultValue=>0 }, - }); - return $class->SUPER::definition($session, $definition); + dbDataType => { + defaultValue => "VARCHAR(22) BINARY", + }, + }); + return $class->SUPER::definition($session, $definition); } diff --git a/lib/WebGUI/Form/IntSlider.pm b/lib/WebGUI/Form/IntSlider.pm index 8b40c6234..6b776b623 100644 --- a/lib/WebGUI/Form/IntSlider.pm +++ b/lib/WebGUI/Form/IntSlider.pm @@ -71,6 +71,9 @@ sub definition { profileEnabled=>{ defaultValue=>1 }, + dbDataType => { + defaultValue => "BIGINT", + }, }); return $class->SUPER::definition($session, $definition); } diff --git a/lib/WebGUI/Form/Integer.pm b/lib/WebGUI/Form/Integer.pm index e476aa68a..f7aa273c9 100644 --- a/lib/WebGUI/Form/Integer.pm +++ b/lib/WebGUI/Form/Integer.pm @@ -85,6 +85,9 @@ sub definition { profileEnabled=>{ defaultValue=>1 }, + dbDataType => { + defaultValue => "BIGINT", + }, }); return $class->SUPER::definition($session, $definition); } diff --git a/lib/WebGUI/Form/Interval.pm b/lib/WebGUI/Form/Interval.pm index 0da25f0e2..434995b1d 100644 --- a/lib/WebGUI/Form/Interval.pm +++ b/lib/WebGUI/Form/Interval.pm @@ -76,6 +76,9 @@ sub definition { profileEnabled=>{ defaultValue=>1 }, + dbDataType => { + defaultValue => "BIGINT", + }, }); return $class->SUPER::definition($session, $definition); } diff --git a/lib/WebGUI/Form/LdapLink.pm b/lib/WebGUI/Form/LdapLink.pm index efbbf3956..b2ac52b94 100644 --- a/lib/WebGUI/Form/LdapLink.pm +++ b/lib/WebGUI/Form/LdapLink.pm @@ -99,7 +99,10 @@ sub definition { }, afterEdit=>{ defaultValue=>undef - } + }, + dbDataType => { + defaultValue => "TEXT", + }, }); return $class->SUPER::definition($session, $definition); } diff --git a/lib/WebGUI/Form/List.pm b/lib/WebGUI/Form/List.pm index e05be3ace..055d10923 100644 --- a/lib/WebGUI/Form/List.pm +++ b/lib/WebGUI/Form/List.pm @@ -171,6 +171,9 @@ sub definition { profileEnabled=>{ defaultValue=>0 }, + dbDataType => { + defaultValue => "TEXT", + }, }); return $class->SUPER::definition($session, $definition); } diff --git a/lib/WebGUI/Form/SelectBox.pm b/lib/WebGUI/Form/SelectBox.pm index d7dba2a9e..0ccfc02ab 100644 --- a/lib/WebGUI/Form/SelectBox.pm +++ b/lib/WebGUI/Form/SelectBox.pm @@ -84,6 +84,9 @@ sub definition { profileEnabled=>{ defaultValue=>1, }, + dbDataType => { + defaultValue => "VARCHAR(255)", + } }); return $class->SUPER::definition($session, $definition); } diff --git a/lib/WebGUI/Form/SelectList.pm b/lib/WebGUI/Form/SelectList.pm index e3c144405..0a09e33b5 100644 --- a/lib/WebGUI/Form/SelectList.pm +++ b/lib/WebGUI/Form/SelectList.pm @@ -68,18 +68,21 @@ sub definition { push(@{$definition}, { formName=>{ defaultValue=>$i18n->get("484"), - }, + }, multiple=>{ defaultValue=>1 - }, + }, size=>{ defaultValue=>5 - }, + }, profileEnabled=>{ defaultValue=>1 - }, - }); - return $class->SUPER::definition($session, $definition); + }, + dbDataType => { + defaultValue => "LONGTEXT", + }, + }); + return $class->SUPER::definition($session, $definition); } #------------------------------------------------------------------- diff --git a/lib/WebGUI/Form/SelectSlider.pm b/lib/WebGUI/Form/SelectSlider.pm index 07fb786fc..25bb963e0 100644 --- a/lib/WebGUI/Form/SelectSlider.pm +++ b/lib/WebGUI/Form/SelectSlider.pm @@ -54,6 +54,9 @@ sub definition { profileEnabled=>{ defaultValue=>1 }, + dbDataType => { + defaultValue => "TEXT", + }, }); return $class->SUPER::definition($session, $definition); } diff --git a/lib/WebGUI/Form/Template.pm b/lib/WebGUI/Form/Template.pm index 03631ce40..2b9a9c96d 100644 --- a/lib/WebGUI/Form/Template.pm +++ b/lib/WebGUI/Form/Template.pm @@ -79,6 +79,9 @@ sub definition { namespace=>{ defaultValue=>undef }, + dbDataType => { + defaultValue => "VARCHAR(22) BINARY", + }, }); return $class->SUPER::definition($session, $definition); } diff --git a/lib/WebGUI/Form/Textarea.pm b/lib/WebGUI/Form/Textarea.pm index efe137327..86b6d07d1 100644 --- a/lib/WebGUI/Form/Textarea.pm +++ b/lib/WebGUI/Form/Textarea.pm @@ -92,6 +92,9 @@ sub definition { profileEnabled=>{ defaultValue=>1 }, + dbDataType => { + defaultValue => "LONGTEXT", + }, }); return $class->SUPER::definition($session, $definition); } diff --git a/lib/WebGUI/Form/TimeField.pm b/lib/WebGUI/Form/TimeField.pm index aaf810b5a..afa5fa563 100644 --- a/lib/WebGUI/Form/TimeField.pm +++ b/lib/WebGUI/Form/TimeField.pm @@ -90,6 +90,9 @@ sub definition { profileEnabled=>{ defaultValue=>1 }, + dbDataType => { + defaultValue => "BIGINT", + }, }); return $class->SUPER::definition($session, $definition); } diff --git a/lib/WebGUI/Form/User.pm b/lib/WebGUI/Form/User.pm index 78bb593b3..b9ce5f511 100644 --- a/lib/WebGUI/Form/User.pm +++ b/lib/WebGUI/Form/User.pm @@ -81,6 +81,9 @@ sub definition { readOnly=>{ defaultValue=>0, }, + dbDataType => { + defaultValue => "VARCHAR(22) BINARY", + }, }); return $class->SUPER::definition($session, $definition); } diff --git a/lib/WebGUI/Form/Workflow.pm b/lib/WebGUI/Form/Workflow.pm index 83be8f1d6..a805ed625 100644 --- a/lib/WebGUI/Form/Workflow.pm +++ b/lib/WebGUI/Form/Workflow.pm @@ -85,7 +85,10 @@ sub definition { }, none=>{ defaulValue=>0 - } + }, + dbDataType => { + defaultValue => "VARCHAR(22) BINARY", + }, }); return $class->SUPER::definition($session, $definition); } diff --git a/lib/WebGUI/Form/YesNo.pm b/lib/WebGUI/Form/YesNo.pm index 6f58d909c..e4ee837a7 100644 --- a/lib/WebGUI/Form/YesNo.pm +++ b/lib/WebGUI/Form/YesNo.pm @@ -72,6 +72,9 @@ sub definition { profileEnabled=>{ defaultValue=>1 }, + dbDataType => { + defaultValue => "INT(1)", + }, }); return $class->SUPER::definition($session, $definition); } diff --git a/lib/WebGUI/Operation/Profile.pm b/lib/WebGUI/Operation/Profile.pm index 7b48148a7..191d22905 100644 --- a/lib/WebGUI/Operation/Profile.pm +++ b/lib/WebGUI/Operation/Profile.pm @@ -84,10 +84,14 @@ email address to check for duplication =cut sub isDuplicateEmail { - my $session = shift; - my $email = shift; - my ($otherEmail) = $session->db->quickArray("select count(*) from userProfileData where fieldName='email' and fieldData = ".$session->db->quote($email)." and userId <> ".$session->db->quote($session->user->userId)); - return ($otherEmail > 0); + my $session = shift; + my $email = shift; + my ($otherEmail) + = $session->db->quickArray( + 'select count(*) from userProfileData where email = ? and userId <> ?', + [$email, $session->user->userId] + ); + return ($otherEmail > 0); } #------------------------------------------------------------------- diff --git a/lib/WebGUI/Operation/User.pm b/lib/WebGUI/Operation/User.pm index f739db605..88e1e5670 100644 --- a/lib/WebGUI/Operation/User.pm +++ b/lib/WebGUI/Operation/User.pm @@ -138,10 +138,9 @@ sub doUserSearch { } $keyword = $session->db->quote($keyword); my $sql = "select users.userId, users.username, users.status, users.dateCreated, users.lastUpdated, - email.fieldData as email from users - left join userProfileData email on users.userId=email.userId and email.fieldName='email' - left join userProfileData useralias on users.userId=useralias.userId and useralias.fieldName='alias' - where $selectedStatus and (users.username like ".$keyword." or useralias.fieldData like ".$keyword." or email.fieldData like ".$keyword.") + userProfileData.email from users + left join userProfileData on users.userId=userProfileData.userId + where $selectedStatus and (users.username like ".$keyword." or alias like ".$keyword." or email like ".$keyword.") and users.userId not in (".$session->db->quoteAndJoin($userFilter).") order by users.username"; if ($returnPaginator) { my $p = WebGUI::Paginator->new($session,$session->url->page("op=".$op)); diff --git a/lib/WebGUI/ProfileField.pm b/lib/WebGUI/ProfileField.pm index daddc33a4..f53c98b1c 100644 --- a/lib/WebGUI/ProfileField.pm +++ b/lib/WebGUI/ProfileField.pm @@ -94,20 +94,45 @@ The unique id of the category to assign this field to. Defaults to "1" (misc). =cut sub create { - my $class = shift; - my $session = shift; - my $fieldName = shift; - my $properties = shift; - my $categoryId = shift || "1"; - my $fieldNameExists = $session->db->quickScalar("select count(*) from userProfileField where fieldName=?", [$fieldName]); - return undef if $fieldNameExists; - return undef if $class->isReservedFieldName($fieldName); + my $class = shift; + my $session = shift; + my $fieldName = shift; + my $properties = shift; + my $categoryId = shift || "1"; + + my $db = $session->db; - my $id = $session->db->setRow("userProfileField","fieldName",{fieldName=>"new"},$fieldName); - my $self = $class->new($session,$id); - $self->setCategory($categoryId); - $self->set($properties); - return $self; + ### Check data + # Check if the field already exists + my $fieldNameExists + = $session->db->quickScalar( + "select count(*) from userProfileField where fieldName=?", + [$fieldName] + ); + return undef if $fieldNameExists; + return undef if $class->isReservedFieldName($fieldName); + + ### Data okay, create the field + # Add the record + my $id + = $session->db->setRow("userProfileField","fieldName",{fieldName=>"new"},$fieldName); + my $self = $class->new($session,$id); + + # Get the field's data type + my $formClass = 'WebGUI::Form::' . ucfirst $properties->{fieldType}; + eval "use $formClass;"; + my $dbDataType = $formClass->new($session, $self->_formProperties($properties))->get("dbDataType"); + + # Add the column to the userProfileData table + $db->write( + "ALTER TABLE userProfileData ADD " . $db->dbh->quote_identifier($fieldName) + . $dbDataType + ); + + $self->setCategory($categoryId); + $self->set($properties); + + return $self; } #------------------------------------------------------------------- @@ -119,19 +144,31 @@ Deletes this field and all user data attached to it. =cut sub delete { - my $self = shift; - $self->session->db->write("delete from userProfileData where fieldName=?", [$self->getId]); - $self->session->db->deleteRow("userProfileField","fieldName",$self->getId); + my $self = shift; + my $db = $self->session->db; + + # Remove the column from the userProfileData table + $db->write("ALTER TABLE userProfileData DROP " . $self->getId); + + # Remove the record + $db->deleteRow("userProfileField","fieldName",$self->getId); } + #------------------------------------------------------------------- +# Get a hashref of properties to give to a WebGUI::Form::Control sub _formProperties { - my $self = shift; - my $properties = shift || {}; - $properties->{label} = $self->getLabel unless $properties->{label}; - $properties->{fieldType} = $self->get("fieldType"); - $properties->{name} = $self->getId; - my $values = WebGUI::Operation::Shared::secureEval($self->session,$self->get("possibleValues")); + my $self = shift; + my $properties = shift || {}; + + # Make a copy of the properties so we don't clobber them + my %properties = %{$properties}; + + $properties{ label } = $self->getLabel unless $properties->{label}; + $properties{ fieldType } = $self->get("fieldType"); + $properties{ name } = $self->getId; + my $values + = WebGUI::Operation::Shared::secureEval($self->session,$self->get("possibleValues")); unless (ref $values eq 'HASH') { if ($self->get('possibleValues') =~ /\S/) { $self->session->errorHandler->warn("Could not get a hash out of possible values for profile field ".$self->getId); @@ -140,13 +177,13 @@ sub _formProperties { } my $orderedValues = {}; tie %{$orderedValues}, 'Tie::IxHash'; - foreach my $ov (sort keys %{$values}) { - $orderedValues->{$ov} = $values->{$ov}; - } - $properties->{options} = $orderedValues; - $properties->{forceImageOnly} = $self->get("forceImageOnly"); - $properties->{dataDefault} = $self->get("dataDefault"); - return $properties; + for my $ov (sort keys %{$values}) { + $orderedValues->{$ov} = $values->{$ov}; + } + $properties{ options } = $orderedValues; + $properties{ forceImageOnly } = $self->get("forceImageOnly"); + $properties{ dataDefault } = $self->get("dataDefault"); + return \%properties; } =head2 formField ( [ formProperties, withWrapper, userObject ] ) @@ -334,6 +371,19 @@ sub getFields { #------------------------------------------------------------------- +=head2 getFormControlClass + +Returns the full class name of the form control for this profile field. + +=cut + +sub getFormControlClass { + my $self = shift; + return "WebGUI::Form::" . ucfirst $self->get("fieldType"); +} + +#------------------------------------------------------------------- + =head2 getRequiredFields ( session ) Returns an array reference of WebGUI::ProfileField objects that are marked "required". This is a class method. @@ -484,15 +534,15 @@ The unique name of this field. =cut sub new { - my $class = shift; - my $session = shift; - my $id = shift; - return undef unless ($id); - return undef if $class->isReservedFieldName($id); - my $properties = $session->db->getRow("userProfileField","fieldName",$id); - # Reject properties that don't exist. - return undef unless scalar keys %$properties; - bless {_session=>$session, _properties=>$properties}, $class; + my $class = shift; + my $session = shift; + my $id = shift; + return undef unless ($id); + return undef if $class->isReservedFieldName($id); + my $properties = $session->db->getRow("userProfileField","fieldName",$id); + # Reject properties that don't exist. + return undef unless scalar keys %$properties; + bless {_session=>$session, _properties=>$properties}, $class; } #------------------------------------------------------------------- @@ -508,14 +558,40 @@ The new name this field should take. =cut sub rename { - my $self = shift; - my $newName = shift; - my ($fieldNameExists) = $self->session->db->quickArray("select count(*) from userProfileField where fieldName=".$self->session->db->quote($newName)); + my $self = shift; + my $newName = shift; + + my $session = $self->session; + my $db = $session->db; + + ### Check data + # Make sure the field doesn't exist + my $fieldNameExists + = $self->session->db->quickScalar( + "SELECT COUNT(*) FROM userProfileField WHERE fieldName=?", + [$newName] + ); return 0 if ($fieldNameExists); - $self->session->db->write("update userProfileData set fieldName=".$self->session->db->quote($newName)." where fieldName=".$self->session->db->quote($self->getId)); - $self->session->db->write("update userProfileField set fieldName=".$self->session->db->quote($newName)." where fieldName=".$self->session->db->quote($self->getId)); + + # Rename the userProfileData column + my $fieldClass = $self->getFormControlClass; + eval "use $fieldClass;"; + my $dbDataType = $fieldClass->new($session, $self->_formProperties)->get("dbDataType"); + + $self->session->db->write( + "ALTER TABLE userProfileData " + . "CHANGE " . $db->dbh->quote_identifier($self->getId) + . $db->dbh->quote_identifier($newName) . " " . $dbDataType + ); + + # Update the record + $self->session->db->write( + "update userProfileField set fieldName=? where fieldName=?", + [$newName, $self->getId] + ); $self->{_properties}{fieldName} = $newName; - return 1; + + return 1; } @@ -577,9 +653,14 @@ A scalar containing an array reference or scalar declaration of defaultly select =cut sub set { - my $self = shift; - my $properties = shift; - $properties->{visible} = 0 unless ($properties->{visible} == 1); + my $self = shift; + my $properties = shift; + + my $session = $self->session; + my $db = $session->db; + + # Set the defaults + $properties->{visible} = 0 unless ($properties->{visible} == 1); $properties->{editable} = 0 unless ($properties->{editable} == 1); $properties->{protected} = 0 unless ($properties->{protected} == 1); $properties->{required} = 0 unless ($properties->{required} == 1); @@ -594,7 +675,26 @@ sub set { } } $properties->{fieldName} = $self->getId; - $self->session->db->setRow("userProfileField","fieldName",$properties); + + # If the fieldType has changed, modify the userProfileData column + if ($properties->{fieldType} ne $self->get("fieldType")) { + # Create a copy of the new properties so we don't mess them up + my $fieldClass = "WebGUI::Form::".ucfirst($properties->{fieldType}); + eval "use $fieldClass;"; + my $dbDataType + = $fieldClass->new($session, $self->_formProperties($properties))->get("dbDataType"); + + my $sql + = "ALTER TABLE userProfileData MODIFY COLUMN " + . $db->dbh->quote_identifier($self->getId) . q{ } + . $dbDataType + ; + + $db->write($sql); + } + + # Update the record + $db->setRow("userProfileField","fieldName",$properties); foreach my $key (keys %{$properties}) { $self->{_properties}{$key} = $properties->{$key}; } diff --git a/lib/WebGUI/User.pm b/lib/WebGUI/User.pm index f4172ec8e..768895183 100644 --- a/lib/WebGUI/User.pm +++ b/lib/WebGUI/User.pm @@ -58,12 +58,13 @@ These methods are available from this class: #------------------------------------------------------------------- sub _create { - my $session = shift; - my $userId = shift || $session->id->generate(); - $session->db->write("insert into users (userId,dateCreated) values (?,?)",[$userId, time()]); - WebGUI::Group->new($session,2)->addUsers([$userId]); - WebGUI::Group->new($session,7)->addUsers([$userId]); - return $userId; + my $session = shift; + my $userId = shift || $session->id->generate(); + $session->db->write("insert into users (userId,dateCreated) values (?,?)",[$userId, time()]); + $session->db->write("INSERT INTO userProfileData (userId) VALUES (?)",[$userId]); + WebGUI::Group->new($session,2)->addUsers([$userId]); + WebGUI::Group->new($session,7)->addUsers([$userId]); + return $userId; } #------------------------------------------------------------------- @@ -375,41 +376,45 @@ A unique ID to use instead of the ID that WebGUI will generate for you. It must =cut sub new { - my $class = shift; - my $session = shift; - my $userId = shift || 1; - my $overrideId = shift; - $userId = _create($session, $overrideId) if ($userId eq "new"); - my $cache = WebGUI::Cache->new($session,["user",$userId]); - my $userData = $cache->get; - unless ($userData->{_userId} && $userData->{_user}{username}) { - my %user; - tie %user, 'Tie::CPHash'; - %user = $session->db->quickHash("select * from users where userId=?",[$userId]); - my %profile = $session->db->buildHash("select userProfileField.fieldName, userProfileData.fieldData - from userProfileField, userProfileData where userProfileField.fieldName=userProfileData.fieldName and - userProfileData.userId=?",[$user{userId}]); - my %default = $session->db->buildHash("select fieldName, dataDefault from userProfileField"); - foreach my $key (keys %default) { - my $value; - if ($profile{$key} eq "" && $default{$key}) { - $value = eval($default{$key}); - if (ref $value eq "ARRAY") { - $profile{$key} = $$value[0]; - } else { - $profile{$key} = $value; - } - } - } - $profile{alias} = $user{username} if ($profile{alias} =~ /^\W+$/ || $profile{alias} eq ""); - $userData = { - _userId => $userId, - _user => \%user, - _profile => \%profile - }; - $cache->set($userData, 60*60*24); - } - $userData->{_session} = $session; + my $class = shift; + my $session = shift; + my $userId = shift || 1; + my $overrideId = shift; + $userId = _create($session, $overrideId) if ($userId eq "new"); + my $cache = WebGUI::Cache->new($session,["user",$userId]); + my $userData = $cache->get; + unless ($userData->{_userId} && $userData->{_user}{username}) { + my %user; + tie %user, 'Tie::CPHash'; + %user = $session->db->quickHash("select * from users where userId=?",[$userId]); + my %profile + = $session->db->quickHash( + "select * from userProfileData where userId=?", + [$user{userId}] + ); + delete $profile{userId}; + + my %default = $session->db->buildHash("select fieldName, dataDefault from userProfileField"); + foreach my $key (keys %default) { + my $value; + if ($profile{$key} eq "" && $default{$key}) { + $value = eval($default{$key}); + if (ref $value eq "ARRAY") { + $profile{$key} = $$value[0]; + } else { + $profile{$key} = $value; + } + } + } + $profile{alias} = $user{username} if ($profile{alias} =~ /^\W+$/ || $profile{alias} eq ""); + $userData = { + _userId => $userId, + _user => \%user, + _profile => \%profile + }; + $cache->set($userData, 60*60*24); + } + $userData->{_session} = $session; bless $userData, $class; } @@ -433,7 +438,7 @@ sub newByEmail { my $class = shift; my $session = shift; my $email = shift; - my ($id) = $session->dbSlave->quickArray("select userId from userProfileData where fieldName='email' and fieldData=?",[$email]); + my ($id) = $session->dbSlave->quickArray("select userId from userProfileData where email=?",[$email]); my $user = $class->new($session, $id); return undef if ($user->userId eq "1"); # visitor is never valid for this method return undef unless $user->username; @@ -458,22 +463,24 @@ The value to set the profile field name to. =cut sub profileField { - my ($self, $fieldName, $value); - $self = shift; - $fieldName = shift; - $value = shift; - if (!exists $self->{_profile}{$fieldName} && !$self->session->db->quickScalar("SELECT COUNT(*) FROM userProfileField WHERE fieldName = ?", [$fieldName]) ) { - $self->session->errorHandler->warn("No such profile field: $fieldName"); - return undef; - } - if (defined $value) { - $self->uncache; - $self->{_profile}{$fieldName} = $value; - $self->session->db->write("delete from userProfileData where userId=? and fieldName=?",[$self->{_userId}, $fieldName]); - $self->session->db->write("insert into userProfileData values (?,?,?)", [$self->{_userId}, $fieldName,$value]); - my $time = $self->session->datetime->time(); - $self->{_user}{"lastUpdated"} = $time; - $self->session->db->write("update users set lastUpdated=? where userId=?", [$time, $self->{_userId}]); + my $self = shift; + my $fieldName = shift; + my $value = shift; + my $db = $self->session->db; + if (!exists $self->{_profile}{$fieldName} && !$self->session->db->quickScalar("SELECT COUNT(*) FROM userProfileField WHERE fieldName = ?", [$fieldName]) ) { + $self->session->errorHandler->warn("No such profile field: $fieldName"); + return undef; + } + if (defined $value) { + $self->uncache; + $self->{_profile}{$fieldName} = $value; + $db->write( + "UPDATE userProfileData SET ".$db->dbh->quote_identifier($fieldName)."=? WHERE userId=?", + [$value, $self->{_userId}] + ); + my $time = $self->session->datetime->time; + $self->{_user}{"lastUpdated"} = $time; + $self->session->db->write("update users set lastUpdated=? where userId=?", [$time, $self->{_userId}]); } return $self->{_profile}{$fieldName}; }