r28983 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r28982‎ | r28983 | r28984 >
Date:05:39, 29 December 2007
Author:werdna
Status:old
Tags:
Comment:
Add partially complete Invitations extension.
In brief, the invitations extension works like the gmail invite system. It is intended to be used for CentralAuth, first.
Although the back-end code is complete, it is untested, and the extension still lacks an interface. I am hoping to rectify that at some point in the future, although I do not object to somebody else jumping in and doing it (they'd probably do a better job!)
Modified paths:
  • /trunk/extensions/Invitations (added) (history)
  • /trunk/extensions/Invitations/Invitations.php (added) (history)
  • /trunk/extensions/Invitations/Invitations_obj.php (added) (history)
  • /trunk/extensions/Invitations/README (added) (history)
  • /trunk/extensions/Invitations/invitations.sql (added) (history)

Diff [purge]

Index: trunk/extensions/Invitations/invitations.sql
@@ -0,0 +1,19 @@
 2+CREATE TABLE /*$wgDBprefix*/invitation (
 3+ inv_id BIGINT NOT NULL auto_increment,
 4+ inv_inviter BIGINT NOT NULL,
 5+ inv_invitee BIGINT NOT NULL,
 6+ inv_type varchar(255) NOT NULL,
 7+ inv_timestamp timestamp not null default current_timestamp,
 8+ PRIMARY KEY (inv_id),
 9+ KEY (inv_invitee,inv_type),
 10+ KEY (inv_inviter),
 11+ KEY (inv_type),
 12+) /*$wgDBTableOptions*/;
 13+
 14+CREATE TABLE /*$wgDBprefix*/invite_count (
 15+ ic_user BIGINT NOT NULL,
 16+ ic_type varchar(255) NOT NULL,
 17+ ic_count BIGINT NOT NULL DEFAULT 0,
 18+ PRIMARY KEY (ic_user,ic_type),
 19+ KEY (ic_user,ic_type,ic_count),
 20+) /*$wgDBTableOptions*/;
Index: trunk/extensions/Invitations/Invitations_obj.php
@@ -0,0 +1,177 @@
 2+<?php
 3+define('INVITE_RESULT_OK', 0); // No problem
 4+define('INVITE_RESULT_ALREADY_INVITED', 1); // The user has already been invited
 5+define('INVITE_RESULT_NOT_ALLOWED', 2); // The inviter has not been invited
 6+define('INVITE_RESULT_NONE_LEFT', 3); // The inviter has no invites left.
 7+define('INVITE_RESULT_NO_SUCH_FEATURE', 4); // The feature has not been defined.
 8+define('INVITE_RESULT_NONE_YET', 5); // The inviter has no invites yet.
 9+
 10+class Invitations {
 11+
 12+ /*
 13+ * Does this user have a valid invite to this feature?
 14+ * @param string $feature The feature to check.
 15+ * @param object $user The user to check, or null for the current user ($wgUser)
 16+ * @return boolean Whether or not the user has a valid invite to the feature.
 17+ */
 18+ public static function hasInvite( $feature, $user = null ) {
 19+ global $wgUser, $wgInvitationTypes;
 20+ if ($user == null)
 21+ $user = $wgUser;
 22+
 23+ // No such invitation type.
 24+ if (!is_array($wgInvitationTypes[$feature]))
 25+ return false;
 26+
 27+ $dbr = wfGetDb( DB_SLAVE );
 28+
 29+ $res = $dbr->select( 'invitation', array( 1 ), array( inv_invitee => $user->getId(), inv_type => $feature ), __METHOD__ );
 30+
 31+ return ($dbr->numRows($res) > 0);
 32+ }
 33+
 34+ /*
 35+ * Can the given inviter invite the given invitee to the given feature?
 36+ * @param string $feature The feature to check.
 37+ * @param object $invitee The user to be invited.
 38+ * @param object $inviter The inviting user, or null for $wgUser.
 39+ * @return integer One of the INVITE_RESULT constants.
 40+ */
 41+ public static function checkInviteOperation( $feature, $invitee, $inviter = null ) {
 42+ global $wgUser, $wgInvitationTypes;
 43+
 44+ if (!is_array($wgInvitationTypes[$feature]))
 45+ return INVITE_NO_SUCH_FEATURE;
 46+
 47+ if ($inviter == null)
 48+ $inviter = $wgUser;
 49+
 50+ if (!Invitations::hasInvite($feature, $inviter))
 51+ return INVITE_RESULT_NOT_ALLOWED;
 52+
 53+ if (Invitations::hasInvite($feature, $invitee))
 54+ return INVITE_RESULT_ALREADY_INVITED;
 55+
 56+ if ($wgInvitationTypes[$feature][limitedinvites]) {
 57+ if ($wgInvitationTypes[$feature][invitedelay] > 0) {
 58+ // Is the account old enough to have invites?
 59+ $accountAge = time() - wfTimestampOrNull( TS_UNIX, $inviter->mRegistration );
 60+ if ($accountAge < $wgInvitationTypes[$feature][invitedelay]) {
 61+ return INVITE_RESULT_NONE_YET;
 62+ }
 63+ }
 64+
 65+ if (Invitations::getRemainingInvites( $feature, $inviter ) == 0) {
 66+ return INVITE_RESULT_NONE_LEFT;
 67+ }
 68+
 69+ }
 70+
 71+ return INVITE_RESULT_OK;
 72+ }
 73+
 74+ /*
 75+ * How many invites does the given inviter have?
 76+ * @param string $feature The feature to check.
 77+ * @param object $inviter The user to check, or null for $wgUser.
 78+ * @return integer The number of invites left, or -1 for infinity.
 79+ */
 80+ private static function getRemainingInvites( $feature, $user = null ) {
 81+ global $wgUser, $wgInvitationTypes;
 82+ if ($user == null)
 83+ $user = $wgUser;
 84+
 85+ // No such invitation type.
 86+ if (!is_array($wgInvitationTypes[$feature]))
 87+ return 0;
 88+
 89+ // Has none: not invited.
 90+ if (!Invitations::hasInvite($feature, $inviter))
 91+ return 0;
 92+
 93+ if (!$wgInvitationTypes[$feature][limitedinvites])
 94+ return -1;
 95+
 96+ if ($wgInvitationTypes[$feature][invitedelay] > 0) {
 97+ // Is the account old enough to have invites?
 98+ $accountAge = time() - wfTimestampOrNull( TS_UNIX, $inviter->mRegistration );
 99+ if ($accountAge < $wgInvitationTypes[$feature][invitedelay]) {
 100+ return 0;
 101+ }
 102+ }
 103+
 104+ $dbr = wfGetDb( DB_SLAVE );
 105+
 106+ $res = $dbr->select( 'invite_count', array( 'ic_count' ), array( ic_user => $invitee->getId(), ic_type => $feature ), __METHOD__ );
 107+
 108+ if ($dbr->numRows($res) > 0) {
 109+ $num = $dbr->fetchObject($res)->ic_count;
 110+ return $num;
 111+ } else {
 112+ Invitations::insertCountRow( $feature, $user );
 113+ return $wgInvitationTypes[$feature][reserve];
 114+ }
 115+ }
 116+
 117+ /*
 118+ * Insert a row into the invite_count table for the given user and feature.
 119+ * @param string $feature The feature to check.
 120+ * @param object $user The user to check, or null for $wgUser.
 121+ * @param object $count The number to insert, or NULL to insert the amount left normally.
 122+ * @return integer The number of invites left, or -1 for infinity.
 123+ */
 124+ private static function insertCountRow( $feature, $user = null, $count = null ) {
 125+ global $wgUser, $wgInvitationTypes;
 126+ if ($user == null)
 127+ $user = $wgUser;
 128+
 129+ // No such invitation type.
 130+ if (!is_array($wgInvitationTypes[$feature]))
 131+ return false;
 132+
 133+ if ($count === null)
 134+ $count = Invitations::getRemainingInvites( $feature, $user );
 135+
 136+ if ($count) {
 137+ $dbw = wfGetDb( DB_MASTER );
 138+
 139+ $dbw->replace( 'invite_count',
 140+ array( 'ic_user' => $user->getId(), 'ic_type' => $feature, 'ic_count' => $count ),
 141+ __METHOD__ );
 142+ }
 143+ }
 144+
 145+ /*
 146+ * Add an invitation for the given invitee, from the given inviter.
 147+ * @param string $feature The feature to invite to.
 148+ * @param object $invitee The user to be invited.
 149+ * @param object $inviter The inviting user, or null for $wgUser.
 150+ * @return integer One of the INVITE_RESULT constants.
 151+ */
 152+ public static function inviteUser( $feature, $invitee, $inviter = null ) {
 153+ global $wgUser, $wgInvitationTypes;
 154+ if ($user == null)
 155+ $user = $wgUser;
 156+
 157+ if ( ($res = Invitations::checkInviteOperation) != INVITE_RESULT_OK) {
 158+ return $res;
 159+ }
 160+
 161+ // We /should/ be OK to go.
 162+ $dbw = wfGetDB( DB_MASTER );
 163+
 164+ $dbw->update( 'invite_count', array( 'ic_count=ic_count-1' ),
 165+ array( ic_user => $inviter->getId(), ic_type => $feature ), __METHOD__ );
 166+
 167+ $dbw->insert( 'invitation',
 168+ array( 'inv_invitee' => $invitee->getId(), 'inv_inviter' => $inviter->getId(),
 169+ 'inv_type' => $feature ), __METHOD__ );
 170+
 171+ // Log it.
 172+ $log = new LogPage( 'invite' );
 173+
 174+ $log->addEntry( 'invite', $invitee->getUserName, '', array( $feature ) );
 175+
 176+ Invitations::insertCountRow( $feature, $invitee );
 177+ }
 178+}
Index: trunk/extensions/Invitations/Invitations.php
@@ -0,0 +1,34 @@
 2+<?php
 3+#(c) Andrew Garrett 2007 GPL
 4+
 5+if ( !defined( 'MEDIAWIKI' ) ) {
 6+ echo "FlaggedRevs extension\n";
 7+ exit( 1 );
 8+}
 9+
 10+// So that extensions which optionally allow an invitation model will work
 11+define('Invitations',1);
 12+
 13+$wgExtensionCredits['specialpage'][] = array(
 14+ 'author' => 'Andrew Garrett',
 15+ 'name' => 'Invitations',
 16+ 'url' => 'http://www.mediawiki.org/wiki/Extension:Invitations',
 17+ 'description' => 'Allows management of new features by restricting them to an invitation-based system.'
 18+);
 19+
 20+$wgSpecialPages['Invitations'] = 'SpecialInvitations';
 21+$wgAutoloadClasses['SpecialInvitations'] = dirname(__FILE__) . '/Invitations_page.php';
 22+
 23+$wgAutoloadClasses['Invitations'] = dirname(__FILE__) . '/Invitations_obj.php';
 24+
 25+$wgInvitationTypes = array();
 26+
 27+// Example: $wgInvitationTypes['centralauth'] = array( reserve => 5, limitedinvites => true, invitedelay => 24 * 3600 * 4 );
 28+// Limits invites to 'centralauth' to 5 invites per inviter, which can be used 4 days after the user are invited.
 29+
 30+# Add invite log
 31+$wgLogTypes[] = 'invite';
 32+$wgLogNames['invite'] = 'invite-logpage';
 33+$wgLogHeaders['invite'] = 'invite-logpagetext';
 34+$wgLogActions['invite/invite'] = 'invite-logentry';
 35+
Index: trunk/extensions/Invitations/README
@@ -0,0 +1,13 @@
 2+Invitations Extension
 3+(C) 2007 Andrew Garrett
 4+Some rights are granted under the GNU General Public License (GPL).
 5+&nbsp;See LICENSE for further details.
 6+
 7+The Invitations extension allows certain software features to be enabled
 8+&nbsp;only by an invitation, similar to the way in which the use of Google's
 9+ Gmail was propagated.
 10+
 11+This extension is not yet complete. While the main code has been completed,
 12+ an interface still needs to be created, and the code needs to be tested.
 13+ The maximum guarantee is that this code is valid PHP.
 14+

Status & tagging log