diff --git a/checkin_notes b/checkin_notes index 75104c1cc0..064af54ca7 100644 --- a/checkin_notes +++ b/checkin_notes @@ -12304,3 +12304,27 @@ Rom 17 Dec 2007 win_build/installerv2/redist/Windows/x64/ boinccas.dll boinccas95.dll + +David 18 Dec 2007 + - user web: add "friend" and "notification" features + - user web: code cleanup in Profile area + - GUI RPC: add missing "/" in auth2 RPC + + db/ + constraints.sql + schema.sql + html/ + inc/ + forum_db.inc + gallery.inc + profile.inc + team.inc + user.inc + ops/ + db_update.php + user/ + create_profile.php + friend.php (new) + show_user.php + lib/ + gui_rpc_client.C diff --git a/db/bolt_schema.sql b/db/bolt_schema.sql index d67e676e58..18a14af39e 100644 --- a/db/bolt_schema.sql +++ b/db/bolt_schema.sql @@ -51,7 +51,9 @@ create table bolt_view ( primary key (id) ); -create table bolt_result ( +-- represents the result of an exercise +-- +create table bolt_exercise_result ( id integer not null auto_increment, view_id integer not null, score double not null, @@ -59,6 +61,14 @@ create table bolt_result ( primary key(id) ); +-- represents the result of an exercise set +-- +create table bolt_exercise_set_result ( + id integer not null auto_increment, + score double not null, + primary key(id) +); + create table bolt_refresh ( id integer not null auto_increment, view_id integer not null, diff --git a/db/constraints.sql b/db/constraints.sql index 5709017176..dc2be71e1e 100644 --- a/db/constraints.sql +++ b/db/constraints.sql @@ -117,3 +117,9 @@ alter table team_delta alter table team_admin add unique (teamid, userid); + +alter table friend + add unique friend_u (user_src, user_dest); + +alter table notify + add index notify_u (userid); diff --git a/db/schema.sql b/db/schema.sql index 57defbdf91..080f0a0b12 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -536,3 +536,31 @@ create table team_admin ( create_time integer not null, rights integer not null ) type=MyISAM; + +-- A friendship request. +-- The friendship exists if (x,y) and (y,x) +create table friend ( + user_src integer not null, + -- initiator + user_dest integer not null, + -- target + message varchar(255) not null, + create_time integer not null, + reciprocated tinyint not null + -- whether the reciprocal exists +); + +-- a notification of something, e.g. +-- a friend request or confirmation +-- a post in a subscribed thread +-- a personal message +-- These records are deleted when the user acts on them +create table notify ( + id serial primary key, + userid integer not null, + -- destination of notification + create_time integer not null, + type integer not null, + opaque integer not null + -- some other ID, e.g. that of the thread, user or PM record +); diff --git a/html/inc/bolt_ex.inc b/html/inc/bolt_ex.inc index 5d56df7d50..8aedaeec22 100644 --- a/html/inc/bolt_ex.inc +++ b/html/inc/bolt_ex.inc @@ -8,7 +8,7 @@ $bolt_ex_score = 0; function bolt_exclusive_choice($choices) { global $bolt_ex_mode; // input global $bolt_ex_index; // input - global $bolt_ex_score; // output if SCORE + global $bolt_ex_score; // incremental output if SCORE global $bolt_ex_response; // output if SCORE switch ($bolt_ex_mode) { @@ -25,18 +25,26 @@ function bolt_exclusive_choice($choices) { case BOLT_MODE_SCORE: $right_ans = $choices[0]; shuffle($choices); - $response = $_GET["q_$bolt_ex_index"]; - if ($choices[$response] == $right_ans) { - $bolt_ex_score = 1; + $key = "q_$bolt_ex_index"; + if (array_key_exists($key, $_GET)) { + $response = $_GET[$key]; + if ($choices[$response] == $right_ans) { + $bolt_ex_score += 1; + } + $bolt_ex_response = "$bolt_ex_index: $choices[$response]"; } else { - $bolt_ex_score = 0; + $bolt_ex_response = ""; } - $bolt_ex_response = "$bolt_ex_index: $choices[$response]"; break; case BOLT_MODE_ANSWER: $right_ans = $choices[0]; shuffle($choices); - $response = $_GET["q_$bolt_ex_index"]; + $key = "q_$bolt_ex_index"; + if (array_key_exists($key, $_GET)) { + $response = $_GET[$key]; + } else { + $response = -1; + } $i = 0; start_table(); foreach ($choices as $choice) { @@ -55,7 +63,7 @@ function bolt_exclusive_choice($choices) { function bolt_inclusive_choice($choices) { global $bolt_ex_mode; // input global $bolt_ex_index; // input - global $bolt_ex_score; // output if SCORE + global $bolt_ex_score; // incremental output if SCORE global $bolt_ex_response; // output if SCORE switch ($bolt_ex_mode) { @@ -65,7 +73,7 @@ function bolt_inclusive_choice($choices) { start_table(); foreach ($choices as $choice) { $c = $choice[0]; - row2($c, ""); + row2("", $c); $i++; } end_table(); @@ -81,22 +89,24 @@ function bolt_inclusive_choice($choices) { $r = $choice[1]; $correct = ($r && $response) || (!$r && !$response); if ($correct) $score += 1./$n; + $i++; } $bolt_ex_response = "$bolt_ex_index: $choices[$response]"; + $bolt_ex_score += $score; break; case BOLT_MODE_ANSWER: $i = 0; $n = count($choices); shuffle($choices); start_table(); - table_header("", "correct?", "your answer"); + table_header("Choice", "Correct?", "Your answer"); foreach ($choices as $choice) { $c = $choice[0]; $key = "q_".$bolt_ex_index."_$i"; $response = array_key_exists($key, $_GET); $r = $choice[1]; $correct = ($r && $response) || (!$r && !$response); - table_row($c, $r?"X":"
", $response?"X":"
"); + table_row($c, $r?"yes":"no", $response?"yes":"no"); $i++; } end_table(); @@ -109,7 +119,7 @@ function bolt_image_rect($img, $rect) { global $bolt_ex_mode; // input global $bolt_ex_index; // input global $bolt_ex_state; // output if SHOW, else input - global $bolt_ex_score; // output if SCORE + global $bolt_ex_score; // incremental output if SCORE switch ($bolt_ex_mode) { case BOLT_MODE_SHOW: diff --git a/html/inc/forum_db.inc b/html/inc/forum_db.inc index 42fbc78a1e..643e886df3 100644 --- a/html/inc/forum_db.inc +++ b/html/inc/forum_db.inc @@ -224,4 +224,47 @@ class BoincPostRating { } } +class BoincFriend { + static function insert($clause) { + $db = BoincDb::get(); + return $db->insert('friend', $clause); + } + static function lookup($uid1, $uid2) { + $db = BoincDb::get(); + return $db->lookup('friend', 'BoincFriend', "user_src=$uid1 and user_dest=$uid2"); + } + function update($clause) { + $db = BoincDb::get(); + return $db->update_aux('friend', "$clause where user_src=$this->user_src and user_dest=$this->user_dest"); + } + static function enum($clause) { + $db = BoincDb::get(); + return $db->enum('friend', 'BoincFriend', $clause); + } +} + +class BoincNotify { + static function insert($clause) { + $db = BoincDb::get(); + $ret = $db->insert('notify', $clause); + if (!$ret) return null; + return $db->insert_id(); + } + static function enum($clause) { + $db = BoincDb::get(); + return $db->enum('notify', 'BoincNotify', $clause); + } + static function lookup($userid, $type, $opaque) { + $db = BoincDb::get(); + return $db->lookup('notify', 'BoincNotify', "userid=$userid and type=$type and opaque=$opaque"); + } + function delete() { + $db = BoincDb::get(); + return $db->delete($this, 'notify'); + } +} + +define ('NOTIFY_FRIEND_REQ', 1); +define ('NOTIFY_FRIEND_ACCEPT', 2); + ?> diff --git a/html/inc/gallery.inc b/html/inc/gallery.inc index f3a82d7e70..91f49f1ac4 100644 --- a/html/inc/gallery.inc +++ b/html/inc/gallery.inc @@ -11,6 +11,60 @@ require_once("../inc/uotd.inc"); $alphabet = array('A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','0','1','2','3','4','5','6','7','8','9'); +// Builds a summary table of user profiles. +// +// $members is an array of userIDs; +// $offset indicates which entry to begin the table with +// $numToDisplay indicates how many profiles to display in this table +// $cols indicates how many profile summaries should be written per row +// $descriptor is an optional file descriptor to write the table to. + +function show_user_table($members, $offset, $numToDisplay, $cols) { + echo "\n"; + + $rows = ceil($numToDisplay / $cols); + $count = $offset; + $numMembers = count($members); + + for ($row = 0; $row < $rows; $row++) { + if ($count >= $numMembers) { + break; + } + + echo "\n"; + + for ($col = 0; $col < $cols; $col++) { + if ($count < $numMembers) { + $profile = get_profile($members[$count]); + if (!$profile) { + $numMembers--; + continue; + } + + echo ""; + $count++; + } else { + echo ""; + } + } + echo "\n"; + } + echo "
"; + + $show_picture = $profile->has_picture; + if (profile_screening() && $profile->verification != 1) { + $show_picture = false; + } + if ($show_picture) { + echo ""; + } else { + echo " "; + } + + echo "
\n", get_profile_summary($profile), "
\n"; + +} + // Generates a standard set of links between associated multi-page documents. // All linked files must be of the form "$filename_.html". @@ -55,10 +109,6 @@ function build_picture_pages($width, $height) { // TODO: Add support for a computer image gallery. - // TODO: Should we eliminate the has_picture flag? Doesn't really - // seem necessary when we're building static pages- could just use - // file_exists on the username... - // TODO: Standardize "Last modified" string to a function call (util.inc). if (profile_screening()) { diff --git a/html/inc/profile.inc b/html/inc/profile.inc index cbcfca937a..891a433529 100644 --- a/html/inc/profile.inc +++ b/html/inc/profile.inc @@ -7,7 +7,7 @@ require_once("../inc/cache.inc"); require_once("../inc/user.inc"); require_once("../inc/translation.inc"); require_once("../inc/text_transform.inc"); -require_once("../inc/db_forum.inc"); +require_once("../inc/forum.inc"); require_once("../inc/recaptchalib.php"); define('SMALL_IMG_WIDTH', 64); @@ -26,120 +26,34 @@ function profile_screening() { return parse_bool($config, "profile_screening"); } -// output a select form item with the given name, -// from a list of newline-delineated items from the text file. -// If $selection is provided, and if it matches one of the entries in the file, -// it will be selected by default. -// -function show_combo_box($name, $filename, $selection=null) { - if (!file_exists($filename)) { - echo "ERROR: $filename does not exist! Cannot create combo box.
"; - exit(); - } - echo "\n"; - fclose($file); -} - function get_profile($userid) { return BoincProfile::lookup("userid = $userid"); } -function show_profile_creation_page($user) { - $config = get_config(); - $min_credit = parse_config($config, ""); - if ($min_credit && $user->expavg_credit < $min_credit) { - error_page( - "To prevent spam, an average credit of $min_credit or greater is required to create or edit a profile. We apologize for this inconvenience." - ); - } +// TODO: use the following functions instead of hardwired crap everywhere - // If the user already has a profile, - // fill in the fields with their current values. - // - $profile = get_profile($user->id); - if (post_str("submit", true)) { - $privatekey = parse_config($config, ""); - if ($privatekey) { - $resp = recaptcha_check_answer($privatekey, $_SERVER["REMOTE_ADDR"], - $_POST["recaptcha_challenge_field"], $_POST["recaptcha_response_field"] - ); - if (!$resp->is_valid) { - error_page("The reCAPTCHA wasn't entered correctly. Go back and try it again.
". - "(reCAPTCHA said: " . $resp->error . ")" - ); - } - } - - process_create_profile($user, $profile); - exit(); - } - - if ($profile) { - page_head("Edit your profile"); - } else { - page_head("Create a profile"); - } - - echo " -
- "; - start_table_noborder(); - show_description(); - show_questions($profile); - show_picture_option($profile); - show_submit(); - end_table(); - echo "
"; - page_tail(); +function profile_image_path($userid) { + return IMAGE_PATH.$userid.'.jpg'; } -function show_description() { - echo " -

- Your profile lets you share your opinions and background - with the ".PROJECT." community. -

- "; +function profile_thumb_path($userid) { + return IMAGE_PATH.$userid.'_sm.jpg'; } -function show_questions($profile) { - $response1 = ""; - $response2 = ""; - if (isset($profile->response1)) { - $response1 = stripslashes($profile->response1); - } - if (isset($profile->response2)) { - $response2 = stripslashes($profile->response2); - } - - row1(show_profile_heading1()); - rowify(show_profile_question1().html_info()); - rowify("
"); - show_textarea("response1", $response1); - rowify("
"); - row1( show_profile_heading2()); - rowify( show_profile_question2().html_info()); - rowify("
"); - show_textarea("response2", $response2); - rowify("
"); - show_language_selection($profile); - rowify("
"); +function profile_image_url($userid) { + return URL_BASE.IMAGE_URL.$userid.'.jpg'; } -function show_textarea($name, $text) { - rowify(""); +function profile_thumb_url($userid) { + return URL_BASE.IMAGE_URL.$userid.'_sm.jpg'; +} + +function profile_user_thumb_url($user) { + if (!$user->has_profile) return null; + $profile = BoincProfile::lookup("userid=$user->id"); + if (!$profile->has_picture) return null; + if (profile_screening() && $profile->verification!=1) return null; + return profile_thumb_url($user->id); } // When passed profile->verification, this function is used to tell the @@ -165,211 +79,20 @@ function offensive_profile_warning($verify_flag) { return ""; } -function show_picture_option($profile) { - row1("Picture"); - - $warning = ""; - if (profile_screening() && $profile->has_picture) { - $warning = offensive_profile_warning($profile->verification); - } - - if (($profile) && ($profile->has_picture)) { - echo " - - - -"; - rowify("
"); - end_table(); - echo ""; - } else { - rowify(" -If you would like include a picture with your profile, -click the \"Browse\" button and select a JPEG or PNG file. -Please select images of 50KB or less. -

- - "); - rowify("
"); - } -} - -function show_language_selection($profile) { - row1("Language"); - echo "

\n"; -} - -function show_submit() { - row1("Submit profile"); - echo ""; // White looks better :) - $config = get_config(); - $publickey = parse_config($config, ""); - if ($publickey) { - table_row("To protect project's webpages from spam, we ask you to type in two words shown in the image:
\n". - recaptcha_get_html($publickey)); - } - table_row("

"); -} - // If the user with id = $userid has uploaded a picture his/herself, // delete it and its thumbnail. // function delete_user_pictures($userid) { - $filename1 = IMAGE_PATH . $userid . '.jpg'; - $filename2 = IMAGE_PATH . $userid . '_sm.jpg'; - - if (file_exists($filename1)) { - unlink($filename1); + $path = profile_image_path($userid); + if (file_exists($path)) { + unlink($path); } - if (file_exists($filename2)) { - unlink($filename2); + $path = profile_thumb_path($userid); + if (file_exists($path)) { + unlink($path); } } -// $profile is null if user doesn't already have a profile. -// Don't assign to $profile->x if this is the case. -// -function process_create_profile($user, $profile) { - $response1 = $_POST['response1']; - $response2 = $_POST['response2']; - $language = $_POST['language']; - if (isset($_POST['delete_pic'])) { - $delete_pic = $_POST['delete_pic']; - } else { - $delete_pic = "off"; - } - - if (strlen($response1)==0 && - strlen($response2)==0 && - $delete_pic != "on" && - !is_uploaded_file($_FILES['picture']['tmp_name']) - ) { - error_page("Your profile submission was empty."); - exit(); - } - - if ($delete_pic == "on") { - delete_user_pictures($profile->userid); - $profile->has_picture = false; - $profile->verification = 0; - } - - $profile ? $hasPicture = $profile->has_picture: $hasPicture = false; - - if (is_uploaded_file($_FILES['picture']['tmp_name'])) { - $hasPicture = true; - if ($profile) $profile->verification = 0; - - // echo "
Name: " . $_FILES['picture']['name']; - // echo "
Type: " . $_FILES['picture']['type']; - // echo "
Size: " . $_FILES['picture']['size']; - // echo "
Temp name: " . $_FILES['picture']['tmp_name']; - - $images = getImages($_FILES['picture']['tmp_name']); - - // Write the original image file to disk. - // TODO: define a constant for image quality. - ImageJPEG($images[0], IMAGE_PATH . $user->id . '.jpg'); - ImageJPEG($images[1], IMAGE_PATH . $user->id . '_sm.jpg'); - } - $response1 = sanitize_html($response1); - $response2 = sanitize_html($response2); - if ($profile) { - $query = " response1 = '".boinc_real_escape_string($response1)."'," - ." response2 = '".boinc_real_escape_string($response2)."'," - ." language = '".boinc_real_escape_string($language)."'," - ." has_picture = '$hasPicture'," - ." verification = '$profile->verification'" - ." WHERE userid = '$user->id'"; - $result = BoincProfile::update_aux($query); - if (!$result) { - error_page("Couldn't update profile: database error"); - } - } else { - $query = 'SET ' - ." userid = '$user->id'," - ." language = '".boinc_real_escape_string($language)."'," - ." response1 = '".boinc_real_escape_string($response1)."'," - ." response2 = '".boinc_real_escape_string($response2)."'," - ." has_picture = '$hasPicture'," - ." verification=0"; - $result = BoincProfile::insert($query); - if (!$result) { - error_page("Couldn't create profile: database error"); - } - $user->update("has_profile=1"); - } - - show_result_page($user); -} - -// Returns an array containing: -// [0]: The original image refered to by $fileName if its dimensions are -// less than MAX_IMG_WIDTH x MAX_IMG_HEIGHT, or a version scaled to -// those dimensions if it was too large. -// [1]: A scaled version of the above. - -function getImages($fileName) { - $size = getImageSize($fileName); - - // Determine if the filetype uploaded is supported. - // TODO: Change these to constants. - switch($size[2]) { - case '2': // JPEG - $image = imageCreateFromJPEG($fileName); - break; - case '3': // PNG - $image = imageCreateFromPNG($fileName); - break; - default: - error_page("The format of your uploaded image is not supported by our system."); - } - - $width = $size[0]; - $height = $size[1]; - - $smallImage = scale_image($image, $width, $height, SMALL_IMG_WIDTH, SMALL_IMG_HEIGHT); - - if ($width > MAX_IMG_WIDTH || $height > MAX_IMG_HEIGHT) { - $image = scale_image($image, $width, $height, MAX_IMG_WIDTH, MAX_IMG_HEIGHT); - } - - /* - echo "

Image type: $size[2]"; - echo "
Original width: $width"; - echo "
Original height: $height"; - echo "
Scalar: $scalar"; - echo "
Dest width: " . ($width / $scalar); - echo "
Dest height: " . ($height / $scalar); - echo "
Horizontal offset: $horiz_offset"; - echo "
Vertical offset: $vert_offset"; - echo "

View result"; - */ - - return array($image, $smallImage); -} - function scale_image( $image, $origWidth, $origHeight, $targetWidth, $targetHeight ) { @@ -414,72 +137,6 @@ function scale_image( return $newImage; } -function show_result_page($user) { - page_head("Profile Saved"); - - echo " -

Congratulations!

-Your profile was successfully entered into our database.

-id>View your profile
-"; - - page_tail(); -} - -// Builds a summary table of user profiles. -// -// $members is an array of userIDs; -// $offset indicates which entry to begin the table with -// $numToDisplay indicates how many profiles to display in this table -// $cols indicates how many profile summaries should be written per row -// $descriptor is an optional file descriptor to write the table to. - -function show_user_table($members, $offset, $numToDisplay, $cols) { - echo "

userid . '.jpg' . "\">userid . '_sm.jpg' . "\"> - $warning Your profile picture is shown at left. -

-To replace it, -click the \"Browse\" button and select a JPEG or PNG file (50KB or less). -
-

-To remove it from your profile, check this box: - -

-

-

- Select the language in which your profile is written: -

- "; - if (isset($profile->language)) { - show_combo_box("language", LANGUAGE_FILE, $profile->language); - } else { - show_combo_box("language", LANGUAGE_FILE, "English"); - } - echo "

\n"; - - $rows = ceil($numToDisplay / $cols); - $count = $offset; - $numMembers = count($members); - - for ($row = 0; $row < $rows; $row++) { - if ($count >= $numMembers) { - break; - } - - echo "\n"; - - for ($col = 0; $col < $cols; $col++) { - if ($count < $numMembers) { - $profile = get_profile($members[$count]); - if (!$profile) { - $numMembers--; - continue; - } - - echo ""; - $count++; - } else { - echo ""; - } - } - echo "\n"; - } - echo "
"; - - $show_picture = $profile->has_picture; - if (profile_screening() && $profile->verification != 1) { - $show_picture = false; - } - if ($show_picture) { - echo ""; - } else { - echo " "; - } - - echo "
\n", get_profile_summary($profile), "
\n"; - -} - // Generates a string containing: // 1) the name of the user with ID == $userid, // with a link to a view of their profile @@ -507,8 +164,10 @@ function get_profile_summary($profile) { } // Displays a user's profile (if they have one); +// $screen_mode is set if we're in the administrative profile-screening page, +// in which case we show everything -function show_profile($userid, $verify_mode = FALSE) { +function show_profile($userid, $screen_mode = false) { $user = get_user_from_id($userid); if (!$user) { @@ -522,7 +181,7 @@ function show_profile($userid, $verify_mode = FALSE) { if (!$profile) { error_page("No user profile exists for that user ID."); } - if (!$verify_mode) { + if (!$screen_mode) { $logged_in_user = get_logged_in_user(false); if (!$logged_in_user || ($user->id != $logged_in_user->id)) { $caching = true; @@ -532,7 +191,7 @@ function show_profile($userid, $verify_mode = FALSE) { } $can_edit = isset($logged_in_user) && $logged_in_user && $user->id == $logged_in_user->id; - if (!$verify_mode) { + if (!$screen_mode) { page_head("Profile: ".$user->name); } @@ -542,19 +201,18 @@ function show_profile($userid, $verify_mode = FALSE) { row1("Edit your profile"); } - - // If doing screening, only show picture in certain situations + // If screening is enabled, only show picture in certain situations // $show_picture = $profile->has_picture; if (profile_screening()) { - if (!$verify_mode && !$can_edit && $profile->verification!=1) { + if (!$screen_mode && !$can_edit && $profile->verification!=1) { $show_picture = false; } } if ($show_picture) { echo " - id.".jpg\"> + id)."\"> "; } @@ -571,9 +229,9 @@ function show_profile($userid, $verify_mode = FALSE) { // Setup text output options based on logged in user forum settings // - if (!$verify_mode) { + if (!$screen_mode) { $logged_in_user = get_logged_in_user(false); - $logged_in_user = getForumPreferences($logged_in_user); + $logged_in_user = BoincForumPrefs::lookup($logged_in_user); $options = get_transform_settings_from_user($logged_in_user); } @@ -582,7 +240,7 @@ function show_profile($userid, $verify_mode = FALSE) { row1(show_profile_heading2()); row1(output_transform($profile->response2,$options), 2, "foobar"); - if (!$can_edit and !$verify_mode) { + if (!$can_edit and !$screen_mode) { row1("Your feedback on this profile"); row2( "Recommend this profile for User of the Day:", @@ -595,7 +253,7 @@ function show_profile($userid, $verify_mode = FALSE) { } end_table(); - if (!$verify_mode) { + if (!$screen_mode) { page_tail(); } else { echo "


"; diff --git a/html/inc/team.inc b/html/inc/team.inc index 02792d7275..bc86940dad 100644 --- a/html/inc/team.inc +++ b/html/inc/team.inc @@ -269,10 +269,12 @@ function new_member_list($teamid) { $new_members = array(); $yesterday = time() - 86400; $deltas = BoincTeamDelta::enum("teamid=$teamid and timestamp>$yesterday and joining=1 group by userid"); - foreach ($deltas as $delta) { - $u = BoincUser::lookup_id($delta->userid); - if ($u->teamid == $teamid) { - $new_members[] = $u; // they might have later quit + if (count($deltas)) { + foreach ($deltas as $delta) { + $u = BoincUser::lookup_id($delta->userid); + if ($u->teamid == $teamid) { + $new_members[] = $u; // they might have later quit + } } } return $new_members; diff --git a/html/inc/user.inc b/html/inc/user.inc index eb138f8f22..c2b72377ae 100644 --- a/html/inc/user.inc +++ b/html/inc/user.inc @@ -147,6 +147,25 @@ function show_user_stats_private($user) { row2(tra("Stats on your cell phone"), URL_BASE."userw.php?id=$user->id"); } +function notify_description($notify) { + switch ($notify->type) { + case NOTIFY_FRIEND_REQ: + $user = BoincUser::lookup_id($notify->opaque); + return " + opaque>Friend request from $user->name +
+ "; + break; + case NOTIFY_FRIEND_ACCEPT: + $user = BoincUser::lookup_id($notify->opaque); + return " + opaque>$user->name confirmed as friend +
+ "; + break; + } +} + // show static user info (private) // function show_user_info_private($user) { @@ -199,6 +218,25 @@ function show_user_info_private($user) { row2(tra("Private messages"), pm_notification($user).pm_email_remind($user)); + $notifies = BoincNotify::enum("userid=$user->id"); + if (count($notifies)) { + $x = ""; + foreach ($notifies as $notify) { + $x .= notify_description($notify); + } + row2("Notifications", $x); + } + + $friends = BoincFriend::enum("user_src=$user->id and reciprocated=1"); + if (count($friends)) { + $x = ""; + foreach($friends as $friend) { + $fuser = BoincUser::lookup_id($friend->user_dest); + $x .= user_links($fuser); + } + row2("Friends", $x); + } + if ($user->teamid) { $team = lookup_team($user->teamid); $x = "id\">$team->name @@ -234,6 +272,7 @@ function show_user_info_private($user) { // show summary of dynamic and static info (public) // function show_user_summary_public($user) { + global $g_logged_in_user; row2(PROJECT." member since", date_str($user->create_time)); row2("Country", $user->country); if (strlen($user->url)) { @@ -264,7 +303,23 @@ function show_user_summary_public($user) { } } - row2("Contact", "id."\">Send private message"); + $friends = BoincFriend::enum("user_src=$user->id and reciprocated=1"); + if (count($friends)) { + $x = ""; + foreach($friends as $friend) { + $fuser = BoincUser::lookup_id($friend->user_dest); + $x .= user_links($fuser); + } + row2("Friends", $x); + } + + if ($g_logged_in_user && $g_logged_in_user->id != $user->id) { + row2("Contact", "id."\">Send private message"); + $friend = BoincFriend::lookup($g_logged_in_user->id, $user->id); + if (!$friend) { + row2("Community", "id.">Add as friend"); + } + } } function show_profile_link($user) { diff --git a/html/ops/bolt_refresh.php b/html/ops/bolt_refresh.php new file mode 100644 index 0000000000..68ee078da5 --- /dev/null +++ b/html/ops/bolt_refresh.php @@ -0,0 +1,40 @@ +refresh as $r) { + echo " + } +} + +function notify_users() { + $now = time(); + $rs = BoltRefresh::enum("due_time < $now"); + $users = array(); + + foreach($rs as $r) { + $view = BoltView::lookup_id($r->view_id); + $user_id = $view->user_id; + if (!key_exists($user_id)) { + $user = BoincUser::lookup_id($user_id); + BoltUser::lookup($user); + $user->refresh = array(); + $users[$user_id] = $user; + } + $users[$user_id]->refresh[] = $r; + } + + foreach ($users as $user) { + notify_user($user); + } +} + +?> diff --git a/html/ops/db_update.php b/html/ops/db_update.php index 568a27144e..2570f504b1 100755 --- a/html/ops/db_update.php +++ b/html/ops/db_update.php @@ -509,6 +509,31 @@ function update_11_20_2007() { do_query("alter table team add fulltext index team_name(name)"); } +function update_12_18_2007() { + do_query("create table friend ( + user_src integer not null, + user_dest integer not null, + message varchar(255) not null, + create_time integer not null, + reciprocated tinyint not null + ) + "); + do_query("create table notify ( + id serial primary key, + userid integer not null, + create_time integer not null, + type integer not null, + opaque integer not null + ) + "); + do_query("alter table friend + add unique friend_u (user_src, user_dest) + "); + do_query("alter table notify + add index notify_u (userid) + "); +} + // modify the following to call the function you want. // Make sure you do all needed functions, in order. // (Look at your DB structure using "explain" queries to see diff --git a/html/user/bolt.php b/html/user/bolt.php index ba76697666..a3b968d65b 100644 --- a/html/user/bolt.php +++ b/html/user/bolt.php @@ -10,20 +10,28 @@ $user = get_logged_in_user(true); $courses = BoltCourse::enum(); start_table(); table_header( - "Course
Click to start, resume, or review - ", "Status" + "Course", "Status" ); foreach ($courses as $course) { $e = $user?BoltEnrollment::lookup($user->id, $course->id):null; - $start = date_str($e->create_time); - $ago = time_diff(time() - $e->last_view); if ($e) { - $pct = number_format($e->fraction_done*100, 0); - $status = "Started $start; last visit $ago ago; $pct% done"; + $start = date_str($e->create_time); + $view = BoltView::lookup_id($e->last_view_id); + $ago = time_diff(time() - $view->start_time); + $pct = number_format($view->fraction_done*100, 0); + $status = "Started $start +
Last visit: $ago ago +
$pct% done +
id>Resume +
id>History +
id&action=start>Restart + "; } else { - $status = "Not started"; + $status = " + id&action=start>Start + "; } - row2("id&action=start>$course->name + row2("$course->name
$course->description", $status ); diff --git a/html/user/bolt_course.php b/html/user/bolt_course.php index 547ea5906f..5501a4453d 100644 --- a/html/user/bolt_course.php +++ b/html/user/bolt_course.php @@ -9,7 +9,7 @@ function mode_name($mode) { case BOLT_MODE_LESSON: return "lesson"; case BOLT_MODE_SHOW: return "exercise"; case BOLT_MODE_ANSWER: return "exercise answer"; - default: return "unknown"; + default: return "unknown: $mode"; } } @@ -17,9 +17,10 @@ function action_name($action) { switch ($action) { case BOLT_ACTION_NONE: return "None"; case BOLT_ACTION_NEXT: return "Next"; + case BOLT_ACTION_PREV: return "Previous"; case BOLT_ACTION_SUBMIT: return "Submit"; case BOLT_ACTION_QUESTION: return "Question"; - default: return "unknown"; + default: return "unknown: $action"; } } diff --git a/html/user/bolt_sched.php b/html/user/bolt_sched.php index 2be45b51a5..3271f7d823 100644 --- a/html/user/bolt_sched.php +++ b/html/user/bolt_sched.php @@ -27,6 +27,7 @@ if (!$course) { } function update_info() { + global $user; $sex = get_int('sex'); $birth_year = get_int('birth_year'); $user->bolt->update("sex=$sex, birth_year=$birth_year"); @@ -38,31 +39,45 @@ $course_doc = require_once($course->doc_file); function finalize_view($user, $view_id, $action) { if (!$view_id) return null; $view = BoltView::lookup_id($view_id); - if ($view && $view->user_id == $user->id && !$view->end_time) { + if (!$view) { + error_page("no view"); + } + if ($view->user_id != $user->id) { + error_page("wrong user"); + } + if (!$view->end_time) { $now = time(); $view->update("end_time=$now, action=$action"); - return $view; } - return null; + return $view; } function default_mode($item) { return $item->is_exercise()?BOLT_MODE_SHOW:BOLT_MODE_LESSON; } -function create_view($user, $course, $iter, $mode) { +function create_view($user, $course, $iter, $mode, $prev_view_id) { $now = time(); $item = $iter->item; $state = $iter->encode_state(); - return BoltView::insert("(user_id, course_id, item_name, start_time, mode, state, fraction_done) values ($user->id, $course->id, '$item->name', $now, $mode, '$state', $iter->frac_done)"); + return BoltView::insert("(user_id, course_id, item_name, start_time, mode, state, fraction_done, prev_view_id) values ($user->id, $course->id, '$item->name', $now, $mode, '$state', $iter->frac_done, $prev_view_id)"); } -function show_item($iter, $user, $course, $view_id, $mode) { +function show_item($iter, $user, $course, $view_id, $prev_view_id, $mode) { global $bolt_ex_mode; global $bolt_ex_index; + global $bolt_ex_score; $item = $iter->item; - if ($item->is_exercise()) { + page_head(null); + if ($item) { + $title = $item->title; + } else { + $title = "Course completed"; + } + if (function_exists('bolt_header')) bolt_header($title); + + if ($item && $item->is_exercise()) { $bolt_ex_mode = $mode; $bolt_ex_index = 0; switch ($mode) { @@ -75,22 +90,48 @@ function show_item($iter, $user, $course, $view_id, $mode) { "; srand($view_id); require($item->filename); - echo "

"; + if (function_exists('bolt_divide')) bolt_divide(); + $next = ""; break; case BOLT_MODE_ANSWER: require($item->filename); - echo "

id&action=next&view_id=$view_id>Next"; + if (function_exists('bolt_divide')) bolt_divide(); + $next = "id&action=next&view_id=$view_id>Next >>"; + $score_pct = number_format($bolt_ex_score*100); + echo "Score: $score_pct%"; break; } - } else { + } else if ($item) { require_once($item->filename); - echo "

id&action=next&view_id=$view_id>Next"; + if (function_exists('bolt_divide')) bolt_divide(); + $next = "id&action=next&view_id=$view_id>Next >>"; + } else { + echo "Congratulation - you have completed this course."; + $next = ""; } - echo "

Fraction done: $iter->frac_done -

id>Course history -

id&action=prev&view_id=$view_id>Prev + if ($prev_view_id) { + $prev = "id&action=prev&view_id=$view_id><< Prev"; + } else { + $prev = ""; + } + + echo " +

+

+ + + + +
$prevUp$next
+
"; + if (function_exists('bolt_footer')) bolt_footer(); + + $e = new BoltEnrollment(); + $e->user_id = $user->id; + $e->course_id = $course->id; + $e->update("last_view_id=$view_id"); } function start_course($user, $course, $course_doc) { @@ -100,9 +141,9 @@ function start_course($user, $course, $course_doc) { $now = time(); $mode = default_mode($iter->item); - $view_id = create_view($user, $course, $iter, $mode); + $view_id = create_view($user, $course, $iter, $mode, 0); BoltEnrollment::insert("(create_time, user_id, course_id, last_view_id) values ($now, $user->id, $course->id, $view_id)"); - show_item($iter, $user, $course, $view_id, $mode); + show_item($iter, $user, $course, $view_id, 0, $mode); } $e = BoltEnrollment::lookup($user->id, $course_id); @@ -132,7 +173,20 @@ case 'update_info': update_info(); start_course($user, $course, $course_doc); case 'prev': - $view = finalize_view($user, $view_id, BOLT_ACTION_NEXT); + $view = finalize_view($user, $view_id, BOLT_ACTION_PREV); + if ($view->prev_view_id) { + $view = BoltView::lookup_id($view->prev_view_id); + $iter = new BoltIter($course_doc); + $iter->decode_state($view->state); + $iter->at(); + $mode = default_mode($iter->item); + $view_id = create_view( + $user, $course, $iter, $mode, $view->prev_view_id + ); + show_item($iter, $user, $course, $view_id, $view->prev_view_id, $mode); + } else { + error_page("At start of course"); + } break; case 'next': // "next" button in lesson or exercise answer page $view = finalize_view($user, $view_id, BOLT_ACTION_NEXT); @@ -141,16 +195,14 @@ case 'next': // "next" button in lesson or exercise answer page $iter->decode_state($view->state); $iter->next(); - if (!$iter->item) { - page_head("Done with course"); - echo "All done!"; - page_tail(); - exit(); + if ($iter->item) { + $state = $iter->encode_state(); + $mode = default_mode($iter->item); + $view_id = create_view($user, $course, $iter, $mode, $view->id); + show_item($iter, $user, $course, $view_id, $view->id, $mode); + } else { + show_item($iter, $user, $course, 0, $view->id, 0); } - $state = $iter->encode_state(); - $mode = default_mode($iter->item); - $view_id = create_view($user, $course, $iter, $mode); - show_item($iter, $user, $course, $view_id, $mode); break; case 'answer': // submit answer in exercise $view = finalize_view($user, $view_id, BOLT_ACTION_SUBMIT); @@ -175,22 +227,29 @@ case 'answer': // submit answer in exercise ob_end_clean(); $bolt_ex_response = BoltDb::escape_string($bolt_ex_response); + $bolt_ex_score /= $bolt_ex_index; + $result_id = BoltResult::insert( "(view_id, score, response) values ($view->id, $bolt_ex_score, '$bolt_ex_response')" ); $view->update("result_id=$result_id"); srand($view_id); - $view_id = create_view($user, $course, $iter, BOLT_MODE_ANSWER); - show_item($iter, $user, $course, $view_id, BOLT_MODE_ANSWER); + $view_id = create_view($user, $course, $iter, BOLT_MODE_ANSWER, $view->id); + show_item($iter, $user, $course, $view_id, $view->id, BOLT_MODE_ANSWER); break; default: + $view = $e?BoltView::lookup_id($e->last_view_id):null; + if (!$view) { + start_course($user, $course, $course_doc); + break; + } $iter = new BoltIter($course_doc); $iter->decode_state($view->state); $iter->at(); $mode = default_mode($iter->item); - $view_id = create_view($user, $course, $iter, $mode); - show_item($iter, $user, $course, $view_id, $mode); + $view_id = create_view($user, $course, $iter, $mode, $view->id); + show_item($iter, $user, $course, $view_id, $view->id, $mode); break; } diff --git a/html/user/create_profile.php b/html/user/create_profile.php index f28d6470ef..1f42632379 100644 --- a/html/user/create_profile.php +++ b/html/user/create_profile.php @@ -1,7 +1,320 @@ "; + exit(); + } + echo "\n"; + fclose($file); +} + + +function show_picture_option($profile) { + row1("Picture"); + + $warning = ""; + if (profile_screening() && $profile->has_picture) { + $warning = offensive_profile_warning($profile->verification); + } + + if (($profile) && ($profile->has_picture)) { + echo " + + + +"; + rowify("
"); + end_table(); + echo ""; + } else { + rowify(" +If you would like include a picture with your profile, +click the \"Browse\" button and select a JPEG or PNG file. +Please select images of 50KB or less. +

+ + "); + rowify("
"); + } +} + +function show_language_selection($profile) { + row1("Language"); + echo "

\n"; +} + +function show_submit() { + row1("Submit profile"); + echo ""; // White looks better :) + $config = get_config(); + $publickey = parse_config($config, ""); + if ($publickey) { + table_row("To protect project's webpages from spam, we ask you to type in two words shown in the image:
\n". + recaptcha_get_html($publickey)); + } + table_row("

"); +} + +// Returns an array containing: +// [0]: The original image refered to by $fileName if its dimensions are +// less than MAX_IMG_WIDTH x MAX_IMG_HEIGHT, or a version scaled to +// those dimensions if it was too large. +// [1]: A scaled version of the above. + +function getImages($fileName) { + $size = getImageSize($fileName); + + // Determine if the filetype uploaded is supported. + // TODO: Change these to constants. + switch($size[2]) { + case '2': // JPEG + $image = imageCreateFromJPEG($fileName); + break; + case '3': // PNG + $image = imageCreateFromPNG($fileName); + break; + default: + error_page("The format of your uploaded image is not supported."); + } + + $width = $size[0]; + $height = $size[1]; + + $smallImage = scale_image($image, $width, $height, SMALL_IMG_WIDTH, SMALL_IMG_HEIGHT); + + if ($width > MAX_IMG_WIDTH || $height > MAX_IMG_HEIGHT) { + $image = scale_image($image, $width, $height, MAX_IMG_WIDTH, MAX_IMG_HEIGHT); + } + + /* + echo "

Image type: $size[2]"; + echo "
Original width: $width"; + echo "
Original height: $height"; + echo "
Scalar: $scalar"; + echo "
Dest width: " . ($width / $scalar); + echo "
Dest height: " . ($height / $scalar); + echo "
Horizontal offset: $horiz_offset"; + echo "
Vertical offset: $vert_offset"; + echo "

View result"; + */ + + return array($image, $smallImage); +} + +function show_description() { + echo " +

+ Your profile lets you share your opinions and background + with the ".PROJECT." community. +

+ "; +} + +function show_questions($profile) { + $response1 = ""; + $response2 = ""; + if (isset($profile->response1)) { + $response1 = stripslashes($profile->response1); + } + if (isset($profile->response2)) { + $response2 = stripslashes($profile->response2); + } + + row1(show_profile_heading1()); + rowify(show_profile_question1().html_info()); + rowify("
"); + show_textarea("response1", $response1); + rowify("
"); + row1( show_profile_heading2()); + rowify( show_profile_question2().html_info()); + rowify("
"); + show_textarea("response2", $response2); + rowify("
"); + show_language_selection($profile); + rowify("
"); +} + +function show_textarea($name, $text) { + rowify(""); +} + +// $profile is null if user doesn't already have a profile. +// Don't assign to $profile->x if this is the case. +// +function process_create_profile($user, $profile) { + $response1 = $_POST['response1']; + $response2 = $_POST['response2']; + $language = $_POST['language']; + if (isset($_POST['delete_pic'])) { + $delete_pic = $_POST['delete_pic']; + } else { + $delete_pic = "off"; + } + + if (strlen($response1)==0 && + strlen($response2)==0 && + $delete_pic != "on" && + !is_uploaded_file($_FILES['picture']['tmp_name']) + ) { + error_page("Your profile submission was empty."); + exit(); + } + + if ($delete_pic == "on") { + delete_user_pictures($profile->userid); + $profile->has_picture = false; + $profile->verification = 0; + } + + $profile ? $hasPicture = $profile->has_picture: $hasPicture = false; + + if (is_uploaded_file($_FILES['picture']['tmp_name'])) { + $hasPicture = true; + if ($profile) $profile->verification = 0; + + // echo "
Name: " . $_FILES['picture']['name']; + // echo "
Type: " . $_FILES['picture']['type']; + // echo "
Size: " . $_FILES['picture']['size']; + // echo "
Temp name: " . $_FILES['picture']['tmp_name']; + + $images = getImages($_FILES['picture']['tmp_name']); + + // Write the original image file to disk. + // TODO: define a constant for image quality. + ImageJPEG($images[0], IMAGE_PATH . $user->id . '.jpg'); + ImageJPEG($images[1], IMAGE_PATH . $user->id . '_sm.jpg'); + } + $response1 = sanitize_html($response1); + $response2 = sanitize_html($response2); + if ($profile) { + $query = " response1 = '".boinc_real_escape_string($response1)."'," + ." response2 = '".boinc_real_escape_string($response2)."'," + ." language = '".boinc_real_escape_string($language)."'," + ." has_picture = '$hasPicture'," + ." verification = '$profile->verification'" + ." WHERE userid = '$user->id'"; + $result = BoincProfile::update_aux($query); + if (!$result) { + error_page("Couldn't update profile: database error"); + } + } else { + $query = 'SET ' + ." userid = '$user->id'," + ." language = '".boinc_real_escape_string($language)."'," + ." response1 = '".boinc_real_escape_string($response1)."'," + ." response2 = '".boinc_real_escape_string($response2)."'," + ." has_picture = '$hasPicture'," + ." verification=0"; + $result = BoincProfile::insert($query); + if (!$result) { + error_page("Couldn't create profile: database error"); + } + $user->update("has_profile=1"); + } + + page_head("Profile saved"); + + echo " + Congratulations! + Your profile was successfully entered into our database.

+ id>View your profile
+ "; + + page_tail(); +} + +function show_profile_creation_page($user) { + $config = get_config(); + $min_credit = parse_config($config, ""); + if ($min_credit && $user->expavg_credit < $min_credit) { + error_page( + "To prevent spam, an average credit of $min_credit or greater is required to create or edit a profile. We apologize for this inconvenience." + ); + } + + // If the user already has a profile, + // fill in the fields with their current values. + // + $profile = get_profile($user->id); + if (post_str("submit", true)) { + $privatekey = parse_config($config, ""); + if ($privatekey) { + $resp = recaptcha_check_answer($privatekey, $_SERVER["REMOTE_ADDR"], + $_POST["recaptcha_challenge_field"], $_POST["recaptcha_response_field"] + ); + if (!$resp->is_valid) { + error_page("The reCAPTCHA wasn't entered correctly. Go back and try it again.
". + "(reCAPTCHA said: " . $resp->error . ")" + ); + } + } + + process_create_profile($user, $profile); + exit(); + } + + if ($profile) { + page_head("Edit your profile"); + } else { + page_head("Create a profile"); + } + + echo " +
+ "; + start_table_noborder(); + show_description(); + show_questions($profile); + show_picture_option($profile); + show_submit(); + end_table(); + echo ""; + page_tail(); +} + $user = get_logged_in_user(true); show_profile_creation_page($user); diff --git a/html/user/friend.php b/html/user/friend.php new file mode 100644 index 0000000000..eb3fecc95c --- /dev/null +++ b/html/user/friend.php @@ -0,0 +1,235 @@ +id) { + error_page("You can't be friends with yourself"); + } + $destuser = BoincUser::lookup_id($destid); + if (!$destuser) error_page("No such user"); + $friend = BoincFriend::lookup($user->id, $destid); + if ($friend) { + error_page("Friend request already exists"); + } + page_head("Add friend"); + echo " +
+ + + You have asked to add $destuser->name as a friend. + We will notify $destuser->name and will ask him/her to + confirm that you are friends. +

+ Add an optional message here: +
+ +

+ + + "; + page_tail(); +} + +// User really means it. Make DB entry and send notification +// +function handle_add_confirm($user) { + $destid = post_int('userid'); + $destuser = BoincUser::lookup_id($destid); + if (!$destuser) error_page("No such user"); + + $msg = post_str('message', true); + if ($msg) $msg = strip_tags(process_user_text($msg)); + + $now = time(); + $ret = BoincFriend::insert("(user_src, user_dest, message, create_time, reciprocated) values ($user->id, $destid, '$msg', $now, 0)"); + if (!$ret) { + error_page("database error"); + } + $ret = BoincNotify::insert("(userid, create_time, type, opaque) values ($destid, $now, ".NOTIFY_FRIEND_REQ.", $user->id)"); + if (!$ret) { + error_page("Database error"); + } + page_head("Friend request sent"); + echo "We have notified $destuser->name of your request."; + page_tail(); +} + +// Show destination user the details of request, ask if they accept +// +function handle_query($user) { + $srcid = get_int('userid'); + $srcuser = BoincUser::lookup_id($srcid); + if (!$srcuser) error_page("No such user"); + $friend = BoincFriend::lookup($srcid, $user->id); + if (!$friend) error_page("Request not found"); + page_head("Friend request"); + $x = user_links($srcuser); + echo " + $x has added you as a friend. + If $srcuser->name is in fact your friend, please click Accept. + "; + $img_url = profile_user_thumb_url($srcuser); + if ($img_url) { + echo "

\n"; + } + if (strlen($friend->message)) { + echo "

$srcuser->name says: $friend->message

"; + } + echo " +

+ Accept | + Ignore + "; + page_tail(); +} + +// Here if they accepted +// +function handle_accept($user) { + $srcid = get_int('userid'); + $srcuser = BoincUser::lookup_id($srcid); + if (!$srcuser) error_page("No such user"); + + $friend = BoincFriend::lookup($srcid, $user->id); + if (!$friend) { + error_page("No request"); + } + $friend->update("reciprocated=1"); + + // "accept message" not implemented in interface yet + $msg = post_str('message', true); + if ($msg) $msg = strip_tags(process_user_text($msg)); + $now = time(); + $ret = BoincFriend::insert("(user_src, user_dest, message, create_time, reciprocated) values ($user->id, $srcid, '$msg', $now, 1)"); + if (!$ret) { + error_page("database error"); + } + $ret = BoincNotify::insert("(userid, create_time, type, opaque) values ($srcid, $now, ".NOTIFY_FRIEND_ACCEPT.", $user->id)"); + if (!$ret) { + error_page("Database error"); + } + + $notify = BoincNotify::lookup($user->id, NOTIFY_FRIEND_REQ, $srcid); + if ($notify) { + $notify->delete(); + } else { + echo "?? notification not found"; + } + + page_head("Friendship confirmed"); + echo "Your friendship with $srcuser->name has been confirmed."; + page_tail(); +} + +// Here if they declined +// +function handle_ignore($user) { + $srcid = get_int('userid'); + $srcuser = BoincUser::lookup_id($srcid); + if (!$srcuser) error_page("No such user"); + $friend = BoincFriend::lookup($srcid, $user->id); + if (!$friend) { + error_page("No request"); + } + $notify = BoincNotify::lookup($user->id, NOTIFY_FRIEND_REQ, $srcid); + if ($notify) { + $notify->delete(); + } else { + echo "?? notification not found"; + } + page_head("Friendship declined"); + echo "You have declined friendship with $srcuser->name."; + page_tail(); +} + +// Here if initiator clicked on "confirmed" notification. +// Delete notification +// +function handle_accepted($user) { + $destid = get_int('userid'); + $destuser = BoincUser::lookup_id($destid); + if (!$destuser) error_page("No such user"); + $notify = BoincNotify::lookup($user->id, NOTIFY_FRIEND_ACCEPT, $destid); + if ($notify) { + $notify->delete(); + } else { + echo "?? notification not found"; + } + page_head("Friend confirmed"); + echo " + You are now friends with $destuser->name. + "; + page_tail(); +} + +// "home page" has Requests area +// (icon) N friend request(s) + +$user = get_logged_in_user(); + +$action = get_str('action', true); +if (!$action) $action = post_str('action'); + +switch ($action) { +case 'add': + handle_add($user); + break; +case 'add_confirm': + handle_add_confirm($user); + break; +case 'query': + handle_query($user); + break; +case 'accept': + handle_accept($user); + break; +case 'accepted': + handle_accepted($user); + break; +case 'ignore': + handle_ignore($user); + break; +default: + error_page("unknown action"); +} + +?> diff --git a/html/user/show_user.php b/html/user/show_user.php index 6eb5ff43eb..48ae342c86 100644 --- a/html/user/show_user.php +++ b/html/user/show_user.php @@ -66,7 +66,8 @@ if ($format=="xml"){ error_page("No such user found - please check the ID and try again."); } - // Output: + get_logged_in_user(true); + page_head("Account data for $user->name"); start_table(); show_user_summary_public($user); diff --git a/lib/gui_rpc_client.C b/lib/gui_rpc_client.C index fce52a3a02..199751fc91 100644 --- a/lib/gui_rpc_client.C +++ b/lib/gui_rpc_client.C @@ -220,7 +220,7 @@ int RPC_CLIENT::authorize(const char* passwd) { n = snprintf(buf, sizeof(buf), "%s%s", nonce, passwd); if (n >= (int)sizeof(buf)) return ERR_AUTHENTICATOR; md5_block((const unsigned char*)buf, (int)strlen(buf), nonce_hash); - sprintf(buf, "\n%s\n\n", nonce_hash); + sprintf(buf, "\n%s\n\n", nonce_hash); retval = rpc.do_rpc(buf); if (retval) return retval; while (rpc.fin.fgets(buf, 256)) {

userid . '.jpg' . "\">userid . '_sm.jpg' . "\"> + $warning Your profile picture is shown at left. +

+To replace it, +click the \"Browse\" button and select a JPEG or PNG file (50KB or less). +
+

+To remove it from your profile, check this box: + +

+

+

+ Select the language in which your profile is written: +

+ "; + if (isset($profile->language)) { + show_combo_box("language", LANGUAGE_FILE, $profile->language); + } else { + show_combo_box("language", LANGUAGE_FILE, "English"); + } + echo "