diff options
28 files changed, 1001 insertions, 715 deletions
diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 098156e89..7cf0ffdeb 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,10 @@ +Drupal x.x.x, xxxx-xx-xx +------------------------ + +- profile module: + * made it possible to administere profile fields. + * made it possible to browse the profiles by field. + Drupal 4.4.0, xxxx-xx-xx (release candidate) -------------------------------------------- diff --git a/database/database.mysql b/database/database.mysql index e5cdab7a3..10f3ff9dc 100644 --- a/database/database.mysql +++ b/database/database.mysql @@ -282,13 +282,13 @@ CREATE TABLE node ( uid int(10) NOT NULL default '0', status int(4) NOT NULL default '1', created int(11) NOT NULL default '0', + changed int(11) NOT NULL default '0', comment int(2) NOT NULL default '0', promote int(2) NOT NULL default '0', moderate int(2) NOT NULL default '0', users longtext NOT NULL, teaser longtext NOT NULL, body longtext NOT NULL, - changed int(11) NOT NULL default '0', revisions longtext NOT NULL, static int(2) NOT NULL default '0', PRIMARY KEY (nid), @@ -313,6 +313,38 @@ CREATE TABLE page ( ) TYPE=MyISAM; -- +-- Table structure for table 'profile_fields' +-- + +CREATE TABLE profile_fields ( + fid int(10) NOT NULL auto_increment, + title varchar(255) default NULL, + name varchar(128) default NULL, + explanation TEXT default NULL, + category varchar(255) default NULL, + type varchar(128) default NULL, + weight tinyint(1) DEFAULT '0' NOT NULL, + overview tinyint(1) DEFAULT '0' NOT NULL, + options text, + KEY category (category), + UNIQUE KEY name (name), + PRIMARY KEY (fid) +); + +-- +-- Table structure for table 'profile_values' +-- + +CREATE TABLE profile_values ( + fid int(11) unsigned default '0', + uid int(11) unsigned default '0', + value text, + KEY uid (uid), + KEY fid (fid) +); + + +-- -- Table structure for table 'url_alias' -- @@ -514,10 +546,12 @@ CREATE TABLE users ( threshold tinyint(1) default '0', theme varchar(255) NOT NULL default '', signature varchar(255) NOT NULL default '', - timestamp int(11) NOT NULL default '0', + created int(11) NOT NULL default '0', + changed int(11) NOT NULL default '0', status tinyint(4) NOT NULL default '0', timezone varchar(8) default NULL, language char(2) NOT NULL default '', + picture varchar(255) NOT NULL DEFAULT '', init varchar(64) default '', data longtext, rid int(10) unsigned NOT NULL default '0', diff --git a/database/database.pgsql b/database/database.pgsql index 12975aa29..213f71a93 100644 --- a/database/database.pgsql +++ b/database/database.pgsql @@ -511,10 +511,12 @@ CREATE TABLE users ( threshold smallint default '0', theme varchar(255) NOT NULL default '', signature varchar(255) NOT NULL default '', - timestamp integer NOT NULL default '0', + created integer NOT NULL default '0', + changed integer NOT NULL default '0', status smallint NOT NULL default '0', timezone varchar(8) default NULL, language char(2) NOT NULL default '', + picture varchar(255) NOT NULL DEFAULT '', init varchar(64) default '', data text default '', rid integer NOT NULL default '0', diff --git a/database/updates.inc b/database/updates.inc index e39267ef4..79b257352 100644 --- a/database/updates.inc +++ b/database/updates.inc @@ -50,7 +50,8 @@ $sql_updates = array( "2004-01-11" => "update_76", "2004-01-13" => "update_77", "2004-02-03" => "update_78", - "2004-02-21" => "update_79" + "2004-02-21" => "update_79", + "2004-03-11: first update since Drupal 4.4.0 release" => "update_80" ); function update_32() { @@ -760,6 +761,126 @@ function update_79() { return $ret; } +function update_80() { + + // Add a 'created' field to the users table: + update_sql('ALTER TABLE {users} ADD created INT(11) NOT NULL'); + update_sql('ALTER TABLE {users} CHANGE timestamp changed INT(11) NOT NULL'); + + // Add some indices to speed up the update process: + update_sql('ALTER TABLE {comments} ADD index (timestamp)'); + update_sql('ALTER TABLE {node} ADD index (created)'); + + // Assign everyone a created timestamp to begin with: + update_sql("UPDATE {users} SET created = changed WHERE created = ''"); + + // Print a status message:" + print '<p>Note: this might take a while ...</p>'; + + // Try updating the user records using the comment table: + $result = db_query('SELECT DISTINCT(u.uid) FROM {comments} c LEFT JOIN {users} u ON c.uid = u.uid WHERE c.timestamp < u.created'); + while ($account = db_fetch_object($result)) { + // Retrieve the proper timestamp: + $timestamp = db_result(db_query('SELECT MIN(timestamp) FROM {comments} WHERE uid = %d', $account->uid)); + + // Update this user record as well as older records with an older timestamp: + db_query('UPDATE {users} SET created = %d WHERE created > %d AND uid <= %d', $timestamp, $timestamp, $account->uid); + } + + // Try updating the user records using the node table: + $result = db_query('SELECT DISTINCT(u.uid) FROM {node} n LEFT JOIN {users} u ON n.uid = u.uid WHERE n.created < u.created'); + while ($account = db_fetch_object($result)) { + // Retrieve the proper timestamp: + $timestamp = db_result(db_query('SELECT MIN(created) FROM {node} WHERE uid = %d', $account->uid)); + + // Update this user record as well as older records with an older timestamp: + db_query('UPDATE {users} SET created = %d WHERE created > %d AND uid <= %d', $timestamp, $timestamp, $account->uid); + } + + // Add profile module related tables: + update_sql("CREATE TABLE {profile_fields} ( + fid int(10) NOT NULL auto_increment, + title varchar(255) default NULL, + name varchar(128) default NULL, + explanation TEXT default NULL, + category varchar(255) default NULL, + type varchar(128) default NULL, + weight tinyint(1) DEFAULT '0' NOT NULL, + overview tinyint(1) DEFAULT '0' NOT NULL, + options text, + KEY category (category), + UNIQUE KEY name (name), + PRIMARY KEY (fid) + );"); + + update_sql("CREATE TABLE {profile_values} ( + fid int(11) unsigned default '0', + uid int(11) unsigned default '0', + value text, + KEY uid (uid), + KEY fid (fid) + );"); + + // Migrate the old profile data to the new scheme: + $fields = array( + array("Name", "realname", "textfield", NULL, 0), + array("Address", "address", "textfield", NULL, 0), + array("City", "city", "textfield", NULL, 0), + array("State, province or region", "state", "textfield", NULL, 0), + array("Zip or postal code", "zip", "textfield", NULL, 0), + array("Country", "country", "textfield", NULL, 1), + array("Gender", "gender", "selection", "male\nfemale", 1), + array("Job title", "job", "textfield", NULL, 0), + array("ICQ messenger ID", "icq", "textfield", NULL, 0), + array("MSN messenger ID", "msn", "textfield", NULL, 0), + array("Yahoo messenger ID", "yahoo", "textfield", NULL, 0), + array("AIM messenger ID", "aim", "textfield", NULL, 0), + array("URL of homepage", "homepage", "textfield", NULL, 1), + array("Biography", "biography", "textarea", NULL, 0), + array("Interests", "interests", "textarea", NULL, 0), + array("Public key", "publickey", "textarea", NULL, 0) + ); + + // Remove existing data (debug mode): + db_query('DELETE FROM {profile_fields}'); + db_query('DELETE FROM {profile_values}'); + + foreach ($fields as $field) { + db_query("INSERT INTO {profile_fields} (title, name, type, category, options, overview) VALUES ('%s', '%s', '%s', 'Personal information', '%s', %d)", $field[0], $field[1], $field[2], $field[3], $field[4]); + } + db_query("ALTER TABLE {users} ADD picture varchar(255) NOT NULL DEFAULT ''"); + + $result = db_query("SELECT uid FROM {users}"); + while ($account = db_fetch_object($result)) { + // Load the user record: + $account = user_load(array('uid' => $account->uid)); + $edit = array(); + + // Modify the user record: + foreach ($fields as $field) { + $old = "profile_". $field[1]; + $new = $field[1]; + if ($account->$old) { + $edit[$new] = $account->$old; + } + unset($account->$old); + } + + // Gender specific changes: + if ($edit['gender'] == 'f') $edit['gender'] = 'female'; + if ($edit['gender'] == 'm') $edit['gender'] = 'male'; + + // Avatar specific changes: + if ($account->profile_avatar) { + $edit['picture'] = $account->profile_avatar; + } + unset($account->profile_avatar); + + // Save the update record: + user_save($account, $edit); + } +} + function update_sql($sql) { $edit = $_POST["edit"]; $result = db_query($sql); diff --git a/misc/drupal.css b/misc/drupal.css index 48112c3fa..1b6fb965b 100644 --- a/misc/drupal.css +++ b/misc/drupal.css @@ -56,6 +56,7 @@ li a.active { padding: 0.25em 1em 0.25em 0em; } #pager { + clear: both; text-align: center; } #pager div { @@ -156,6 +157,14 @@ li a.active { .form-submit { margin: 0.5em 0; } +.profile { + clear: both; + margin: 1em 0em 1em 0em; +} +.profile .picture { + float: right; + margin: 0 1em 1em 0; +} #aggregator .feed img { float: right; } diff --git a/modules/blog.module b/modules/blog.module index e95937607..9b18115d4 100644 --- a/modules/blog.module +++ b/modules/blog.module @@ -37,12 +37,8 @@ function blog_access($op, $node) { } function blog_user($type, &$edit, &$user) { - switch ($type) { - case "view_public": - case "view_private": - if (user_access("maintain personal blog", $user)) { - return form_item(t("Blog"), l(t("view recent blog entries"), "blog/$user->uid", array("title" => t("Read %username's latest blog entries.", array("%username" => $user->name))))); - } + if ($type == 'view' && user_access("maintain personal blog", $user)) { + return form_item(t("Blog"), l(t("view recent blog entries"), "blog/$user->uid", array("title" => t("Read %username's latest blog entries.", array("%username" => $user->name))))); } } diff --git a/modules/blog/blog.module b/modules/blog/blog.module index e95937607..9b18115d4 100644 --- a/modules/blog/blog.module +++ b/modules/blog/blog.module @@ -37,12 +37,8 @@ function blog_access($op, $node) { } function blog_user($type, &$edit, &$user) { - switch ($type) { - case "view_public": - case "view_private": - if (user_access("maintain personal blog", $user)) { - return form_item(t("Blog"), l(t("view recent blog entries"), "blog/$user->uid", array("title" => t("Read %username's latest blog entries.", array("%username" => $user->name))))); - } + if ($type == 'view' && user_access("maintain personal blog", $user)) { + return form_item(t("Blog"), l(t("view recent blog entries"), "blog/$user->uid", array("title" => t("Read %username's latest blog entries.", array("%username" => $user->name))))); } } diff --git a/modules/comment.module b/modules/comment.module index 69fd729fb..a0225e44b 100644 --- a/modules/comment.module +++ b/modules/comment.module @@ -138,16 +138,15 @@ function comment_settings() { function comment_user($type, $edit, &$user) { switch ($type) { - case "view_public": - case "view_private": + case "view": if ($user->signature) { return form_item(t("Signature"), check_output($user->signature)); } break; - case "edit_form": + case "edit": // when user tries to edit his own data return array(t('Personal information') => form_textarea(t("Signature"), "signature", $user->signature, 70, 3, t("Your signature will be publicly displayed at the end of your comments.") ."<br />". filter_tips_short())); - case "edit_validate": + case "edit": // validate user data editing return array("signature" => $edit["signature"]); } @@ -203,7 +202,7 @@ function comment_reply($pid, $nid) { */ if ($pid) { - $comment = db_fetch_object(db_query("SELECT c.*, u.uid, u.name, u.data FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.cid = %d AND c.status = 0", $pid)); + $comment = db_fetch_object(db_query("SELECT c.*, u.uid, u.name, u.picture, u.data FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.cid = %d AND c.status = 0", $pid)); $comment = drupal_unpack($comment); $output .= theme("comment_view", $comment); } @@ -258,7 +257,7 @@ function comment_preview($edit) { $output .= theme("comment_form", $edit, t("Reply")); if ($edit["pid"]) { - $comment = db_fetch_object(db_query("SELECT c.*, u.uid, u.name, u.data FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.cid = %d AND c.status = 0", $edit["pid"])); + $comment = db_fetch_object(db_query("SELECT c.*, u.uid, u.name, u.picture, u.data FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.cid = %d AND c.status = 0", $edit["pid"])); $comment = drupal_unpack($comment); $output .= theme("comment_view", $comment); } @@ -559,7 +558,7 @@ function comment_render($node, $cid = 0) { $output .= "<form method=\"post\" action=\"". url("comment") ."\"><div>\n"; $output .= form_hidden("nid", $nid); - $result = db_query("SELECT c.cid, c.pid, c.nid, c.subject, c.comment, c.timestamp, u.uid, u.name, u.data, c.score, c.users FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.cid = %d AND c.status = 0 GROUP BY c.cid, c.pid, c.nid, c.subject, c.comment, c.timestamp, u.uid, u.name, u.data, c.score, c.users", $cid); + $result = db_query("SELECT c.cid, c.pid, c.nid, c.subject, c.comment, c.timestamp, u.uid, u.name, u.picture, u.data, c.score, c.users FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.cid = %d AND c.status = 0 GROUP BY c.cid, c.pid, c.nid, c.subject, c.comment, c.timestamp, u.uid, u.name, u.picture, u.data, c.score, c.users", $cid); if ($comment = db_fetch_object($result)) { $output .= theme("comment_view", $comment, theme('links', module_invoke_all('link', 'comment', $comment, 1))); @@ -576,9 +575,9 @@ function comment_render($node, $cid = 0) { ** Multiple comments view */ - $query .= "SELECT c.cid as cid, c.pid, c.nid, c.subject, c.comment, c.timestamp, u.uid, u.name, u.data, c.score, c.users, c.thread FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.nid = '". check_query($nid) ."' AND c.status = 0"; + $query .= "SELECT c.cid as cid, c.pid, c.nid, c.subject, c.comment, c.timestamp, u.uid, u.name, u.picture, u.data, c.score, c.users, c.thread FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.nid = '". check_query($nid) ."' AND c.status = 0"; - $query .= " GROUP BY c.cid, c.pid, c.nid, c.subject, c.comment, c.timestamp, u.uid, u.name, u.data, c.score, c.users, c.thread"; + $query .= " GROUP BY c.cid, c.pid, c.nid, c.subject, c.comment, c.timestamp, u.uid, u.name, u.picture, u.data, c.score, c.users, c.thread"; /* ** We want to use the standard pager, but threads would need every diff --git a/modules/comment/comment.module b/modules/comment/comment.module index 69fd729fb..a0225e44b 100644 --- a/modules/comment/comment.module +++ b/modules/comment/comment.module @@ -138,16 +138,15 @@ function comment_settings() { function comment_user($type, $edit, &$user) { switch ($type) { - case "view_public": - case "view_private": + case "view": if ($user->signature) { return form_item(t("Signature"), check_output($user->signature)); } break; - case "edit_form": + case "edit": // when user tries to edit his own data return array(t('Personal information') => form_textarea(t("Signature"), "signature", $user->signature, 70, 3, t("Your signature will be publicly displayed at the end of your comments.") ."<br />". filter_tips_short())); - case "edit_validate": + case "edit": // validate user data editing return array("signature" => $edit["signature"]); } @@ -203,7 +202,7 @@ function comment_reply($pid, $nid) { */ if ($pid) { - $comment = db_fetch_object(db_query("SELECT c.*, u.uid, u.name, u.data FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.cid = %d AND c.status = 0", $pid)); + $comment = db_fetch_object(db_query("SELECT c.*, u.uid, u.name, u.picture, u.data FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.cid = %d AND c.status = 0", $pid)); $comment = drupal_unpack($comment); $output .= theme("comment_view", $comment); } @@ -258,7 +257,7 @@ function comment_preview($edit) { $output .= theme("comment_form", $edit, t("Reply")); if ($edit["pid"]) { - $comment = db_fetch_object(db_query("SELECT c.*, u.uid, u.name, u.data FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.cid = %d AND c.status = 0", $edit["pid"])); + $comment = db_fetch_object(db_query("SELECT c.*, u.uid, u.name, u.picture, u.data FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.cid = %d AND c.status = 0", $edit["pid"])); $comment = drupal_unpack($comment); $output .= theme("comment_view", $comment); } @@ -559,7 +558,7 @@ function comment_render($node, $cid = 0) { $output .= "<form method=\"post\" action=\"". url("comment") ."\"><div>\n"; $output .= form_hidden("nid", $nid); - $result = db_query("SELECT c.cid, c.pid, c.nid, c.subject, c.comment, c.timestamp, u.uid, u.name, u.data, c.score, c.users FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.cid = %d AND c.status = 0 GROUP BY c.cid, c.pid, c.nid, c.subject, c.comment, c.timestamp, u.uid, u.name, u.data, c.score, c.users", $cid); + $result = db_query("SELECT c.cid, c.pid, c.nid, c.subject, c.comment, c.timestamp, u.uid, u.name, u.picture, u.data, c.score, c.users FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.cid = %d AND c.status = 0 GROUP BY c.cid, c.pid, c.nid, c.subject, c.comment, c.timestamp, u.uid, u.name, u.picture, u.data, c.score, c.users", $cid); if ($comment = db_fetch_object($result)) { $output .= theme("comment_view", $comment, theme('links', module_invoke_all('link', 'comment', $comment, 1))); @@ -576,9 +575,9 @@ function comment_render($node, $cid = 0) { ** Multiple comments view */ - $query .= "SELECT c.cid as cid, c.pid, c.nid, c.subject, c.comment, c.timestamp, u.uid, u.name, u.data, c.score, c.users, c.thread FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.nid = '". check_query($nid) ."' AND c.status = 0"; + $query .= "SELECT c.cid as cid, c.pid, c.nid, c.subject, c.comment, c.timestamp, u.uid, u.name, u.picture, u.data, c.score, c.users, c.thread FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.nid = '". check_query($nid) ."' AND c.status = 0"; - $query .= " GROUP BY c.cid, c.pid, c.nid, c.subject, c.comment, c.timestamp, u.uid, u.name, u.data, c.score, c.users, c.thread"; + $query .= " GROUP BY c.cid, c.pid, c.nid, c.subject, c.comment, c.timestamp, u.uid, u.name, u.picture, u.data, c.score, c.users, c.thread"; /* ** We want to use the standard pager, but threads would need every diff --git a/modules/drupal.module b/modules/drupal.module index 77a5c8f3a..b6392a501 100644 --- a/modules/drupal.module +++ b/modules/drupal.module @@ -185,22 +185,4 @@ function drupal_login($arguments) { } } -function drupal_user($type, $edit, $user) { - - $module = "drupal"; - $name = module_invoke($module, "info", "name"); - switch ($type) { - case "view_private": - $result = user_get_authname($user, $module); - $title = t("%name ID", array("%name" => $name)); - if ($result) { - return form_item($title, $result); - } - else { - // TODO: use a variation of $base_url instead of $HTTP_HOST below - return form_item($title, "$user->name@". $_SERVER["HTTP_HOST"]); - } - } -} - ?> diff --git a/modules/drupal/drupal.module b/modules/drupal/drupal.module index 77a5c8f3a..b6392a501 100644 --- a/modules/drupal/drupal.module +++ b/modules/drupal/drupal.module @@ -185,22 +185,4 @@ function drupal_login($arguments) { } } -function drupal_user($type, $edit, $user) { - - $module = "drupal"; - $name = module_invoke($module, "info", "name"); - switch ($type) { - case "view_private": - $result = user_get_authname($user, $module); - $title = t("%name ID", array("%name" => $name)); - if ($result) { - return form_item($title, $result); - } - else { - // TODO: use a variation of $base_url instead of $HTTP_HOST below - return form_item($title, "$user->name@". $_SERVER["HTTP_HOST"]); - } - } -} - ?> diff --git a/modules/locale.module b/modules/locale.module index eedba7595..907c99834 100644 --- a/modules/locale.module +++ b/modules/locale.module @@ -74,7 +74,7 @@ function locale_link($type) { function locale_user($type, &$edit, &$user) { global $languages; - if ($type == "edit_form" && count($languages) > 1) { + if ($type == 'edit' && count($languages) > 1) { return array(t('Locale settings') => form_radios(t("Language"), 'language', $user->language, $languages, t("Selecting a different language will change the language of the site."))); } } diff --git a/modules/locale/locale.module b/modules/locale/locale.module index eedba7595..907c99834 100644 --- a/modules/locale/locale.module +++ b/modules/locale/locale.module @@ -74,7 +74,7 @@ function locale_link($type) { function locale_user($type, &$edit, &$user) { global $languages; - if ($type == "edit_form" && count($languages) > 1) { + if ($type == 'edit' && count($languages) > 1) { return array(t('Locale settings') => form_radios(t("Language"), 'language', $user->language, $languages, t("Selecting a different language will change the language of the site."))); } } diff --git a/modules/node.module b/modules/node.module index 3e1a62155..16c7015d6 100644 --- a/modules/node.module +++ b/modules/node.module @@ -340,7 +340,7 @@ function node_load($conditions, $revision = -1) { ** Retrieve the node: */ - $node = db_fetch_object(db_query('SELECT n.*, u.uid, u.name, u.data FROM {node} n INNER JOIN {users} u ON u.uid = n.uid WHERE '. implode(' AND ', $cond))); + $node = db_fetch_object(db_query('SELECT n.*, u.uid, u.name, u.picture, u.data FROM {node} n INNER JOIN {users} u ON u.uid = n.uid WHERE '. implode(' AND ', $cond))); $node = drupal_unpack($node); /* diff --git a/modules/node/node.module b/modules/node/node.module index 3e1a62155..16c7015d6 100644 --- a/modules/node/node.module +++ b/modules/node/node.module @@ -340,7 +340,7 @@ function node_load($conditions, $revision = -1) { ** Retrieve the node: */ - $node = db_fetch_object(db_query('SELECT n.*, u.uid, u.name, u.data FROM {node} n INNER JOIN {users} u ON u.uid = n.uid WHERE '. implode(' AND ', $cond))); + $node = db_fetch_object(db_query('SELECT n.*, u.uid, u.name, u.picture, u.data FROM {node} n INNER JOIN {users} u ON u.uid = n.uid WHERE '. implode(' AND ', $cond))); $node = drupal_unpack($node); /* diff --git a/modules/profile.module b/modules/profile.module index a16c47c20..d1e5e5f0c 100644 --- a/modules/profile.module +++ b/modules/profile.module @@ -1,306 +1,300 @@ <?php -// $Id$ - -function _profile_init() { - /* - ** Add here any field you might need. Leave array[0] blank if you - ** need a special tool (like birthday or avatar). - ** TODO: add a clear description/explanation. - */ - - $GLOBALS["profile_fields"] = array( - "realname" => array("textfield", t("Name"), "", 64, 64, ""), - "address" => array("textfield", t("Address"), "", 64, 64, ""), - "city" => array("textfield", t("City"), "", 64, 64, ""), - "state" => array("textfield", t("State, province or region"), "", 64, 64, ""), - "zip" => array("textfield", t("Zip or postal code"), "", 7, 10, ""), - "country" => array("textfield", t("Country"), "", 64, 64, ""), - "birthday" => array("", t("Birthday"), ""), - "gender" => array("select", t("Gender"), "", array(0 => "-", "m" => t("male"), "f" => t("female")), "", 0, 0), - "job" => array("textfield", t("Job title"), "", 64, 64, ""), - "icq" => array("textfield", t("ICQ messenger ID"), "", 12, 12, ""), - "msn" => array("textfield", t("MSN messenger ID"), "", 64, 64, ""), - "yahoo" => array("textfield", t("Yahoo messenger ID"), "", 64, 64, ""), - "aim" => array("textfield", t("AIM messenger ID"), "", 64, 64, ""), - "homepage" => array("textfield", t("URL of homepage"), "", 64, 64, t("Make sure you enter a fully qualified URL: remember to include \"http://\".")), - "biography" => array("textarea", t("Biography"), "", 64, 4, ""), - "interests" => array("textarea", t("Interests"), "", 64, 4, ""), - "publickey" => array("textarea", t("Public key"), "", 64, 4, ""), - "avatar" => array("", t("Avatar or picture"), t("Your virtual face or picture. Maximum dimensions are %dimensions and the maximum size is %size kB.", array("%dimensions" => variable_get("profile_avatar_dimensions", "85x85"), "%size" => variable_get("profile_avatar_file_size", "30")))) - ); - - $GLOBALS["profile_days"] = array_merge(array(0 => t("day")), drupal_map_assoc(range(1, 31))); - $GLOBALS["profile_months"] = array(0 => t("month"), 1 => t("January"), 2 => t("February"), 3 => t("March"), 4 => t("April"), 5 => t("May"), 6 => t("June"), 7 => t("July"), 8 => t("August"), 9 => t("September"), 10 => t("October"), 11 => t("November"), 12 => t("December")); -} -function profile_help($section) { - $output = ""; +// TODO: add a 'date' field so we can migrate the birthday information. +function profile_help($section) { switch ($section) { case 'admin/system/modules#description': $output = t("Support for configurable user profiles."); break; - case 'admin/system/modules/profile': - $output = t("When a user creates an account you can ask for some extra information, as well as letting the user have a small picture, called an avatar. <ul><li>In order for a user to enter information, you <strong>must</strong> check <em>enable</em>.</li><li>In order for other people to see the entered information, you must make it <em>public</em>.</li><li>If an item is <em>public</em>, but not enabled, the user can never give it a value and it will never be seen. <em>Public</em> does <strong>not</strong> imply <em>enable</em>.</li></ul>", array("%edit" => url("user/edit"))); - break; } return $output; + + } -function profile_settings() { - global $profile_fields; +function profile_link($type) { + if ($type == 'system') { + menu('profile', t('browse'), 'profile_browse', 0, MENU_HIDE); - if (!$profile_fields) { - _profile_init(); + if (user_access('administer users')) { + menu('admin/system/modules/profile', t('profile'), 'profile_admin_overview'); + menu('admin/system/modules/profile/add', NULL, 'profile_admin_add', 0, MENU_HIDE); + menu('admin/system/modules/profile/edit', NULL, 'profile_admin_edit', 0, MENU_HIDE); + menu('admin/system/modules/profile/delete', NULL, 'profile_admin_delete', 0, MENU_HIDE); + } } +} - if (!file_check_directory(file_create_path(variable_get('profile_avatar_path', 'avatars')))) { - $error['profile_avatar_path'] = theme('error', t('Directory does not exist, or is not writable.')); - } +function profile_browse() { - $profile_public_fields = variable_get("profile_public_fields", array()); - $profile_private_fields = variable_get("profile_private_fields", array()); - $profile_required_fields = variable_get("profile_required_fields", array()); - - $header = array(t("field"), t("enable"), t("public"), t("required")); - $i = 0; - foreach ($profile_fields as $key => $field) { - $row[$i][] = $field[1]; - $row[$i][] = form_checkbox("", "profile_private_fields][", $key, in_array($key, $profile_private_fields)); - $row[$i][] = form_checkbox("", "profile_public_fields][", $key, in_array($key, $profile_public_fields)); - $row[$i][] = form_checkbox("", "profile_required_fields][", $key, in_array($key, $profile_required_fields)); - $i++; - } + $value = arg(2) ? arg(2) : 1; - $avatar = form_textfield(t("Avatar image path"), "profile_avatar_path", variable_get("profile_avatar_path", "avatars"), 30, 255, t("Subdirectory in the directory '%dir' where avatars will be stored.", array('%dir' => variable_get('file_directory_path', 'files') . FILE_SEPARATOR)) . $error['profile_avatar_path']); - $avatar .= form_textfield(t("Avatar maximum dimensions"), "profile_avatar_dimensions", variable_get("profile_avatar_dimensions", "85x85"), 10, 10, t("Maximum dimensions for avatars.")); - $avatar .= form_textfield(t("Avatar maximum file size"), "profile_avatar_file_size", variable_get("profile_avatar_file_size", "30"), 10, 10, t("Maximum file size for avatars, in kB.")); + // Determine the field to group users by: + $field = db_fetch_object(db_query("SELECT DISTINCT(f.fid), f.type, f.title FROM {profile_fields} f INNER JOIN {profile_values} v ON f.fid = v.fid WHERE f.name = '%s' AND v.value = '%s' ORDER BY f.category, f.weight", arg(1), $value)); - $output = theme("table", $header, $row); - $output .= form_group(t('Avatars'), $avatar); + if ($field->fid) { + // Compile a list of fields to show: + $fields = array(); + $result = db_query("SELECT name, title, type FROM {profile_fields} WHERE fid != %d AND overview = 1", $field->fid); + while ($record = db_fetch_object($result)) { + $fields[] = $record; + } - return $output; -} + // Extract the affected users: + $result = pager_query("SELECT u.uid FROM {users} u INNER JOIN {profile_values} v ON u.uid = v.uid WHERE v.fid = $field->fid AND v.value = '". check_query($value) ."' ORDER BY u.changed DESC", 20); -function profile_user($type, $edit, &$user) { - global $profile_fields; - if (!$profile_fields) { - _profile_init(); + $output = ''; + while ($account = db_fetch_object($result)) { + $output .= theme('profile_profile', user_load(array('uid' => $account->uid)), $fields); + } + $output .= theme('pager', NULL, 20); + + if ($field->type == "selection") { + $title = arg(2); + } + else { + $title = $field->title; + } + + print theme('page', $output, $title); + } + else { + drupal_not_found(); } +} - switch ($type) { - case "edit_form": - // when user tries to edit his own data - return _profile_form(object2array($user), "private"); - case "edit_validate": - // validate user data editing - return _profile_validate($edit, "private", $user); - case "view_public": - // when others look at user data - return _profile_user_view($user, "public"); - case "view_private": - // when user looks at his own data - return _profile_user_view($user, "private"); +function profile_load_profile(&$user) { + $result = db_query('SELECT f.name, v.value FROM {profile_fields} f INNER JOIN {profile_values} v ON f.fid = v.fid WHERE uid = %d', $user->uid); + while ($field = db_fetch_object($result)) { + if (empty($user->{$field->name})) { + $user->{$field->name} = $field->value; + } } } -function profile_required($title) { - // this pleads "theme, theme" ;) - return $title ." ". theme("mark"); +function profile_save_profile($edit, $user) { + db_query('DELETE FROM {profile_values} WHERE uid = %d', $user->uid); + $result = db_query('SELECT fid, name FROM profile_fields'); + while ($field = db_fetch_object($result)) { + if ($edit[$field->name]) { + db_query("INSERT INTO {profile_values} (fid, uid, value) VALUES (%d, %d, '%s')", $field->fid, $user->uid, $edit[$field->name]); + unset($edit[$field->name]); + } + } } -function _profile_form($edit, $mode) { - global $profile_fields, $user; +function profile_view_profile($user) { - $reg_fields = _profile_active_fields($mode); - $required_fields = _profile_active_fields("required"); + profile_load_profile(&$user); - foreach ($profile_fields as $name => $field) { - if ($field[0] && in_array($name, $reg_fields)) { - $f = "form_". $field[0]; - $t = "profile_". $name; - $output .= $f((in_array($name, $required_fields) ? profile_required($field[1]) : $field[1]), $t, $edit[$t], $field[3], $field[4], $field[5], $field[6]); + $result = db_query('SELECT * FROM {profile_fields} ORDER BY category, weight'); + while ($field = db_fetch_object($result)) { + if ($value = $user->{$field->name}) { + switch ($field->type) { + case 'textfield': + case 'textarea': + $output .= form_item($field->title, check_output($value)); + break; + case 'selection': + $output .= form_item($field->title, l($value, "profile/$field->name/$value")); + break; + case 'checkbox': + $output .= '<p>'. l($field->title, "profile/$field->name/") .'</p>'; + } } } - if (in_array("birthday", $reg_fields)) { - $output .= form_item((in_array("birthday", $required_fields) ? profile_required($profile_fields["birthday"][1]) : $profile_fields["birthday"][1]), _profile_edit_birth(array2object($edit)), $profile_fields["birthday"][2]); - } + return $output; +} - if (in_array("avatar", $reg_fields)) { - if ($edit["profile_avatar"] && file_exists($edit["profile_avatar"])) { - $output .= form_item(t("Avatar"), '<img src="'. file_create_url($edit["profile_avatar"]) .'" alt="" title="" />'); +function profile_edit_profile($edit, $user) { + + $result = db_query('SELECT * FROM {profile_fields} ORDER BY category, weight'); + + while ($field = db_fetch_object($result)) { + switch ($field->type) { + case 'textfield': + $fields[$field->category] .= form_textfield($field->title, $field->name, $edit[$field->name], 70, 255, $field->explanation); + break; + case 'textarea': + $fields[$field->category] .= form_textarea($field->title, $field->name, $edit[$field->name], 60, 4, $field->explanation); + break; + case 'checkbox': + $fields[$field->category] .= form_checkbox($field->title, $field->name, 1, $edit[$field->name], $field->explanation); + break; + case 'selection': + $options = array('--'); + $lines = split("[\n\r]", $field->options); + foreach ($lines as $line) { + if ($line = trim($line)) { + $options[$line] = $line; + } + } + + $fields[$field->category] .= form_select($field->title, $field->name, $edit[$field->name], $options, $field->explanation); + break; } - $output .= form_file($profile_fields["avatar"][1], "profile_avatar", 64, $profile_fields["avatar"][2]); } - return array(t('Personal information') => $output); + return $fields; } -function _profile_validate($edit, $mode, $user) { +function profile_user($type, $edit, &$user) { + switch ($type) { + case 'load': + return profile_load_profile($user); + case 'update': + return profile_save_profile($edit, $user); + case 'view': + return profile_view_profile($user); + case 'edit': + return profile_edit_profile($edit, $user); + case 'validate': + return $edit; + } +} - global $profile_fields; +function profile_validate_form($edit) { - $enabled_fields = _profile_active_fields($mode); + // Validate the title: - if (in_array("birthday", $enabled_fields) && ($birth_error = _profile_validate_birth($edit))) { - $error .= $birth_error ."<br />"; + if (!$edit['title']) { + return t('You must enter a title.'); } - if (in_array("avatar", $enabled_fields) && ($avatar_error = _profile_validate_avatar($edit, $user))) { - $error .= $avatar_error ."<br />"; - } + // Validate the 'form name': - foreach (array_keys($profile_fields) as $field) { - // replicate any key which was saved during registration but is not in this form - if (!$edit[$field] && $user->$field) { - $edit[$field] = $user->$field; - } + if (eregi('[^a-z0-9_-]', $edit['name'])) { + return t('The specified form name contains one or more illegal characters. Spaces or any other special characters expect dash (-) and underscore (_) are not allowed.'); } - // now check for required fields - foreach (_profile_active_fields("required") as $required) { - if ($required != "0" && in_array($required, $enabled_fields)) { - if (!$edit["profile_". $required]) { - $error .= t("This required field is missing: %a", array("%a" => $profile_fields[$required][1])) ."<br />"; - } - } + if (in_array($edit['name'], user_fields())) { + return t('The specified form name is reserved for use by Drupal.'); } - return $error ? $error : $edit; + // Validate the category: + + if (!$edit['category']) { + return t('You must enter a category.'); + } } -function _profile_user_view(&$user, $mode) { - global $profile_fields; +function profile_admin_add($type) { + $type = _profile_field_types($type); - foreach (_profile_active_fields($mode) as $name) { - $field = $profile_fields[$name]; - $t = "profile_". $name; - if (!empty($user->$t)) { - switch ($field[0]) { - case "textfield": - case "textarea": - case "checkbox": - $value = ($t == "profile_homepage") ? "<a href=\"". drupal_specialchars($user->$t) ."\">". check_output($user->$t) ."</a>" : check_output($user->$t); - $output .= form_item($field[1], $value); - break; - case "select": - $output .= form_item($field[1], check_output($profile_fields[$name][3][$user->$t])); - break; - case "": - // special - if ($t == "profile_avatar") { - if (file_exists($user->$t)) { - $output .= form_item(t("Avatar"), '<img src="'. file_create_url($user->$t) .'" alt="" title="" />'); - } - } + if ($_POST['op']) { + $data = $_POST['edit']; - if ($t == "profile_birthday") { - if (isset($user->profile_birthday) && isset($user->profile_birthmonth) && isset($user->profile_birthyear)) { - // this is very european-centric, can we use format_date? - $time = mktime(0, 0, 0, $user->profile_birthmonth, $user->profile_birthday, $user->profile_birthyear); - $output .= form_item(t("Birthday"), format_date($time, "custom", "F j, Y")); - } - } - } + if ($error = profile_validate_form($data)) { + drupal_set_message($error, 'error'); } - } - return $output; -} + else { + db_query("INSERT INTO {profile_fields} (title, name, explanation, category, type, weight, overview, options) VALUES ('%s', '%s', '%s', '%s', '%s', %d, %d, '%s')", $data['title'], $data['name'], $data['explanation'], $data['category'], $type, $data['weight'], $data['overview'], $data['options']); -function profile_file_download($file) { - if (strpos($file, variable_get("profile_avatar_path", "avatars") . FILE_SEPARATOR . 'avatar-') === 0) { - list($width, $height, $type, $attr) = getimagesize(file_create_path($file)); - $types = array( - IMAGETYPE_GIF => 'image/gif', - IMAGETYPE_JPEG => 'image/jpeg', - IMAGETYPE_PNG => 'image/png', - IMAGETYPE_SWF => 'application/x-shockwave-flash', - IMAGETYPE_PSD => 'image/psd', - IMAGETYPE_BMP => 'image/bmp', - IMAGETYPE_TIFF_II => 'image/tiff', - IMAGETYPE_TIFF_MM => 'image/tiff', - IMAGETYPE_JPC => 'application/octet-stream', - IMAGETYPE_JP2 => 'image/jp2', - IMAGETYPE_JPX => 'application/octet-stream', - IMAGETYPE_JB2 => 'application/octet-stream', - IMAGETYPE_SWC => 'application/x-shockwave-flash', - IMAGETYPE_IFF => 'image/iff', - IMAGETYPE_WBMP => 'image/vnd.wap.wbmp', - IMAGETYPE_XBM => 'image/xbm' - ); - return array('Content-type: '. $types[$type]); + drupal_set_message(t('the field has been created.')); + } } + else { + $data = array('name' => 'profile_'); + } + + print theme('page', _profile_field_form($type, $data), t('Add new %type', array('%type' => $type))); } -function _profile_validate_avatar(&$edit, $user) { - // check that uploaded file is an image, with a maximum file size and maximum height/width +function profile_admin_edit($fid) { - unset($edit["profile_avatar"]); + if ($_POST['op']) { + $data = $_POST['edit']; - if (!$file = file_check_upload('profile_avatar')) { - $edit["profile_avatar"] = $user->profile_avatar; - return; - } + if ($error = profile_validate_form($data)) { + drupal_set_message($error, 'error'); - $extension = strtolower(strrchr($file->name, ".")); - $size = getimagesize($file->path); - list($maxwidth, $maxheight) = explode("x", variable_get("profile_avatar_dimensions", "85x85")); - if ((!in_array($size[2], array(1, 2, 3))) || (!in_array($extension, array(".gif", ".jpg", ".png", ".jpeg")))) { - $error = t("The uploaded file was not an image."); - } - else if ($file->size > (variable_get("profile_avatar_file_size", "30") * 1000)) { - $error = t("The uploaded image is too large; the maximum file size is %a kB.", array("%a" => variable_get("profile_avatar_file_size", "30"))); - } - else if ($size[0] > $maxwidth || $size[1] > $maxheight) { - $error = t("The uploaded image is too large; the maximum dimensions are %a pixels.", array("%a" => variable_get("profile_avatar_dimensions", "85x85"))); - } - else if ($file = file_save_upload('profile_avatar', variable_get("profile_avatar_path", "avatars") . FILE_SEPARATOR .'avatar-'. $user->uid . $extension, 1)) { - $edit["profile_avatar"] = $file->path; + } + else { + db_query("UPDATE {profile_fields} SET title = '%s', name = '%s', explanation = '%s', category = '%s', weight = %d, overview = %d, options = '%s' WHERE fid = %d", $data['title'], $data['name'], $data['explanation'], $data['category'], $data['weight'], $data['overview'], $data['options'], $fid); + + drupal_set_message(t('the field has been updated.')); + } } else { - $error = t("Failed to upload the avatar image; the '%directory' directory doesn't exist.", array("%directory" => variable_get("profile_avatar_path", "avatars"))); + $data = db_fetch_array(db_query('SELECT * FROM {profile_fields} WHERE fid = %d', $fid)); } - return $error ? "$error<br />" : ""; + print theme('page', _profile_field_form($data['type'], $data), t('Edit %type', array('%type' => $edit['type']))); } -function _profile_active_fields($mode) { - return variable_get("profile_". $mode ."_fields", array()); +function profile_admin_delete($fid) { + db_query('DELETE FROM {profile_fields} WHERE fid = %d', $fid); + drupal_set_message(t('the field has been deleted.')); + print theme('page', '', t('Delete field')); } -function _profile_edit_birth($edit = "") { - global $profile_months, $profile_days; - $output = _profile_select("profile_birthday", $edit->profile_birthday, $profile_days); - $output .= " "; - $output .= _profile_select("profile_birthmonth", $edit->profile_birthmonth, $profile_months); - $output .= " "; - $output .= "<input type=\"text\" maxlength=\"4\" name=\"edit[profile_birthyear]\" size=\"5\" value=\"$edit->profile_birthyear\" />"; - return $output; -} +function _profile_field_form($type, $edit = array()) { + + $output = form_textfield(t('Title'), 'title', $edit['title'], 70, 128, t("The title of the new field. The title will be shown to the user. An example title is 'Favorite color'."), NULL, FORM_REQUIRED); + $output .= form_textfield(t('Form name'), 'name', $edit['name'], 70, 128, t("The name of the field. The form name is not shown to the user but used internally in the HTML code and URLs. +Unless you know what you are doing, it is highly recommended that you prefix the form name with <code>profile_</code> to avoid name clashes with other fields. Because the form name's usage, spaces or any other special characters except dash (-) and underscore (_) are not allowed. An example for, name is 'profile_favorite_color' or just 'profile_color'.")); + $output .= form_textarea(t('Explanation'), 'explanation', $edit['explanation'], 70, 3, t("An optional explanation to go with the new field. The explanation will be shown to the user.")); + $output .= form_textfield(t('Category'), 'category', $edit['category'], 70, 128, t("The category the new field should be part of. Categories are used to group fields logically. An example category is 'Personal information'.")); + $output .= form_weight(t('Weight'), 'weight', $edit['weight'], 5, t("The weights define the order in which the form fields are shown. Lighter fields \"float up\" towards the top of the category.")); + $output .= form_checkbox(t('Display this field on member listsings'), 'overview', 1, $edit['overview']); -function _profile_validate_birth(&$edit) { - if (!$edit["profile_birthday"] && !$edit["profile_birthmonth"] && !$edit["profile_birthyear"]) { - // change this if you want required birth - return; + if ($type == 'selection') { + $output .= form_textarea(t('Selection options'), 'options', $edit['options'], 70, 8, t("A list op all options. Put each option on a separate line. Example options are 'red', 'blue', 'green', etc.")); } - if ($edit["profile_birthyear"] > 1900 && checkdate($edit["profile_birthmonth"], $edit["profile_birthday"], $edit["profile_birthyear"])) { - return; + $output .= form_submit(t('Save field')); + + return form($output); +} + +function profile_admin_overview() { + + $result = db_query('SELECT * FROM {profile_fields} ORDER BY category, weight'); + while ($field = db_fetch_object($result)) { + $rows[] = array($field->title, $field->name, $field->type, $field->category, l(t('edit'), "admin/system/modules/profile/edit/$field->fid"), l(t('delete'), "admin/system/modules/profile/delete/$field->fid")); } - else { - return t("The specified birthday is not valid.") ."<br />"; + + $header = array(t('title'), t('name'), t('type'), t('category'), array('data' => t('operations'), 'colspan' => '2')); + + $output = theme('table', $header, $rows); + $output .= '<h2>'. t('Create new field') .'</h2>'; + $output .= '<ul>'; + foreach (_profile_field_types() as $key => $value) { + $output .= "<li>". l(t('Add new %type', array('%type' => $value)), "admin/system/modules/profile/add/$key") ."</li>"; } + $output .= '</ul>'; + + print theme('page', $output); } -function _profile_select($name, $value, $options, $extra = 0, $multiple = 0) { - if (count($options) > 0) { - foreach ($options as $key=>$choice) { - $select .= "<option value=\"$key\"". (is_array($value) ? (in_array($key, $value) ? " selected=\"selected\"" : "") : ($key == $value ? " selected=\"selected\"" : "")) .">". check_form($choice) ."</option>"; +function theme_profile_profile($user, $fields = array()) { + + $output = "<div class=\"profile\">\n"; + $output .= theme('user_picture', $user); + $output .= " <div class=\"name\">". format_name($user) ."</div>\n"; + + foreach ($fields as $field) { + if ($user->{$field->name}) { + if ($field->type == 'checkbox') { + $output .= "<div class=\"field\">". $field->title ."</div>"; + } + else { + $output .= "<div class=\"field\">". $user->{$field->name} ."</div>"; + } } - return "<select name=\"edit[$name]". ($multiple ? "[]" : "") ."\"". ($multiple ? " multiple " : "") . ($extra ? " $extra" : "") .">$select</select>"; } + + $output .= "</div>\n"; + + return $output; +} + +function _profile_field_types($type = NULL) { + $types = array('textfield', 'textarea', 'checkbox', 'selection'); + return isset($type) ? $types[$type] : $types; } ?> diff --git a/modules/profile/profile.module b/modules/profile/profile.module index a16c47c20..d1e5e5f0c 100644 --- a/modules/profile/profile.module +++ b/modules/profile/profile.module @@ -1,306 +1,300 @@ <?php -// $Id$ - -function _profile_init() { - /* - ** Add here any field you might need. Leave array[0] blank if you - ** need a special tool (like birthday or avatar). - ** TODO: add a clear description/explanation. - */ - - $GLOBALS["profile_fields"] = array( - "realname" => array("textfield", t("Name"), "", 64, 64, ""), - "address" => array("textfield", t("Address"), "", 64, 64, ""), - "city" => array("textfield", t("City"), "", 64, 64, ""), - "state" => array("textfield", t("State, province or region"), "", 64, 64, ""), - "zip" => array("textfield", t("Zip or postal code"), "", 7, 10, ""), - "country" => array("textfield", t("Country"), "", 64, 64, ""), - "birthday" => array("", t("Birthday"), ""), - "gender" => array("select", t("Gender"), "", array(0 => "-", "m" => t("male"), "f" => t("female")), "", 0, 0), - "job" => array("textfield", t("Job title"), "", 64, 64, ""), - "icq" => array("textfield", t("ICQ messenger ID"), "", 12, 12, ""), - "msn" => array("textfield", t("MSN messenger ID"), "", 64, 64, ""), - "yahoo" => array("textfield", t("Yahoo messenger ID"), "", 64, 64, ""), - "aim" => array("textfield", t("AIM messenger ID"), "", 64, 64, ""), - "homepage" => array("textfield", t("URL of homepage"), "", 64, 64, t("Make sure you enter a fully qualified URL: remember to include \"http://\".")), - "biography" => array("textarea", t("Biography"), "", 64, 4, ""), - "interests" => array("textarea", t("Interests"), "", 64, 4, ""), - "publickey" => array("textarea", t("Public key"), "", 64, 4, ""), - "avatar" => array("", t("Avatar or picture"), t("Your virtual face or picture. Maximum dimensions are %dimensions and the maximum size is %size kB.", array("%dimensions" => variable_get("profile_avatar_dimensions", "85x85"), "%size" => variable_get("profile_avatar_file_size", "30")))) - ); - - $GLOBALS["profile_days"] = array_merge(array(0 => t("day")), drupal_map_assoc(range(1, 31))); - $GLOBALS["profile_months"] = array(0 => t("month"), 1 => t("January"), 2 => t("February"), 3 => t("March"), 4 => t("April"), 5 => t("May"), 6 => t("June"), 7 => t("July"), 8 => t("August"), 9 => t("September"), 10 => t("October"), 11 => t("November"), 12 => t("December")); -} -function profile_help($section) { - $output = ""; +// TODO: add a 'date' field so we can migrate the birthday information. +function profile_help($section) { switch ($section) { case 'admin/system/modules#description': $output = t("Support for configurable user profiles."); break; - case 'admin/system/modules/profile': - $output = t("When a user creates an account you can ask for some extra information, as well as letting the user have a small picture, called an avatar. <ul><li>In order for a user to enter information, you <strong>must</strong> check <em>enable</em>.</li><li>In order for other people to see the entered information, you must make it <em>public</em>.</li><li>If an item is <em>public</em>, but not enabled, the user can never give it a value and it will never be seen. <em>Public</em> does <strong>not</strong> imply <em>enable</em>.</li></ul>", array("%edit" => url("user/edit"))); - break; } return $output; + + } -function profile_settings() { - global $profile_fields; +function profile_link($type) { + if ($type == 'system') { + menu('profile', t('browse'), 'profile_browse', 0, MENU_HIDE); - if (!$profile_fields) { - _profile_init(); + if (user_access('administer users')) { + menu('admin/system/modules/profile', t('profile'), 'profile_admin_overview'); + menu('admin/system/modules/profile/add', NULL, 'profile_admin_add', 0, MENU_HIDE); + menu('admin/system/modules/profile/edit', NULL, 'profile_admin_edit', 0, MENU_HIDE); + menu('admin/system/modules/profile/delete', NULL, 'profile_admin_delete', 0, MENU_HIDE); + } } +} - if (!file_check_directory(file_create_path(variable_get('profile_avatar_path', 'avatars')))) { - $error['profile_avatar_path'] = theme('error', t('Directory does not exist, or is not writable.')); - } +function profile_browse() { - $profile_public_fields = variable_get("profile_public_fields", array()); - $profile_private_fields = variable_get("profile_private_fields", array()); - $profile_required_fields = variable_get("profile_required_fields", array()); - - $header = array(t("field"), t("enable"), t("public"), t("required")); - $i = 0; - foreach ($profile_fields as $key => $field) { - $row[$i][] = $field[1]; - $row[$i][] = form_checkbox("", "profile_private_fields][", $key, in_array($key, $profile_private_fields)); - $row[$i][] = form_checkbox("", "profile_public_fields][", $key, in_array($key, $profile_public_fields)); - $row[$i][] = form_checkbox("", "profile_required_fields][", $key, in_array($key, $profile_required_fields)); - $i++; - } + $value = arg(2) ? arg(2) : 1; - $avatar = form_textfield(t("Avatar image path"), "profile_avatar_path", variable_get("profile_avatar_path", "avatars"), 30, 255, t("Subdirectory in the directory '%dir' where avatars will be stored.", array('%dir' => variable_get('file_directory_path', 'files') . FILE_SEPARATOR)) . $error['profile_avatar_path']); - $avatar .= form_textfield(t("Avatar maximum dimensions"), "profile_avatar_dimensions", variable_get("profile_avatar_dimensions", "85x85"), 10, 10, t("Maximum dimensions for avatars.")); - $avatar .= form_textfield(t("Avatar maximum file size"), "profile_avatar_file_size", variable_get("profile_avatar_file_size", "30"), 10, 10, t("Maximum file size for avatars, in kB.")); + // Determine the field to group users by: + $field = db_fetch_object(db_query("SELECT DISTINCT(f.fid), f.type, f.title FROM {profile_fields} f INNER JOIN {profile_values} v ON f.fid = v.fid WHERE f.name = '%s' AND v.value = '%s' ORDER BY f.category, f.weight", arg(1), $value)); - $output = theme("table", $header, $row); - $output .= form_group(t('Avatars'), $avatar); + if ($field->fid) { + // Compile a list of fields to show: + $fields = array(); + $result = db_query("SELECT name, title, type FROM {profile_fields} WHERE fid != %d AND overview = 1", $field->fid); + while ($record = db_fetch_object($result)) { + $fields[] = $record; + } - return $output; -} + // Extract the affected users: + $result = pager_query("SELECT u.uid FROM {users} u INNER JOIN {profile_values} v ON u.uid = v.uid WHERE v.fid = $field->fid AND v.value = '". check_query($value) ."' ORDER BY u.changed DESC", 20); -function profile_user($type, $edit, &$user) { - global $profile_fields; - if (!$profile_fields) { - _profile_init(); + $output = ''; + while ($account = db_fetch_object($result)) { + $output .= theme('profile_profile', user_load(array('uid' => $account->uid)), $fields); + } + $output .= theme('pager', NULL, 20); + + if ($field->type == "selection") { + $title = arg(2); + } + else { + $title = $field->title; + } + + print theme('page', $output, $title); + } + else { + drupal_not_found(); } +} - switch ($type) { - case "edit_form": - // when user tries to edit his own data - return _profile_form(object2array($user), "private"); - case "edit_validate": - // validate user data editing - return _profile_validate($edit, "private", $user); - case "view_public": - // when others look at user data - return _profile_user_view($user, "public"); - case "view_private": - // when user looks at his own data - return _profile_user_view($user, "private"); +function profile_load_profile(&$user) { + $result = db_query('SELECT f.name, v.value FROM {profile_fields} f INNER JOIN {profile_values} v ON f.fid = v.fid WHERE uid = %d', $user->uid); + while ($field = db_fetch_object($result)) { + if (empty($user->{$field->name})) { + $user->{$field->name} = $field->value; + } } } -function profile_required($title) { - // this pleads "theme, theme" ;) - return $title ." ". theme("mark"); +function profile_save_profile($edit, $user) { + db_query('DELETE FROM {profile_values} WHERE uid = %d', $user->uid); + $result = db_query('SELECT fid, name FROM profile_fields'); + while ($field = db_fetch_object($result)) { + if ($edit[$field->name]) { + db_query("INSERT INTO {profile_values} (fid, uid, value) VALUES (%d, %d, '%s')", $field->fid, $user->uid, $edit[$field->name]); + unset($edit[$field->name]); + } + } } -function _profile_form($edit, $mode) { - global $profile_fields, $user; +function profile_view_profile($user) { - $reg_fields = _profile_active_fields($mode); - $required_fields = _profile_active_fields("required"); + profile_load_profile(&$user); - foreach ($profile_fields as $name => $field) { - if ($field[0] && in_array($name, $reg_fields)) { - $f = "form_". $field[0]; - $t = "profile_". $name; - $output .= $f((in_array($name, $required_fields) ? profile_required($field[1]) : $field[1]), $t, $edit[$t], $field[3], $field[4], $field[5], $field[6]); + $result = db_query('SELECT * FROM {profile_fields} ORDER BY category, weight'); + while ($field = db_fetch_object($result)) { + if ($value = $user->{$field->name}) { + switch ($field->type) { + case 'textfield': + case 'textarea': + $output .= form_item($field->title, check_output($value)); + break; + case 'selection': + $output .= form_item($field->title, l($value, "profile/$field->name/$value")); + break; + case 'checkbox': + $output .= '<p>'. l($field->title, "profile/$field->name/") .'</p>'; + } } } - if (in_array("birthday", $reg_fields)) { - $output .= form_item((in_array("birthday", $required_fields) ? profile_required($profile_fields["birthday"][1]) : $profile_fields["birthday"][1]), _profile_edit_birth(array2object($edit)), $profile_fields["birthday"][2]); - } + return $output; +} - if (in_array("avatar", $reg_fields)) { - if ($edit["profile_avatar"] && file_exists($edit["profile_avatar"])) { - $output .= form_item(t("Avatar"), '<img src="'. file_create_url($edit["profile_avatar"]) .'" alt="" title="" />'); +function profile_edit_profile($edit, $user) { + + $result = db_query('SELECT * FROM {profile_fields} ORDER BY category, weight'); + + while ($field = db_fetch_object($result)) { + switch ($field->type) { + case 'textfield': + $fields[$field->category] .= form_textfield($field->title, $field->name, $edit[$field->name], 70, 255, $field->explanation); + break; + case 'textarea': + $fields[$field->category] .= form_textarea($field->title, $field->name, $edit[$field->name], 60, 4, $field->explanation); + break; + case 'checkbox': + $fields[$field->category] .= form_checkbox($field->title, $field->name, 1, $edit[$field->name], $field->explanation); + break; + case 'selection': + $options = array('--'); + $lines = split("[\n\r]", $field->options); + foreach ($lines as $line) { + if ($line = trim($line)) { + $options[$line] = $line; + } + } + + $fields[$field->category] .= form_select($field->title, $field->name, $edit[$field->name], $options, $field->explanation); + break; } - $output .= form_file($profile_fields["avatar"][1], "profile_avatar", 64, $profile_fields["avatar"][2]); } - return array(t('Personal information') => $output); + return $fields; } -function _profile_validate($edit, $mode, $user) { +function profile_user($type, $edit, &$user) { + switch ($type) { + case 'load': + return profile_load_profile($user); + case 'update': + return profile_save_profile($edit, $user); + case 'view': + return profile_view_profile($user); + case 'edit': + return profile_edit_profile($edit, $user); + case 'validate': + return $edit; + } +} - global $profile_fields; +function profile_validate_form($edit) { - $enabled_fields = _profile_active_fields($mode); + // Validate the title: - if (in_array("birthday", $enabled_fields) && ($birth_error = _profile_validate_birth($edit))) { - $error .= $birth_error ."<br />"; + if (!$edit['title']) { + return t('You must enter a title.'); } - if (in_array("avatar", $enabled_fields) && ($avatar_error = _profile_validate_avatar($edit, $user))) { - $error .= $avatar_error ."<br />"; - } + // Validate the 'form name': - foreach (array_keys($profile_fields) as $field) { - // replicate any key which was saved during registration but is not in this form - if (!$edit[$field] && $user->$field) { - $edit[$field] = $user->$field; - } + if (eregi('[^a-z0-9_-]', $edit['name'])) { + return t('The specified form name contains one or more illegal characters. Spaces or any other special characters expect dash (-) and underscore (_) are not allowed.'); } - // now check for required fields - foreach (_profile_active_fields("required") as $required) { - if ($required != "0" && in_array($required, $enabled_fields)) { - if (!$edit["profile_". $required]) { - $error .= t("This required field is missing: %a", array("%a" => $profile_fields[$required][1])) ."<br />"; - } - } + if (in_array($edit['name'], user_fields())) { + return t('The specified form name is reserved for use by Drupal.'); } - return $error ? $error : $edit; + // Validate the category: + + if (!$edit['category']) { + return t('You must enter a category.'); + } } -function _profile_user_view(&$user, $mode) { - global $profile_fields; +function profile_admin_add($type) { + $type = _profile_field_types($type); - foreach (_profile_active_fields($mode) as $name) { - $field = $profile_fields[$name]; - $t = "profile_". $name; - if (!empty($user->$t)) { - switch ($field[0]) { - case "textfield": - case "textarea": - case "checkbox": - $value = ($t == "profile_homepage") ? "<a href=\"". drupal_specialchars($user->$t) ."\">". check_output($user->$t) ."</a>" : check_output($user->$t); - $output .= form_item($field[1], $value); - break; - case "select": - $output .= form_item($field[1], check_output($profile_fields[$name][3][$user->$t])); - break; - case "": - // special - if ($t == "profile_avatar") { - if (file_exists($user->$t)) { - $output .= form_item(t("Avatar"), '<img src="'. file_create_url($user->$t) .'" alt="" title="" />'); - } - } + if ($_POST['op']) { + $data = $_POST['edit']; - if ($t == "profile_birthday") { - if (isset($user->profile_birthday) && isset($user->profile_birthmonth) && isset($user->profile_birthyear)) { - // this is very european-centric, can we use format_date? - $time = mktime(0, 0, 0, $user->profile_birthmonth, $user->profile_birthday, $user->profile_birthyear); - $output .= form_item(t("Birthday"), format_date($time, "custom", "F j, Y")); - } - } - } + if ($error = profile_validate_form($data)) { + drupal_set_message($error, 'error'); } - } - return $output; -} + else { + db_query("INSERT INTO {profile_fields} (title, name, explanation, category, type, weight, overview, options) VALUES ('%s', '%s', '%s', '%s', '%s', %d, %d, '%s')", $data['title'], $data['name'], $data['explanation'], $data['category'], $type, $data['weight'], $data['overview'], $data['options']); -function profile_file_download($file) { - if (strpos($file, variable_get("profile_avatar_path", "avatars") . FILE_SEPARATOR . 'avatar-') === 0) { - list($width, $height, $type, $attr) = getimagesize(file_create_path($file)); - $types = array( - IMAGETYPE_GIF => 'image/gif', - IMAGETYPE_JPEG => 'image/jpeg', - IMAGETYPE_PNG => 'image/png', - IMAGETYPE_SWF => 'application/x-shockwave-flash', - IMAGETYPE_PSD => 'image/psd', - IMAGETYPE_BMP => 'image/bmp', - IMAGETYPE_TIFF_II => 'image/tiff', - IMAGETYPE_TIFF_MM => 'image/tiff', - IMAGETYPE_JPC => 'application/octet-stream', - IMAGETYPE_JP2 => 'image/jp2', - IMAGETYPE_JPX => 'application/octet-stream', - IMAGETYPE_JB2 => 'application/octet-stream', - IMAGETYPE_SWC => 'application/x-shockwave-flash', - IMAGETYPE_IFF => 'image/iff', - IMAGETYPE_WBMP => 'image/vnd.wap.wbmp', - IMAGETYPE_XBM => 'image/xbm' - ); - return array('Content-type: '. $types[$type]); + drupal_set_message(t('the field has been created.')); + } } + else { + $data = array('name' => 'profile_'); + } + + print theme('page', _profile_field_form($type, $data), t('Add new %type', array('%type' => $type))); } -function _profile_validate_avatar(&$edit, $user) { - // check that uploaded file is an image, with a maximum file size and maximum height/width +function profile_admin_edit($fid) { - unset($edit["profile_avatar"]); + if ($_POST['op']) { + $data = $_POST['edit']; - if (!$file = file_check_upload('profile_avatar')) { - $edit["profile_avatar"] = $user->profile_avatar; - return; - } + if ($error = profile_validate_form($data)) { + drupal_set_message($error, 'error'); - $extension = strtolower(strrchr($file->name, ".")); - $size = getimagesize($file->path); - list($maxwidth, $maxheight) = explode("x", variable_get("profile_avatar_dimensions", "85x85")); - if ((!in_array($size[2], array(1, 2, 3))) || (!in_array($extension, array(".gif", ".jpg", ".png", ".jpeg")))) { - $error = t("The uploaded file was not an image."); - } - else if ($file->size > (variable_get("profile_avatar_file_size", "30") * 1000)) { - $error = t("The uploaded image is too large; the maximum file size is %a kB.", array("%a" => variable_get("profile_avatar_file_size", "30"))); - } - else if ($size[0] > $maxwidth || $size[1] > $maxheight) { - $error = t("The uploaded image is too large; the maximum dimensions are %a pixels.", array("%a" => variable_get("profile_avatar_dimensions", "85x85"))); - } - else if ($file = file_save_upload('profile_avatar', variable_get("profile_avatar_path", "avatars") . FILE_SEPARATOR .'avatar-'. $user->uid . $extension, 1)) { - $edit["profile_avatar"] = $file->path; + } + else { + db_query("UPDATE {profile_fields} SET title = '%s', name = '%s', explanation = '%s', category = '%s', weight = %d, overview = %d, options = '%s' WHERE fid = %d", $data['title'], $data['name'], $data['explanation'], $data['category'], $data['weight'], $data['overview'], $data['options'], $fid); + + drupal_set_message(t('the field has been updated.')); + } } else { - $error = t("Failed to upload the avatar image; the '%directory' directory doesn't exist.", array("%directory" => variable_get("profile_avatar_path", "avatars"))); + $data = db_fetch_array(db_query('SELECT * FROM {profile_fields} WHERE fid = %d', $fid)); } - return $error ? "$error<br />" : ""; + print theme('page', _profile_field_form($data['type'], $data), t('Edit %type', array('%type' => $edit['type']))); } -function _profile_active_fields($mode) { - return variable_get("profile_". $mode ."_fields", array()); +function profile_admin_delete($fid) { + db_query('DELETE FROM {profile_fields} WHERE fid = %d', $fid); + drupal_set_message(t('the field has been deleted.')); + print theme('page', '', t('Delete field')); } -function _profile_edit_birth($edit = "") { - global $profile_months, $profile_days; - $output = _profile_select("profile_birthday", $edit->profile_birthday, $profile_days); - $output .= " "; - $output .= _profile_select("profile_birthmonth", $edit->profile_birthmonth, $profile_months); - $output .= " "; - $output .= "<input type=\"text\" maxlength=\"4\" name=\"edit[profile_birthyear]\" size=\"5\" value=\"$edit->profile_birthyear\" />"; - return $output; -} +function _profile_field_form($type, $edit = array()) { + + $output = form_textfield(t('Title'), 'title', $edit['title'], 70, 128, t("The title of the new field. The title will be shown to the user. An example title is 'Favorite color'."), NULL, FORM_REQUIRED); + $output .= form_textfield(t('Form name'), 'name', $edit['name'], 70, 128, t("The name of the field. The form name is not shown to the user but used internally in the HTML code and URLs. +Unless you know what you are doing, it is highly recommended that you prefix the form name with <code>profile_</code> to avoid name clashes with other fields. Because the form name's usage, spaces or any other special characters except dash (-) and underscore (_) are not allowed. An example for, name is 'profile_favorite_color' or just 'profile_color'.")); + $output .= form_textarea(t('Explanation'), 'explanation', $edit['explanation'], 70, 3, t("An optional explanation to go with the new field. The explanation will be shown to the user.")); + $output .= form_textfield(t('Category'), 'category', $edit['category'], 70, 128, t("The category the new field should be part of. Categories are used to group fields logically. An example category is 'Personal information'.")); + $output .= form_weight(t('Weight'), 'weight', $edit['weight'], 5, t("The weights define the order in which the form fields are shown. Lighter fields \"float up\" towards the top of the category.")); + $output .= form_checkbox(t('Display this field on member listsings'), 'overview', 1, $edit['overview']); -function _profile_validate_birth(&$edit) { - if (!$edit["profile_birthday"] && !$edit["profile_birthmonth"] && !$edit["profile_birthyear"]) { - // change this if you want required birth - return; + if ($type == 'selection') { + $output .= form_textarea(t('Selection options'), 'options', $edit['options'], 70, 8, t("A list op all options. Put each option on a separate line. Example options are 'red', 'blue', 'green', etc.")); } - if ($edit["profile_birthyear"] > 1900 && checkdate($edit["profile_birthmonth"], $edit["profile_birthday"], $edit["profile_birthyear"])) { - return; + $output .= form_submit(t('Save field')); + + return form($output); +} + +function profile_admin_overview() { + + $result = db_query('SELECT * FROM {profile_fields} ORDER BY category, weight'); + while ($field = db_fetch_object($result)) { + $rows[] = array($field->title, $field->name, $field->type, $field->category, l(t('edit'), "admin/system/modules/profile/edit/$field->fid"), l(t('delete'), "admin/system/modules/profile/delete/$field->fid")); } - else { - return t("The specified birthday is not valid.") ."<br />"; + + $header = array(t('title'), t('name'), t('type'), t('category'), array('data' => t('operations'), 'colspan' => '2')); + + $output = theme('table', $header, $rows); + $output .= '<h2>'. t('Create new field') .'</h2>'; + $output .= '<ul>'; + foreach (_profile_field_types() as $key => $value) { + $output .= "<li>". l(t('Add new %type', array('%type' => $value)), "admin/system/modules/profile/add/$key") ."</li>"; } + $output .= '</ul>'; + + print theme('page', $output); } -function _profile_select($name, $value, $options, $extra = 0, $multiple = 0) { - if (count($options) > 0) { - foreach ($options as $key=>$choice) { - $select .= "<option value=\"$key\"". (is_array($value) ? (in_array($key, $value) ? " selected=\"selected\"" : "") : ($key == $value ? " selected=\"selected\"" : "")) .">". check_form($choice) ."</option>"; +function theme_profile_profile($user, $fields = array()) { + + $output = "<div class=\"profile\">\n"; + $output .= theme('user_picture', $user); + $output .= " <div class=\"name\">". format_name($user) ."</div>\n"; + + foreach ($fields as $field) { + if ($user->{$field->name}) { + if ($field->type == 'checkbox') { + $output .= "<div class=\"field\">". $field->title ."</div>"; + } + else { + $output .= "<div class=\"field\">". $user->{$field->name} ."</div>"; + } } - return "<select name=\"edit[$name]". ($multiple ? "[]" : "") ."\"". ($multiple ? " multiple " : "") . ($extra ? " $extra" : "") .">$select</select>"; } + + $output .= "</div>\n"; + + return $output; +} + +function _profile_field_types($type = NULL) { + $types = array('textfield', 'textarea', 'checkbox', 'selection'); + return isset($type) ? $types[$type] : $types; } ?> diff --git a/modules/system.module b/modules/system.module index 046920158..7169250d5 100644 --- a/modules/system.module +++ b/modules/system.module @@ -77,7 +77,7 @@ function system_link($type) { } function system_user($type, $edit, &$user) { - if ($type == "edit_form") { + if ($type == 'edit') { $options = "<option value=\"\">". t("Default theme") ."</option>\n"; if (count($themes = list_themes()) > 1) { foreach ($themes as $key => $value) { diff --git a/modules/system/system.module b/modules/system/system.module index 046920158..7169250d5 100644 --- a/modules/system/system.module +++ b/modules/system/system.module @@ -77,7 +77,7 @@ function system_link($type) { } function system_user($type, $edit, &$user) { - if ($type == "edit_form") { + if ($type == 'edit') { $options = "<option value=\"\">". t("Default theme") ."</option>\n"; if (count($themes = list_themes()) > 1) { foreach ($themes as $key => $value) { diff --git a/modules/tracker.module b/modules/tracker.module index 6bbc80f43..8e5d8a11c 100644 --- a/modules/tracker.module +++ b/modules/tracker.module @@ -79,12 +79,8 @@ function tracker_posts($id = 0) { } function tracker_user($type, &$edit, &$user) { - switch ($type) { - case "view_private": - case "view_public": - if (user_access("access content")) { - return form_item(t("Recent posts"), l(t("recent posts"), "tracker/$user->uid")); - } + if ($type == 'view' && user_access("access content")) { + return form_item(t("Recent posts"), l(t("recent posts"), "tracker/$user->uid")); } } diff --git a/modules/tracker/tracker.module b/modules/tracker/tracker.module index 6bbc80f43..8e5d8a11c 100644 --- a/modules/tracker/tracker.module +++ b/modules/tracker/tracker.module @@ -79,12 +79,8 @@ function tracker_posts($id = 0) { } function tracker_user($type, &$edit, &$user) { - switch ($type) { - case "view_private": - case "view_public": - if (user_access("access content")) { - return form_item(t("Recent posts"), l(t("recent posts"), "tracker/$user->uid")); - } + if ($type == 'view' && user_access("access content")) { + return form_item(t("Recent posts"), l(t("recent posts"), "tracker/$user->uid")); } } diff --git a/modules/user.module b/modules/user.module index 68d25921a..1dbc33daf 100644 --- a/modules/user.module +++ b/modules/user.module @@ -78,12 +78,13 @@ function user_save($account, $array = array()) { $query .= "data = '%s', "; $v[] = serialize($data); - db_query("UPDATE {users} SET $query timestamp = %d WHERE uid = %d", array_merge($v, array(time(), $account->uid))); + db_query("UPDATE {users} SET $query changed = %d WHERE uid = %d", array_merge($v, array(time(), $account->uid))); $user = user_load(array('uid' => $account->uid)); } else { - $array['timestamp'] = time(); + $array['created'] = time(); + $array['changed'] = time(); $array['uid'] = db_next_id("{users}_uid"); foreach ($array as $key => $value) { @@ -112,11 +113,11 @@ function user_save($account, $array = array()) { $user = user_load(array('name' => $array['name'])); - module_invoke_all('user', "insert", $array, $user); + module_invoke_all('user', 'insert', $array, $user); } foreach ($array as $key => $value) { - if (substr($key, 0, 4) == "auth") { + if (substr($key, 0, 4) == 'auth') { $authmaps[$key] = $value; } } @@ -136,7 +137,7 @@ function user_validate_name($name) { if (!$name) return t("You must enter a username."); if (substr($name, 0, 1) == ' ') return t("The username cannot begin with a space."); if (substr($name, -1) == ' ') return t("The username cannot end with a space."); - if (ereg(" ", $name)) return t("The username cannot contain multiple spaces in a row."); + if (ereg(' ', $name)) return t("The username cannot contain multiple spaces in a row."); if (ereg('[^ [:alnum:]@_.-]', $name)) return t("The username contains an illegal character."); if (ereg('@', $name) && !eregi('@([0-9a-z](-?[0-9a-z])*.)+[a-z]{2}([zmuvtg]|fo|me)?$', $name)) return t("The username is not a valid authentication ID."); if (strlen($name) > 56) return t("The username '%name' is too long: it must be less than 56 characters.", array("%name" => $name)); @@ -149,6 +150,35 @@ function user_validate_mail($mail) { } } +function user_validate_picture($file, &$edit, $user) { + + // initialize the picture: + $edit['picture'] = $user->picture; + + // check that uploaded file is an image, with a maximum file size and maximum height/width + $extension = strtolower(strrchr($file->name, ".")); + $size = getimagesize($file->path); + list($maxwidth, $maxheight) = explode("x", variable_get('user_picture_dimensions', "85x85")); + + if ((!in_array($size[2], array(1, 2, 3))) || (!in_array($extension, array(".gif", ".jpg", ".png", ".jpeg")))) { + $error = t("The uploaded file was not an image."); + } + else if ($file->size > (variable_get('user_picture_file_size', "30") * 1000)) { + $error = t("The uploaded image is too large; the maximum file size is %a kB.", array("%a" => variable_get('user_picture_file_size', "30"))); + } + else if ($size[0] > $maxwidth || $size[1] > $maxheight) { + $error = t("The uploaded image is too large; the maximum dimensions are %a pixels.", array("%a" => variable_get('user_picture_dimensions', "85x85"))); + } + else if ($file = file_save_upload('picture', variable_get('user_picture_path', "pictures") . FILE_SEPARATOR .'picture-'. $user->uid . $extension, 1)) { + $edit['picture'] = $file->path; + } + else { + $error = t("Failed to upload the picture image; the '%directory' directory doesn't exist.", array("%directory" => variable_get('user_picture_path', "pictures"))); + } + + return $error; +} + function user_validate_authmap($account, $authname, $module) { $result = db_query("SELECT COUNT(*) from {authmap} WHERE uid != %d AND authname = '%s'", $account->uid, $authname); if (db_result($result) > 0) { @@ -281,7 +311,7 @@ function user_fields() { } else { // Make sure we return the default fields at least - $fields = array('uid', 'name', 'pass', "mail", "mode", "sort", "threshold", "theme", "signature", "timestamp", "status", "timezone", "language", "init", "data", "rid"); + $fields = array('uid', 'name', 'pass', "mail", "picture", "mode", "sort", "threshold", "theme", "signature", "created", "changed", "status", "timezone", "language", "init", "data", "rid"); } } @@ -428,6 +458,42 @@ function user_block($op = "list", $delta = 0) { } } +function theme_user_picture($account) { + if (variable_get('user_pictures', 0)) { + if ($account->picture && file_exists($account->picture)) { + $picture = file_create_url($account->picture); + } + else if (variable_get('user_picture_default', '')) { + $picture = variable_get('user_picture_default', ''); + } + + if ($picture) { + $picture = "<img src=\"$picture\" alt=\"" . t("%user's picture", array("%user" => $account->name ? $account->name : t(variable_get("anonymous", "Anonymous")))) . "\" />"; + if ($account->uid) { + $picture = l($picture, "user/view/$account->uid", array("title" => t("View user profile."))); + } + + return "<div class=\"picture\">$picture</div>"; + } + } +} + +function theme_user_profile($account) { + $output = "<div class=\"profile\">\n"; + $output .= theme('user_picture', $account); + $output .= form_item(t('Name'), $account->name); + $output .= implode("\n", module_invoke_all('user', 'view', '', $account)); + $output .= form_item(t('Member for'), format_interval(time() - $account->created)); + + if (user_access("administer users")) { + $output .= form_item(t("Administration"), l(t("edit account"), "admin/user/edit/$account->uid")); + } + + $output .= "</div>\n"; + + return $output; +} + function theme_user_list($items, $title = NULL) { return theme("item_list", $items, $title); } @@ -595,7 +661,7 @@ function user_login($edit = array(), $msg = "") { watchdog('user', "session opened for '$user->name'"); // update the user table timestamp noting user has logged in - db_query("UPDATE {users} SET timestamp = '%d' WHERE uid = '%s'", time(), $user->uid); + db_query("UPDATE {users} SET changed = '%d' WHERE uid = '%s'", time(), $user->uid); user_module_invoke("login", $edit, $user); @@ -685,7 +751,7 @@ function user_logout() { */ session_destroy(); - module_invoke_all('user', "logout", NULL, $user); + module_invoke_all('user', 'logout', NULL, $user); unset($user); } @@ -884,7 +950,15 @@ function user_edit($edit = array()) { else if ($edit['mail'] && db_num_rows(db_query("SELECT uid FROM {users} WHERE uid != '$user->uid' AND LOWER(mail) = LOWER('%s')", $edit['mail'])) > 0) { $error = t("The e-mail address '%s' is already taken.", array("%s" => $edit['mail'])); } - else if ($user->uid) { + else { + /* + ** If required, validate the picture. + */ + + if ($file = file_check_upload('picture')) { + $error = user_validate_picture($file, $edit, $user); + } + /* ** If required, check that proposed passwords match. If so, ** add new password to $edit. @@ -922,7 +996,7 @@ function user_edit($edit = array()) { foreach (module_list() as $module) { if (module_hook($module, 'user')) { - $result = module_invoke($module, 'user', "edit_validate", $edit, $user); + $result = module_invoke($module, 'user', 'validate', $edit, $user); } if (is_array($result)) { $data = array_merge($data, $result); @@ -940,7 +1014,7 @@ function user_edit($edit = array()) { $user = user_save($user, array_merge($edit, $data)); - drupal_set_message(t("your user information changes have been saved.")); + drupal_set_message(t('your user information changes have been saved.')); } } } @@ -953,15 +1027,25 @@ function user_edit($edit = array()) { $edit = object2array($user); } - $output = form_textfield(t("Username"), 'name', $edit['name'], 30, 55, t("Your full name or your preferred username: only letters, numbers and spaces are allowed.")); - $output .= form_textfield(t("E-mail address"), "mail", $edit['mail'], 30, 55, t("Insert a valid e-mail address. All e-mails from the system will be sent to this address. The e-mail address is not made public and will only be used if you wish to receive a new password or wish to receive certain news or notifications by e-mail.")); - $output .= form_item(t("Password"), "<input type=\"password\" name=\"edit[pass1]\" size=\"12\" maxlength=\"24\" /> <input type=\"password\" name=\"edit[pass2]\" size=\"12\" maxlength=\"24\" />", t("Enter your new password twice if you want to change your current password or leave it blank if you are happy with your current password.")); - $output = form_group(t('Account information'), $output); + $group = form_textfield(t("Username"), 'name', $edit['name'], 30, 55, t("Your full name or your preferred username: only letters, numbers and spaces are allowed.")); + $group .= form_textfield(t("E-mail address"), "mail", $edit['mail'], 30, 55, t("Insert a valid e-mail address. All e-mails from the system will be sent to this address. The e-mail address is not made public and will only be used if you wish to receive a new password or wish to receive certain news or notifications by e-mail.")); + $group .= form_item(t("Password"), "<input type=\"password\" name=\"edit[pass1]\" size=\"12\" maxlength=\"24\" /> <input type=\"password\" name=\"edit[pass2]\" size=\"12\" maxlength=\"24\" />", t("Enter your new password twice if you want to change your current password or leave it blank if you are happy with your current password.")); + $output = form_group(t('Account information'), $group); + + if (variable_get('user_pictures', 0)) { + $group = ''; + if (file_exists($user->picture)) { + $group .= '<img src="'. file_create_url($edit['picture']) .'" alt="" title="" />'; + } + $group .= form_file(t('Upload picture or picture'), 'picture', 48, t("Your virtual face or picture. Maximum dimensions are %dimensions and the maximum size is %size kB.", array("%dimensions" => variable_get('user_picture_dimensions', "85x85"), "%size" => variable_get('user_picture_file_size', "30"))) ." ". variable_get('user_picture_guidelines', '')); + $output .= form_group(t('Picture'), $group); + } + $output .= _user_profile($edit, $user); $output .= form_submit(t("Save user information")); $output = form($output, "post", 0, array("enctype" => "multipart/form-data")); - // the "enctype" attribute is required to upload files such as avatars + // the "enctype" attribute is required to upload files such as pictures } else { $output = user_login(); @@ -973,7 +1057,7 @@ function user_edit($edit = array()) { function _user_profile($edit, $account) { foreach (module_list() as $module) { - if ($data = module_invoke($module, 'user', 'edit_form', $edit, $account)) { + if ($data = module_invoke($module, 'user', 'edit', $edit, $account)) { foreach ($data as $title => $form) { $groups[$title] .= $form; } @@ -991,37 +1075,27 @@ function _user_profile($edit, $account) { function user_view($uid = 0) { global $user; - if (!$uid) { - $uid = $user->uid; - } - - if ($user->uid && $user->uid == $uid) { - $output = form_item(t("Name"), "$user->name ($user->init)"); - $output .= form_item(t("E-mail address"), $user->mail, t("Please note that only you can see your own e-mail address - it is not publicly visible.")); - - $output .= implode("\n", module_invoke_all('user', "view_private", "", $user)); - - print theme('page', $output, $user->name); - } - else if ($uid && $account = user_load(array('uid' => $uid, "status" => 1))) { - $output = form_item(t("Name"), $account->name); - - $output .= implode("\n", module_invoke_all('user', "view_public", "", $account)); - - if (user_access("administer users")) { - $output .= form_item(t("Administration"), l(t("edit account"), "admin/user/edit/$account->uid")); + if ($uid == 0) { + if ($user->uid) { + print theme('page', theme('user_profile', $user), $user->name); } + else { + $output = user_login(); + if (variable_get("user_register", 1)) { + $output .= user_register(); + } + $output .= user_pass(); - print theme('page', $output, $account->name); + print theme('page', $output, t("User login")); + } } else { - $output = user_login(); - if (variable_get("user_register", 1)) { - $output .= user_register(); + if ($account = user_load(array('uid' => $uid, "status" => 1))) { + print theme('page', theme('user_profile', $account), $account->name); + } + else { + drupal_not_found(); } - $output .= user_pass(); - - print theme('page', $output, t("User login")); } } @@ -1124,6 +1198,20 @@ function user_settings() { $group .= form_textarea(t("Body of password recovery e-mail"), "user_mail_pass_body", _user_mail_text("pass_body"), 70, 10, t("Customize the body of the forgotten password e-mail.") ." ". t("Available variables are:") ." %username, %site, %password, %uri, %uri_brief, %mailto, %login_uri, %edit_uri."); $output .= form_group(t("User email settings"), $group); + // picture settings: + if (!file_check_directory(file_create_path(variable_get('user_picture_path', 'pictures')))) { + $error = theme('error', t('The picture directory does not exist, or is not writable.')); + } + + $group = form_radios(t('Picture support'), 'user_pictures', variable_get('user_pictures', 0), array(t('Disabled'), t('Enabled')), t('Enable picture support.')); + $group .= form_textfield(t("Picture image path"), 'user_picture_path', variable_get('user_picture_path', "pictures"), 45, 255, t("Subdirectory in the directory '%dir' where pictures will be stored.", array('%dir' => variable_get('file_directory_path', 'files') . FILE_SEPARATOR)) . $error); + $group .= form_textfield(t('Default picture'), 'user_picture_default', variable_get('user_picture_default', ''), 45, 255, t('URL of picture to display for users with no custom picture selected. Leave blank for none.')); + $group .= form_textfield(t("Picture maximum dimensions"), 'user_picture_dimensions', variable_get('user_picture_dimensions', "85x85"), 10, 10, t("Maximum dimensions for pictures.")); + $group .= form_textfield(t("Picture maximum file size"), 'user_picture_file_size', variable_get('user_picture_file_size', "30"), 10, 10, t("Maximum file size for pictures, in kB.")); + $group .= form_textarea(t("Picture guidelines"), 'user_picture_guidelines', variable_get('user_picture_guidelines', ''), 70, 4, t("This text is displayed at the picture upload form in addition to the default guidelines. It's useful for helping or instructing your users.")); + + $output .= form_group(t('Pictures'), $group); + // "Who's online" block settings $period = drupal_map_assoc(array(30, 60, 120, 180, 300, 600, 900, 1800, 2700, 3600, 5400, 7200, 10800, 21600, 43200, 86400), "format_interval"); $group = form_select(t("User activity"), "user_block_seconds_online", variable_get("user_block_seconds_online", 900), $period, t("Affects \"Who's online\" block. A user is considered online for this long after they have last viewed a page.")); @@ -1270,7 +1358,7 @@ function user_admin_perm($edit = array()) { ** Compile permission array: */ - $perms = module_invoke_all("perm"); + $perms = module_invoke_all('perm'); asort($perms); /* @@ -1376,7 +1464,7 @@ function user_admin_edit($edit = array()) { if ($op == t("Save account")) { foreach (module_list() as $module) { if (module_hook($module, 'user')) { - $result = module_invoke($module, 'user', "edit_validate", $edit, $account); + $result = module_invoke($module, 'user', 'validate', $edit, $account); } if (is_array($result)) { $data = array_merge($data, $result); @@ -1402,6 +1490,14 @@ function user_admin_edit($edit = array()) { } /* + ** If required, validate the picture. + */ + + if ($file = file_check_upload('picture')) { + $error = user_validate_picture($file, $edit, $account); + } + + /* ** If required, check that proposed passwords match. If so, ** add new password to $edit. */ @@ -1429,7 +1525,7 @@ function user_admin_edit($edit = array()) { db_query("DELETE FROM {users} WHERE uid = %d", $account->uid); db_query("DELETE FROM {authmap} WHERE uid = %d", $account->uid); drupal_set_message(t("the account has been deleted.")); - module_invoke_all('user', "delete", $account, $user); + module_invoke_all('user', 'delete', $account, $user); } else { $error = t("Failed to delete account: the account has to be blocked first."); @@ -1445,14 +1541,24 @@ function user_admin_edit($edit = array()) { ** Display user form: */ - $output .= form_item(t("User ID"), $account->uid); - $output .= form_textfield(t("Username"), 'name', $account->name, 30, 55, t("Your full name or your preferred username: only letters, numbers and spaces are allowed.")); - $output .= form_textfield(t("E-mail address"), "mail", $account->mail, 30, 55, t("Insert a valid e-mail address. All e-mails from the system will be sent to this address. The e-mail address is not made public and will only be used if you wish to receive a new password or wish to receive certain news or notifications by e-mail.")); - $output .= form_item(t("Password"), "<input type=\"password\" name=\"edit[pass1]\" size=\"12\" maxlength=\"24\" /> <input type=\"password\" name=\"edit[pass2]\" size=\"12\" maxlength=\"24\" />", t("Enter a new password twice if you want to change the current password for this user or leave it blank if you are happy with the current password.")); - $output .= form_radios(t("Status"), "status", $account->status, array(t("Blocked"), t("Active"))); - $output .= form_radios(t("Role"), "rid", $account->rid, user_roles(1)); + $group = form_item(t("User ID"), $account->uid); + $group .= form_textfield(t("Username"), 'name', $account->name, 30, 55, t("Your full name or your preferred username: only letters, numbers and spaces are allowed.")); + $group .= form_textfield(t("E-mail address"), "mail", $account->mail, 30, 55, t("Insert a valid e-mail address. All e-mails from the system will be sent to this address. The e-mail address is not made public and will only be used if you wish to receive a new password or wish to receive certain news or notifications by e-mail.")); + $group .= form_item(t("Password"), "<input type=\"password\" name=\"edit[pass1]\" size=\"12\" maxlength=\"24\" /> <input type=\"password\" name=\"edit[pass2]\" size=\"12\" maxlength=\"24\" />", t("Enter a new password twice if you want to change the current password for this user or leave it blank if you are happy with the current password.")); + $group .= form_radios(t("Status"), "status", $account->status, array(t("Blocked"), t("Active"))); + $group .= form_radios(t("Role"), "rid", $account->rid, user_roles(1)); + + $output = form_group(t('Account information'), $group); + + if (variable_get('user_pictures', 0)) { + $group = ''; + if (file_exists($account->picture)) { + $group .= '<img src="'. file_create_url($account->picture) .'" alt="" title="" />'; + } + $group .= form_file(t('Upload picture or picture'), 'picture', 48, t("Maximum dimensions are %dimensions and the maximum size is %size kB.", array("%dimensions" => variable_get('user_picture_dimensions', "85x85"), "%size" => variable_get('user_picture_file_size', "30")))); + $output .= form_group(t('Picture'), $group); + } - $output = form_group(t('Account information'), $output); $output .= _user_profile($edit, $account); $output .= form_submit(t("Save account")); @@ -1473,16 +1579,16 @@ function user_admin_account() { array("data" => t("username"), "field" => "u.name"), array("data" => t("status"), "field" => "u.status"), array("data" => t("role"), "field" => "u.rid"), - array("data" => t("last access"), "field" => "u.timestamp", "sort" => "desc"), + array("data" => t("last access"), "field" => "u.changed", "sort" => "desc"), t("operations") ); - $sql = "SELECT u.uid, u.name, u.status, u.timestamp, r.name AS rolename FROM {role} r INNER JOIN {users} u ON r.rid = u.rid WHERE uid != 0"; + $sql = "SELECT u.uid, u.name, u.status, u.changed, r.name AS rolename FROM {role} r INNER JOIN {users} u ON r.rid = u.rid WHERE uid != 0"; $sql .= tablesort_sql($header); $result = pager_query($sql, 50); $status = array(t("blocked"), t("active")); while ($account = db_fetch_object($result)) { - $rows[] = array($account->uid, format_name($account), $status[$account->status], $account->rolename, format_date($account->timestamp, "small"), l(t("edit account"), "admin/user/edit/$account->uid")); + $rows[] = array($account->uid, format_name($account), $status[$account->status], $account->rolename, format_date($account->changed, "small"), l(t("edit account"), "admin/user/edit/$account->uid")); } $pager = theme("pager", NULL, 50, 0, tablesort_pager()); @@ -1692,18 +1798,14 @@ function user_help($section = "admin/help#user") { function julia_user(\$type, \$edit, &\$user) { // What type of registration action are we taking? switch (\$type) { - case t(\"view_public\"): - // when others look at user data - return form_item(\"Favorite Ingredient\", \$user->julia_favingredient); - case t(\"view_private\"): - // when user tries to view his own user page. + case t(\"view\"): return form_item(\"Favorite Ingredient\", \$user->julia_favingredient); - case t(\"edit_form\"): + case t(\"edit\"): // when user tries to edit his own user page. return form_textfield(\"Favorite Ingredient\", \"julia_favingredient\", \$user->julia_favingredient, 50, 65, \"Tell everyone your secret spice\"); - case t(\"edit_validate\"): // Make sure the data they edited is \"valid\". + case t(\"validate\"): // Make sure the data they edited is \"valid\". return user_save(\$user, array(\"julia_favingredient\" => \$edit[\"julia_favingredient\"])); } } diff --git a/modules/user/user.module b/modules/user/user.module index 68d25921a..1dbc33daf 100644 --- a/modules/user/user.module +++ b/modules/user/user.module @@ -78,12 +78,13 @@ function user_save($account, $array = array()) { $query .= "data = '%s', "; $v[] = serialize($data); - db_query("UPDATE {users} SET $query timestamp = %d WHERE uid = %d", array_merge($v, array(time(), $account->uid))); + db_query("UPDATE {users} SET $query changed = %d WHERE uid = %d", array_merge($v, array(time(), $account->uid))); $user = user_load(array('uid' => $account->uid)); } else { - $array['timestamp'] = time(); + $array['created'] = time(); + $array['changed'] = time(); $array['uid'] = db_next_id("{users}_uid"); foreach ($array as $key => $value) { @@ -112,11 +113,11 @@ function user_save($account, $array = array()) { $user = user_load(array('name' => $array['name'])); - module_invoke_all('user', "insert", $array, $user); + module_invoke_all('user', 'insert', $array, $user); } foreach ($array as $key => $value) { - if (substr($key, 0, 4) == "auth") { + if (substr($key, 0, 4) == 'auth') { $authmaps[$key] = $value; } } @@ -136,7 +137,7 @@ function user_validate_name($name) { if (!$name) return t("You must enter a username."); if (substr($name, 0, 1) == ' ') return t("The username cannot begin with a space."); if (substr($name, -1) == ' ') return t("The username cannot end with a space."); - if (ereg(" ", $name)) return t("The username cannot contain multiple spaces in a row."); + if (ereg(' ', $name)) return t("The username cannot contain multiple spaces in a row."); if (ereg('[^ [:alnum:]@_.-]', $name)) return t("The username contains an illegal character."); if (ereg('@', $name) && !eregi('@([0-9a-z](-?[0-9a-z])*.)+[a-z]{2}([zmuvtg]|fo|me)?$', $name)) return t("The username is not a valid authentication ID."); if (strlen($name) > 56) return t("The username '%name' is too long: it must be less than 56 characters.", array("%name" => $name)); @@ -149,6 +150,35 @@ function user_validate_mail($mail) { } } +function user_validate_picture($file, &$edit, $user) { + + // initialize the picture: + $edit['picture'] = $user->picture; + + // check that uploaded file is an image, with a maximum file size and maximum height/width + $extension = strtolower(strrchr($file->name, ".")); + $size = getimagesize($file->path); + list($maxwidth, $maxheight) = explode("x", variable_get('user_picture_dimensions', "85x85")); + + if ((!in_array($size[2], array(1, 2, 3))) || (!in_array($extension, array(".gif", ".jpg", ".png", ".jpeg")))) { + $error = t("The uploaded file was not an image."); + } + else if ($file->size > (variable_get('user_picture_file_size', "30") * 1000)) { + $error = t("The uploaded image is too large; the maximum file size is %a kB.", array("%a" => variable_get('user_picture_file_size', "30"))); + } + else if ($size[0] > $maxwidth || $size[1] > $maxheight) { + $error = t("The uploaded image is too large; the maximum dimensions are %a pixels.", array("%a" => variable_get('user_picture_dimensions', "85x85"))); + } + else if ($file = file_save_upload('picture', variable_get('user_picture_path', "pictures") . FILE_SEPARATOR .'picture-'. $user->uid . $extension, 1)) { + $edit['picture'] = $file->path; + } + else { + $error = t("Failed to upload the picture image; the '%directory' directory doesn't exist.", array("%directory" => variable_get('user_picture_path', "pictures"))); + } + + return $error; +} + function user_validate_authmap($account, $authname, $module) { $result = db_query("SELECT COUNT(*) from {authmap} WHERE uid != %d AND authname = '%s'", $account->uid, $authname); if (db_result($result) > 0) { @@ -281,7 +311,7 @@ function user_fields() { } else { // Make sure we return the default fields at least - $fields = array('uid', 'name', 'pass', "mail", "mode", "sort", "threshold", "theme", "signature", "timestamp", "status", "timezone", "language", "init", "data", "rid"); + $fields = array('uid', 'name', 'pass', "mail", "picture", "mode", "sort", "threshold", "theme", "signature", "created", "changed", "status", "timezone", "language", "init", "data", "rid"); } } @@ -428,6 +458,42 @@ function user_block($op = "list", $delta = 0) { } } +function theme_user_picture($account) { + if (variable_get('user_pictures', 0)) { + if ($account->picture && file_exists($account->picture)) { + $picture = file_create_url($account->picture); + } + else if (variable_get('user_picture_default', '')) { + $picture = variable_get('user_picture_default', ''); + } + + if ($picture) { + $picture = "<img src=\"$picture\" alt=\"" . t("%user's picture", array("%user" => $account->name ? $account->name : t(variable_get("anonymous", "Anonymous")))) . "\" />"; + if ($account->uid) { + $picture = l($picture, "user/view/$account->uid", array("title" => t("View user profile."))); + } + + return "<div class=\"picture\">$picture</div>"; + } + } +} + +function theme_user_profile($account) { + $output = "<div class=\"profile\">\n"; + $output .= theme('user_picture', $account); + $output .= form_item(t('Name'), $account->name); + $output .= implode("\n", module_invoke_all('user', 'view', '', $account)); + $output .= form_item(t('Member for'), format_interval(time() - $account->created)); + + if (user_access("administer users")) { + $output .= form_item(t("Administration"), l(t("edit account"), "admin/user/edit/$account->uid")); + } + + $output .= "</div>\n"; + + return $output; +} + function theme_user_list($items, $title = NULL) { return theme("item_list", $items, $title); } @@ -595,7 +661,7 @@ function user_login($edit = array(), $msg = "") { watchdog('user', "session opened for '$user->name'"); // update the user table timestamp noting user has logged in - db_query("UPDATE {users} SET timestamp = '%d' WHERE uid = '%s'", time(), $user->uid); + db_query("UPDATE {users} SET changed = '%d' WHERE uid = '%s'", time(), $user->uid); user_module_invoke("login", $edit, $user); @@ -685,7 +751,7 @@ function user_logout() { */ session_destroy(); - module_invoke_all('user', "logout", NULL, $user); + module_invoke_all('user', 'logout', NULL, $user); unset($user); } @@ -884,7 +950,15 @@ function user_edit($edit = array()) { else if ($edit['mail'] && db_num_rows(db_query("SELECT uid FROM {users} WHERE uid != '$user->uid' AND LOWER(mail) = LOWER('%s')", $edit['mail'])) > 0) { $error = t("The e-mail address '%s' is already taken.", array("%s" => $edit['mail'])); } - else if ($user->uid) { + else { + /* + ** If required, validate the picture. + */ + + if ($file = file_check_upload('picture')) { + $error = user_validate_picture($file, $edit, $user); + } + /* ** If required, check that proposed passwords match. If so, ** add new password to $edit. @@ -922,7 +996,7 @@ function user_edit($edit = array()) { foreach (module_list() as $module) { if (module_hook($module, 'user')) { - $result = module_invoke($module, 'user', "edit_validate", $edit, $user); + $result = module_invoke($module, 'user', 'validate', $edit, $user); } if (is_array($result)) { $data = array_merge($data, $result); @@ -940,7 +1014,7 @@ function user_edit($edit = array()) { $user = user_save($user, array_merge($edit, $data)); - drupal_set_message(t("your user information changes have been saved.")); + drupal_set_message(t('your user information changes have been saved.')); } } } @@ -953,15 +1027,25 @@ function user_edit($edit = array()) { $edit = object2array($user); } - $output = form_textfield(t("Username"), 'name', $edit['name'], 30, 55, t("Your full name or your preferred username: only letters, numbers and spaces are allowed.")); - $output .= form_textfield(t("E-mail address"), "mail", $edit['mail'], 30, 55, t("Insert a valid e-mail address. All e-mails from the system will be sent to this address. The e-mail address is not made public and will only be used if you wish to receive a new password or wish to receive certain news or notifications by e-mail.")); - $output .= form_item(t("Password"), "<input type=\"password\" name=\"edit[pass1]\" size=\"12\" maxlength=\"24\" /> <input type=\"password\" name=\"edit[pass2]\" size=\"12\" maxlength=\"24\" />", t("Enter your new password twice if you want to change your current password or leave it blank if you are happy with your current password.")); - $output = form_group(t('Account information'), $output); + $group = form_textfield(t("Username"), 'name', $edit['name'], 30, 55, t("Your full name or your preferred username: only letters, numbers and spaces are allowed.")); + $group .= form_textfield(t("E-mail address"), "mail", $edit['mail'], 30, 55, t("Insert a valid e-mail address. All e-mails from the system will be sent to this address. The e-mail address is not made public and will only be used if you wish to receive a new password or wish to receive certain news or notifications by e-mail.")); + $group .= form_item(t("Password"), "<input type=\"password\" name=\"edit[pass1]\" size=\"12\" maxlength=\"24\" /> <input type=\"password\" name=\"edit[pass2]\" size=\"12\" maxlength=\"24\" />", t("Enter your new password twice if you want to change your current password or leave it blank if you are happy with your current password.")); + $output = form_group(t('Account information'), $group); + + if (variable_get('user_pictures', 0)) { + $group = ''; + if (file_exists($user->picture)) { + $group .= '<img src="'. file_create_url($edit['picture']) .'" alt="" title="" />'; + } + $group .= form_file(t('Upload picture or picture'), 'picture', 48, t("Your virtual face or picture. Maximum dimensions are %dimensions and the maximum size is %size kB.", array("%dimensions" => variable_get('user_picture_dimensions', "85x85"), "%size" => variable_get('user_picture_file_size', "30"))) ." ". variable_get('user_picture_guidelines', '')); + $output .= form_group(t('Picture'), $group); + } + $output .= _user_profile($edit, $user); $output .= form_submit(t("Save user information")); $output = form($output, "post", 0, array("enctype" => "multipart/form-data")); - // the "enctype" attribute is required to upload files such as avatars + // the "enctype" attribute is required to upload files such as pictures } else { $output = user_login(); @@ -973,7 +1057,7 @@ function user_edit($edit = array()) { function _user_profile($edit, $account) { foreach (module_list() as $module) { - if ($data = module_invoke($module, 'user', 'edit_form', $edit, $account)) { + if ($data = module_invoke($module, 'user', 'edit', $edit, $account)) { foreach ($data as $title => $form) { $groups[$title] .= $form; } @@ -991,37 +1075,27 @@ function _user_profile($edit, $account) { function user_view($uid = 0) { global $user; - if (!$uid) { - $uid = $user->uid; - } - - if ($user->uid && $user->uid == $uid) { - $output = form_item(t("Name"), "$user->name ($user->init)"); - $output .= form_item(t("E-mail address"), $user->mail, t("Please note that only you can see your own e-mail address - it is not publicly visible.")); - - $output .= implode("\n", module_invoke_all('user', "view_private", "", $user)); - - print theme('page', $output, $user->name); - } - else if ($uid && $account = user_load(array('uid' => $uid, "status" => 1))) { - $output = form_item(t("Name"), $account->name); - - $output .= implode("\n", module_invoke_all('user', "view_public", "", $account)); - - if (user_access("administer users")) { - $output .= form_item(t("Administration"), l(t("edit account"), "admin/user/edit/$account->uid")); + if ($uid == 0) { + if ($user->uid) { + print theme('page', theme('user_profile', $user), $user->name); } + else { + $output = user_login(); + if (variable_get("user_register", 1)) { + $output .= user_register(); + } + $output .= user_pass(); - print theme('page', $output, $account->name); + print theme('page', $output, t("User login")); + } } else { - $output = user_login(); - if (variable_get("user_register", 1)) { - $output .= user_register(); + if ($account = user_load(array('uid' => $uid, "status" => 1))) { + print theme('page', theme('user_profile', $account), $account->name); + } + else { + drupal_not_found(); } - $output .= user_pass(); - - print theme('page', $output, t("User login")); } } @@ -1124,6 +1198,20 @@ function user_settings() { $group .= form_textarea(t("Body of password recovery e-mail"), "user_mail_pass_body", _user_mail_text("pass_body"), 70, 10, t("Customize the body of the forgotten password e-mail.") ." ". t("Available variables are:") ." %username, %site, %password, %uri, %uri_brief, %mailto, %login_uri, %edit_uri."); $output .= form_group(t("User email settings"), $group); + // picture settings: + if (!file_check_directory(file_create_path(variable_get('user_picture_path', 'pictures')))) { + $error = theme('error', t('The picture directory does not exist, or is not writable.')); + } + + $group = form_radios(t('Picture support'), 'user_pictures', variable_get('user_pictures', 0), array(t('Disabled'), t('Enabled')), t('Enable picture support.')); + $group .= form_textfield(t("Picture image path"), 'user_picture_path', variable_get('user_picture_path', "pictures"), 45, 255, t("Subdirectory in the directory '%dir' where pictures will be stored.", array('%dir' => variable_get('file_directory_path', 'files') . FILE_SEPARATOR)) . $error); + $group .= form_textfield(t('Default picture'), 'user_picture_default', variable_get('user_picture_default', ''), 45, 255, t('URL of picture to display for users with no custom picture selected. Leave blank for none.')); + $group .= form_textfield(t("Picture maximum dimensions"), 'user_picture_dimensions', variable_get('user_picture_dimensions', "85x85"), 10, 10, t("Maximum dimensions for pictures.")); + $group .= form_textfield(t("Picture maximum file size"), 'user_picture_file_size', variable_get('user_picture_file_size', "30"), 10, 10, t("Maximum file size for pictures, in kB.")); + $group .= form_textarea(t("Picture guidelines"), 'user_picture_guidelines', variable_get('user_picture_guidelines', ''), 70, 4, t("This text is displayed at the picture upload form in addition to the default guidelines. It's useful for helping or instructing your users.")); + + $output .= form_group(t('Pictures'), $group); + // "Who's online" block settings $period = drupal_map_assoc(array(30, 60, 120, 180, 300, 600, 900, 1800, 2700, 3600, 5400, 7200, 10800, 21600, 43200, 86400), "format_interval"); $group = form_select(t("User activity"), "user_block_seconds_online", variable_get("user_block_seconds_online", 900), $period, t("Affects \"Who's online\" block. A user is considered online for this long after they have last viewed a page.")); @@ -1270,7 +1358,7 @@ function user_admin_perm($edit = array()) { ** Compile permission array: */ - $perms = module_invoke_all("perm"); + $perms = module_invoke_all('perm'); asort($perms); /* @@ -1376,7 +1464,7 @@ function user_admin_edit($edit = array()) { if ($op == t("Save account")) { foreach (module_list() as $module) { if (module_hook($module, 'user')) { - $result = module_invoke($module, 'user', "edit_validate", $edit, $account); + $result = module_invoke($module, 'user', 'validate', $edit, $account); } if (is_array($result)) { $data = array_merge($data, $result); @@ -1402,6 +1490,14 @@ function user_admin_edit($edit = array()) { } /* + ** If required, validate the picture. + */ + + if ($file = file_check_upload('picture')) { + $error = user_validate_picture($file, $edit, $account); + } + + /* ** If required, check that proposed passwords match. If so, ** add new password to $edit. */ @@ -1429,7 +1525,7 @@ function user_admin_edit($edit = array()) { db_query("DELETE FROM {users} WHERE uid = %d", $account->uid); db_query("DELETE FROM {authmap} WHERE uid = %d", $account->uid); drupal_set_message(t("the account has been deleted.")); - module_invoke_all('user', "delete", $account, $user); + module_invoke_all('user', 'delete', $account, $user); } else { $error = t("Failed to delete account: the account has to be blocked first."); @@ -1445,14 +1541,24 @@ function user_admin_edit($edit = array()) { ** Display user form: */ - $output .= form_item(t("User ID"), $account->uid); - $output .= form_textfield(t("Username"), 'name', $account->name, 30, 55, t("Your full name or your preferred username: only letters, numbers and spaces are allowed.")); - $output .= form_textfield(t("E-mail address"), "mail", $account->mail, 30, 55, t("Insert a valid e-mail address. All e-mails from the system will be sent to this address. The e-mail address is not made public and will only be used if you wish to receive a new password or wish to receive certain news or notifications by e-mail.")); - $output .= form_item(t("Password"), "<input type=\"password\" name=\"edit[pass1]\" size=\"12\" maxlength=\"24\" /> <input type=\"password\" name=\"edit[pass2]\" size=\"12\" maxlength=\"24\" />", t("Enter a new password twice if you want to change the current password for this user or leave it blank if you are happy with the current password.")); - $output .= form_radios(t("Status"), "status", $account->status, array(t("Blocked"), t("Active"))); - $output .= form_radios(t("Role"), "rid", $account->rid, user_roles(1)); + $group = form_item(t("User ID"), $account->uid); + $group .= form_textfield(t("Username"), 'name', $account->name, 30, 55, t("Your full name or your preferred username: only letters, numbers and spaces are allowed.")); + $group .= form_textfield(t("E-mail address"), "mail", $account->mail, 30, 55, t("Insert a valid e-mail address. All e-mails from the system will be sent to this address. The e-mail address is not made public and will only be used if you wish to receive a new password or wish to receive certain news or notifications by e-mail.")); + $group .= form_item(t("Password"), "<input type=\"password\" name=\"edit[pass1]\" size=\"12\" maxlength=\"24\" /> <input type=\"password\" name=\"edit[pass2]\" size=\"12\" maxlength=\"24\" />", t("Enter a new password twice if you want to change the current password for this user or leave it blank if you are happy with the current password.")); + $group .= form_radios(t("Status"), "status", $account->status, array(t("Blocked"), t("Active"))); + $group .= form_radios(t("Role"), "rid", $account->rid, user_roles(1)); + + $output = form_group(t('Account information'), $group); + + if (variable_get('user_pictures', 0)) { + $group = ''; + if (file_exists($account->picture)) { + $group .= '<img src="'. file_create_url($account->picture) .'" alt="" title="" />'; + } + $group .= form_file(t('Upload picture or picture'), 'picture', 48, t("Maximum dimensions are %dimensions and the maximum size is %size kB.", array("%dimensions" => variable_get('user_picture_dimensions', "85x85"), "%size" => variable_get('user_picture_file_size', "30")))); + $output .= form_group(t('Picture'), $group); + } - $output = form_group(t('Account information'), $output); $output .= _user_profile($edit, $account); $output .= form_submit(t("Save account")); @@ -1473,16 +1579,16 @@ function user_admin_account() { array("data" => t("username"), "field" => "u.name"), array("data" => t("status"), "field" => "u.status"), array("data" => t("role"), "field" => "u.rid"), - array("data" => t("last access"), "field" => "u.timestamp", "sort" => "desc"), + array("data" => t("last access"), "field" => "u.changed", "sort" => "desc"), t("operations") ); - $sql = "SELECT u.uid, u.name, u.status, u.timestamp, r.name AS rolename FROM {role} r INNER JOIN {users} u ON r.rid = u.rid WHERE uid != 0"; + $sql = "SELECT u.uid, u.name, u.status, u.changed, r.name AS rolename FROM {role} r INNER JOIN {users} u ON r.rid = u.rid WHERE uid != 0"; $sql .= tablesort_sql($header); $result = pager_query($sql, 50); $status = array(t("blocked"), t("active")); while ($account = db_fetch_object($result)) { - $rows[] = array($account->uid, format_name($account), $status[$account->status], $account->rolename, format_date($account->timestamp, "small"), l(t("edit account"), "admin/user/edit/$account->uid")); + $rows[] = array($account->uid, format_name($account), $status[$account->status], $account->rolename, format_date($account->changed, "small"), l(t("edit account"), "admin/user/edit/$account->uid")); } $pager = theme("pager", NULL, 50, 0, tablesort_pager()); @@ -1692,18 +1798,14 @@ function user_help($section = "admin/help#user") { function julia_user(\$type, \$edit, &\$user) { // What type of registration action are we taking? switch (\$type) { - case t(\"view_public\"): - // when others look at user data - return form_item(\"Favorite Ingredient\", \$user->julia_favingredient); - case t(\"view_private\"): - // when user tries to view his own user page. + case t(\"view\"): return form_item(\"Favorite Ingredient\", \$user->julia_favingredient); - case t(\"edit_form\"): + case t(\"edit\"): // when user tries to edit his own user page. return form_textfield(\"Favorite Ingredient\", \"julia_favingredient\", \$user->julia_favingredient, 50, 65, \"Tell everyone your secret spice\"); - case t(\"edit_validate\"): // Make sure the data they edited is \"valid\". + case t(\"validate\"): // Make sure the data they edited is \"valid\". return user_save(\$user, array(\"julia_favingredient\" => \$edit[\"julia_favingredient\"])); } } diff --git a/themes/xtemplate/default/xtemplate.css b/themes/xtemplate/default/xtemplate.css index cc42447dc..fcf8d4f08 100644 --- a/themes/xtemplate/default/xtemplate.css +++ b/themes/xtemplate/default/xtemplate.css @@ -222,7 +222,7 @@ table { font-size: 0.8em; padding: 1.5em; } -.node .avatar { +.node .picture { border: 1px solid #ddd; float: right; margin: 0.5em; @@ -242,7 +242,7 @@ table { float: right; color: red; } -.comment .avatar { +.comment .picture { border: 1px solid #abc; float: right; margin: 0.5em; diff --git a/themes/xtemplate/default/xtemplate.xtmpl b/themes/xtemplate/default/xtemplate.xtmpl index b6c74d22b..a1e0e72ac 100644 --- a/themes/xtemplate/default/xtemplate.xtmpl +++ b/themes/xtemplate/default/xtemplate.xtmpl @@ -56,9 +56,9 @@ <!-- BEGIN: node --> <div class="node {static}"> - <!-- BEGIN: avatar --> - <div class="avatar">{avatar}</div> - <!-- END: avatar --> + <!-- BEGIN: picture --> + {picture} + <!-- END: picture --> <!-- BEGIN: title --> <h2 class="title"><a href="{link}">{title}</a></h2> <!-- END: title --> @@ -75,9 +75,9 @@ <!-- BEGIN: comment --> <div class="comment"> - <!-- BEGIN: avatar --> - <div class="avatar">{avatar}</div> - <!-- END: avatar --> + <!-- BEGIN: picture --> + {picture} + <!-- END: picture --> <h3 class="title">{title}</h3><!-- BEGIN: new --><span class="new">{new}</span><!-- END: new --> <div class="submitted">{submitted}</div> <div class="content">{content}</div> diff --git a/themes/xtemplate/pushbutton/xtemplate.css b/themes/xtemplate/pushbutton/xtemplate.css index dd06bc2b9..e3c72a2d4 100644 --- a/themes/xtemplate/pushbutton/xtemplate.css +++ b/themes/xtemplate/pushbutton/xtemplate.css @@ -312,7 +312,7 @@ table#footer-links { font-size: 0.83em; padding: 1.5em; } -.node .avatar { +.node .picture { border: 1px solid #fff; float: right; margin: 0.5em; @@ -334,7 +334,7 @@ table#footer-links { float: right; color: red; } -.comment .avatar { +.comment .picture { border: 1px solid #fff; float: right; margin: 0.5em; diff --git a/themes/xtemplate/pushbutton/xtemplate.xtmpl b/themes/xtemplate/pushbutton/xtemplate.xtmpl index f2eccd4c1..df01933c5 100644 --- a/themes/xtemplate/pushbutton/xtemplate.xtmpl +++ b/themes/xtemplate/pushbutton/xtemplate.xtmpl @@ -59,9 +59,9 @@ <!-- BEGIN: node --> <div class="node {static}"> - <!-- BEGIN: avatar --> - <div class="avatar">{avatar}</div> - <!-- END: avatar --> + <!-- BEGIN: picture --> + {picture} + <!-- END: picture --> <!-- BEGIN: title --> <h1 class="title"><a href="{link}">{title}</a></h1> <!-- END: title --> @@ -78,9 +78,9 @@ <!-- BEGIN: comment --> <div class="comment"> - <!-- BEGIN: avatar --> - <div class="avatar">{avatar}</div> - <!-- END: avatar --> + <!-- BEGIN: picture --> + <div class="picture">{picture}</div> + <!-- END: picture --> <h3 class="title">{title}</h3><!-- BEGIN: new --><span class="new">{new}</span><!-- END: new --> <div class="submitted">{submitted}</div> <div class="content">{content}</div> diff --git a/themes/xtemplate/xtemplate.theme b/themes/xtemplate/xtemplate.theme index ac3e43a14..910698707 100644 --- a/themes/xtemplate/xtemplate.theme +++ b/themes/xtemplate/xtemplate.theme @@ -32,7 +32,8 @@ function xtemplate_settings() { $group .= form_radios(t('Search box'), 'xtemplate_search_box', variable_get('xtemplate_search_box', 0), array(t('Disabled'), t('Enabled')), t('Show a search box in the upper right corner.')); $output .= form_group(t('Header settings'), $group); - $group = form_checkbox(t('Display avatars with posts'), 'xtemplate_avatar_node', 1, variable_get('xtemplate_avatar_node', 0), t('Display individualized pictures identifying users with posts they start.')) . form_checkbox(t('Display avatars with comments'), 'xtemplate_avatar_comment', 1, variable_get('xtemplate_avatar_comment', 0), t('Display individualized pictures identifying users with their comments.')) . form_textfield(t('Default avatar'), 'xtemplate_avatar_default', variable_get('xtemplate_avatar_default', ''), 70, 300, t('URL of avatar to display for users with no custom avatar selected. Leave blank for none.')); + $group = form_checkbox(t('Display pictures with posts'), 'xtemplate_picture_node', 1, variable_get('xtemplate_picture_node', 0), t('Display individualized pictures identifying users with posts they start.')); + $gourp .= form_checkbox(t('Display pictures with comments'), 'xtemplate_picture_comment', 1, variable_get('xtemplate_picture_comment', 0), t('Display individualized pictures identifying users with their comments.')); $output .= form_group(t('Avatar settings'), $group); return $output; @@ -69,22 +70,9 @@ function xtemplate_node($node, $main = 0, $page = 0) { $xtemplate->template->parse("node.title"); } - if (module_exist("profile") && variable_get("xtemplate_avatar_node", 0)) { - $avatar = $node->profile_avatar; - if (empty($avatar) || !file_exists($avatar)) { - $avatar = variable_get("xtemplate_avatar_default", ""); - } - else { - $avatar = file_create_url($avatar); - } - if ($avatar) { - $avatar = "<img src=\"$avatar\" alt=\"" . t("%user's avatar", array("%user" => $node->name ? $node->name : t(variable_get("anonymous", "Anonymous")))) . "\" />"; - if ($node->uid) { - $avatar = l($avatar, "user/view/$node->uid", array("title" => t("View user profile."))); - } - $xtemplate->template->assign("avatar", $avatar); - $xtemplate->template->parse("node.avatar"); - } + if ($picture = theme('user_picture', $node)) { + $xtemplate->template->assign("picture", $picture); + $xtemplate->template->parse("node.picture"); } if (module_exist("taxonomy") && ($taxonomy = taxonomy_link("taxonomy terms", $node))) { @@ -121,22 +109,9 @@ function xtemplate_comment($comment, $links = 0) { $xtemplate->template->parse("comment.new"); } - if (module_exist("profile") && variable_get("xtemplate_avatar_comment", 0)) { - $avatar = $comment->profile_avatar; - if (empty($avatar) || !file_exists($avatar)) { - $avatar = variable_get("xtemplate_avatar_default", ""); - } - else { - $avatar = file_create_url($avatar); - } - if ($avatar) { - $avatar = "<img src=\"$avatar\" alt=\"" . t("%user's avatar", array("%user" => $comment->name ? $comment->name : t(variable_get("anonymous", "Anonymous")))) . "\" />"; - if ($comment->uid) { - $avatar = l($avatar, "user/view/$comment->uid", array("title" => t("View user profile."))); - } - $xtemplate->template->assign("avatar", $avatar); - $xtemplate->template->parse("comment.avatar"); - } + if ($picture = theme('user_picture', $comment)) { + $xtemplate->template->assign("picture", $picture); + $xtemplate->template->parse("comment.picture"); } if ($links) { |