1<?php 2// This file is part of BOINC. 3// http://boinc.berkeley.edu 4// Copyright (C) 2015 University of California 5// 6// BOINC is free software; you can redistribute it and/or modify it 7// under the terms of the GNU Lesser General Public License 8// as published by the Free Software Foundation, 9// either version 3 of the License, or (at your option) any later version. 10// 11// BOINC is distributed in the hope that it will be useful, 12// but WITHOUT ANY WARRANTY; without even the implied warranty of 13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14// See the GNU Lesser General Public License for more details. 15// 16// You should have received a copy of the GNU Lesser General Public License 17// along with BOINC. If not, see <http://www.gnu.org/licenses/>. 18 19require_once('../inc/boinc_db.inc'); 20require_once('../inc/email.inc'); 21require_once('../inc/profile.inc'); 22 23if (!defined('UOTD_THRESHOLD')) { 24 define('UOTD_THRESHOLD', 7); 25 // email sysadmin if # of UOTD candidates falls below this 26} 27 28function uotd_thumbnail($profile, $user) { 29 if ($profile->has_picture) { 30 return "<a href=\"".url_base()."view_profile.php?userid=$user->id\"><img border=0 vspace=4 hspace=8 align=left src=\"".profile_thumb_url($user->id)."\" alt=\"".tra("User profile")."\"></a>"; 31 } else { 32 return ""; 33 } 34} 35 36// show UOTD in a small box 37// 38function show_uotd($profile) { 39 $user = BoincUser::lookup_id($profile->userid); 40 echo uotd_thumbnail($profile, $user); 41 echo user_links($user, BADGE_HEIGHT_MEDIUM)."<br>"; 42 $x = output_transform($profile->response1); 43 $x = sanitize_tags($x); 44 echo sub_sentence($x, ' ', 150, true); 45} 46 47// return the last UOTD profile, or null 48// 49function get_current_uotd() { 50 $profiles = BoincProfile::enum("uotd_time is not NULL and uotd_time>0", "ORDER BY uotd_time DESC LIMIT 1"); 51 if (sizeof($profiles)) { 52 return $profiles[0]; 53 } 54 return null; 55} 56 57// Select a (possibly new) UOTD 58// 59function select_uotd($force_new = false) { 60 echo gmdate("F d Y", time())." UTC: Starting\n"; 61 $current_uotd = get_current_uotd(); 62 if ($current_uotd && !$force_new) { 63 $assigned = getdate($current_uotd->uotd_time); 64 $now = getdate(time()); 65 if ($assigned['mday'] == $now['mday']) { 66 $user = BoincUser::lookup_id($current_uotd->userid); 67 echo "Already have UOTD for today\n"; 68 generate_uotd_gadget($current_uotd, $user); 69 exit(); 70 } 71 } 72 if ($force_new) { 73 echo "Forcing new UOTD\n"; 74 } 75 76 // get a list of profiles that have been 'approved' for UOTD, 77 // using a project-specific query if supplied in project.inc 78 // 79 if (function_exists('uotd_candidates_query')) { 80 $query = uotd_candidates_query(); 81 echo "using project supplied candidates_query\n"; 82 } else { 83 $query = default_uotd_candidates_query(); 84 echo "using default candidates_query\n"; 85 } 86 $db = BoincDb::get(); 87 $result = $db->do_query($query); 88 89 // If the number of approved profiles dips below a threshold, 90 // email the sys admin every time we pick a new one. 91 // 92 if (defined('UOTD_ADMIN_EMAIL') 93 && $result 94 && $result->num_rows < UOTD_THRESHOLD 95 ) { 96 echo "approved candidates for UOTD under UOTD_THRESHOLD\n"; 97 $u = new BoincUser; 98 $u->email_addr = UOTD_ADMIN_EMAIL; 99 $u->name = "UOTD admin"; 100 send_email($u, 101 PROJECT . ": User of the Day pool is running low!", 102 "The pool of approved candidates for User of the Day has". 103 " reached your assigned threshold: there are now only " . $result->num_rows . " approved users.\n\n". 104 "To approve more candidates for User of the Day,". 105 " go to the " . PROJECT . " administration page and click \"Screen user profiles\"" 106 ); 107 } 108 109 if ($result && $result->num_rows == 0) { 110 echo "no new verified profile found, selecting old UOTD that was shown least recently\n"; 111 $result->free(); 112 // If all verified profiles have been selected as UOTD, 113 // reshow a random one of the 100 least recently shown profiles. 114 // 115 $inner = "SELECT profile.userid FROM profile,user WHERE profile.userid=user.id AND verification=1 AND uotd_time>0 ORDER BY uotd_time ASC LIMIT 100"; 116 $result = $db->do_query("SELECT * from ($inner) as t ORDER BY RAND() LIMIT 1"); 117 } 118 119 if (!$result || $result->num_rows == 0) { 120 // No valid users of the day - do something. 121 echo "No screened users found\n"; 122 exit(); 123 } 124 $candidate = $result->fetch_object(); 125 $result->free(); 126 127 // depending on the candidates query the profile must not exist 128 // 129 $profile = BoincProfile::lookup_userid($candidate->userid); 130 if (!$profile) { 131 echo "Could not find profile returned from candidates query.\n"; 132 exit(); 133 } 134 135 // "orphaned" profiles can only be detected if the candidate query doesn't join profile and user table 136 // if this happens, delete the profile and try again 137 // 138 $user = BoincUser::lookup_id($candidate->userid); 139 if (!$user) { 140 echo "Profile for user $candidate->userid is orphaned and will be deleted\n"; 141 $profile->delete(); 142 select_uotd($force_new); 143 exit(); 144 } 145 146 $profile->uotd_time = time(); 147 $profile->update("uotd_time = ".time()); 148 149 send_email($user, 150 "You're the " . PROJECT . " user of the day!", 151 "Congratulations!\n\nYou've been chosen as the " 152 . PROJECT . " user of the day! 153 Your profile will be featured on the " . PROJECT . " website for the next 24 hours." 154 ); 155 echo "Chose user $user->id as UOTD\n"; 156 157 generate_uotd_gadget($profile, $user); 158} 159 160// This query defines the set of users eligible to be UOTD. 161// To override this with your own policy, create a similar function in 162// your own project.inc called uotd_candidates_query() 163// 164function default_uotd_candidates_query(){ 165 $query = "SELECT * FROM profile,user WHERE profile.userid=user.id "; 166 $query .= " AND verification=1 "; 167 $query .= " AND expavg_credit>1 "; 168 $query .= " AND (uotd_time IS NULL or uotd_time=0) "; 169 $query .= "ORDER BY RAND()"; 170 return $query; 171} 172 173// get a list of profiles that have been 'approved' for UOTD, 174// using a project-specific query if supplied in project.inc 175// 176function count_uotd_candidates(){ 177 $n = -1; // negative value returned on error 178 if (function_exists('uotd_candidates_query')) { 179 $query = uotd_candidates_query(); 180 } else { 181 $query = default_uotd_candidates_query(); 182 } 183 184 $db = BoincDb::get(); 185 $result = $db->do_query($query); 186 if($result) { 187 $n = $result->num_rows; 188 } 189 $result->free(); 190 191 return $n; 192} 193 194// iGoogle gadget - generate the gadget content page 195// 196function generate_uotd_gadget($profile, $user) { 197 $x = "<font size='2'>\n"; 198 $gadget = PROFILE_PATH."uotd_gadget.html"; 199 if( $h = fopen($gadget, "w") ){ 200 $age = time()-$profile->uotd_time; 201 echo "age: $age"; 202 if($age <= 86400+3600) { // allow for slop 203 $x .= uotd_thumbnail($profile, $user); 204 $x .= user_links($user, BADGE_HEIGHT_MEDIUM); 205 $resp = sanitize_tags(output_transform($profile->response1)); 206 $x .= " ". sub_sentence($resp, ' ', 250, true); 207 } 208 else { 209 $x .= "<font color='fuscia'> 210 There is no User of the Day today. 211 Only volunteers who have created a Profile 212 (with a picture), and have recent credit, 213 are eligible to be chosen as User of the Day. 214 We have run out of these, so there isn't a 215 User of the Day. 216 </font>"; 217 } 218 $x .= "\n</font>\n"; 219 fwrite($h, $x); 220 fclose($h); 221 } 222} 223 224?> 225