Jack Phoenix has submitted this change and it was merged. Change subject: SVN exported r2730 from ShoutWiki SVN, /branches/jack/Challenge ......................................................................
SVN exported r2730 from ShoutWiki SVN, /branches/jack/Challenge This extension is still very much under development and not yet suitable for a production wiki. See the extension's MediaWiki.org info page for more details. Change-Id: I87ae4147794558be54c570fd0b7f40c5faf1ae03 --- A Challenge.alias.php A Challenge.class.php A Challenge.php A ChallengeAction.php A ChallengeHistory.php A ChallengeStandings.php A ChallengeUser.php A ChallengeView.php A challenge.sql A i18n/en.json A i18n/fi.json A resources/css/ext.challenge.history.css A resources/css/ext.challenge.standings.css A resources/css/ext.challenge.user.css A resources/css/ext.challenge.view.css A resources/images/userpageIcon.png A resources/js/Challenge.js A resources/js/DatePicker.js A resources/js/ValidateDate.js A templates/challengeuser.tmpl.php A templates/challengeview.tmpl.php 21 files changed, 2,538 insertions(+), 0 deletions(-) Approvals: Jack Phoenix: Verified; Looks good to me, approved diff --git a/Challenge.alias.php b/Challenge.alias.php new file mode 100644 index 0000000..a93424a --- /dev/null +++ b/Challenge.alias.php @@ -0,0 +1,27 @@ +<?php +/** + * Special page aliases for the Challenge extension. + * + * @file + * @ingroup Extensions + */ + +$aliases = array(); + +/** English */ +$aliases['en'] = array( + 'ChallengeAction' => array( 'ChallengeAction' ), + 'ChallengeHistory' => array( 'ChallengeHistory' ), + 'ChallengeStandings' => array( 'ChallengeStandings' ), + 'ChallengeUser' => array( 'ChallengeUser' ), + 'ChallengeView' => array( 'ChallengeView' ), +); + +/** Finnish (Suomi) */ +$aliases['fi'] = array( + 'ChallengeAction' => array( 'Haastetoiminto' ), + 'ChallengeHistory' => array( 'Haastehistoria' ), + 'ChallengeStandings' => array( 'Haastetilastot' ), + 'ChallengeUser' => array( 'Haasta_käyttäjä' ), + 'ChallengeView' => array( 'Tarkastele_haastetta' ), +); \ No newline at end of file diff --git a/Challenge.class.php b/Challenge.class.php new file mode 100644 index 0000000..e580f86 --- /dev/null +++ b/Challenge.class.php @@ -0,0 +1,576 @@ +<?php +/** + * @file + */ +class Challenge { + + public $rating_names = array( + 1 => 'positive', + -1 => 'negative', + 0 => 'neutral' + ); + + /** + * Quickie wrapper function for sending out an email as properly rendered + * HTML instead of plaintext. + * + * The functions in this class that call this function used to use + * User::sendMail(), but it was causing the mentioned bug, hence why this + * function had to be introduced. + * + * @see https://bugzilla.wikimedia.org/show_bug.cgi?id=68045 + * @see https://gerrit.wikimedia.org/r/#/c/146514/ + * + * @param User $string User (object) whom to send an email + * @param string $subject Email subject + * @param $string $body Email contents (HTML) + * @return Status object + */ + public function sendMail( $user, $subject, $body ) { + global $wgPasswordSender; + $sender = new MailAddress( $wgPasswordSender, + wfMessage( 'emailsender' )->inContentLanguage()->text() ); + $to = new MailAddress( $user ); + return UserMailer::send( $to, $sender, $subject, $body, null, 'text/html; charset=UTF-8' ); + } + + /** + * Add a challenge to the database and send a challenge request mail to the + * challenged user. + * + * @param string $user_to Name of the person who was challenged + * @param $info + * @param $event_date + * @param string $description User-supplied description of the challenge + * @param string $win_terms User-supplied win terms + * @param string $lose_terms User-supplied lose terms + */ + public function addChallenge( $user_to, $info, $event_date, $description, $win_terms, $lose_terms ) { + global $wgUser; + + $user_id_to = User::idFromName( $user_to ); + + $dbw = wfGetDB( DB_MASTER ); + $dbw->insert( + 'challenge', + array( + 'challenge_user_id_1' => $wgUser->getId(), + 'challenge_username1' => $wgUser->getName(), + 'challenge_user_id_2' => $user_id_to, + 'challenge_username2' => $user_to, + 'challenge_info' => $info, + 'challenge_description' => $description, + 'challenge_win_terms' => $win_terms, + 'challenge_lose_terms' => $lose_terms, + 'challenge_status' => 0, + 'challenge_date' => $dbw->timestamp(), + 'challenge_event_date' => $event_date + ), + __METHOD__ + ); + + $this->challenge_id = $dbw->insertId(); + $this->sendChallengeRequestEmail( $user_id_to, $wgUser->getName(), $this->challenge_id ); + } + + public function sendChallengeRequestEmail( $user_id_to, $user_from, $id ) { + $user = User::newFromId( $user_id_to ); + $user->loadFromDatabase(); + + if ( $user->isEmailConfirmed() && $user->getIntOption( 'notifychallenge', 1 ) ) { + $challenge_view_title = SpecialPage::getTitleFor( 'ChallengeView' ); + $update_profile_link = SpecialPage::getTitleFor( 'UpdateProfile' ); + $subject = wfMessage( 'challenge_request_subject', $user_from )->text(); + $body = wfMessage( + 'challenge_request_body', + $user->getName(), + $user_from, + $challenge_view_title->getFullURL( array( 'id' => $id ) ), + $update_profile_link->getFullURL() + )->text(); + $this->sendMail( $user, $subject, $body ); + } + } + + public function sendChallengeAcceptEmail( $user_id_to, $user_from, $id ) { + $user = User::newFromId( $user_id_to ); + $user->loadFromDatabase(); + + if ( $user->isEmailConfirmed() && $user->getIntOption( 'notifychallenge', 1 ) ) { + $challenge_view_title = SpecialPage::getTitleFor( 'ChallengeView' ); + $update_profile_link = SpecialPage::getTitleFor( 'UpdateProfile' ); + $subject = wfMessage( 'challenge_accept_subject', $user_from )->text(); + $body = wfMessage( + 'challenge_accept_body', + $user->getName(), + $user_from, + $challenge_view_title->getFullURL( array( 'id' => $id ) ), + $update_profile_link->getFullURL() + )->text(); + $this->sendMail( $user, $subject, $body ); + } + } + + public function sendChallengeLoseEmail( $user_id_to, $user_from, $id ) { + $user = User::newFromId( $user_id_to ); + $user->loadFromDatabase(); + + if ( $user->isEmailConfirmed() && $user->getIntOption( 'notifychallenge', 1 ) ) { + $challenge_view_title = SpecialPage::getTitleFor( 'ChallengeView' ); + $update_profile_link = SpecialPage::getTitleFor( 'UpdateProfile' ); + $subject = wfMessage( + 'challenge_lose_subject', + $user_from, + $id + )->parse(); + $body = wfMessage( + 'challenge_lose_body', + $user->getName(), + $user_from, + $challenge_view_title->getFullURL( array( 'id' => $id ) ), + $update_profile_link->getFullURL() + )->text(); + $this->sendMail( $user, $subject, $body ); + } + } + + public function sendChallengeWinEmail( $user_id_to, $user_from, $id ) { + $user = User::newFromId( $user_id_to ); + $user->loadFromDatabase(); + if ( $user->isEmailConfirmed() && $user->getIntOption( 'notifychallenge', 1 ) ) { + $challenge_view_title = SpecialPage::getTitleFor( 'ChallengeView' ); + $update_profile_link = SpecialPage::getTitleFor( 'UpdateProfile' ); + $subject = wfMessage( 'challenge_win_subject', $user_from, $id )->parse(); + $body = wfMessage( + 'challenge_win_body', + $user->getName(), + $user_from, + $challenge_view_title->getFullURL( array( 'id' => $id ) ), + $update_profile_link->getFullURL() + )->text(); + $this->sendMail( $user, $subject, $body ); + } + } + + /** + * Update the status of the given challenge (via its ID) to $status. + * + * @param int $challenge_id Challenge identifier + * @param int $status Status code + * @param bool $email Send emails to challenge participants (if they have confirmed their addresses)? + */ + public function updateChallengeStatus( $challenge_id, $status, $email = true ) { + $dbw = wfGetDB( DB_MASTER ); + $dbw->update( + 'challenge', + array( 'challenge_status' => $status ), + array( 'challenge_id' => $challenge_id ), + __METHOD__ + ); + $c = $this->getChallenge( $challenge_id ); + + switch ( $status ) { + case 1: // challenge was accepted + // Update social stats for both users involved in challenge + $stats = new UserStatsTrack( 1, $c['user_id_1'], $c['user_name_1'] ); + $stats->incStatField( 'challenges' ); + + $stats = new UserStatsTrack( 1, $c['user_id_2'], $c['user_name_2'] ); + $stats->incStatField( 'challenges' ); + + if ( $email ) { + $this->sendChallengeAcceptEmail( $c['user_id_1'], $c['user_name_2'], $challenge_id ); + } + + break; + case 3: // challenge was completed, send email to loser + $stats = new UserStatsTrack( 1, $c['winner_user_id'], $c['winner_user_name'] ); + $stats->incStatField( 'challenges_won' ); + + $this->updateUserStandings( $challenge_id ); + if ( $c['winner_user_id'] == $c['user_id_1'] ) { + $loser_id = $c['user_id_2']; + $loser_name = $c['user_name_2']; + } else { + $loser_id = $c['user_id_1']; + $loser_name = $c['user_name_1']; + } + + if ( $email ) { + $this->sendChallengeLoseEmail( $loser_id, $c['winner_user_name'], $challenge_id ); + $this->sendChallengeWinEmail( $c['winner_user_id'], $loser_name, $challenge_id ); + } + break; + } + } + + /** + * Update challenge standings for both participants. + * + * @param int $id Challenge identifier + */ + public function updateUserStandings( $id ) { + $dbr = wfGetDB( DB_MASTER ); + $s = $dbr->selectRow( + 'challenge', + array( + 'challenge_user_id_1', 'challenge_username1', 'challenge_user_id_2', + 'challenge_username2', 'challenge_info', 'challenge_event_date', + 'challenge_description', 'challenge_win_terms', + 'challenge_lose_terms', 'challenge_winner_user_id', + 'challenge_winner_username', 'challenge_status' + ), + array( 'challenge_id' => $id ), + __METHOD__ + ); + + if ( $s !== false ) { + if ( $s->challenge_winner_user_id != -1 ) { // if it's not a tie + if ( $s->challenge_user_id_1 == $s->challenge_winner_user_id ) { + $winner_id = $s->challenge_user_id_1; + $loser_id = $s->challenge_user_id_2; + } else { + $winner_id = $s->challenge_user_id_2; + $loser_id = $s->challenge_user_id_1; + } + $this->updateUserRecord( $winner_id, 1 ); + $this->updateUserRecord( $loser_id, -1 ); + } else { + $this->updateUserRecord( $s->challenge_user_id_1, 0 ); + $this->updateUserRecord( $s->challenge_user_id_2, 0 ); + } + } + } + + public function updateChallengeWinner( $id, $user_id ) { + $user = User::newFromId( $user_id ); + $user_name = $user->getName(); + $dbr = wfGetDB( DB_MASTER ); + $dbr->update( + 'challenge', + array( + 'challenge_winner_user_id' => $user_id, + 'challenge_winner_username' => $user_name + ), + array( 'challenge_id' => $id ), + __METHOD__ + ); + } + + public function updateUserRecord( $id, $type ) { + $user = User::newFromId( $id ); + $username = $user->getName(); + + $dbr = wfGetDB( DB_SLAVE ); + $dbw = wfGetDB( DB_MASTER ); + $wins = 0; + $losses = 0; + $ties = 0; + + $res = $dbr->select( + 'challenge_user_record', + array( 'challenge_wins', 'challenge_losses', 'challenge_ties' ), + array( 'challenge_record_user_id' => $id ), + __METHOD__, + array( 'LIMIT' => 1 ) + ); + $row = $dbr->fetchObject( $res ); + if ( !$row ) { + switch ( $type ) { + case -1: + $losses = 1; + break; + case 0: + $ties = 1; + break; + case 1: + $wins = 1; + break; + } + $dbw->insert( + 'challenge_user_record', + array( + 'challenge_record_user_id' => $id, + 'challenge_record_username' => $username, + 'challenge_wins' => $wins, + 'challenge_losses' => $losses, + 'challenge_ties' => $ties + ), + __METHOD__ + ); + } else { + $wins = $row->challenge_wins; + $losses = $row->challenge_losses; + $ties = $row->challenge_ties; + switch ( $type ) { + case -1: + $losses++; + break; + case 0: + $ties++; + break; + case 1: + $wins++; + break; + } + $dbw->update( + 'challenge_user_record', + array( + 'challenge_wins' => $wins, + 'challenge_losses' => $losses, + 'challenge_ties' => $ties + ), + array( 'challenge_record_user_id' => $id ), + __METHOD__ + ); + } + } + + /** + * Is the supplied user (ID) a participant in the challenge, identified by + * its ID? + * + * @param int $userId User ID + * @param int $challengeId Challenge ID + * @return bool + */ + public function isUserInChallenge( $userId, $challengeId ) { + $dbr = wfGetDB( DB_MASTER ); + $s = $dbr->selectRow( + 'challenge', + array( 'challlenge_user_id_1', 'challlenge_user_id_2' ), + array( 'challenge_id' => $challengeId ), + __METHOD__ + ); + if ( $s !== false ) { + if ( $userId == $s->challlenge_user_id_1 || $userId == $s->challlenge_user_id_2 ) { + return true; + } + } + return false; + } + + /** + * Get the amount of open challenges for the given user (ID). + * + * @param int $userId User ID + * @return int Challenge count for the given user (ID) + */ + static function getOpenChallengeCount( $userId ) { + $dbr = wfGetDB( DB_MASTER ); + $openChallengeCount = 0; + $s = $dbr->selectRow( + 'challenge', + array( 'COUNT(*) AS count' ), + array( 'challenge_user_id_2' => $userId, 'challenge_status' => 0 ), + __METHOD__ + ); + if ( $s !== false ) { + $openChallengeCount = $s->count; + } + return $openChallengeCount; + } + + /** + * Get the amount of total challenges for the given user (ID). + * + * @param int $userId User ID + * @return int Challenge count for the given user (ID) + */ + static function getChallengeCount( $userId = 0 ) { + $dbr = wfGetDB( DB_SLAVE ); + $challengeCount = 0; + + $userSQL = array(); + if ( $userId ) { + $userSQL = array( 'challenge_user_id_1' => $userId ); + } + + $s = $dbr->selectRow( + 'challenge', + array( 'COUNT(*) AS count' ), + $userSQL, + __METHOD__ + ); + + if ( $s !== false ) { + $challengeCount = $s->count; + } + + return $challengeCount; + } + + /** + * Fetch everything we know about a challenge from the database when given + * a challenge identifier. + * + * @param int $id Challenge identifier + * @return array + */ + public function getChallenge( $id ) { + $id = (int) $id; // paranoia! + $dbr = wfGetDB( DB_MASTER ); + $sql = "SELECT {$dbr->tableName( 'challenge' )}.challenge_id AS id, challenge_user_id_1, challenge_username1, challenge_user_id_2, challenge_username2, challenge_info, challenge_description, challenge_event_date, challenge_status, challenge_winner_username, challenge_winner_user_id, + challenge_win_terms, challenge_lose_terms, challenge_rate_score, challenge_rate_comment + FROM {$dbr->tableName( 'challenge' )} LEFT JOIN {$dbr->tableName( 'challenge_rate' )} + ON {$dbr->tableName( 'challenge_rate' )}.challenge_id = {$dbr->tableName( 'challenge' )}.challenge_id + WHERE {$dbr->tableName( 'challenge' )}.challenge_id = {$id}"; + $res = $dbr->query( $sql, __METHOD__ ); + + $challenge = array(); + foreach ( $res as $row ) { + $challenge[] = array( + 'id' => $row->id, + 'status' => $row->challenge_status, + 'user_id_1' => $row->challenge_user_id_1, + 'user_name_1' => $row->challenge_username1, + 'user_id_2' => $row->challenge_user_id_2, + 'user_name_2' => $row->challenge_username2, + 'info' => $row->challenge_info, + 'description' => $row->challenge_description, + 'date' => $row->challenge_event_date, + 'win_terms' => $row->challenge_win_terms, + 'lose_terms' => $row->challenge_lose_terms, + 'winner_user_id' => $row->challenge_winner_user_id, + 'winner_user_name' => $row->challenge_winner_username, + 'rating' => $row->challenge_rate_score, + 'rating_comment' => $row->challenge_rate_comment + ); + } + + return $challenge[0]; + } + + /** + * Get the list of challenges that match the given conditions for a given + * user (via their user name). + * + * @param string $user_name User name + * @param int $status Challenge status code (or null for all challenges) + * @param int $limit SQL query LIMIT, i.e. get this many results + * @param int $page SQL query OFFSET, i.e. skip this many results + * @return array + */ + public function getChallengeList( $user_name, $status = null, $limit = 0, $page = 0 ) { + $limit_sql = $status_sql = $user_sql = ''; + if ( $limit > 0 && is_int( $limit ) ) { + $limitvalue = 0; + if ( $page && is_int( $page ) ) { + $limitvalue = $page * $limit - ( $limit ); + } + $limit_sql = " LIMIT {$limitvalue},{$limit} "; + } + + if ( $status != null && is_int( $status ) ) { + $status_sql = " AND challenge_status = {$status}"; + } + if ( $user_name ) { + $user_id = User::idFromName( $user_name ); + $user_sql = " AND (challenge_user_id_1 = {$user_id} OR challenge_user_id_2 = {$user_id}) "; + } + + $dbr = wfGetDB( DB_MASTER ); + $sql = "SELECT {$dbr->tableName( 'challenge' )}.challenge_id AS id, challenge_user_id_1, challenge_username1, challenge_user_id_2, challenge_username2, challenge_info, challenge_description, challenge_event_date, challenge_status, challenge_winner_username, challenge_winner_user_id, + challenge_win_terms, challenge_lose_terms, challenge_rate_score, challenge_rate_comment + FROM {$dbr->tableName( 'challenge' )} LEFT JOIN {$dbr->tableName( 'challenge_rate' )} ON + {$dbr->tableName( 'challenge_rate' )}.challenge_id = {$dbr->tableName( 'challenge' )}.challenge_id + WHERE 1=1 + {$user_sql} + {$status_sql} + ORDER BY challenge_date DESC + {$limit_sql}"; + + $res = $dbr->query( $sql, __METHOD__ ); + + $challenges = array(); + foreach ( $res as $row ) { + $challenges[] = array( + 'id' => $row->id, + 'status' => $row->challenge_status, + 'user_id_1' => $row->challenge_user_id_1, + 'user_name_1' => $row->challenge_username1, + 'user_id_2' => $row->challenge_user_id_2, + 'user_name_2' => $row->challenge_username2, + 'info' => $row->challenge_info, + 'description' => $row->challenge_description, + 'date' => $row->challenge_event_date, + 'win_terms' => $row->challenge_win_terms, + 'lose_terms' => $row->challenge_lose_terms, + 'winner_user_id' => $row->challenge_winner_user_id, + 'winner_user_name' => $row->challenge_winner_username, + 'rating' => $row->challenge_rate_score, + 'rating_comment' => $row->challenge_rate_comment + ); + } + + return $challenges; + } + + /** + * Get the challenge record for a given user ID. + * + * @param int $userId User ID + * @return string Wins, losses and ties separated by a dash + */ + public static function getUserChallengeRecord( $userId ) { + $dbr = wfGetDB( DB_MASTER ); + $s = $dbr->selectRow( + 'challenge_user_record', + array( 'challenge_wins', 'challenge_losses', 'challenge_ties' ), + array( 'challenge_record_user_id' => $userId ), + __METHOD__ + ); + if ( $s !== false ) { + return $s->challenge_wins . '-' . $s->challenge_losses . '-' . $s->challenge_ties; + } else { + return '0-0-0'; + } + } + + /** + * @param int $rateType + * @param int $userId + * @return int + */ + public static function getUserFeedbackScoreByType( $rateType, $userId ) { + $dbr = wfGetDB( DB_MASTER ); + return (int) $dbr->selectField( + 'challenge_rate', + 'COUNT(*) AS total', + array( + 'challenge_rate_user_id' => $userId, + 'challenge_rate_score' => $rateType + ), + __METHOD__ + ); + } + + /** + * Given a numeric status code, returns the corresponding human-readable + * status name. + * + * @param int $status Challenge status code + * @return string + */ + public static function getChallengeStatusName( $status ) { + $out = ''; + switch ( $status ) { + case -1: + $out .= wfMessage( 'challenge-status-rejected' )->plain(); + break; + case -2: + $out .= wfMessage( 'challenge-status-removed' )->plain(); + break; + case 0: + $out .= wfMessage( 'challenge-status-awaiting' )->plain(); + break; + case 1: + $out .= wfMessage( 'challenge-status-in-progress' )->plain(); + break; + case 3: + $out .= wfMessage( 'challenge-status-completed' )->plain(); + break; + } + return $out; + } +} \ No newline at end of file diff --git a/Challenge.php b/Challenge.php new file mode 100644 index 0000000..212ea95 --- /dev/null +++ b/Challenge.php @@ -0,0 +1,129 @@ +<?php +/** + * Challenge extension - allows challenging other users + * + * @file + * @ingroup Extensions + * @author Aaron Wright <aaron.wright{ at }gmail{ dot }com> + * @author David Pean <david.pean{ at }gmail{ dot }com> + * @author Jack Phoenix <j...@countervandalism.net> + * @version 1.0 + * @link https://www.mediawiki.org/wiki/Extension:Challenge Documentation + * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later + */ + +// Extension credits that show up on Special:Version +$wgExtensionCredits['other'][] = array( + 'name' => 'Challenge', + 'version' => '1.0', + 'author' => array( 'Aaron Wright', 'David Pean', 'Jack Phoenix' ), + 'description' => 'Allows challenging other users', + 'url' => 'https://www.mediawiki.org/wiki/Extension:Challenge' +); + +// ResourceLoader support for MediaWiki 1.17+ +$commonCSSModuleProperties = array( + 'localBasePath' => __DIR__, + 'remoteExtPath' => 'Challenge', + 'position' => 'top' +); + +$wgResourceModules['ext.challenge.history'] = $commonCSSModuleProperties + array( + 'styles' => 'resources/css/ext.challenge.history.css' +); + +$wgResourceModules['ext.challenge.user'] = $commonCSSModuleProperties + array( + 'styles' => 'resources/css/ext.challenge.user.css' +); + +$wgResourceModules['ext.challenge.standings'] = $commonCSSModuleProperties + array( + 'styles' => 'resources/css/ext.challenge.standings.css' +); + +$wgResourceModules['ext.challenge.view'] = $commonCSSModuleProperties + array( + 'styles' => 'resources/css/ext.challenge.view.css' +); + +$wgResourceModules['ext.challenge.js.main'] = array( + 'scripts' => 'resources/js/Challenge.js', + 'messages' => array( + 'challenge-js-event-required', 'challenge-js-date-required', + 'challenge-js-description-required', 'challenge-js-win-terms-required', + 'challenge-js-lose-terms-required', 'challenge-js-challenge-removed', + 'challenge-js-accepted', 'challenge-js-rejected', 'challenge-js-countered', + 'challenge-js-winner-recorded', 'challenge-js-rating-submitted' + ), + 'localBasePath' => __DIR__, + 'remoteExtPath' => 'Challenge' +); + +$wgResourceModules['ext.challenge.js.datevalidator'] = array( + 'scripts' => 'resources/js/ValidateDate.js', + 'messages' => array( + 'challenge-js-error-date-format', 'challenge-js-error-invalid-month', + 'challenge-js-error-invalid-day', 'challenge-js-error-invalid-year', + 'challenge-js-error-invalid-date', 'challenge-js-error-future-date', + 'challenge-js-error-is-backwards' + ), + 'localBasePath' => __DIR__, + 'remoteExtPath' => 'Challenge' +); + +$wgResourceModules['ext.challenge.js.datepicker'] = array( + 'scripts' => 'resources/js/DatePicker.js', + 'localBasePath' => __DIR__, + 'remoteExtPath' => 'Challenge' +); + +// Don't leak our temporary variable into global scope +unset( $commonCSSModuleProperties ); + +// New user right, required to pick the challenge winner via Special:ChallengeView +$wgAvailableRights[] = 'challengeadmin'; +$wgGroupPermissions['sysop']['challengeadmin'] = true; + +// i18n +$wgMessagesDirs['Challenge'] = __DIR__ . '/i18n'; +$wgExtensionMessagesFiles['ChallengeAliases'] = __DIR__ . '/Challenge.alias.php'; + +// Classes to be autoloaded +$wgAutoloadClasses['Challenge'] = __DIR__ . '/Challenge.class.php'; +$wgAutoloadClasses['ChallengeAction'] = __DIR__ . '/ChallengeAction.php'; +$wgAutoloadClasses['ChallengeHistory'] = __DIR__ . '/ChallengeHistory.php'; +$wgAutoloadClasses['ChallengeStandings'] = __DIR__ . '/ChallengeStandings.php'; +$wgAutoloadClasses['ChallengeUser'] = __DIR__ . '/ChallengeUser.php'; +$wgAutoloadClasses['ChallengeView'] = __DIR__ . '/ChallengeView.php'; + +// New special pages +$wgSpecialPages['ChallengeAction'] = 'ChallengeAction'; +$wgSpecialPages['ChallengeHistory'] = 'ChallengeHistory'; +$wgSpecialPages['ChallengeStandings'] = 'ChallengeStandings'; +$wgSpecialPages['ChallengeUser'] = 'ChallengeUser'; +$wgSpecialPages['ChallengeView'] = 'ChallengeView'; + +/** + * Adds the three new required database tables into the database when the + * user runs /maintenance/update.php (the core database updater script). + * + * @param DatabaseUpdater $updater + * @return bool + */ +$wgHooks['LoadExtensionSchemaUpdates'][] = function( $updater ) { + $dir = __DIR__; + + $dbType = $updater->getDB()->getType(); + + $filename = 'challenge.sql'; + // For non-MySQL/MariaDB/SQLite DBMSes, use the appropriately named file + /* + if ( !in_array( $dbType, array( 'mysql', 'sqlite' ) ) ) { + $filename = "schema.{$dbType}.sql"; + } + */ + + $updater->addExtensionUpdate( array( 'addTable', 'challenge', "{$dir}/{$filename}", true ) ); + $updater->addExtensionUpdate( array( 'addTable', 'challenge_rate', "{$dir}/{$filename}", true ) ); + $updater->addExtensionUpdate( array( 'addTable', 'challenge_user_record', "{$dir}/{$filename}", true ) ); + + return true; +}; \ No newline at end of file diff --git a/ChallengeAction.php b/ChallengeAction.php new file mode 100644 index 0000000..8c4ffe5 --- /dev/null +++ b/ChallengeAction.php @@ -0,0 +1,68 @@ +<?php + +class ChallengeAction extends UnlistedSpecialPage { + + public function __construct() { + parent::__construct( 'ChallengeAction' ); + } + + /** + * Show the special page + * + * @param mixed $par Parameter passed to the page or null + */ + public function execute( $par ) { + $request = $this->getRequest(); + $c = new Challenge(); + + switch ( $request->getVal( 'action' ) ) { + case 1: + $c->updateChallengeStatus( + $request->getVal( 'id' ), + $request->getVal( 'status' ) + ); + break; + case 2: + //if ( $this->getUser()->isAllowed( 'challengeadmin' ) ) { + $c->updateChallengeWinner( + $request->getVal( 'id' ), + $request->getVal( 'userid' ) + ); + $c->updateChallengeStatus( $request->getVal( 'id' ), 3 ); + //} + break; + case 3: + // Update social stats for both users involved in challenge + $stats = new UserStatsTrack( + 1, + $request->getVal( 'loser_userid' ), + $request->getVal( 'loser_username' ) + ); + if ( $request->getVal( 'challenge_rate' ) == 1 ) { + $stats->incStatField( 'challenges_rating_positive' ); + } + if ( $request->getVal( 'challenge_rate' ) == -1 ) { + $stats->incStatField( 'challenges_rating_negative' ); + } + + $dbw = wfGetDB( DB_MASTER ); + $dbw->insert( + 'challenge_rate', + array( + 'challenge_id' => $request->getVal( 'id' ), + 'challenge_rate_submitter_user_id' => $this->getUser()->getId(), + 'challenge_rate_submitter_username' => $this->getUser()->getName(), + 'challenge_rate_user_id' => $request->getVal( 'loser_userid' ), + 'challenge_rate_username' => $request->getVal( 'loser_username' ), + 'challenge_rate_date' => $dbw->timestamp(), + 'challenge_rate_score' => $request->getVal( 'challenge_rate' ), + 'challenge_rate_comment' => $request->getVal( 'rate_comment' ) + ), + __METHOD__ + ); + break; + } + + $this->getOutput()->setArticleBodyOnly( true ); + } +} \ No newline at end of file diff --git a/ChallengeHistory.php b/ChallengeHistory.php new file mode 100644 index 0000000..e6e97f5 --- /dev/null +++ b/ChallengeHistory.php @@ -0,0 +1,248 @@ +<?php + +class ChallengeHistory extends SpecialPage { + + public function __construct() { + parent::__construct( 'ChallengeHistory' ); + } + + /** + * Under which header this special page is listed in Special:SpecialPages? + * + * @return string + */ + protected function getGroupName() { + return 'users'; + } + + private function displayUserHeader( $user_name, $userId ) { + //$avatar = new wAvatar( $userId, 'l' ); + $pos = Challenge::getUserFeedbackScoreByType( 1, $userId ); + $neg = Challenge::getUserFeedbackScoreByType( -1, $userId ); + $neu = Challenge::getUserFeedbackScoreByType( 0, $userId ); + $total = ( $pos + $neg + $neu ); + $percent = 0; + if ( $pos ) { + $percent = $pos / $total * 100; + } + + $out = '<b>' . $this->msg( 'challengehistory-overall' )->plain() . '</b>: (' . + Challenge::getUserChallengeRecord( $userId ) . ')<br /><br />'; + $out .= '<b>' . $this->msg( 'challengehistory-ratings-loser' )->plain() . '</b>: <br />'; + $out .= '<span class="challenge-rate-positive">' . + $this->msg( 'challengehistory-positive' )->plain() . '</span>: ' . $pos . ' (' . $percent . '%)<br />'; + $out .= '<span class="challenge-rate-negative">' . + $this->msg( 'challengehistory-negative' )->plain() . '</span>: ' . $neg . '<br />'; + $out .= '<span class="challenge-rate-neutral">' . + $this->msg( 'challengehistory-neutral' )->plain() . '</span>: ' . $neu . '<br /><br />'; + return $out; + } + + /** + * Show the special page + * + * @param mixed $par Parameter passed to the page or null + */ + public function execute( $par ) { + global $wgExtensionAssetsPath; + + $request = $this->getRequest(); + $imgPath = $wgExtensionAssetsPath . '/Challenge/resources/images/'; + $spImgPath = $wgExtensionAssetsPath . '/SocialProfile/images/'; + $u = $request->getVal( 'user', $par ); + + $this->getOutput()->addModuleStyles( 'ext.challenge.history' ); + + $out = $standings_link = ''; + if ( $u ) { + $userTitle = Title::newFromDBkey( $u ); + if ( $userTitle ) { + $userId = User::idFromName( $userTitle->getText() ); + } else { + // invalid user + // @todo FIXME: in this case, what is $userId when it gets passed + // to displayUserHeader() below? + } + + $this->getOutput()->setPageTitle( + $this->msg( 'challengehistory-users-history', + $userTitle->getText() )->parse() + ); + $out .= $this->displayUserHeader( $userTitle->getText(), $userId ); + } else { + $this->getOutput()->setPageTitle( $this->msg( 'challengehistory-recentchallenges' ) ); + $standings_link = " - <img src=\"{$imgPath}userpageIcon.png\" alt=\"\" /> "; + $standings_link .= Linker::link( + SpecialPage::getTitleFor( 'ChallengeStandings' ), + $this->msg( 'challengehistory-view-standings' )->plain() + ); + } + + $challenge_link = SpecialPage::getTitleFor( 'ChallengeUser' ); + $status = (int) $request->getVal( 'status' ); + $out .= ' + <div class="challenge-nav"> + <div class="challenge-history-filter">' . $this->msg( 'challengehistory-filter' )->plain(); + // @todo CHECKME: is this secure enough? + $sanitizedUser = htmlspecialchars( $u, ENT_QUOTES ); + $out .= "<select name=\"status-filter\" data-username=\"{$sanitizedUser}\"> + <option value='' " . ( $status == '' && strlen( $status ) == 0 ? ' selected="selected"' : '' ) . '>' . $this->msg( 'challengehistory-all' )->plain() . "</option> + <option value=0 " . ( $status == 0 && strlen( $status ) == 1 ? ' selected="selected"' : '' ) . '>' . $this->msg( 'challengehistory-awaiting' )->plain() . '</option> + <option value="1"' . ( $status == 1 ? ' selected="selected"' : '' ) . '>' . $this->msg( 'challengehistory-inprogress' )->plain() . '</option> + <option value="-1"' . ( $status == -1 ? ' selected="selected"' : '' ) . '>' . $this->msg( 'challengehistory-rejected' )->plain() . '</option> + <option value="3"' . ( $status == 3 ? ' selected="selected"' : '' ) . '>' . $this->msg( 'challengehistory-completed' )->plain() . "</option> + </select> + </div> + <div class=\"challenge-link\"> + <img src=\"{$spImgPath}challengeIcon.png\" alt=\"\" /> "; + if ( $u ) { + $msg = $this->msg( 'challengehistory-challenge-user', $userTitle->getText() )->parse(); + } else { + $msg = $this->msg( 'challengehistory-challenge-someone' )->plain(); + } + $out .= Linker::link( + $challenge_link, + $msg, + array(), + ( ( $u ) ? array( 'user' => $u ) : array() ) + ); + $out .= $this->msg( 'word-separator' )->escaped(); + $out .= "{$standings_link} + </div> + <div class=\"cleared\"></div> + </div> + + <table class=\"challenge-history-table\"> + <tr> + <td class=\"challenge-history-header\">" . $this->msg( 'challengehistory-event' )->plain() . '</td> + <td class="challenge-history-header">' . $this->msg( 'challengehistory-challenger-desc' )->plain() . '</td> + <td class="challenge-history-header">' . $this->msg( 'challengehistory-challenger' )->plain() . '</td> + <td class="challenge-history-header">' . $this->msg( 'challengehistory-target' )->plain() . '</td> + <td class="challenge-history-header">' . $this->msg( 'challengehistory-status' )->plain() . '</td> + </tr>'; + + $page = (int) $request->getVal( 'page', 1 ); + $perPage = 25; + + $c = new Challenge(); + $challengeList = $c->getChallengeList( + $u, + $status, + $perPage, + $page + ); + $totalChallenges = $c->getChallengeCount(); + + if ( $challengeList ) { + foreach ( $challengeList as $challenge ) { + // Set up avatars and wiki titles for challenge and target + $avatar1 = new wAvatar( $challenge['user_id_1'], 's' ); + $avatar2 = new wAvatar( $challenge['user_id_2'], 's' ); + + $title1 = Title::makeTitle( NS_USER, $challenge['user_name_1'] ); + $title2 = Title::makeTitle( NS_USER, $challenge['user_name_2'] ); + + // Set up titles for pages used in table + $challenge_view_title = SpecialPage::getTitleFor( 'ChallengeView' ); + $challengeViewLink = Linker::link( + $challenge_view_title, + htmlspecialchars( $challenge['info'] . ' [' . $challenge['date'] . ']' ), + array(), + array( 'id' => $challenge['id'] ) + ); + + $av1 = $avatar1->getAvatarURL( array( 'align' => 'absmiddle' ) ); // @todo FIXME: invalid HTML5 + $av2 = $avatar2->getAvatarURL( array( 'align' => 'absmiddle' ) ); // @todo FIXME: invalid HTML5 + $winnerSymbol = Html::element( + 'img', + array( + 'src' => $imgPath . 'winner-check.gif', + 'alt' => '', + 'align' => 'absmiddle' // @todo FIXME: invalid HTML5 + ) + ); + + $out .= "<tr> + <td class=\"challenge-data\">{$challengeViewLink}</td> + <td class=\"challenge-data challenge-data-description\">{$challenge['description']}</td> + <td class=\"challenge-data\">{$av1}"; + $out .= Linker::link( + $title1, + $challenge['user_name_1'] + ); + $out .= $this->msg( 'word-separator' )->escaped(); + if ( $challenge['winner_user_id'] == $challenge['user_id_1'] ) { + $out .= $winnerSymbol; + } + $out .= "</td> + <td class=\"challenge-data\">{$av2}"; + $out .= Linker::link( + $title2, + $challenge['user_name_2'] + ); + $out .= $this->msg( 'word-separator' )->escaped(); + if ( $challenge['winner_user_id'] == $challenge['user_id_2'] ) { + $out .= $winnerSymbol; + } + $out .= "</td> + <td class=\"challenge-data\">{$c->getChallengeStatusName( $challenge['status'] )}</td> + </tr>"; + } + } else { + $out .= '<tr><td class="challenge-history-empty"><br />'; + $out .= $this->msg( 'challengehistory-empty' )->parse(); + $out .= '</td></tr>'; + } + + $out .= '</table>'; + + // Build next/prev navigation + $numOfPages = $totalChallenges / $perPage; + + if ( $numOfPages > 1 && !$u ) { + $challenge_history_title = SpecialPage::getTitleFor( 'ChallengeHistory' ); + $out .= '<div class="page-nav">'; + if ( $page > 1 ) { + $out .= Linker::link( + $challenge_history_title, + $this->msg( 'challengehistory-prev' )->plain(), + array(), + array( 'user' => $user_name, 'page' => ( $page - 1 ) ) + ) . $this->msg( 'word-separator' )->escaped(); + } + + if ( ( $total % $perPage ) != 0 ) { + $numOfPages++; + } + if ( $numOfPages >= 9 ) { + $numOfPages = 9 + $page; + } + + for ( $i = 1; $i <= $numOfPages; $i++ ) { + if ( $i == $page ) { + $out .= ( $i . ' ' ); + } else { + $out .= Linker::link( + $challenge_history_title, + $i, + array(), + array( 'user' => $user_name, 'page' => $i ) + ) . $this->msg( 'word-separator' )->escaped(); + } + } + + if ( ( $total - ( $perPage * $page ) ) > 0 ) { + $out .= $this->msg( 'word-separator' )->escaped() . Linker::link( + $challenge_history_title, + $this->msg( 'challengehistory-next' )->plain(), + array(), + array( 'user' => $user_name, 'page' => ( $page + 1 ) ) + ); + } + $out .= '</div>'; + } + + $this->getOutput()->addModules( 'ext.challenge.js.main' ); + $this->getOutput()->addHTML( $out ); + } +} \ No newline at end of file diff --git a/ChallengeStandings.php b/ChallengeStandings.php new file mode 100644 index 0000000..1961b8a --- /dev/null +++ b/ChallengeStandings.php @@ -0,0 +1,84 @@ +<?php + +class ChallengeStandings extends SpecialPage { + + public function __construct() { + parent::__construct( 'ChallengeStandings' ); + } + + /** + * Under which header this special page is listed in Special:SpecialPages? + * + * @return string + */ + protected function getGroupName() { + return 'users'; + } + + /** + * Show the special page + * + * @param mixed $par Parameter passed to the page or null + */ + public function execute( $par ) { + $this->getOutput()->setPageTitle( $this->msg( 'challengestandings-title' ) ); + + $out = '<table class="challenge-standings-table"> + <tr> + <td class="challenge-standings-title">#</td> + <td class="challenge-standings-title">' . $this->msg( 'challengestandings-user' )->plain() . '</td> + <td class="challenge-standings-title">' . $this->msg( 'challengestandings-w' )->plain() . '</td> + <td class="challenge-standings-title">' . $this->msg( 'challengestandings-l' )->plain() . '</td> + <td class="challenge-standings-title">' . $this->msg( 'challengestandings-t' )->plain() . '</td> + <td class="challenge-standings-title">%</td> + <td class="challenge-standings-title"></td> + </tr>'; + + $dbr = wfGetDB( DB_SLAVE ); + $sql = "SELECT challenge_record_username, challenge_record_user_id, challenge_wins, challenge_losses, challenge_ties, (challenge_wins / (challenge_wins + challenge_losses + challenge_ties) ) AS winning_percentage FROM {$dbr->tableName( 'challenge_user_record' )} ORDER BY (challenge_wins / (challenge_wins + challenge_losses + challenge_ties) ) DESC, challenge_wins DESC LIMIT 0,25"; + $res = $dbr->query( $sql, __METHOD__ ); + $x = 1; + + foreach ( $res as $row ) { + $avatar1 = new wAvatar( $row->challenge_record_user_id, 's' ); + $out .= '<tr> + <td class="challenge-standings">' . $x . '</td> + <td class="challenge-standings">'; + $out .= $avatar1->getAvatarURL( array( 'align' => 'absmiddle' ) ); // @todo FIXME: invalid HTML5 + $out .= Linker::link( + SpecialPage::getTitleFor( 'ChallengeHistory' ), + $row->challenge_record_username, + array( 'class' => 'challenge-standings-history-link' ), + array( 'user' => $row->challenge_record_username ) + ); + $out .= $this->msg( 'word-separator' )->escaped(); + $out .= $user1Icon . '</td>'; + + $out .= '<td class="challenge-standings">' . $row->challenge_wins . '</td> + <td class="challenge-standings">' . $row->challenge_losses . '</td> + <td class="challenge-standings">' . $row->challenge_ties . '</td> + <td class="challenge-standings">' . + // @todo FIXME: not i18n-compatible, should use $this->getLanguage()->formatNum( $row->winning_percentage ) or something instead... + str_replace( '.0', '.', number_format( $row->winning_percentage, 3 ) ) . + '</td>'; + + if ( $row->challenge_record_username != $this->getUser()->getName() ) { + $out .= '<td class="challenge-standings">'; + $out .= Linker::link( + SpecialPage::getTitleFor( 'ChallengeUser' ), + $this->msg( 'challengestandings-challengeuser' )->plain(), + array( 'class' => 'challenge-standings-user-link' ), + array( 'user' => $row->challenge_record_username ) + ); + $out .= '</td>'; + } + + $out .= '</td></tr>'; + $x++; + } + + $out .= '</table>'; + + $this->getOutput()->addHTML( $out ); + } +} \ No newline at end of file diff --git a/ChallengeUser.php b/ChallengeUser.php new file mode 100644 index 0000000..fe5f9fc --- /dev/null +++ b/ChallengeUser.php @@ -0,0 +1,181 @@ +<?php + +class ChallengeUser extends SpecialPage { + + public function __construct() { + parent::__construct( 'ChallengeUser' ); + } + + /** + * Under which header this special page is listed in Special:SpecialPages? + * + * @return string + */ + protected function getGroupName() { + return 'users'; + } + + /** + * Show the special page + * + * @param mixed $par Parameter passed to the page or null + */ + public function execute( $par ) { + $output = $this->getOutput(); + $request = $this->getRequest(); + $user = $this->getUser(); + + // Add CSS & JS via ResourceLoader + $output->addModuleStyles( 'ext.challenge.user' ); + $output->addModules( array( + 'ext.challenge.js.main', + 'ext.challenge.js.datepicker' + ) ); + + $userTitle = Title::newFromDBkey( $request->getVal( 'user', $par ) ); + if ( !$userTitle ) { + $output->addHTML( $this->displayFormNoUser() ); + return false; + } + + $this->user_name_to = $userTitle->getText(); + $this->user_id_to = User::idFromName( $this->user_name_to ); + + if ( $user->getId() == $this->user_id_to ) { + $output->setPageTitle( $this->msg( 'challengeuser-error-page-title' ) ); + $output->addHTML( $this->msg( 'challengeuser-self' )->plain() ); + } elseif ( $this->user_id_to == 0 ) { + $output->setPageTitle( $this->msg( 'challengeuser-error-page-title' ) ); + $output->addHTML( $this->msg( 'challengeuser-nouser' )->plain() ); + } elseif ( $user->getId() == 0 ) { + $output->setPageTitle( $this->msg( 'challengeuser-error-page-title' ) ); + $output->addHTML( $this->msg( 'challengeuser-login' )->plain() ); + } else { + if ( $request->wasPosted() && $_SESSION['alreadysubmitted'] === false ) { + $_SESSION['alreadysubmitted'] = true; + $c = new Challenge(); + $c->addChallenge( + $this->user_name_to, + $request->getVal( 'info' ), + $request->getVal( 'date' ), + $request->getVal( 'description' ), + $request->getVal( 'win' ), + $request->getVal( 'lose' ) + ); + + $output->setPageTitle( + $this->msg( 'challengeuser-challenge-sent-title', + $this->user_name_to )->parse() + ); + + $out = '<div class="challenge-links">'; + //$out .= "<a href=\"index.php?title=User:{$this->user_name_to}\">< {$this->user_name_to}'s User Page</a>"; + // $out .= " - <a href=\"index.php?title=Special:ViewGifts&user={$this->user_name_to}\">View All of {$this->user_name_to}'s Gifts</a>"; + if ( $this->getUser()->isLoggedIn() ) { + // $out .= " - <a href=\"index.php?title=Special:ViewGifts&user={$wgUser->getName()}\">View All of Your Gifts</a>"; + } + $out .= '</div>'; + + $out .= '<div class="challenge-sent-message">'; + $out .= $this->msg( 'challengeuser-sent', $this->user_name_to )->parse(); + $out .= '</div>'; + + $out .= '<div class="cleared"></div>'; + + $output->addHTML( $out ); + } else { + $_SESSION['alreadysubmitted'] = false; + $output->addHTML( $this->displayForm() ); + } + } + } + + function displayFormNoUser() { + global $wgFriendingEnabled; + + $this->getOutput()->setPageTitle( $this->msg( + 'challengeuser-info-title-no-user' )->plain() ); + + // @todo FIXME: rename form & HTML classes/IDs + $output = '<form action="" method="get" enctype="multipart/form-data" name="gift"> + <input type="hidden" name="title" value="' . htmlspecialchars( $this->getRequest()->getVal( 'title' ), ENT_QUOTES ) . '" />'; + + $output .= '<div class="give-gift-message">'; + $output .= $this->msg( 'challengeuser-info-body-no-user' )->plain(); + $output .= '</div>'; + + if ( $wgFriendingEnabled ) { + $rel = new UserRelationship( $this->getUser()->getName() ); + $friends = $rel->getRelationshipList( 1 ); + if ( $friends ) { + $output .= '<div class="give-gift-title">'; + $output .= $this->msg( 'challengeuser-select-friend-from-list' )->plain(); + $output .= '</div> + <div class="give-gift-selectbox"> + <select id="challenge-user-selector">'; + $output .= '<option value="#" selected="selected">'; + $output .= $this->msg( 'challengeuser-select-friend' )->plain(); + $output .= '</option>'; + foreach ( $friends as $friend ) { + $output .= Html::element( + 'option', + array( 'value' => $friend['user_name'] ), + $friend['user_name'] + ); + } + $output .= '</select> + </div>'; + } + } + + $output .= '<p class="challenge-user-or">'; + $output .= $this->msg( 'challengeuser-or' )->plain(); + $output .= '</p>'; + $output .= '<div class="give-gift-title">'; + $output .= $this->msg( 'challengeuser-type-username' )->plain(); + $output .= '</div>'; + $output .= '<div class="give-gift-textbox"> + <input type="text" width="85" name="user" value="" /> + <input class="site-button" type="button" value="' . + $this->msg( 'challengeuser-start-button' )->plain() . + '" onclick="document.gift.submit()" /> + </div>'; + + return $output; + } + + /** + * Displays the "challenge a user" form + * + * @return string Generated HTML for the challenge form + */ + function displayForm() { + $this->getOutput()->setPageTitle( + $this->msg( 'challengeuser-title-user', + $this->user_name_to )->parse() + ); + + $user_title = Title::makeTitle( NS_USER, $this->user_name_to ); + $challenge_history_title = SpecialPage::getTitleFor( 'ChallengeHistory' ); + $avatar = new wAvatar( $this->user_id_to, 'l' ); + + $pos = Challenge::getUserFeedbackScoreByType( 1, $this->user_id_to ); + $neg = Challenge::getUserFeedbackScoreByType( -1, $this->user_id_to ); + $neu = Challenge::getUserFeedbackScoreByType( 0, $this->user_id_to ); + $total = ( $pos + $neg + $neu ); + + require_once( 'templates/challengeuser.tmpl.php' ); + $template = new ChallengeUserTemplate(); + + $template->set( 'pos', $pos ); + $template->set( 'neg', $neg ); + $template->set( 'neu', $neu ); + $template->set( 'total', $total ); + $template->setRef( 'class', $this ); + $template->setRef( 'user_title', $user_title ); + $template->setRef( 'challenge_history_title', $challenge_history_title ); + $template->setRef( 'avatar', $avatar ); + + return $template->getHTML(); + } +} \ No newline at end of file diff --git a/ChallengeView.php b/ChallengeView.php new file mode 100644 index 0000000..97409e3 --- /dev/null +++ b/ChallengeView.php @@ -0,0 +1,169 @@ +<?php + +class ChallengeView extends SpecialPage { + + public function __construct() { + parent::__construct( 'ChallengeView' ); + } + + /** + * Under which header this special page is listed in Special:SpecialPages? + * + * @return string + */ + protected function getGroupName() { + return 'users'; + } + + /** + * Show the special page + * + * @param mixed $par Parameter (challenge ID) passed to the page or null + */ + public function execute( $par ) { + $this->getOutput()->setPageTitle( $this->msg( 'challengeview' ) ); + + $id = (int) $this->getRequest()->getVal( 'id', $par ); + if ( $id == '' ) { + $this->getOutput()->addHTML( $this->msg( 'challengeview-nochallenge' )->plain() ); + } else { + $this->getOutput()->addHTML( $this->displayChallenge( $id ) ); + } + } + + private function displayChallenge( $id ) { + $this->getOutput()->addModuleStyles( 'ext.challenge.view' ); + $this->getOutput()->addModules( 'ext.challenge.js.main' ); + + $c = new Challenge(); + $challenge = $c->getChallenge( $id ); + + $u = $this->getUser(); + $avatar1 = new wAvatar( $challenge['user_id_1'], 'l' ); + $avatar2 = new wAvatar( $challenge['user_id_2'], 'l' ); + $title1 = Title::makeTitle( NS_USER, $challenge['user_name_1'] ); + $title2 = Title::makeTitle( NS_USER, $challenge['user_name_2'] ); + + require_once( 'templates/challengeview.tmpl.php' ); + $template = new ChallengeViewTemplate(); + + $template->setRef( 'c', $c ); + $template->set( 'challenge', $challenge ); + $template->setRef( 'avatar1', $avatar1 ); + $template->setRef( 'avatar2', $avatar2 ); + $template->setRef( 'title1', $title1 ); + $template->setRef( 'title2', $title2 ); + $template->setRef( 'user', $u ); + + $out = ''; + switch ( $challenge['status'] ) { + case 0: + if ( $this->getUser()->getId() != $challenge['user_id_2'] ) { + $out .= $this->msg( 'challengeview-acceptance' )->plain(); + } else { + $out .= $this->msg( 'challengeview-sent-to-you' )->plain(); + $out .= '<br /><br /> + <select id="challenge_action"> + <option value="1">' . $this->msg( 'challengeview-accept' )->plain() . '</option> + <option value="-1">' . $this->msg( 'challengeview-reject' )->plain() . '</option> + <option value="2">' . $this->msg( 'challengeview-counterterms' )->plain() . "</option> + </select> + <input type=\"hidden\" id=\"status\" value=\"{$challenge['status']}\" /> + <input type=\"hidden\" id=\"challenge_id\" value=\"{$challenge['id']}\" /> + <input type=\"button\" class=\"challenge-response-button site-button\" value=\"" . $this->msg( 'challengeview-submit-button' )->plain() . + '" />'; + } + break; + case 1: + if ( + !$this->getUser()->isAllowed( 'challengeadmin' ) || + $challenge['user_id_1'] == $this->getUser()->getId() || + $challenge['user_id_2'] == $this->getUser()->getId() + ) + { + $out .= $this->msg( 'challengeview-inprogress' )->plain(); + } else { + $out .= $this->msg( 'challengeview-admintext' )->escaped(); + $out .= "<br /><br /> + <select id=\"challenge_winner_userid\"> + <option value=\"{$challenge['user_id_1']}\">{$challenge['user_name_1']}</option> + <option value=\"{$challenge['user_id_2']}\">{$challenge['user_name_2']}</option> + <option value=\"-1\">"; + $out .= $this->msg( 'challengeview-push' )->plain(); + $out .= "</option> + </select> + <input type=\"hidden\" id=\"status\" value=\"{$challenge['status']}\" /> + <input type=\"hidden\" id=\"challenge_id\" value=\"{$challenge['id']}\" /> + <input type=\"button\" class=\"challenge-approval-button site-button\" value=\"" . + $this->msg( 'challengeview-submit-button' )->plain() . + '" />'; + } + break; + case -1: + $out .= $this->msg( 'challengeview-rejected' )->plain(); + break; + case -2: + $out .= $this->msg( 'challengeview-removed' )->plain(); + break; + case 3: + if ( $challenge['winner_user_id'] != -1 ) { + $out .= $this->msg( 'challengeview-won-by', $challenge['winner_user_name'] )->escaped(); + $out .= '<br /><br />'; + if ( $challenge['rating'] ) { + $out .= '<span class="challenge-title">'; + $out .= $this->msg( 'challengeview-rating' )->plain(); + $out .= '</span><br />'; + $out .= $this->msg( 'challengeview-by', $challenge['winner_user_name'] )->escaped(); + $out .= '<br /><br />' . $this->msg( 'challengeview-rating2' )->escaped() . + " <span class=\"challenge-rating-{$c->rating_names[$challenge['rating']]}\">{$c->rating_names[$challenge['rating']]}</span> + <br />"; + $out .= $this->msg( 'challengeview-comment', $challenge['rating_comment'] )->escaped(); + } else { + if ( $this->getUser()->getId() == $challenge['winner_user_id'] ) { + $out .= '<span class="challenge-title">'; + $out .= $this->msg( 'challengeview-rating' )->plain(); + $out .= '</span><br /> + <span class="challenge-won">'; + $out .= $this->msg( 'challengeview-you-won' )->plain(); + $out .= '</span><br /><br /> + <span class="challenge-form">'; + $out .= $this->msg( 'challengeview-rateloser' )->plain(); + $out .= '</span><br /> + <select id="challenge_rate"> + <option value="1">' . $this->msg( 'challengeview-positive' )->plain() . '</option> + <option value="-1">' . $this->msg( 'challengeview-negative' )->plain() . '</option> + <option value="0">' . $this->msg( 'challengeview-neutral' )->plain() . "</option> + </select> + <input type=\"hidden\" id=\"status\" value=\"{$challenge['status']}\" /> + <input type=\"hidden\" id=\"challenge_id\" value=\"{$challenge['id']}\" />"; + if ( $challenge['winner_user_id'] == $challenge['user_id_1'] ) { + $loser_id = $challenge['user_id_2']; + $loser_username = $challenge['user_name_2']; + } else { + $loser_id = $challenge['user_id_1']; + $loser_username = $challenge['user_name_1']; + } + $out .= "<input type=\"hidden\" id=\"loser_userid\" value=\"{$loser_id}\" /> + <input type=\"hidden\" id=\"loser_username\" value=\"{$loser_username}\" /> + <br /><br /><span class=\"challenge-form\">"; + $out .= $this->msg( 'challengeview-additionalcomments' )->plain(); + $out .= '</span><br /> + <textarea class="createbox" rows="2" cols="50" id="rate_comment"></textarea><br /><br /> + <input type="button" class="challenge-rate-button site-button" value="' . + $this->msg( 'challengeview-submit-button' )->plain() . + '" />'; + } else { + $out .= $this->msg( 'challengeview-notyetrated' )->plain(); + } + } + } else { + $out .= $this->msg( 'challengeview-was-push' )->plain() . '<br /><br />'; + } + break; + } + + $template->set( 'challenge-status-html', $out ); + + return $template->getHTML(); + } +} \ No newline at end of file diff --git a/challenge.sql b/challenge.sql new file mode 100644 index 0000000..a55ed2c --- /dev/null +++ b/challenge.sql @@ -0,0 +1,43 @@ +CREATE TABLE /*_*/challenge ( + challenge_id int(11) NOT NULL auto_increment PRIMARY KEY, + -- Challenger + challenge_user_id_1 int(11) NOT NULL default 0, + challenge_username1 varchar(255) NOT NULL default '', + -- Challenged + challenge_user_id_2 int(11) NOT NULL default 0, + challenge_username2 varchar(255) NOT NULL default '', + challenge_info varchar(200) NOT NULL default '', + challenge_event_date varchar(15) NOT NULL default '0000-00-00', + challenge_description text, + challenge_win_terms varchar(200) NOT NULL default '', + challenge_lose_terms varchar(200) NOT NULL default '', + challenge_winner_user_id int(11) NOT NULL default 0, + challenge_winner_username varchar(255) NOT NULL default '', + challenge_status int(11) NOT NULL default 0, + -- The following two fields appear to be currently unused but were used in + -- the past by Special:ChallengeAction...the question is: should we drop these + -- or bring them back? + challenge_accept_date datetime NOT NULL default '0000-00-00 00:00:00', + challenge_complete_date datetime NOT NULL default '0000-00-00 00:00:00', + challenge_date varbinary(14) NOT NULL default '' +)/*$wgDBTableOptions*/; + +CREATE TABLE /*_*/challenge_rate ( + challenge_rate_id int(11) NOT NULL auto_increment PRIMARY KEY, + challenge_id int(11) NOT NULL default 0, + challenge_rate_date varbinary(14) NOT NULL default '', + challenge_rate_user_id int(11) NOT NULL default 0, + challenge_rate_username varchar(255) NOT NULL default '', + challenge_rate_submitter_user_id int(11) NOT NULL default 0, + challenge_rate_submitter_username varchar(255) NOT NULL default '', + challenge_rate_score int(11) NOT NULL default 0, + challenge_rate_comment text NOT NULL +)/*$wgDBTableOptions*/; + +CREATE TABLE /*_*/challenge_user_record ( + challenge_record_user_id int(11) NOT NULL default 0, + challenge_record_username varchar(255) NOT NULL default '', + challenge_wins int(11) NOT NULL default 0, + challenge_losses int(11) NOT NULL default 0, + challenge_ties int(11) NOT NULL default 0 +)/*$wgDBTableOptions*/; \ No newline at end of file diff --git a/i18n/en.json b/i18n/en.json new file mode 100644 index 0000000..c59a1a1 --- /dev/null +++ b/i18n/en.json @@ -0,0 +1,157 @@ +{ + "@metadata": { + "authors": [ + "Aaron Wright", + "David Pean" + ] + }, + "challenge-status-rejected": "rejected", + "challenge-status-removed": "removed", + "challenge-status-awaiting": "awaiting acceptance", + "challenge-status-in-progress": "in progress", + "challenge-status-completed": "completed", + "challenge-js-event-required": "The event is required", + "challenge-js-date-required": "Event date is required", + "challenge-js-description-required": "Description is required", + "challenge-js-win-terms-required": "Win terms are required", + "challenge-js-lose-terms-required": "Lose terms required", + "challenge-js-challenge-removed": "Challenge has been removed", + "challenge-js-accepted": "Accepted", + "challenge-js-rejected": "Rejected", + "challenge-js-countered": "Countered", + "challenge-js-winner-recorded": "Challenge winner has been recorded", + "challenge-js-rating-submitted": "Your rating has been submitted", + "challenge-js-error-date-format": "The date format should be: mm/dd/yyyy", + "challenge-js-error-invalid-month": "Please enter a valid month", + "challenge-js-error-invalid-day": "Please enter a valid day", + "challenge-js-error-invalid-year": "Please enter a valid 4 digit year between $1 and $2", + "challenge-js-error-invalid-date": "Please enter a valid date", + "challenge-js-error-future-date": "Date entered is a future date.", + "challenge-js-error-is-backwards": "Begin date is greater than end date.", + "challenge_request_subject": "$1 has challenged you on {{SITENAME}}!", + "challenge_request_body": "Hi $1.\n\n\t$2 has challenged you on {{SITENAME}}!\n\nGo to $3 to view the challenge details and respond to $2.\n\nThanks\n\nThe {{SITENAME}} Team\n\n---\n\nHey, want to stop getting e-mails from us?\n\nClick $4\nand change your settings to disable e-mail notifications.", + "challenge_accept_subject": "$1 has accepted your challenge on {{SITENAME}}!", + "challenge_accept_body": "Hi $1.\n\n\t$2 has accepted your challenge on {{SITENAME}}!\n\nGo to $3 to view the challenge details.\n\nThanks\n\nThe {{SITENAME}} Team\n\n---\n\nHey, want to stop getting e-mails from us?\n\nClick $4\nand change your settings to disable e-mail notifications.", + "challenge_lose_subject": "Oh no! $1 has beaten you on {{SITENAME}} challenge #$2!", + "challenge_lose_body": "Hi $1.\n\n\t$2 has won the challenge on {{SITENAME}}!\n\nGo to $3 to view the challenge details and terms.\n\nThanks\n\nThe {{SITENAME}} Team\n\n---\n\nHey, want to stop getting e-mails from us?\n\nClick $4\nand change your settings to disable e-mail notifications.", + "challenge_win_subject": "Congratulations! You've beaten $1 in {{SITENAME}} challenge #$2!", + "challenge_win_body": "Hi $1.\n\n\tYou've won the {{SITENAME}} challenge against $2!\n\nGo to $3 to view the challenge details and terms.\n\nThanks\n\nThe {{SITENAME}} Team\n\n---\n\nHey, want to stop getting e-mails from us?\n\nClick $4\nand change your settings to disable e-mail notifications.", "challengeaction": "Challenge Standings", + "challengehistory": "Challenge History", + "challengehistory-all": "All", + "challengehistory-accepted": "accepted", + "challengehistory-awaiting": "Awaiting Acceptance", + "challengehistory-challenge-user": "Challenge $1", + "challengehistory-challenge-someone": "Challenge someone", + "challengehistory-challenger": "challenger", + "challengehistory-challenger-desc": "challenger description", + "challengehistory-completed": "Completed", + "challengehistory-completed2": "completed", + "challengehistory-empty": "There is no current challenge history.", + "challengehistory-event": "event", + "challengehistory-filter": "filter:", + "challengehistory-inprogress": "In progress", + "challengehistory-losers-rating": "loser's rating", + "challengehistory-negative": "Negative", + "challengehistory-negative2": "$1 negative", + "challengehistory-neutral": "Neutral", + "challengehistory-neutral2": "$1 neutral", + "challengehistory-next": "next", + "challengehistory-overall": "Overall Record", + "challengehistory-positive": "Positive", + "challengehistory-positive2": "$1 positive", + "challengehistory-prev": "prev", + "challengehistory-ratings-loser": "Ratings When Loser", + "challengehistory-recentchallenges": "Recent {{SITENAME}} Challenges", + "challengehistory-rejected": "Rejected", + "challengehistory-removed": "removed", + "challengehistory-status": "status", + "challengehistory-target": "target", + "challengehistory-user": "Challenge This User", + "challengehistory-users-history": "$1's challenge history", + "challengehistory-view-standings": "View standings", + "challengestandings": "Challenge Standings", + "challengestandings-challengeuser": "challenge user", + "challengestandings-l": "L", + "challengestandings-t": "T", + "challengestandings-title": "{{SITENAME}} Challenge Standings", + "challengestandings-user": "user", + "challengestandings-w": "W", + "challengeuser": "Challenge a User", + "challengeuser-challenge-sent-title": "You have issued a challenge to $1", + "challengeuser-completehistory": "View Complete Challenge History", + "challengeuser-date": "the event date (mm/dd/yyyy)", + "challengeuser-description": "description (ex: I'm taking the Eagles w/ the spread (+3))", + "challengeuser-enter-info": "Enter challenge information", + "challengeuser-error-page-title": "Woops!", + "challengeuser-event": "the event (ex: Giants vs. Eagles)", + "challengeuser-feedback": "feedback score: <b>$1</b>", + "challengeuser-helppage": "Community Challenges", + "challengeuser-info": "Challenge Info", + "challengeuser-info-body-no-user": "Challenges are a fun way to put your wiki where your mouth is!", + "challengeuser-info-body": "Challenges are a great way to prove your sports knowledge to the community, as well as get others to build your content.", + "challengeuser-info-title": "What are Challenges?", + "challengeuser-info-title-no-user": "Who would you like to challenge?", + "challengeuser-login": "You must be logged in to issue challenges.", + "challengeuser-loseterms": "lose terms (ex: I am willing to edit the 2005 team results page)", + "challengeuser-nouser": "No user selected. Please challenge a user through the correct link.", + "challengeuser-or": "or", + "challengeuser-record": "record:", + "challengeuser-rules": "Please read rules and stuff", + "challengeuser-select-friend": "select a friend", + "challengeuser-select-friend-from-list": "Select from your list of friends", + "challengeuser-self": "You cannot challenge yourself!", + "challengeuser-sent": "The challenge has been sent, and is awaiting acceptance by $1", + "challengeuser-start-button": "start challenge", + "challengeuser-submit-button": "Submit", + "challengeuser-title": "Challenge User", + "challengeuser-title-user": "Challenge user $1", + "challengeuser-type-username": "If you know the name of the user, type it in below", + "challengeuser-users-stats": "$1's challenge stats", + "challengeuser-view-all-challenges": "View all challenges", + "challengeuser-view-userpage": "View $1's userpage", + "challengeuser-viewhistory": "You can view your challenge history here", + "challengeuser-viewstatus": "You can view the status of your challenge here", + "challengeuser-winterms": "win terms (ex: My opponent must fill out the 1991 roster page)", + "challengeview": "View challenge", + "challengeview-accept": "Accept", + "challengeview-accepted": "Accepted", + "challengeview-acceptance": "Awaiting acceptance", + "challengeview-additionalcomments": "Additional comments (ex: He did a lousy job completing the task)", + "challengeview-admin": "Admin cancel challenge due to abuse", + "challengeview-admintext": "You are an admin, so you can pick the winner if the Event has been completed<br />Who won the bet?", + "challengeview-by": "by <b>$1</b>", + "challengeview-by-on": "by <b>$1</b> on $2", + "challengeview-comment": "<b>comment</b>: $1", + "challengeview-counterterms": "Counter Terms", + "challengeview-description": "$1's description:", + "challengeview-event": "Event:", + "challengeview-ifwins": "if $1 wins, $2 has to . . . ", + "challengeview-inprogress": "In progress -- awaiting completion of event and admin approval", + "challengeview-invalidid": "Invalid challenge ID", + "challengeview-issue-challenge": "issue challenge", + "challengeview-negative": "negative", + "challengeview-negative2": "Negative", + "challengeview-neutral": "neutral", + "challengeview-neutral2": "Neutral", + "challengeview-nochallenge": "No challenge specified", + "challengeview-notyetrated": "This challenge has not yet been rated by the winner", + "challengeview-positive": "positive", + "challengeview-positive2": "Positive", + "challengeview-push": "push", + "challengeview-rateloser": "Please rate the loser's end of the bargain", + "challengeview-rating": "Challenge Rating", + "challengeview-rating2": "<b>rating:</b>", + "challengeview-reject": "Reject", + "challengeview-rejected": "Rejected", + "challengeview-removed": "Removed due to violation of rules", + "challengeview-sent-to-you": "This challenge has been sent to you. Please choose your response", + "challengeview-submit-button": "Submit", + "challengeview-status": "Challenge Status", + "challengeview-title": "{{SITENAME}} Challenge Info", + "challengeview-versus": "vs.", + "challengeview-view-history": "view challenge history", + "challengeview-won-by": "Challenge won by <b>$1</b>", + "challengeview-was-push": "Challenge was a push!", + "challengeview-you-won": "You won the challenge!", + "right-challengeadmin": "Administrate challenges" +} diff --git a/i18n/fi.json b/i18n/fi.json new file mode 100644 index 0000000..48f3d66 --- /dev/null +++ b/i18n/fi.json @@ -0,0 +1,149 @@ +{ + "@metadata": { + "authors": [ + "Jack Phoenix" + ] + }, + "challenge-status-rejected": "hylätty", + "challenge-status-removed": "poistettu", + "challenge-status-awaiting": "odottaa hyväksyntää", + "challenge-status-in-progress": "meneillään", + "challenge-status-completed": "valmis", + "challenge-js-event-required": "Tapahtuma on pakollinen tieto", + "challenge-js-date-required": "Tapahtumapäiväys on pakollinen tieto", + "challenge-js-description-required": "Kuvaus on pakollinen tieto", + "challenge-js-win-terms-required": "Voiton ehdot ovat pakollisia", + "challenge-js-lose-terms-required": "Häviön ehdot ovat pakollisia", + "challenge-js-challenge-removed": "Haaste on poistettu", + "challenge-js-accepted": "Hyväksytty", + "challenge-js-rejected": "Hylätty", + "challenge-js-countered": "Vastaehdot annettu", + "challenge-js-winner-recorded": "Haasteen voittaja on tallennettu", + "challenge-js-rating-submitted": "Arviosi on lähetetty", + "challenge-js-error-date-format": "Päiväyksen tulisi olla muodossa KK/PP/VVVV", + "challenge-js-error-invalid-month": "Anna kelvollinen kuukausi", + "challenge-js-error-invalid-day": "Anna kelvollinen päivä", + "challenge-js-error-invalid-year": "Anna kelvollinen nelinumeroinen vuosi väliltä $1-$2", + "challenge-js-error-invalid-date": "Anna kelvollinen päiväys", + "challenge-js-error-future-date": "Annettu päiväys on tulevaisuudessa.", + "challenge-js-error-is-backwards": "Aloituspäiväys on suurempi kuin lopetuspäiväys.", + "challengeaction": "Haastetilastot", + "challengehistory": "Haastehistoria", + "challengehistory-all": "Kaikki", + "challengehistory-accepted": "hyväksytty", + "challengehistory-awaiting": "Hyväksyntää odottavat", + "challengehistory-challenge-user": "Haasta $1", + "challengehistory-challenge-someone": "Haasta joku", + "challengehistory-challenger": "haastaja", + "challengehistory-challenger-desc": "haastajan kuvaus", + "challengehistory-completed": "Suoritettu", + "challengehistory-empty": "Nykyinen haastehistoria on tyhjä.", + "challengehistory-event": "tapahtuma", + "challengehistory-filter": "suodata:", + "challengehistory-inprogress": "Käynnissä", + "challengehistory-inprogress2": "käynnissä", + "challengehistory-losers-rating": "häviäjän arvostelu", + "challengehistory-negative": "Negatiivinen", + "challengehistory-negative2": "{{PLURAL:$1|$1 negatiivinen|$1 negatiivista}}", + "challengehistory-neutral": "Neutraali", + "challengehistory-neutral2": "{{PLURAL:$1|$1 neutraali|$1 neutraalia}}", + "challengehistory-next": "seur.", + "challengehistory-overall": "Yleiskatsaus tuloksista", + "challengehistory-positive": "Positiivinen", + "challengehistory-positive2": "{{PLURAL:$1|$1 positiivinen|$1 positiivista}}", + "challengehistory-prev": "edell.", + "challengehistory-ratings-loser": "Arvostelut kun häviäjä", + "challengehistory-recentchallenges": "Tuoreet {{GRAMMAR:genitive|{{SITENAME}}}} haasteet", + "challengehistory-rejected": "Hylätty", + "challengehistory-removed": "poistettu", + "challengehistory-status": "tila", + "challengehistory-target": "kohde", + "challengehistory-user": "Haasta tämä käyttäjä", + "challengehistory-users-history": "Käyttäjän $1 haastehistoria", + "challengehistory-view-standings": "Tarkastele tilastoja", + "challengestandings": "Haastetilastot", + "challengestandings-challengeuser": "haasta käyttäjä", + "challengestandings-l": "H", + "challengestandings-t": "T", + "challengestandings-title": "{{GRAMMAR:genitive|{{SITENAME}}}} haastetilastot", + "challengestandings-user": "käyttäjä", + "challengestandings-w": "V", + "challengeuser": "Haasta käyttäjä", + "challengeuser-challenge-sent-title": "Olet antanut haasteen käyttäjälle $1", + "challengeuser-completehistory": "Katso täydellinen haastehistoria", + "challengeuser-date": "tapahtumapäivä (KK/PP/VVVV)", + "challengeuser-description": "kuvaus (esim. I'm taking the Eagles w/ the spread (+3))", + "challengeuser-enter-info": "Anna haasteen tiedot", + "challengeuser-error-page-title": "Ups!", + "challengeuser-event": "tapahtuma (esim. Giants vs. Eagles)", + "challengeuser-feedback": "palautepisteet: <b>$1</b>", + "challengeuser-helppage": "Yhteisöhaasteet", + "challengeuser-info": "Haasteen tiedot", + "challengeuser-info-body-no-user": "Haasteet ovat hauska tapa laittaa wikisi sinne, missä suusi on!", + "challengeuser-info-body": "Haasteet ovat loistava tapa todistaa yhteisölle urheilutietoutesi sekä saada muut rakentamaan sisältöäsi.", + "challengeuser-info-title": "Mitä haasteet ovat?", + "challengeuser-info-title-no-user": "Kenet haluaisit haastaa?", + "challengeuser-login": "Sinun tulee olla kirjautunut sisään antaaksesi haasteita.", + "challengeuser-loseterms": "häviön ehdot (esim. suostun muokkaamaan vuoden 2005 tulossivua)", + "challengeuser-nouser": "Käyttäjää ei valittu. Ole hyvä ja haasta käyttäjiä oikean linkin kautta.", + "challengeuser-or": "tai", + "challengeuser-record": "ennätys:", + "challengeuser-rules": "Ole hyvä ja lue säännöt ja muut", + "challengeuser-select-friend": "valitse ystävä", + "challengeuser-select-friend-from-list": "Valitse ystävälistastasi", + "challengeuser-self": "Et voi haastaa itseäsi!", + "challengeuser-sent": "Haasteesi on lähetetty ja se odottaa käyttäjän $1 hyväksyntää", + "challengeuser-start-button": "aloita haaste", + "challengeuser-submit-button": "Lähetä", + "challengeuser-title": "Haasta käyttäjä", + "challengeuser-title-user": "Haasta käyttäjä $1", + "challengeuser-type-username": "Jos tiedät käyttäjän nimen, kirjoita se alapuolelle", + "challengeuser-users-stats": "Käyttäjän $1 haastetilastot", + "challengeuser-view-all-challenges": "Tarkastele kaikkia haasteita", + "challengeuser-view-userpage": "Tarkastele käyttäjän $1 käyttäjäsivua", + "challengeuser-viewhistory": "Voit katsoa haastehistoriasi täällä", + "challengeuser-viewstatus": "Voit katsoa haasteesi tilan täällä", + "challengeuser-winterms": "voiton ehdot (esim. vastustajani tulee täyttää vuoden 1991 sivu)", + "challengeview": "Haastenäkymä", + "challengeview-accept": "Hyväksy", + "challengeview-accepted": "Hyväksytty", + "challengeview-acceptance": "Odotetaan hyväksyntää", + "challengeview-additionalcomments": "Lisäkommentit (esim. hän teki surkeaa työtä tehtävän suorittamisessa)", + "challengeview-admin": "Ylläpitäjän peruutuspainike väärinkäytön vuoksi", + "challengeview-admintext": "Olet ylläpitäjä, joten voit valita voittajan jos tapahtuma on suoritettu<br />Kumpi voitti vedon?", + "challengeview-by": "<b>$1</b>", + "challengeview-by-on": "<b>$1</b> $2", + "challengeview-comment": "<b>kommentti</b>: $1", + "challengeview-counterterms": "Vastaehdot", + "challengeview-description": "$1:n kuvaus:", + "challengeview-event": "Tapahtuma:", + "challengeview-ifwins": "jos $1 voittaa, käyttäjän $2 tulee . . . ", + "challengeview-inprogress": "Käynnissä -- odotetaan tapahtuman suoritusta ja ylläpitäjän hyväksyntää", + "challengeview-invalidid": "Kelpaamaton haasteen tunniste", + "challengeview-issue-challenge": "haasta", + "challengeview-negative": "negatiivinen", + "challengeview-negative2": "Negatiivinen", + "challengeview-neutral": "neutraali", + "challengeview-neutral2": "Neutraali", + "challengeview-nochallenge": "Haastetta ei määritetty", + "challengeview-notyetrated": "Voittaja ei ole vielä arvostellut tätä haastetta", + "challengeview-positive": "positiivinen", + "challengeview-positive2": "Positiivinen", + "challengeview-push": "tasapeli", + "challengeview-rateloser": "Ole hyvä ja arvostele häviäjän kauppaosa", + "challengeview-rating": "Arvio", + "challengeview-rating2": "<b>arvio:</b>", + "challengeview-reject": "Hylkää", + "challengeview-rejected": "Hylätty", + "challengeview-removed": "Poistettu sääntörikkomuksen vuoksi", + "challengeview-sent-to-you": "Tämä haaste on lähetetty sinulle. Ole hyvä ja valitse vastauksesi", + "challengeview-status": "Haasteen tila", + "challengeview-submit-button": "Lähetä", + "challengeview-title": "{{GRAMMAR:genitive|{{SITENAME}}}} haastetiedot", + "challengeview-versus": "vs.", + "challengeview-view-history": "tarkastele haastehistoriaa", + "challengeview-won-by": "Haasteen voitti <b>$1</b>", + "challengeview-was-push": "Haaste oli tasapeli!", + "challengeview-you-won": "Voitit haasteen!", + "right-challengeadmin": "Hallinnoida haasteita" +} diff --git a/resources/css/ext.challenge.history.css b/resources/css/ext.challenge.history.css new file mode 100644 index 0000000..8a70d49 --- /dev/null +++ b/resources/css/ext.challenge.history.css @@ -0,0 +1,54 @@ +select[name="status-filter"] { + font-size: 10px; +} + +.challenge-nav { + padding-bottom: 25px; + width: 740; +} + +.challenge-history-table { + border-collapse: collapse; /* cellspacing=0 equivalent */ + border: 0; + padding: 3px; + width: 830px; +} + +.challenge-history-header { + color: #666666; + font-size: 12px; + font-weight: 800; + padding-right: 5px; +} + +.challenge-history-empty { + font-size: 11px; +} + +.challenge-data { + border-bottom: 1px solid #eeeeee; + font-size: 11px; + padding: 5px; + vertical-align: top; +} + +.challenge-data img { + padding: 2px; +} + +.challenge-data-description { + width: 150px; +} + +.challenge-history-filter { + float: right; +} + +.challenge-link { + float: left; +} + +.challenge-link a { + font-weight: 800; + font-size: 15px; +} \ No newline at end of file diff --git a/resources/css/ext.challenge.standings.css b/resources/css/ext.challenge.standings.css new file mode 100644 index 0000000..fa88f7a --- /dev/null +++ b/resources/css/ext.challenge.standings.css @@ -0,0 +1,31 @@ +.challenge-standings-table { + border: 0; + border-collapse: collapse; + padding: 3px; +} + +.challenge-standings-title { + font-size: 12px; + font-weight: 800; + padding-right: 5px; +} + +.challenge-standings { + border-bottom: 1px solid #eeeeee; + font-size: 11px; + padding: 5px; + vertical-align: top; +} + +.challenge-standings img { + padding: 2px; +} + +.challenge-standings-user-link { + color: #666; +} + +.challenge-standings-history-link { + font-size: 13px; + font-weight: bold; +} \ No newline at end of file diff --git a/resources/css/ext.challenge.user.css b/resources/css/ext.challenge.user.css new file mode 100644 index 0000000..1e62a87 --- /dev/null +++ b/resources/css/ext.challenge.user.css @@ -0,0 +1,49 @@ +.challenge-user-top { + padding-bottom: 15px; +} + +.challenge-user-title { + color: #78BA5D; + font-size: 15px; + font-weight: 800; + padding-bottom: 4px; +} + +.challenge-field { + padding-bottom: 10px; +} + +.challenge-label { + color: #666666; +} + +.challenge-user-info { + padding-bottom: 20px; + width: 400px; +} + +.challenge-info { + border: 1px solid #cccccc; + float: right; + padding: 5px; + width: 200px; +} + +.challenge-user-top a { + font-size: 12px; + font-weight: 800; + text-decoration: none; +} + +.challenge-user-avatar { + float: left; + width: 75px; +} + +.challenge-user-stats { + float: right; +} + +.challenge-user-or { + margin: 10px 0px 10px 0px; +} \ No newline at end of file diff --git a/resources/css/ext.challenge.view.css b/resources/css/ext.challenge.view.css new file mode 100644 index 0000000..fd0abbf --- /dev/null +++ b/resources/css/ext.challenge.view.css @@ -0,0 +1,55 @@ +.challenge-main-table { + background-color: #eeeeee; + border: 1px solid #cccccc; + border-collapse: collapse; /* cellspacing=0 equivalent */ + padding: 8px; +} + +.challenge-title { + color: #78BA5D; + font-size: 15px; + font-weight: 800; + padding-bottom: 10px; +} + +.challenge-small-link { + font-size: 11px; +} + +.challenge-user-link { + font-size: 14px; + font-weight: 800; +} + +.challenge-status-text { + color: #666666; + font-size: 14px; + font-weight: 800; +} + +.challenge-form { + font-size: 12px; + font-weight: 400; +} + +/* Admin-only link for cancelling the challenge */ +a.challenge-admin-cancel-link { + color: #990000; +} + +.challenge-terms-container { + border-collapse: collapse; /* cellspacing=0 equivalent */ + padding: 0; +} + +.challenge-terms { + border-collapse: collapse; /* cellspacing=0 equivalent */ + padding: 0; + width: 300px; +} + +.challenge-line { + border-bottom: 1px solid #cccccc; + margin-bottom: 15px; + width: 800px; +} \ No newline at end of file diff --git a/resources/images/userpageIcon.png b/resources/images/userpageIcon.png new file mode 100644 index 0000000..ae2bb9b --- /dev/null +++ b/resources/images/userpageIcon.png Binary files differ diff --git a/resources/js/Challenge.js b/resources/js/Challenge.js new file mode 100644 index 0000000..cbd0aef --- /dev/null +++ b/resources/js/Challenge.js @@ -0,0 +1,153 @@ +var Challenge = { + chooseFriend: function( friend ) { + window.location = mw.util.getUrl( 'Special:ChallengeUser', { 'user': friend } ); + }, + + changeFilter: function( user, status ) { + window.location = mw.util.getUrl( 'Special:ChallengeHistory', { 'user': user, 'status': status } ); + }, + + send: function() { + var err = '', + req = [ + // field name | i18n message name + 'info|challenge-js-event-required', + 'date|challenge-js-date-required', + 'description|challenge-js-description-required', + 'win|challenge-js-win-terms-required', + 'lose|challenge-js-lose-terms-required' + ]; + + for ( var x = 0; x <= req.length - 1; x++ ) { + var fld = req[x].split( '|' ); + if ( document.getElementById( fld[0] ) == '' ) { + err += mw.msg( fld[1] ) + '\n'; + } + } + + if ( !err ) {//&& isDate($F('date'))==true){ + document.challenge.submit(); + } else { + if ( err ) { + alert( err ); + } + } + }, + + cancel: function( id ) { + $.ajax( { + type: 'POST', + url: mw.util.wikiScript( 'index' ), + data: { + title: 'Special:ChallengeAction', + action: 1, + 'id': id, + status: -2 + } + } ).done( function() { + var text = mw.msg( 'challenge-js-challenge-removed' ); + alert( text ); + $( '#challenge-status' ).text( text ); + } ); + }, + + response: function() { + $( '#challenge-status' ).hide(); + $.ajax( { + type: 'POST', + url: mw.util.wikiScript( 'index' ), + data: { + title: 'Special:ChallengeAction', + action: 1, + 'id': $( '#challenge_id' ).val(), + status: $( '#challenge_action' ).val() + } + } ).done( function() { + var newStatus; + switch ( parseInt( $( '#challenge_action' ).val() ) ) { + case 1: + newStatus = mw.msg( 'challenge-js-accepted' ); + break; + case -1: + newStatus = mw.msg( 'challenge-js-rejected' ); + break; + case 2: + newStatus = mw.msg( 'challenge-js-countered' ); + break; + } + + $( '#challenge-status' ).text( newStatus ).show( 500 ); + } ); + }, + + approval: function() { + $( '#challenge-status' ).hide(); + $.ajax( { + type: 'POST', + url: mw.util.wikiScript( 'index' ), + data: { + title: 'Special:ChallengeAction', + action: 2, + 'id': $( '#challenge_id' ).val(), + userid: $( '#challenge_winner_userid' ).val() + } + } ).done( function() { + $( '#challenge-status' ).text( mw.msg( 'challenge-js-winner-recorded' ) ).show( 500 ); + } ); + }, + + rate: function() { + $( '#challenge-status' ).hide(); + $.ajax( { + type: 'POST', + url: mw.util.wikiScript( 'index' ), + data: { + title: 'Special:ChallengeAction', + action: 3, + 'id': $( '#challenge_id' ).val(), + challenge_rate: $( '#challenge_rate' ).val(), + rate_comment: $( '#rate_comment' ).val(), + loser_userid: $( '#loser_userid' ).val(), + loser_username: $( '#loser_username' ).val() + } + } ).done( function() { + $( '#challenge-status' ).text( mw.msg( 'challenge-js-rating-submitted' ) ).show( 500 ); + } ); + } +}; + +$( document ).ready( function() { + // Special:ChallengeHistory (SpecialChallengeHistory.php) + $( 'select[name="status-filter"]' ).on( 'change', function() { + Challenge.changeFilter( $( this ).data( 'username' ), $( this ).val() ); + } ); + + // Special:ChallengeUser (SpecialChallengeUser.php) + $( '#challenge-user-selector' ).on( 'change', function() { + Challenge.chooseFriend( this.value ); + } ); + + // Special:ChallengeUser (templates/challengeuser.tmpl.php) + $( 'input.challenge-send-button' ).on( 'click', function() { + Challenge.send(); + } ); + + // Special:ChallengeView (templates/challengeview.tmpl.php) + $( 'a.challenge-admin-cancel-link' ).on( 'click', function( e ) { + e.preventDefault(); + Challenge.cancel( $( this ).data( 'challenge-id' ) ); + } ); + + // Special:ChallengeView (SpecialChallengeView.php) + $( 'input.challenge-approval-button' ).on( 'click', function() { + Challenge.approval(); + } ); + + $( 'input.challenge-rate-button' ).on( 'click', function() { + Challenge.rate(); + } ); + + $( 'input.challenge-response-button' ).on( 'click', function() { + Challenge.response(); + } ); +} ); \ No newline at end of file diff --git a/resources/js/DatePicker.js b/resources/js/DatePicker.js new file mode 100644 index 0000000..aab609c --- /dev/null +++ b/resources/js/DatePicker.js @@ -0,0 +1,9 @@ +$( document ).ready( function() { + mw.loader.using( 'jquery.ui.datepicker', function() { + $( '#date' ).datepicker( { + changeYear: true, + yearRange: '1930:c', + dateFormat: 'mm/dd/yy' + } ); + } ); +} ); \ No newline at end of file diff --git a/resources/js/ValidateDate.js b/resources/js/ValidateDate.js new file mode 100644 index 0000000..f045732 --- /dev/null +++ b/resources/js/ValidateDate.js @@ -0,0 +1,134 @@ +var ChallengeDateValidator = { + // Declaring valid date character, minimum year and maximum year + dtCh: '/', + minYear: 1900, + maxYear: 2100, + + isInteger: function( sx ) { + for ( var i = 0; i < sx.length; i++ ) { + // Check that current character is number. + var c = sx.charAt( i ); + if ( ( ( c < '0' ) || ( c > '9' ) ) ) { + return false; + } + } + + // All characters are numbers. + return true; + }, + + stripCharsInBag: function( sx, bag ) { + var returnString = ''; + // Search through string's characters one by one. + // If character is not in bag, append to returnString. + for ( var i = 0; i < sx.length; i++ ) { + var c = sx.charAt( i ); + if ( bag.indexOf( c ) == -1 ) { + returnString += c; + } + } + return returnString; + }, + + daysInFebruary: function( year ) { + // February has 29 days in any year evenly divisible by four, + // EXCEPT for centurial years which are not also divisible by 400. + return ( ( ( year % 4 == 0 ) && ( ( !( year % 100 == 0 ) ) || ( year % 400 == 0 ) ) ) ? 29 : 28 ); + }, + + DaysArray: function( nx ) { + for ( var i = 1; i <= nx; i++ ) { + this[i] = 31; + if ( i == 4 || i == 6 || i == 9 || i == 11 ) { + this[i] = 30; + } + if ( i == 2 ) { + this[i] = 29; + } + } + return this; + }, + + isDate: function( dtStr ) { + var daysInMonth = ChallengeDateValidator.DaysArray( 12 ); + var pos1 = dtStr.indexOf( ChallengeDateValidator.dtCh ); + var pos2 = dtStr.indexOf( ChallengeDateValidator.dtCh, pos1 + 1 ); + var strMonth = dtStr.substring( 0, pos1 ); + var strDay = dtStr.substring( pos1 + 1, pos2 ); + var strYear = dtStr.substring( pos2 + 1 ); + + strYr = strYear; + + if ( strDay.charAt( 0 ) == '0' && strDay.length > 1 ) { + strDay = strDay.substring( 1 ); + } + if ( strMonth.charAt( 0 ) == '0' && strMonth.length > 1 ) { + strMonth = strMonth.substring( 1 ); + } + + for ( var i = 1; i <= 3; i++ ) { + if ( strYr.charAt( 0 ) == '0' && strYr.length > 1 ) { + strYr = strYr.substring( 1 ); + } + } + + month = parseInt( strMonth ); + day = parseInt( strDay ); + year = parseInt( strYr ); + + if ( pos1 == -1 || pos2 == -1 ) { + alert( mw.msg( 'challenge-js-error-date-format' ) ); + return false; + } + if ( strMonth.length < 1 || month < 1 || month > 12 ) { + alert( mw.msg( 'challenge-js-error-invalid-month' ) ); + return false; + } + if ( + strDay.length < 1 || day < 1 || day > 31 || + ( month == 2 && day > ChallengeDateValidator.daysInFebruary( year ) ) || + day > ChallengeDateValidator.DaysArray[month] + ) + { + alert( mw.msg( 'challenge-js-error-invalid-day' ) ); + return false; + } + if ( strYear.length != 4 || year == 0 || year < ChallengeDateValidator.minYear || year > ChallengeDateValidator.maxYear ) { + alert( mw.msg( 'challenge-js-error-invalid-year', ChallengeDateValidator.minYear, ChallengeDateValidator.maxYear ) ); + return false; + } + if ( + dtStr.indexOf( ChallengeDateValidator.dtCh, pos2 + 1 ) != -1 || + ChallengeDateValidator.isInteger( ChallengeDateValidator.stripCharsInBag( dtStr, ChallengeDateValidator.dtCh ) ) === false + ) + { + alert( mw.msg( 'challenge-js-error-invalid-date' ) ); + return false; + } + return true; + }, + + isFuture: function( dtStr ) { + var today = new Date(); + var tstDate = new Date( dtStr ); + + if ( Math.round( ( tstDate - today ) / ( 60 * 60 * 60 * 24 ) ) < 0 ) { + return true; + } else { + alert( mw.msg( 'challenge-js-error-future-date' ) ); + return false; + } + }, + + isBackwards: function( dtBeg, dtEnd ) { + var startDate = new Date( dtBeg ); + var endDate = new Date( dtEnd ); + + if ( Math.round( ( endDate - startDate ) / ( 60 * 60 * 60 * 24 ) ) >= 0 ) { + return true; + } else { + alert( mw.msg( 'challenge-js-error-is-backwards' ) ); + return false; + } + } +}; \ No newline at end of file diff --git a/templates/challengeuser.tmpl.php b/templates/challengeuser.tmpl.php new file mode 100644 index 0000000..727facc --- /dev/null +++ b/templates/challengeuser.tmpl.php @@ -0,0 +1,98 @@ +<?php +/** + * @file + */ +if ( !defined( 'MEDIAWIKI' ) ) { + die( -1 ); +} + +/** + * HTML template for Special:ChallengeUser + * @ingroup Templates + */ +class ChallengeUserTemplate extends QuickTemplate { + public function execute() { +?> + <div class="challenge-user-top"> + <?php echo Linker::link( + $this->data['user_title'], + wfMessage( + 'challengeuser-view-userpage', + $this->data['class']->user_name_to + )->escaped() + ) . ' - ' . + Linker::link( + $this->data['challenge_history_title'], + wfMessage( 'challengeuser-completehistory' )->plain(), + array(), + array( 'user' => $this->data['class']->user_name_to ) + ) . ' - ' . + Linker::link( + $this->data['challenge_history_title'], + wfMessage( 'challengeuser-view-all-challenges' )->plain() + ); + ?> + </div> + <div class="challenge-info"> + <div class="challenge-user-title"><?php echo wfMessage( 'challengeuser-info-title' )->plain() ?></div> + <?php echo wfMessage( 'challengeuser-info-body' )->plain() ?> + </div> + <div class="challenge-user-info"> + <div class="challenge-user-avatar"> + <?php echo $this->data['avatar']->getAvatarURL( array( 'align' => 'middle' ) ); ?> + </div> + <div class="challenge-user-stats"> + <div class="challenge-user-title"><?php echo wfMessage( 'challengeuser-users-stats', $this->data['class']->user_name_to )->escaped(); ?></div> + <div class="challenge-user-record"><?php echo wfMessage( 'challengeuser-record' )->plain() ?> <b><?php echo Challenge::getUserChallengeRecord( $this->data['class']->user_id_to ) ?></b></div> + <div class="challenge-user-feedback"><?php echo wfMessage( 'challengeuser-feedback' )->numParams( $this->data['total'] )->parse() ?> (<?php echo + $this->data['class']->getLanguage()->pipeList( array( + wfMessage( 'challengehistory-positive2' )->numParams( $this->data['pos'] )->parse(), + wfMessage( 'challengehistory-negative2' )->numParams( $this->data['neg'] )->parse(), + wfMessage( 'challengehistory-neutral2' )->numParams( $this->data['neu'] )->parse() + ) ); ?>)</div> + </div> + </div> + <div class="cleared"></div> + + <div class="challenge-user-title"><?php echo wfMessage( 'challengeuser-enter-info' )->plain() ?></div> + <form action="" method="post" enctype="multipart/form-data" name="challenge"> + <div class="challenge-field"> + <div class="challenge-label"><?php echo wfMessage( 'challengeuser-event' )->plain() ?></div> + <div class="challenge-form"> + <input type="text" class="createbox" size="35" name="info" id="info" value="" /> + </div> + </div> + <div class="challenge-field"> + <div class="challenge-label"><?php echo wfMessage( 'challengeuser-date' )->plain() ?></div> + <div class="challenge-form"> + <input type="text" class="createbox" size="10" name="date" id="date" value="" /> + </div> + </div> + <div class="challenge-field"> + <div class="challenge-label"><?php echo wfMessage( 'challengeuser-description' )->plain() ?></div> + <div class="challenge-form"> + <input type="text" class="createbox" size="50" name="description" id="description" value="" /> + </div> + </div> + + <div class="challenge-field"> + <div class="challenge-label"><?php echo wfMessage( 'challengeuser-winterms' )->plain() ?></div> + <div class="challenge-form"> + <textarea class="createbox" name="win" id="win" rows="2" cols="50"></textarea> + </div> + </div> + + <div class="challenge-field"> + <div class="challenge-label"><?php echo wfMessage( 'challengeuser-loseterms' )->plain() ?></div> + <div class="challenge-form"> + <textarea class="createbox" name="lose" id="lose" rows="2" cols="50"></textarea> + </div> + </div> + <div class="challenge-buttons"> + <input type="button" class="createbox challenge-send-button site-button" value="<?php echo wfMessage( 'challengeuser-submit-button' )->plain() ?>" size="20" /> + </div> + <div class="cleared"></div> + </form> +<?php + } // execute() +} // class \ No newline at end of file diff --git a/templates/challengeview.tmpl.php b/templates/challengeview.tmpl.php new file mode 100644 index 0000000..afec0c8 --- /dev/null +++ b/templates/challengeview.tmpl.php @@ -0,0 +1,124 @@ +<?php +/** + * @file + */ +if ( !defined( 'MEDIAWIKI' ) ) { + die( -1 ); +} + +/** + * HTML template for Special:ChallengeView + * @ingroup Templates + */ +class ChallengeViewTemplate extends QuickTemplate { + public function execute() { + $challenge_history_title = SpecialPage::getTitleFor( 'ChallengeHistory' ); + $challenge_user_title = SpecialPage::getTitleFor( 'ChallengeUser' ); + + $challenge = $this->data['challenge']; + $user = $this->data['user']; +?> + <table class="challenge-main-table"> + <tr> + <td><?php echo $this->data['avatar1']->getAvatarURL(); ?></td> + <td> + <span class="challenge-user-title"><?php echo Linker::link( + $this->data['title1'], + $this->data['title1']->getText(), + array( 'class' => 'challenge-user-link' ) + ) ?></span> (<?php echo $this->data['c']->getUserChallengeRecord( $challenge['user_id_1'] ) ?>) + <br /><?php echo Linker::link( + $challenge_history_title, + wfMessage( 'challengeview-view-history' )->plain(), + array( 'class' => 'challenge-small-link' ), + array( 'user' => $this->data['title1']->getDBkey() ) + ); ?> + <?php if ( $user->getName() !== $this->data['title1']->getText() ) { ?> + <br /><?php echo Linker::link( + $challenge_user_title, + wfMessage( 'challengeview-issue-challenge' )->plain(), + array( 'class' => 'challenge-small-link' ), + array( 'user' => $this->data['title1']->getDBkey() ) + ); ?> + <?php } ?> + </td> + <td> + <b><?php echo wfMessage( 'challengeview-versus' )->plain() ?></b> + </td> + <td><?php echo $this->data['avatar2']->getAvatarURL(); ?></td> + <td> + <span class="challenge-user-link"><?php echo Linker::link( + $this->data['title2'], + $this->data['title2']->getText(), + array( 'class' => 'challenge-user-link' ) + ) ?></span> (<?php echo $this->data['c']->getUserChallengeRecord( $challenge['user_id_2'] ) ?>) + <br /><?php echo Linker::link( + $challenge_history_title, + wfMessage( 'challengeview-view-history' )->plain(), + array( 'class' => 'challenge-small-link' ), + array( 'user' => $this->data['title2']->getDBkey() ) + ); ?> + <?php if ( $user->getName() !== $this->data['title2']->getText() ) { ?> + <br /><?php echo Linker::link( + $challenge_user_title, + wfMessage( 'challengeview-issue-challenge' )->plain(), + array( 'class' => 'challenge-small-link' ), + array( 'user' => $this->data['title2']->getDBkey() ) + ); ?> + <?php } ?> + </td> + </tr> + </table> + <br /> + <table> + <tr> + <td> + <b><?php echo wfMessage( 'challengeview-event' )->plain() ?></b> <span class="challenge-event"><?php echo $challenge['info'] . ' [' . $challenge['date'] . ']' ?></span> + <br /><b><?php echo wfMessage( 'challengeview-description', $challenge['user_name_1'] )->parse() ?></b><span class="challenge-description"><?php echo $challenge['description'] ?></span> + </td> + </tr> + </table> + + <!--</td></tr></table><br />--> + + <table class="challenge-terms-container"> + <tr> + <td valign="top"> + <span class="challenge-title"><?php echo wfMessage( + 'challengeview-ifwins', + $challenge['user_name_1'], + $challenge['user_name_2'] + )->parse() ?></span> + <table class="challenge-terms"><tr><td><?php echo $challenge['win_terms'] ?></td></tr></table><br /> + </td> + <td width="20"> </td> + <td valign="top"> + <span class="challenge-title"><?php echo wfMessage( + 'challengeview-ifwins', + $challenge['user_name_2'], + $challenge['user_name_1'] + )->parse() ?></span> + <table class="challenge-terms"><tr><td><?php echo $challenge['lose_terms'] ?></td></tr></table> + </td> + </tr> + </table> + +<?php + if ( $user->isAllowed( 'challengeadmin' ) && $challenge['user_id_2'] != $user->getId() && $challenge['user_id_1'] != $user->getId() ) { + $adminLink = "<a class=\"challenge-admin-cancel-link\" data-challenge-id=\"{$challenge['id']}\" href=\"#\">"; + $adminLink .= wfMessage( 'challengeview-admin' )->plain(); + $adminLink .= '</a>'; + echo $adminLink; + } +?> + <div class="challenge-line"></div> + <span class="challenge-title"><?php echo wfMessage( 'challengeview-status' )->plain() ?></span><br /> + <div class="challenge-status-text"> + <span id="challenge-status"> + <?php echo $this->data['challenge-status-html']; ?> + </span> + </div> + <span id="status2"></span> +<?php + } // execute() +} // class \ No newline at end of file -- To view, visit https://gerrit.wikimedia.org/r/164770 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: I87ae4147794558be54c570fd0b7f40c5faf1ae03 Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/Challenge Gerrit-Branch: master Gerrit-Owner: Jack Phoenix <j...@countervandalism.net> Gerrit-Reviewer: Jack Phoenix <j...@countervandalism.net> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits