Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Tagged FBConnect (r375) before the rebrand

  • Loading branch information...
commit 619ee829363e6eb8ec91019bc69344e09e768586 0 parents
@garbear authored
Showing with 10,203 additions and 0 deletions.
  1. +39 −0 FBConnect.alias.php
  2. +2,569 −0 FBConnect.i18n.php
  3. +300 −0 FBConnect.php
  4. +291 −0 FBConnectAPI.php
  5. +210 −0 FBConnectDB.php
  6. +783 −0 FBConnectHooks.php
  7. +401 −0 FBConnectLanguage.i18n.php
  8. +154 −0 FBConnectLanguage.php
  9. +73 −0 FBConnectProfilePic.php
  10. +366 −0 FBConnectPushEvent.php
  11. +184 −0 FBConnectUser.php
  12. +184 −0 FBConnectXFBML.php
  13. +291 −0 PreferencesExtension.php
  14. +1,065 −0 SpecialConnect.php
  15. +204 −0 config.default.php
  16. +76 −0 css/fbModal.css
  17. +78 −0 fbconnect.css
  18. +401 −0 fbconnect.js
  19. +963 −0 php-sdk/facebook.php
  20. +121 −0 php-sdk/fb_ca_chain_bundle.crt
  21. +48 −0 prefs.js
  22. +57 −0 pushEvents/FBPushEvents.i18n.php
  23. +59 −0 pushEvents/FBPush_OnAchBadge.php
  24. +62 −0 pushEvents/FBPush_OnAddBlogPost.php
  25. +71 −0 pushEvents/FBPush_OnAddImage.php
  26. +58 −0 pushEvents/FBPush_OnAddVideo.php
  27. +61 −0 pushEvents/FBPush_OnArticleComment.php
  28. +61 −0 pushEvents/FBPush_OnBlogComment.php
  29. +71 −0 pushEvents/FBPush_OnLargeEdit.php
  30. +51 −0 pushEvents/FBPush_OnRateArticle.php
  31. +57 −0 pushEvents/FBPush_OnWatchArticle.php
  32. +10 −0 sql/fbconnect_event_show.pg.sql
  33. +10 −0 sql/fbconnect_event_show.sql
  34. +11 −0 sql/fbconnect_event_stats.pg.sql
  35. +11 −0 sql/fbconnect_event_stats.sql
  36. +10 −0 sql/user_fbconnect.pg.sql
  37. +10 −0 sql/user_fbconnect.sql
  38. +392 −0 templates/ChooseNameTemplate.class.php
  39. +48 −0 templates/ajaxLoginMerge.tmpl.php
  40. +292 −0 wikia/fbconnect_customizations.php
39 FBConnect.alias.php
@@ -0,0 +1,39 @@
+<?php
+/**
+ * Aliases for special pages.
+ *
+ * @file
+ * @ingroup Extensions
+ */
+
+$specialPageAliases = array();
+
+/** English (English) */
+$specialPageAliases['en'] = array(
+ 'Connect' => array( 'Connect', 'ConnectAccount' ),
+);
+
+/** Spanish (Español) */
+$specialPageAliases['es'] = array(
+ 'Connect' => array( 'Conectar', 'ConectarCuenta' ),
+);
+
+/** Japanese (日本語) */
+$specialPageAliases['ja'] = array(
+ 'Connect' => array( '接続' ),
+);
+
+/** Malayalam (മലയാളം) */
+$specialPageAliases['ml'] = array(
+ 'Connect' => array( 'ബന്ധിപ്പിക്കുക', 'അംഗത്വംബന്ധിപ്പിക്കുക' ),
+);
+
+/** Dutch (Nederlands) */
+$specialPageAliases['nl'] = array(
+ 'Connect' => array( 'Verbinden', 'GebruikerVerbinden' ),
+);
+
+/**
+ * For backwards compatibility with MediaWiki 1.15 and earlier.
+ */
+$aliases =& $specialPageAliases;
2,569 FBConnect.i18n.php
2,569 additions, 0 deletions not shown
300 FBConnect.php
@@ -0,0 +1,300 @@
+<?php
+/*
+ * Copyright © 2008-2010 Garrett Brown <http://www.mediawiki.org/wiki/User:Gbruin>
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * FBConnect plugin. Integrates Facebook Connect into MediaWiki.
+ *
+ * Features include single sign on (SSO) experience and XFBML.
+ *
+ * Info is available at <http://www.mediawiki.org/wiki/Extension:FBConnect>.
+ * Limited support is available at
+ * <http://www.mediawiki.org/wiki/Extension_talk:FBConnect>.
+ *
+ * @file
+ * @ingroup Extensions
+ * @author Garrett Brown, Sean Colombo
+ * @copyright Copyright © 2010 Garrett Brown, Sean Colombo
+ * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
+ */
+
+
+/*
+ * Not a valid entry point, skip unless MEDIAWIKI is defined.
+ */
+if ( !defined( 'MEDIAWIKI' ) ) {
+ die( 'This file is a MediaWiki extension, it is not a valid entry point' );
+}
+
+// Make it so that the code will survive the push until the config gets updated.
+$wgEnablePreferencesExt = true;
+
+// FBConnect version
+define( 'MEDIAWIKI_FBCONNECT_VERSION', '3.0-beta, January 2, 2011' );
+
+// Magic string to use in substitution (must be defined prior to including config.php).
+define( 'FBCONNECT_LOCALE', '%LOCALE%');
+
+/*
+ * Add information about this extension to Special:Version.
+ */
+$wgExtensionCredits['specialpage'][] = array(
+ 'path' => __FILE__,
+ 'name' => 'Facebook Connect Plugin',
+ 'author' => 'Garrett Brown, Sean Colombo, Tomek Odrobny',
+ 'url' => 'http://www.mediawiki.org/wiki/Extension:FBConnect',
+ 'descriptionmsg' => 'fbconnect-desc',
+ 'version' => MEDIAWIKI_FBCONNECT_VERSION,
+);
+
+/*
+ * Initialization of the autoloaders and special extension pages.
+ */
+$dir = dirname( __FILE__ ) . '/';
+// Load the default configuration
+// It's recommended that you override these in LocalSettings.php
+include_once $dir . 'config.default.php';
+// If config.php exists, import those settings over the default ones
+if (file_exists( $dir . 'config.php' )) {
+ require_once $dir . 'config.php';
+}
+// Load the PHP SDK
+require_once $dir . 'php-sdk/facebook.php';
+
+$wgExtensionFunctions[] = 'FBConnect::init';
+
+if( !empty( $wgFbEnablePushToFacebook ) ) {
+ // Need to include it explicitly instead of autoload since it has initialization code of its own.
+ // This should be done after FBConnect::init is added to wgExtensionFunctions so that FBConnect
+ // gets fully initialized first.
+ require_once $dir . 'FBConnectPushEvent.php';
+}
+
+$wgExtensionMessagesFiles['FBConnect'] = $dir . 'FBConnect.i18n.php';
+$wgExtensionMessagesFiles['FBPushEvents'] = $dir . 'pushEvents/FBPushEvents.i18n.php';
+$wgExtensionMessagesFiles['FBConnectLanguage'] = $dir . 'FBConnectLanguage.i18n.php';
+$wgExtensionAliasesFiles['FBConnect'] = $dir . 'FBConnect.alias.php';
+
+$wgAutoloadClasses['FBConnectAPI'] = $dir . 'FBConnectAPI.php';
+$wgAutoloadClasses['FBConnectDB'] = $dir . 'FBConnectDB.php';
+$wgAutoloadClasses['FBConnectHooks'] = $dir . 'FBConnectHooks.php';
+$wgAutoloadClasses['FBConnectProfilePic'] = $dir . 'FBConnectProfilePic.php';
+$wgAutoloadClasses['FBConnectLanguage'] = $dir . 'FBConnectLanguage.php';
+$wgAutoloadClasses['FBConnectUser'] = $dir . 'FBConnectUser.php';
+$wgAutoloadClasses['FBConnectXFBML'] = $dir . 'FBConnectXFBML.php';
+$wgAutoloadClasses['SpecialConnect'] = $dir . 'SpecialConnect.php';
+$wgAutoloadClasses['ChooseNameTemplate'] = $dir . 'templates/ChooseNameTemplate.class.php';
+
+$wgSpecialPages['Connect'] = 'SpecialConnect';
+
+// Define new autopromote condition (use quoted text, numbers can cause collisions)
+define( 'APCOND_FB_INGROUP', 'fb*g' );
+define( 'APCOND_FB_ISOFFICER', 'fb*o' );
+define( 'APCOND_FB_ISADMIN', 'fb*a' );
+
+// rt#68127 - Giving basic permissions to other groups might open security holes
+// See <http://trac.wikia-code.com/changeset/27160> and <http://trac.wikia-code.com/changeset/27928> for fix
+$wgGroupPermissions['fb-user'] = $wgGroupPermissions['user']; // Create a new group for Facebook users
+
+$wgAjaxExportList[] = 'FBConnect::disconnectFromFB';
+$wgAjaxExportList[] = 'SpecialConnect::getLoginButtonModal';
+$wgAjaxExportList[] = 'SpecialConnect::ajaxModalChooseName';
+$wgAjaxExportList[] = 'SpecialConnect::checkCreateAccount';
+
+// These hooks need to be hooked up prior to init() because runhooks may be called for them before init is run.
+$wgFbHooksToAddImmediately = array( 'SpecialPage_initList' );
+foreach( $wgFbHooksToAddImmediately as $hookName ) {
+ $wgHooks[$hookName][] = "FBConnectHooks::$hookName";
+}
+
+/**
+ * Class FBConnect
+ *
+ * This class initializes the extension, and contains the core non-hook,
+ * non-authentification code.
+ */
+class FBConnect {
+ static private $fbOnLoginJs;
+
+ /**
+ * Initializes and configures the extension.
+ */
+ public static function init() {
+ global $wgXhtmlNamespaces, $wgSharedTables, $facebook, $wgHooks,
+ $wgFbOnLoginJsOverride, $wgFbHooksToAddImmediately, $wgFbUserRightsFromGroup;
+
+ // The xmlns:fb attribute is required for proper rendering on IE
+ $wgXhtmlNamespaces['fb'] = 'http://www.facebook.com/2008/fbml';
+
+ // Facebook/username associations should be shared when $wgSharedDB is enabled
+ $wgSharedTables[] = 'user_fbconnect';
+
+ // Create our Facebook instance and make it available through $facebook
+ $facebook = new FBConnectAPI();
+
+ // Install all public static functions in class FBConnectHooks as MediaWiki hooks
+ $hooks = self::enumMethods( 'FBConnectHooks' );
+ foreach( $hooks as $hookName ) {
+ if (!in_array( $hookName, $wgFbHooksToAddImmediately )) {
+ $wgHooks[$hookName][] = "FBConnectHooks::$hookName";
+ }
+ }
+
+ // Allow configurable over-riding of the onLogin handler.
+ if( !empty( $wgFbOnLoginJsOverride ) ) {
+ self::$fbOnLoginJs = $wgFbOnLoginJsOverride;
+ } else {
+ self::$fbOnLoginJs = 'window.location.reload(true);';
+ }
+
+ // Default to pull new info from Facebook
+ global $wgDefaultUserOptions;
+ foreach (FBConnectUser::$availableUserUpdateOptions as $option) {
+ $wgDefaultUserOptions["fbconnect-update-on-login-$option"] = 1;
+ }
+
+ // If we are configured to pull group info from Facebook, then set up
+ // the group permissions here
+ if ( !empty( $wgFbUserRightsFromGroup ) ) {
+ global $wgGroupPermissions, $wgImplictGroups, $wgAutopromote;
+ $wgGroupPermissions['fb-groupie'] = $wgGroupPermissions['user'];
+ $wgGroupPermissions['fb-officer'] = $wgGroupPermissions['bureaucrat'];
+ $wgGroupPermissions['fb-admin'] = $wgGroupPermissions['sysop'];
+ $wgImplictGroups[] = 'fb-groupie';
+ $wgImplictGroups[] = 'fb-officer';
+ $wgImplictGroups[] = 'fb-admin';
+ $wgAutopromote['fb-groupie'] = APCOND_FB_INGROUP;
+ $wgAutopromote['fb-officer'] = APCOND_FB_ISOFFICER;
+ $wgAutopromote['fb-admin'] = APCOND_FB_ISADMIN;
+ }
+ }
+
+ /**
+ * Returns an array with the names of all public static functions
+ * in the specified class.
+ */
+ public static function enumMethods( $className ) {
+ $hooks = array();
+ try {
+ $class = new ReflectionClass( $className );
+ foreach( $class->getMethods( ReflectionMethod::IS_PUBLIC ) as $method ) {
+ if ( $method->isStatic() ) {
+ $hooks[] = $method->getName();
+ }
+ }
+ } catch( Exception $e ) {
+ // If PHP's version doesn't support the Reflection API, then exit
+ die( 'PHP version (' . phpversion() . ') must be great enough to support the Reflection API' );
+ // Or list the extensions here manually...
+ $hooks = array(
+ 'AuthPluginSetup', 'UserLoadFromSession',
+ 'RenderPreferencesForm', 'PersonalUrls',
+ 'ParserAfterTidy', 'BeforePageDisplay', /*...*/
+ );
+ }
+ return $hooks;
+ }
+
+ /**
+ * Return the code for the permissions attribute (with leading space) to use on all fb:login-buttons.
+ */
+ public static function getPermissionsAttribute() {
+ global $wgFbExtendedPermissions;
+ $attr = '';
+ if (!empty($wgFbExtendedPermissions)) {
+ $attr = ' perms="' . implode( ',', $wgFbExtendedPermissions ) . '"';
+ }
+ return $attr;
+ } // end getPermissionsAttribute()
+
+ /**
+ * Return the code for the onlogin attribute which should be appended to all fb:login-button's in this
+ * extension.
+ *
+ * TODO: Generate the entire fb:login-button in a function in this class. We have numerous buttons now.
+ */
+ public static function getOnLoginAttribute() {
+ $attr = '';
+ if ( !empty( self::$fbOnLoginJs ) ) {
+ $attr = ' onlogin="' . self::$fbOnLoginJs . '"';
+ }
+ return $attr;
+ } // end getOnLoginAttribute()
+
+ public static function getFBButton( $onload = '', $id = '' ) {
+ global $wgFbExtendedPermissions;
+ return '<fb:login-button length="short" size="large" onlogin="' . $onload .
+ '" perms="' . implode(',', $wgFbExtendedPermissions) . '" id="' . $id .
+ '"></fb:login-button>';
+ }
+
+ /**
+ * Ajax function to disconect from Facebook.
+ */
+ public static function disconnectFromFB( $user = null ) {
+ $response = new AjaxResponse();
+ $response->addText(json_encode(self::coreDisconnectFromFB($user)));
+ return $response;
+ }
+
+ /**
+ * Facebook disconnect function and send mail with temp password.
+ */
+ public static function coreDisconnectFromFB( $user = null ) {
+ global $wgRequest, $wgUser, $wgAuth;
+
+ wfLoadExtensionMessages('FBConnect');
+
+ if ($user == null) {
+ $user = $wgUser;
+ }
+ $statusError = array('status' => 'error', 'msg' => wfMsg('fbconnect-unknown-error'));
+
+ if ($user->getId() == 0) {
+ return $statusError;
+ }
+
+ $dbw = wfGetDB( DB_MASTER, array(), FBConnectDB::sharedDB() );
+ $dbw->begin();
+ $rows = FBConnectDB::removeFacebookID($user);
+
+ // Remind password attemp
+ $params = new FauxRequest(array (
+ 'wpName' => $user->getName()
+ ));
+
+ if( !$wgAuth->allowPasswordChange() ) {
+ return $statusError;
+ }
+
+ $result = array();
+ $loginForm = new LoginForm($params);
+
+ if ($wgUser->getOption('fbFromExist')) {
+ $res = $loginForm->mailPasswordInternal( $wgUser, true, 'fbconnect-passwordremindertitle-exist', 'fbconnect-passwordremindertext-exist' );
+ } else {
+ $res = $loginForm->mailPasswordInternal( $wgUser, true, 'fbconnect-passwordremindertitle', 'fbconnect-passwordremindertext' );
+ }
+
+ if( WikiError::isError( $res ) ) {
+ return $statusError;
+ }
+
+ return array( 'status' => 'ok' );
+ $dbw->commit();
+ return $response;
+ }
+}
291 FBConnectAPI.php
@@ -0,0 +1,291 @@
+<?php
+/*
+ * Copyright © 2008-2010 Garrett Brown <http://www.mediawiki.org/wiki/User:Gbruin>
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+/*
+ * Not a valid entry point, skip unless MEDIAWIKI is defined.
+ */
+if ( !defined( 'MEDIAWIKI' ) ) {
+ die( 'This file is a MediaWiki extension, it is not a valid entry point' );
+}
+
+
+/**
+ * Class FBConnectAPI
+ *
+ * This class contains the code used to interface with Facebook via the
+ * Facebook Platform API.
+ */
+class FBConnectAPI extends Facebook {
+ // Constructor
+ public function __construct() {
+ global $wgFbAppId, $wgFbSecret, $wgFbDomain;
+ // Check to make sure config.default.php was renamed properly, unless we
+ // are running update.php from the command line
+ // TODO: use $wgCommandLineMode, if it is propper to do so
+ if ( !defined( 'MW_CMDLINE_CALLBACK' ) && !$this->isConfigSetup() ) {
+ die ( '<strong>Please update $wgFbAppId and $wgFbSecret.</strong>' );
+ }
+ $config = array(
+ 'appId' => $wgFbAppId,
+ 'secret' => $wgFbSecret,
+ 'cookie' => true,
+ );
+ // Include the optional domain parameter if it has been set
+ if ( !empty( $wgFbDomain ) && $wgFbDomain != 'BASE_DOMAIN' ) {
+ $config['domain'] = $wgFbDomain;
+ }
+ parent::__construct( $config );
+ }
+
+ /**
+ * Check to make sure config.sample.php was properly renamed to config.php
+ * and the instructions to fill out the first two important variables were
+ * followed correctly.
+ */
+ public function isConfigSetup() {
+ global $wgFbAppId, $wgFbSecret;
+ $isSetup = isset( $wgFbAppId ) && $wgFbAppId != 'YOUR_APP_KEY' &&
+ isset( $wgFbSecret ) && $wgFbSecret != 'YOUR_SECRET';
+ if( !$isSetup ) {
+ // Check to see if they are still using the old variables
+ global $fbApiKey, $fbApiSecret;
+ if ( isset( $fbApiKey ) ) {
+ $wgFbAppId = $fbApiKey;
+ }
+ if ( isset( $fbApiSecret ) ) {
+ $wgFbSecret= $fbApiSecret;
+ }
+ $isSetup = isset( $wgFbAppId ) && $wgFbAppId != 'YOUR_APP_KEY' &&
+ isset( $wgFbSecret ) && $wgFbSecret != 'YOUR_SECRET';
+ }
+ return $isSetup;
+ }
+
+ /**
+ * Calls users.getInfo. Requests information about the user from Facebook.
+ */
+ public function getUserInfo() {
+ // First check to see if we have a session (if not, return null)
+ $user = $this->getUser();
+ if ( !$user ) {
+ return null;
+ }
+ try {
+ // Cache information about users to reduce the number of Facebook hits
+ static $userinfo = array();
+
+ if ( !isset( $userinfo[$user] ) ) {
+ // Query the Facebook API with the users.getInfo method
+ $query = array(
+ 'method' => 'users.getInfo',
+ 'uids' => $user,
+ 'fields' => join( ',', array(
+ 'first_name', 'name', 'sex', 'timezone', 'locale',
+ /*'profile_url',*/
+ 'username', 'proxied_email', 'contact_email',
+ )),
+ );
+ $user_details = $this->api( $query );
+ // Cache the data in the $userinfo array
+ // Also avoid "Notice: Uninitialized string offset: 0"
+ $userinfo[$user] = !empty( $user_details[0] ) ? $user_details[0] : null;
+ }
+ return isset( $userinfo[$user] ) ? $userinfo[$user] : null;
+ } catch ( FacebookApiException $e ) {
+ error_log( 'Failure in the api when requesting users.getInfo ' .
+ "on uid $user: " . $e->getMessage() );
+ }
+ return null;
+ }
+
+ /**
+ * Retrieves group membership data from Facebook.
+ */
+ public function getGroupRights( $user = null ) {
+ global $wgFbUserRightsFromGroup;
+
+ // Groupies can be members, officers or admins (the latter two infer the former)
+ $rights = array(
+ 'member' => false,
+ 'officer' => false,
+ 'admin' => false
+ );
+
+ $gid = !empty( $wgFbUserRightsFromGroup ) ? $wgFbUserRightsFromGroup : false;
+ // If no group ID is specified, then there's no group to belong to
+ if (
+ !$gid ||
+ // If $user wasn't specified, set it to the logged in user
+ !$user ||
+ // If there is no logged in user
+ !( $user = $this->getUser() )
+ ) {
+ return $rights;
+ }
+
+ // If a User object was provided, translate it into a Facebook ID
+ if ( $user instanceof User ) {
+ // TODO: Does this call for a special api call without access_token?
+ $users = FBConnectDB::getFacebookIDs( $user );
+ if ( count($users) ) {
+ $user = $users[0];
+ } else {
+ // Not a Connected user, can't be in a group
+ return $rights;
+ }
+ }
+
+ // Cache the rights for an individual user to prevent hitting Facebook for duplicate info
+ static $rights_cache = array();
+ if ( array_key_exists( $user, $rights_cache ) ) {
+ // Retrieve the rights from the cache
+ return $rights_cache[$user];
+ }
+
+ // This can contain up to 500 IDs, avoid requesting this info twice
+ static $members = false;
+ // Get a random 500 group members, along with officers, admins and not_replied's
+ if ( $members === false ) {
+ try {
+ // Check to make sure our session is still valid
+ $members = $this->api( array(
+ 'method' => 'groups.getMembers',
+ 'gid' => $gid
+ ));
+ } catch ( FacebookApiException $e ) {
+ // Invalid session; we're not going to be able to get the rights
+ error_log( $e );
+ $rights_cache[$user] = $rights;
+ return $rights;
+ }
+ }
+
+ if ( $members ) {
+ // Check to see if the user is an officer
+ if ( array_key_exists( 'officers', $members ) && in_array( $user, $members['officers'] ) ) {
+ $rights['member'] = $rights['officer'] = true;
+ }
+ // Check to see if the user is an admin of the group
+ if ( array_key_exists( 'admins', $members ) && in_array( $user, $members['admins'] ) ) {
+ $rights['member'] = $rights['admin'] = true;
+ }
+ // Because the latter two rights infer the former, this step isn't always necessary
+ if( !$rights['member'] ) {
+ // Check to see if we are one of the (up to 500) random users
+ if ( ( array_key_exists( 'not_replied', $members ) && is_array( $members['not_replied'] ) &&
+ in_array( $user, $members['not_replied'] ) ) || in_array( $user, $members['members'] ) ) {
+ $rights['member'] = true;
+ } else {
+ // For groups of over 500ish, we must use this extra API call
+ // Notice that it occurs last, because we can hopefully avoid having to call it
+ try {
+ $group = $this->api( array(
+ 'method' => 'groups.get',
+ 'uid' => $user,
+ 'gids' => $gid
+ ));
+ } catch ( FacebookApiException $e ) {
+ error_log( $e );
+ }
+ if ( is_array( $group ) && is_array( $group[0] ) && $group[0]['gid'] == "$gid" ) {
+ $rights['member'] = true;
+ }
+ }
+ }
+ }
+ // Cache the rights
+ $rights_cache[$user] = $rights;
+ return $rights;
+ }
+
+ /*
+ * Publish message on Facebook wall.
+ */
+ public function publishStream( $href, $description, $short, $link, $img ) {
+ /*
+ // Retrieve the message and substitute the params for the actual values
+ $msg = wfMsg( $message_name ) ;
+ foreach ($params as $key => $value) {
+ $msg = str_replace($key, $value, $msg);
+ }
+ // If $FB_NAME isn't provided, simply blank it out
+ $msg = str_replace('$FB_NAME', '', $msg);
+
+ /**/
+ $attachment = array(
+ 'name' => $link,
+ 'href' => $href,
+ 'description' => $description,
+ 'media' => array(array(
+ 'type' => 'image',
+ 'src' => $img,
+ 'href' => $href,
+ )),
+ );
+ /*
+ if( count( $media ) > 0 ) {
+ foreach ( $media as $value ) {
+ $attachment['media'][] = $value;
+ }
+ }
+ /**/
+
+ $query = array(
+ 'method' => 'stream.publish',
+ 'message' => $short,
+ 'attachment' => json_encode( $attachment ),
+ /*
+ 'action_links' => json_encode( array(
+ 'text' => $link_title,
+ 'href' => $link
+ )),
+ /**/
+ );
+
+ $result = json_decode( $this->api( $query ) );
+
+ if ( is_array( $result ) ) {
+ // Error
+ #error_log( FacebookAPIErrorCodes::$api_error_descriptions[$result] );
+ error_log( "stream.publish returned error code $result->error_code" );
+ return $result->error_code;
+ }
+ else if ( is_string( $result ) ) {
+ // Success! Return value is "$UserId_$PostId"
+ return 0;
+ } else {
+ error_log( 'stream.publish: Unknown return type: ' . gettype( $result ) );
+ return -1;
+ }
+ }
+
+ /**
+ * Verify that the user ID matches the hash provided by the GET parameters
+ * in the account reclaimation link. This algorithm comes from the function
+ * Facebook::verify_account_reclamation($user, $hash) in the old Facebook
+ * PHP Client Library (replaced by the PHP SDK in 2010).
+ *
+ * See also <http://wiki.developers.facebook.com/index.php/Reclaiming_Accounts>.
+ */
+ function verifyAccountReclamation( $fb_user_id, $hash ) {
+ if ( $hash != md5( $user . $this->apiSecret ) ) {
+ return false;
+ }
+ return FBConnectDB::getUser( $fb_user_id );
+ }
+}
210 FBConnectDB.php
@@ -0,0 +1,210 @@
+<?php
+/*
+ * Copyright © 2010 Garrett Brown <http://www.mediawiki.org/wiki/User:Gbruin>
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+/*
+ * Not a valid entry point, skip unless MEDIAWIKI is defined.
+ */
+if ( !defined( 'MEDIAWIKI' ) ) {
+ die( 'This file is a MediaWiki extension, it is not a valid entry point' );
+}
+
+
+/**
+ * Class FBConnectDB
+ *
+ * This class abstracts the manipulation of the custom table used by this
+ * extension. If $wgDBprefix is set, this class will pull from the translated
+ * tables. If the table 'users_fbconnect' does not exist in your database
+ * you will receive errors like this:
+ *
+ * Database error from within function "FBConnectDB::getUser". Database
+ * returned error "Table 'user_fbconnect' doesn't exist".
+ *
+ * In this case, you will need to fix this by running the MW updater:
+ * >php maintenance/update.php
+ */
+class FBConnectDB {
+ /**
+ * Find the Facebook IDs of the given user, if any, using the database connection provided.
+ *
+ */
+ public static function getFacebookIDs( $user, $db = DB_SLAVE ) {
+ global $wgMemc;
+
+ $dbr = wfGetDB( $db, array(), self::sharedDB() );
+ $fbid = array();
+ if ( $user instanceof User && $user->getId() != 0 ) {
+ $memkey = wfMemcKey( "fb_user_id", $user->getId() );
+ $val = $wgMemc->get($memkey);
+ if ( ( is_array($val) ) && ( $db == DB_SLAVE ) ){
+ return $val;
+ }
+
+ $prefix = self::getPrefix();
+ $res = $dbr->select(
+ array( "{$prefix}user_fbconnect" ),
+ array( 'user_fbid' ),
+ array( 'user_id' => $user->getId() ),
+ __METHOD__
+ );
+ foreach( $res as $row ) {
+ $fbid[] = $row->user_fbid;
+ }
+ $res->free();
+ $wgMemc->set($memkey,$fbid);
+ }
+ return $fbid;
+ }
+
+ /**
+ * Find the user by their Facebook ID.
+ * If there is no user found for the given id, returns null.
+ */
+ public static function getUser( $fbid ) {
+ $prefix = self::getPrefix();
+
+ // NOTE: Do not just pass this dbr into getUserByDB since that function prevents
+ // rewriting of the database name for shared tables.
+ $dbr = wfGetDB( DB_SLAVE, array(), self::sharedDB() );
+
+ $id = $dbr->selectField(
+ array( "{$prefix}user_fbconnect" ),
+ array( 'user_id' ),
+ array( 'user_fbid' => $fbid ),
+ __METHOD__
+ );
+
+ if ( $id ) {
+ return User::newFromId( $id );
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Given a facebook id and database connection with read permission,
+ * finds the Facebook user by their id.
+ * If there is no user found for the given id, returns null.
+ */
+ public static function getUserByDB( $fbid, $dbr ){
+ $prefix = self::getPrefix();
+ $id = $dbr->selectField(
+ "`{$prefix}user_fbconnect`",
+ 'user_id',
+ array( 'user_fbid' => $fbid ),
+ __METHOD__
+ );
+ if ( $id ) {
+ return User::newFromId( $id );
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Add a User <-> Facebook ID association to the database.
+ */
+ public static function addFacebookID( $user, $fbid ) {
+ global $wgMemc;
+ wfProfileIn( __METHOD__ );
+
+ $memkey = wfMemcKey( "fb_user_id", $user->getId() );
+
+ if($user->getId() == 0){
+ wfDebug("FBConnect: tried to store a mapping from fbid \"$fbid\" to a user with no id (ie: not logged in).\n");
+ } else {
+ $prefix = self::getPrefix();
+ $dbw = wfGetDB( DB_MASTER, array(), self::sharedDB() );
+ $dbw->insert(
+ "{$prefix}user_fbconnect",
+ array(
+ 'user_id' => $user->getId(),
+ 'user_fbid' => $fbid
+ ),
+ __METHOD__
+ );
+ }
+
+ $dbw->commit();
+ $wgMemc->set($memkey, self::getFacebookIDs($user, DB_MASTER ) );
+
+ wfProfileOut( __METHOD__ );
+ }
+
+ /**
+ * Remove a User <-> Facebook ID association from the database.
+ */
+ public static function removeFacebookID( $user ) {
+ global $wgMemc;
+ $prefix = self::getPrefix();
+ if ( $user instanceof User && $user->getId() != 0 ) {
+ $dbw = wfGetDB( DB_MASTER, array(), self::sharedDB() );
+ $memkey = wfMemcKey( "fb_user_id", $user->getId() );
+ $dbw->delete(
+ "{$prefix}user_fbconnect",
+ array(
+ 'user_id' => $user->getId()
+ ),
+ __METHOD__
+ );
+ $dbw->commit();
+ $wgMemc->set($memkey, self::getFacebookIDs($user, DB_MASTER ) );
+ }
+
+ return (bool) $dbw->affectedRows();
+ }
+
+ /**
+ * Estimates the total number of User <-> Facebook ID associations in the
+ * database. If there are no users, then the estimate will probably be 1.
+ */
+ public static function countUsers() {
+ $prefix = self::getPrefix();
+ $dbr = wfGetDB( DB_SLAVE, array(), self::sharedDB() );
+ // An estimate is good enough for choosing a unique nickname
+ $count = $dbr->estimateRowCount( "{$prefix}user_fbconnect" );
+ // Avoid returning 0 or -1
+ return $count >= 1 ? $count : 1;
+ }
+
+ /**
+ * Returns the name of the shared database, if one is in use for the Facebook
+ * Connect users table. Note that 'user_fbconnect' (without respecting
+ * $wgSharedPrefix) is added to $wgSharedTables in FBConnect::init by default.
+ * This function can also be used as a test for whether a shared database for
+ * Facebook Connect users is in use.
+ *
+ * See also: <http://www.mediawiki.org/wiki/Manual:Shared_database>
+ */
+ public static function sharedDB() {
+ global $wgExternalSharedDB;
+ if ( !empty( $wgExternalSharedDB ) ) {
+ return $wgExternalSharedDB;
+ }
+ return false;
+ }
+
+ /**
+ * Returns the table prefix name, either $wgDBprefix, $wgSharedPrefix
+ * depending on whether a shared database is in use.
+ */
+ private static function getPrefix() {
+ global $wgDBprefix, $wgSharedPrefix;
+ return self::sharedDB() ? $wgSharedPrefix : $wgDBprefix;
+ }
+}
783 FBConnectHooks.php
@@ -0,0 +1,783 @@
+<?php
+/*
+ * Copyright © 2008-2010 Garrett Brown <http://www.mediawiki.org/wiki/User:Gbruin>
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Class FBConnectHooks
+ *
+ * This class contains all the hooks used in this extension. HOOKS DO NOT NEED
+ * TO BE EXPLICITLY ADDED TO $wgHooks. Simply write a public static function
+ * with the same name as the hook that provokes it, place it inside this class
+ * and let FBConnect::init() do its magic. Helper functions should be private,
+ * because only public static methods are added as hooks.
+ */
+class FBConnectHooks {
+ /**
+ * Hook is called whenever an article is being viewed... Currently, figures
+ * out the Facebook ID of the user that the userpage belongs to.
+ */
+ public static function ArticleViewHeader( &$article, &$outputDone, &$pcache ) {
+ // Get the article title
+ $nt = $article->getTitle();
+ // If the page being viewed is a user page
+ if ($nt && $nt->getNamespace() == NS_USER && strpos($nt->getText(), '/') === false) {
+ $user = User::newFromName($nt->getText());
+ if (!$user || $user->getID() == 0) {
+ return true;
+ }
+ $fb_id = FBConnectDB::getFacebookIDs($user->getId());
+ if (!count($fb_id) || !($fb_id = $fb_id[0])) {
+ return true;
+ }
+ // TODO: Something with the Facebook ID stored in $fb_id
+ return true;
+ }
+ return true;
+ }
+
+ /**
+ * Checks the autopromote condition for a user.
+ */
+ static function AutopromoteCondition( $cond_type, $args, $user, &$result ) {
+ global $wgFbUserRightsFromGroup;
+
+ // Probably a redundant check, but with PHP you can never be too sure...
+ if (empty($wgFbUserRightsFromGroup)) {
+ // No group to pull rights from, so the user can't be a member
+ $result = false;
+ return true;
+ }
+ $types = array(
+ APCOND_FB_INGROUP => 'member',
+ APCOND_FB_ISOFFICER => 'officer',
+ APCOND_FB_ISADMIN => 'admin'
+ );
+ $type = $types[$cond_type];
+ switch( $type ) {
+ case 'member':
+ case 'officer':
+ case 'admin':
+ global $facebook;
+ // Connect to the Facebook API and ask if the user is in the group
+ $rights = $facebook->getGroupRights($user);
+ $result = $rights[$type];
+ }
+ return true;
+ }
+
+ /**
+ * Injects some important CSS and Javascript into the <head> of the page.
+ */
+ public static function BeforePageDisplay( &$out, &$sk ) {
+ global $wgUser, $wgVersion, $wgFbLogo, $wgFbScript, $wgFbExtensionScript,
+ $wgFbScriptEnableLocales, $wgJsMimeType, $wgStyleVersion;
+
+ // Wikiaphone mobile device skin doesn't need JS or CSS additions
+ if ( get_class( $wgUser->getSkin() ) === 'SkinWikiaphone' )
+ return true;
+
+ // If the user's language is different from the default language, use the correctly localized facebook code.
+ // NOTE: Can't use wgLanguageCode here because the same FBConnect config can run for many wgLanguageCode's on one site (such as Wikia).
+ if ($wgFbScriptEnableLocales) {
+ global $wgFbScriptLangCode, $wgLang;
+ wfProfileIn(__METHOD__ . "::fb-locale-by-mediawiki-lang");
+ if ($wgLang->getCode() !== $wgFbScriptLangCode) {
+ // Attempt to find a matching facebook locale.
+ $defaultLocale = FBConnectLanguage::getFbLocaleForLangCode($wgFbScriptLangCode);
+ $locale = FBConnectLanguage::getFbLocaleForLangCode($wgLang->getCode());
+ if($defaultLocale != $locale){
+ global $wgFbScriptByLocale;
+ $wgFbScript = str_replace(FBCONNECT_LOCALE, $locale, $wgFbScriptByLocale);
+ }
+ }
+ wfProfileOut(__METHOD__ . "::fb-locale-by-mediawiki-lang");
+ }
+
+ // Asynchronously load the Facebook Connect JavaScript SDK before the page's content
+ global $wgNoExternals;
+ if ( !empty($wgFbScript) && empty($wgNoExternals) ) {
+ $out->prependHTML('
+ <div id="fb-root"></div>
+ <script type="text/javascript">
+ (function(){var e=document.createElement("script");e.type="' .
+ $wgJsMimeType . '";e.src="' . $wgFbScript .
+ '";e.async=true;document.getElementById("fb-root").appendChild(e)})();
+ </script>' . "\n"
+ );
+ }
+
+ // Inserts list of global JavaScript variables if necessary
+ if (self::MGVS_hack( $mgvs_script )) {
+ $out->addInlineScript( $mgvs_script );
+ }
+
+ // Add a Facebook logo to the class .mw-fblink
+ $style = empty($wgFbLogo) ? '' : <<<STYLE
+ /* Add a pretty logo to Facebook links */
+ .mw-fblink {
+ background: url($wgFbLogo) top left no-repeat !important;
+ padding-left: 17px !important;
+ }
+STYLE;
+
+ // Things get a little simpler in 1.16...
+ if ( version_compare( $wgVersion, '1.16', '>=' ) ) {
+ // Add a pretty Facebook logo if $wgFbLogo is set
+ if ( !empty( $wgFbLogo) ) {
+ $out->addInlineStyle( $style );
+ }
+ // Include the common jQuery library (alias defaults to $j instead of $)
+ $out->includeJQuery();
+ // Add the script file specified by $url
+ if ( !empty( $wgFbExtensionScript ) ) {
+ $out->addScriptFile( $wgFbExtensionScript );
+ }
+ } else {
+ // Add a pretty Facebook logo if $wgFbLogo is set
+ if ( !empty( $wgFbLogo) ) {
+ $out->addScript( '<style type="text/css">' . $style . '</style>' );
+ }
+ // Include the most recent 1.4 version (currently 1.4.4)
+ $out->addScriptFile( 'http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js' );
+ // Add the script file specified by $url
+ if( !empty( $wgFbExtensionScript ) ) {
+ $out->addScript("<script type=\"$wgJsMimeType\" src=\"$wgFbExtensionScript?$wgStyleVersion\"></script>\n");
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Fired when MediaWiki is updated (from the command line updater utility or,
+ * if using version 1.17+, from the initial installer). This hook allows
+ * FBConnect to update the database with the required tables. Each table
+ * listed below should have a corresponding schema file in the sql directory
+ * for each supported database type.
+ *
+ * MYSQL ONLY: If $wgDBprefix is set, then the table 'user_fbconnect' will
+ * be prefixed accordingly. Make sure that the .sql files are modified with
+ * the database prefix beforehand.
+ *
+ * The $updater parameter added in r71140 (after 1.16)
+ * <http://svn.wikimedia.org/viewvc/mediawiki?view=revision&revision=71140>
+ */
+ static function LoadExtensionSchemaUpdates( $updater = null ) {
+ global $wgSharedDB, $wgDBname, $wgDBtype, $wgDBprefix;
+ // Don't create tables on a shared database
+ if( !empty( $wgSharedDB ) && $wgSharedDB !== $wgDBname ) {
+ return true;
+ }
+ // Tables to add to the database
+ $tables = array( 'user_fbconnect', 'fbconnect_event_stats', 'fbconnect_event_show' );
+ // Sql directory inside the extension folder
+ $sql = dirname( __FILE__ ) . '/sql';
+ // Extension of the table schema file (depending on the database type)
+ switch ( $updater !== null ? $updater->getDB()->getType() : $wgDBtype ) {
+ case 'mysql':
+ $ext = 'sql';
+ break;
+ case 'postgres':
+ $ext = 'pg.sql';
+ break;
+ default:
+ $ext = 'sql';
+ }
+ // Do the updating
+ foreach ( $tables as $table ) {
+ if ( $wgDBprefix ) {
+ $table = $wgDBprefix . $table;
+ }
+ // Location of the table schema file
+ $schema = "$sql/$table.$ext";
+ // If we're using the new version of the LoadExtensionSchemaUpdates hook
+ if ( $updater !== null ) {
+ $updater->addExtensionUpdate( array( 'addTable', $table, $schema, true ) );
+ } else {
+ global $wgExtNewTables;
+ $wgExtNewTables[] = array( $table, $schema );
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Adds several Facebook Connect variables to the page:
+ *
+ * fbAppId The application ID (see $wgFbAppId in config.php)
+ * fbSession Assist the JavaScript SDK with loading the session
+ * fbUseMarkup Should XFBML tags be rendered (see $wgFbUseMarkup in config.php)
+ * fbLogo Facebook logo (see $wgFbLogo in config.php)
+ * fbLogoutURL The URL to be redirected to on a disconnect
+ *
+ * This hook was added in MediaWiki version 1.14. See:
+ * http://svn.wikimedia.org/viewvc/mediawiki/trunk/phase3/includes/Skin.php?view=log&pathrev=38397
+ * If we are not at revision 38397 or later, this function is called from BeforePageDisplay
+ * to retain backward compatability.
+ */
+ public static function MakeGlobalVariablesScript( &$vars ) {
+ global $wgFbAppId, $facebook, $wgFbUseMarkup, $wgFbLogo, $wgTitle, $wgRequest, $wgStyleVersion;
+ if (!isset($vars['wgPageQuery'])) {
+ $query = $wgRequest->getValues();
+ if (isset($query['title'])) {
+ unset($query['title']);
+ }
+ $vars['wgPageQuery'] = wfUrlencode( wfArrayToCGI( $query ) );
+ }
+ if (!isset($vars['wgStyleVersion'])) {
+ $vars['wgStyleVersion'] = $wgStyleVersion;
+ }
+ $vars['fbAppId'] = $wgFbAppId;
+ // TODO: For debugging purposes -- remove in production
+ $vars['fbSession'] = $facebook->getSession();
+ $vars['fbUseMarkup'] = $wgFbUseMarkup;
+ $vars['fbLogo'] = $wgFbLogo ? true : false;
+ $vars['fbLogoutURL'] = Skin::makeSpecialUrl( 'Userlogout',
+ $wgTitle->isSpecial('Preferences') ? '' : 'returnto=' . $wgTitle->getPrefixedURL() );
+
+ $vals = $wgRequest->getValues();
+ if( !empty( $vals ) && !empty( $vals['title'] ) ) {
+ $vars['fbReturnToTitle'] = $vals['title'];
+ }
+
+ return true;
+ }
+
+ /**
+ * Hack: Run MakeGlobalVariablesScript for backwards compatability.
+ * The MakeGlobalVariablesScript hook was added to MediaWiki 1.14 in revision 38397:
+ * http://svn.wikimedia.org/viewvc/mediawiki/trunk/phase3/includes/Skin.php?view=log&pathrev=38397
+ */
+ private static function MGVS_hack( &$script ) {
+ global $wgVersion, $IP;
+ if (version_compare($wgVersion, '1.14.0', '<')) {
+ $script = "";
+ $vars = array();
+ wfRunHooks('MakeGlobalVariablesScript', array(&$vars));
+ foreach( $vars as $name => $value ) {
+ $script .= "\t\tvar $name = " . json_encode($value) . ";\n";
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Installs a parser hook for every tag reported by FBConnectXFBML::availableTags().
+ * Accomplishes this by asking FBConnectXFBML to create a hook function that then
+ * redirects to FBConnectXFBML::parserHook().
+ */
+ public static function ParserFirstCallInit( &$parser ) {
+ $pHooks = FBConnectXFBML::availableTags();
+ foreach( $pHooks as $tag ) {
+ $parser->setHook( $tag, FBConnectXFBML::createParserHook( $tag ));
+ }
+ return true;
+ }
+
+
+ private static function showButton( $which ) {
+ global $wgUser, $wgFbShowPersonalUrls, $wgFbHidePersonalUrlsBySkin;
+ // If the button isn't marked to be shown in the first place
+ if (!in_array($which, $wgFbShowPersonalUrls)) {
+ return false;
+ }
+ $skinName = get_class($wgUser->getSkin());
+ // If no blacklist rules exist for the skin
+ if (!array_key_exists($skinName, $wgFbHidePersonalUrlsBySkin)) {
+ return true;
+ }
+ // If the value is a string, it's a simple comparison
+ if (is_string($wgFbHidePersonalUrlsBySkin[$skinName])) {
+ return $wgFbHidePersonalUrlsBySkin[$skinName] != '*' &&
+ $wgFbHidePersonalUrlsBySkin[$skinName] != $which;
+ } else {
+ return !in_array($which, $wgFbHidePersonalUrlsBySkin[$skinName]) &&
+ !in_array('*', $wgFbHidePersonalUrlsBySkin[$skinName]);
+ }
+ }
+
+ /**
+ * Modify the user's persinal toolbar (in the upper right).
+ *
+ * TODO: Better 'returnto' code
+ */
+ public static function PersonalUrls( &$personal_urls, &$wgTitle ) {
+ global $wgUser, $wgFbUseRealName, $wgFbConnectOnly, $facebook;
+
+ wfLoadExtensionMessages('FBConnect');
+
+ // Always false, as 'alt-talk' isn't a valid option currently
+ if (self::showButton( 'alt-talk' ) &&
+ array_key_exists('mytalk', $personal_urls)) {
+ unset($personal_urls['mytalk']);
+ }
+
+ // If the user is logged in and connected
+ if ( $wgUser->isLoggedIn() && $facebook->getSession() &&
+ count( FBConnectDB::getFacebookIDs($wgUser) ) > 0 ) {
+ if ( !empty( $wgFbUseRealName ) ) {
+ // Start with the real name in the database
+ $name = $wgUser->getRealName();
+ if (!$name || $name == '') {
+ // Ask Facebook for the real name
+ try {
+ // This might fail if we load a stale session from cookies
+ $fbUser = $facebook->api('/me');
+ $name = $fbUser['name'];
+ } catch (FacebookApiException $e) {
+ error_log($e);
+ }
+ }
+ // Make sure we were able to get a name from the database or Facebook
+ if ($name && $name != '') {
+ $personal_urls['userpage']['text'] = $name;
+ }
+ }
+
+ if (self::showButton( 'logout' )) {
+ // Replace logout link with a button to disconnect from Facebook Connect
+ unset( $personal_urls['logout'] );
+ $personal_urls['fblogout'] = array(
+ 'text' => wfMsg( 'fbconnect-logout' ),
+ 'href' => '#',
+ 'active' => false,
+ );
+ /*
+ $html = Xml::openElement('span', array('id' => 'fbuser' ));
+ $html .= Xml::openElement('a', array('href' => $personal_urls['userpage']['href'], 'class' => 'fb_button fb_button_small fb_usermenu_button' ));
+ $html .= Xml::closeElement( 'a' );
+ $html .= Xml::closeElement( 'span' );
+ $personal_urls['fbuser']['html'] = $html;
+ */
+ }
+
+ /*
+ * Personal URLs option: link_back_to_facebook
+ */
+ if (self::showButton( 'link' )) {
+ try {
+ $fbUser = $facebook->api('/me');
+ $link = $fbUser['link'];
+ } catch (FacebookApiException $e) {
+ $link = 'http://www.facebook.com/profile.php?id=' .
+ $facebook->getUser();
+ }
+ $personal_urls['fblink'] = array(
+ 'text' => wfMsg( 'fbconnect-link' ),
+ 'href' => $link,
+ 'active' => false
+ );
+ }
+ }
+ // User is logged in but not Connected
+ else if ($wgUser->isLoggedIn()) {
+ if (self::showButton( 'convert' )) {
+ $personal_urls['fbconvert'] = array(
+ 'text' => wfMsg( 'fbconnect-convert' ),
+ 'href' => SpecialConnect::getTitleFor('Connect', 'Convert')->getLocalURL(
+ 'returnto=' . $wgTitle->getPrefixedURL()),
+ 'active' => $wgTitle->isSpecial( 'Connect' )
+ );
+ }
+ }
+ // User is not logged in
+ else {
+ if (self::showButton( 'connect' ) || self::showButton( 'connect-simple' )) {
+ // Add an option to connect via Facebook Connect
+ $personal_urls['fbconnect'] = array(
+ 'text' => wfMsg( 'fbconnect-connect' ),
+ 'class' => 'fb_button fb_button_small',
+ 'href' => '#', # SpecialPage::getTitleFor('Connect')->getLocalUrl('returnto=' . $wgTitle->getPrefixedURL()),
+ 'active' => $wgTitle->isSpecial('Connect'),
+ );
+ }
+
+ if (self::showButton( 'connect-simple' )) {
+ $html = Xml::openElement('span', array('id' => 'fbconnect' ));
+ $html .= Xml::openElement('a', array('href' => '#', 'class' => 'fb_button fb_button_small' ));
+ $html .= Xml::openElement('span', array('class' => 'fb_button_text' ));
+ $html .= wfMsg( 'fbconnect-connect-simple' );
+ $html .= Xml::closeElement( 'span' );
+ $html .= Xml::closeElement( 'a' );
+ $html .= Xml::closeElement( 'span' );
+ $personal_urls['fbconnect']['html'] = $html;
+ }
+
+ if ( !empty( $wgFbConnectOnly ) ) {
+ // Remove other personal toolbar links
+ foreach (array('login', 'anonlogin') as $k) {
+ if (array_key_exists($k, $personal_urls)) {
+ unset($personal_urls[$k]);
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ public static function GetPreferences( $user, &$preferences ) {
+ $email = FBConnectUser::getCleanEmail($preferences['emailaddress']['default']);
+ if ($email != $preferences['emailaddress']['default']) {
+ // User is using a Facebook proxy email address
+ #$preferences['emailaddress']['default'] = $email;
+ $preferences['enotifrevealaddr']['disabled'] = true;
+ // TODO: Inject some JS into the page to take of changing email addresses
+ }
+ return true;
+ }
+
+ /**
+ * Modify the preferences form. At the moment, we simply turn the user name
+ * into a link to the user's facebook profile.
+ *
+ * TODO!
+ */
+ public static function RenderPreferencesForm( $form, $output ) {
+ //global $facebook, $wgUser;
+
+ // This hook no longer seems to work...
+
+ /*
+ $ids = FBConnectDB::getFacebookIDs($wgUser);
+
+ $fb_user = $facebook->getUser();
+ if( $fb_user && count($ids) > 0 && in_array( $fb_user, $ids )) {
+ $html = $output->getHTML();
+ $name = $wgUser->getName();
+ $i = strpos( $html, $name );
+ if ($i !== FALSE) {
+ // If the user has a valid Facebook ID, link to the Facebook profile
+ try {
+ $fbUser = $facebook->api('/me');
+ // Replace the old output with the new output
+ $html = substr( $html, 0, $i ) .
+ preg_replace("/$name/", "$name (<a href=\"$fbUser[link]\" " .
+ "class='mw-userlink mw-fbconnectuser'>" .
+ wfMsg('fbconnect-link-to-profile') . "</a>)",
+ substr( $html, $i ), 1);
+ $output->clearHTML();
+ $output->addHTML( $html );
+ } catch (FacebookApiException $e) {
+ error_log($e);
+ }
+ }
+ }
+ /**/
+ return true;
+ }
+
+ /**
+ * Adds the class "mw-userlink" to links belonging to Connect accounts on
+ * the page Special:ListUsers.
+ */
+ static function SpecialListusersFormatRow( &$item, $row ) {
+ global $fbSpecialUsers;
+
+ // Only modify Facebook Connect users
+ if (empty( $fbSpecialUsers ) ||
+ !count(FBConnectDB::getFacebookIDs(User::newFromName($row->user_name)))) {
+ return true;
+ }
+
+ // Look to see if class="..." appears in the link
+ $regs = array();
+ preg_match( '/^([^>]*?)class=(["\'])([^"]*)\2(.*)/', $item, $regs );
+ if (count( $regs )) {
+ // If so, append " mw-userlink" to the end of the class list
+ $item = $regs[1] . "class=$regs[2]$regs[3] mw-userlink$regs[2]" . $regs[4];
+ } else {
+ // Otherwise, stick class="mw-userlink" into the link just before the '>'
+ preg_match( '/^([^>]*)(.*)/', $item, $regs );
+ $item = $regs[1] . ' class="mw-userlink"' . $regs[2];
+ }
+ return true;
+ }
+
+ /**
+ * Adds some info about the governing Facebook group to the header form of
+ * Special:ListUsers.
+ */
+ // r274: Fix error with PHP 5.3 involving parameter references (thanks, PChott)
+ static function SpecialListusersHeaderForm( /*&*/$pager, &$out ) {
+ global $wgFbUserRightsFromGroup, $facebook;
+
+ if ( empty($wgFbUserRightsFromGroup) ) {
+ return true;
+ }
+
+ // TODO: Do we need to verify the Facebook session here?
+
+ $gid = $wgFbUserRightsFromGroup;
+ // Connect to the API and get some info about the group
+ try {
+ $group = $facebook->api('/' . $gid);
+ } catch (FacebookApiException $e) {
+ error_log($e);
+ return true;
+ }
+ $out .= '
+ <table style="border-collapse: collapse;">
+ <tr>
+ <td>
+ ' . wfMsgWikiHtml( 'fbconnect-listusers-header',
+ wfMsg( 'group-bureaucrat-member' ), wfMsg( 'group-sysop-member' ),
+ "<a href=\"http://www.facebook.com/group.php?gid=$gid\">$group[name]</a>",
+ "<a href=\"http://www.facebook.com/profile.php?id={$group['owner']['id']}\" " .
+ "class=\"mw-userlink\">{$group['owner']['name']}</a>") . "
+ </td>
+ <td>
+ <img src=\"https://graph.facebook.com/$gid/picture?type=large\" title=\"$group[name]\" alt=\"$group[name]\">
+ </td>
+ </tr>
+ </table>";
+ return true;
+ }
+
+ /**
+ * Removes Special:UserLogin and Special:CreateAccount from the list of
+ * special pages if $wgFbConnectOnly is set to true.
+ */
+ static function SpecialPage_initList( &$aSpecialPages ) {
+ global $wgFbConnectOnly;
+ if ( !empty( $wgFbConnectOnly) ) {
+ // U can't touch this
+ $aSpecialPages['Userlogin'] = array(
+ 'SpecialRedirectToSpecial',
+ 'UserLogin',
+ 'Connect',
+ false,
+ array( 'returnto', 'returntoquery' ),
+ );
+ // Used in 1.12.x and above
+ $aSpecialPages['CreateAccount'] = array(
+ 'SpecialRedirectToSpecial',
+ 'CreateAccount',
+ 'Connect',
+ );
+ }
+ return true;
+ }
+
+ /**
+ * HACK: Please someone fix me or explain why this is necessary!
+ *
+ * Unstub $wgUser to avoid race conditions and stop returning stupid false
+ * negatives!
+ *
+ * This might be due to a bug in User::getRights() [called from
+ * User::isAllowed('read'), called from Title::userCanRead()], where mRights
+ * is retrieved from an uninitialized user. From my probing, it seems that
+ * the user is uninitialized with almost all members blank except for mFrom,
+ * equal to 'session'. The second time around, $user seems to point to the
+ * User object after being loaded from the session. After the user is loaded
+ * it has all the appropriate groups. However, before being loaded it seems
+ * that instead of being null, mRights is equal to the array
+ * (createaccount, createpage, createtalk, writeapi).
+ */
+ static function userCan (&$title, &$user, $action, &$result) {
+ // Unstub $wgUser (is there a more succinct way to do this?)
+ $user->getId();
+ return true;
+ }
+
+ /**
+ * Removes the 'createaccount' right from all users if $wgFbConnectOnly is
+ * enabled.
+ */
+ // r270: fix for php 5.3 (cherry picked from http://trac.wikia-code.com/changeset/24606)
+ static function UserGetRights( /*&*/$user, &$aRights ) {
+ global $wgFbConnectOnly;
+ if ( !empty( $wgFbConnectOnly ) ) {
+ // If you would like sysops to still be able to create accounts
+ $whitelistSysops = false;
+ if ($whitelistSysops && in_array( 'sysop', $user->getGroups() )) {
+ return true;
+ }
+ foreach ( $aRights as $i => $right ) {
+ if ( $right == 'createaccount' ) {
+ unset( $aRights[$i] );
+ break;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * If the user isn't logged in, try to auto-authenticate via Facebook
+ * Connect. The Single Sign On magic of FBConnect happens in this function.
+ */
+ static function UserLoadFromSession( $user, &$result ) {
+ global $facebook, $wgCookiePrefix, $wgTitle, $wgOut, $wgUser;
+
+ // Check to see if the user can be logged in from Facebook
+ $fbId = $facebook->getSession() ? $facebook->getUser() : 0;
+ // Check to see if the user can be loaded from the session
+ $localId = isset($_COOKIE["{$wgCookiePrefix}UserID"]) ?
+ intval($_COOKIE["{$wgCookiePrefix}UserID"]) :
+ (isset($_SESSION['wsUserID']) ? $_SESSION['wsUserID'] : 0);
+
+ // Case: Not logged into Facebook, but logged into the wiki
+ /*
+ if (!$fbId && $localId) {
+ $mwUser = User::newFromId($localId);
+ // If the user was Connected, the JS should have logged them out...
+ // TODO: test to see if they logged in normally (with a password)
+ #if (FBConnectDB::userLoggedInWithPassword($mwUser)) return true;
+ if (count(FBConnectDB::getFacebookIDs($mwUser))) {
+ // Oh well, they shouldn't be here anyways; silently log them out
+ $mwUser->logout();
+ // Defaults have just been loaded, so save some time
+ $result = false;
+ }
+ }
+ // Case: Logged into Facebook, not logged into the wiki
+ else /**/ if ($fbId && !$localId) {
+ // Look up the MW ID of the Facebook user
+ $mwUser = FBConnectDB::getUser($fbId);
+ $id = $mwUser ? $mwUser->getId() : 0;
+ // If the user doesn't exist, ask them to name their new account
+ if (!$id) {
+ // TODO: $wgTitle was empty for some strange reason...
+ if (!empty( $wgTitle )) {
+ $returnto = $wgTitle->isSpecial('Userlogout') || $wgTitle->isSpecial('Connect') ?
+ '' : 'returnto=' . $wgTitle->getPrefixedURL();
+ } else {
+ $returnto = '';
+ }
+ // Don't redirect if we're on certain special pages
+ if ($returnto != '') {
+ // Redirect to Special:Connect so the Facebook user can choose a nickname
+ $wgOut->redirect($wgUser->getSkin()->makeSpecialUrl('Connect', $returnto));
+ }
+ } else {
+ // TODO: To complete the SSO experience, this should log the user on
+ /*
+ // Load the user from their ID
+ $user->mId = $id;
+ $user->mFrom = 'id';
+ $user->load();
+ // Update user's info from Facebook
+ $fbUser = new FBConnectUser($mwUser);
+ $fbUser->updateFromFacebook();
+ // Authentification okay, no need to continue with User::loadFromSession()
+ $result = true;
+ /**/
+ }
+ }
+ // Case: Not logged into Facebook or the wiki
+ // Case: Logged into Facebook, logged into the wiki
+ return true;
+ }
+
+ /**
+ * Create a disconnect button and other things in preferences.
+ */
+ static function initPreferencesExtensionForm( $user, &$preferences ) {
+ global $wgOut, $wgJsMimeType, $wgExtensionsPath, $wgStyleVersion, $wgBlankImgUrl;
+ $wgOut->addScript("<script type=\"{$wgJsMimeType}\" src=\"{$wgExtensionsPath}/FBConnect/prefs.js?{$wgStyleVersion}\"></script>\n");
+ wfLoadExtensionMessages('FBConnect');
+ $prefsection = 'fbconnect-prefstext';
+
+ $id = FBConnectDB::getFacebookIDs($user, DB_MASTER);
+ if( count($id) > 0 ) {
+ $html = Xml::openElement("div",array("id" => "fbDisconnectLink" ));
+ $html .= '<br/>'.wfMsg('fbconnect-disconnect-link');
+ $html .= Xml::closeElement( "div" );
+
+ $html .= Xml::openElement("div",array("style" => "display:none","id" => "fbDisconnectProgress" ));
+ $html .= '<br/>'.wfMsg('fbconnect-disconnect-done');
+ $html .= Xml::openElement("img",array("id" => "fbDisconnectProgressImg", 'src' => $wgBlankImgUrl, "class" => "sprite progress" ),true);
+ $html .= Xml::closeElement( "div" );
+
+ $html .= Xml::openElement("div",array("style" => "display:none","id" => "fbDisconnectDone" ));
+ $html .= '<br/>'.wfMsg('fbconnect-disconnect-info');
+ $html .= Xml::closeElement( "div" );
+
+ $preferences['fbconnect-prefstext'] = array(
+ 'label' => '',
+ 'type' => 'info',
+ 'section' => 'fbconnect-prefstext',
+ );
+
+ $preferences['tog-fbconnect-push-allow-never'] = array(
+ 'name' => 'toggle',
+ 'label-message' => 'fbconnect-push-allow-never',
+ 'section' => 'fbconnect-prefstext',
+ 'default' => 1,
+ );
+
+ $preferences['fbconnect-connect'] = array(
+ 'help' => $html,
+ 'label' => '',
+ 'type' => 'info',
+ 'section' => 'fbconnect-prefstext',
+ );
+
+ } else {
+ // User is a MediaWiki user but isn't connected yet
+ // Display a message and button to connect
+ $loginButton = '<fb:login-button id="fbPrefsConnect" ' .
+ FBConnect::getPermissionsAttribute() .
+ FBConnect::getOnLoginAttribute() . '></fb:login-button>';
+ $html = wfMsg('fbconnect-convert') . '<br/>' . $loginButton;
+ $html .= "<!-- Convert button -->\n";
+ $preferences['fbconnect-disconnect'] = array(
+ 'help' => $html,
+ 'label' => '',
+ 'type' => 'info',
+ 'section' => 'fbconnect-prefstext',
+ );
+ }
+ return true;
+ }
+
+ /**
+ * Add facebook connect html to ajax script.
+ */
+ public static function afterAjaxLoginHTML( &$html ) {
+ $tmpl = new EasyTemplate( dirname( __FILE__ ) . '/templates/' );
+ wfLoadExtensionMessages('FBConnect');
+ if ( !LoginForm::getLoginToken() ) {
+ LoginForm::setLoginToken();
+ }
+ $tmpl->set( 'loginToken', LoginForm::getLoginToken() );
+ $tmpl->set( 'fbButtton', FBConnect::getFBButton( 'sendToConnectOnLoginForSpecificForm();', 'fbPrefsConnect' ) );
+ $html = $tmpl->execute( 'ajaxLoginMerge' );
+ return true;
+ }
+
+ public static function SkinTemplatePageBeforeUserMsg(&$msg) {
+ global $wgRequest, $wgUser, $wgServer, $facebook;
+ wfLoadExtensionMessages('FBConnect');
+ $pref = Title::newFromText('Preferences', NS_SPECIAL);
+ if ($wgRequest->getVal('fbconnected', '') == 1) {
+ $id = FBConnectDB::getFacebookIDs($wgUser, DB_MASTER);
+ if( count($id) > 0 ) {
+ $msg = Xml::element("img", array("id" => "fbMsgImage", "src" => $wgServer.'/skins/common/fbconnect/fbiconbig.png' ));
+ $msg .= "<p>".wfMsg('fbconnect-connect-msg', array("$1" => $pref->getFullUrl() ))."</p>";
+ }
+ }
+
+ if ($wgRequest->getVal('fbconnected', '') == 2) {
+ if( strlen($facebook->getUser()) < 1 ) {
+ $msg = Xml::element("img", array("id" => "fbMsgImage", "src" => $wgServer.'/skins/common/fbconnect/fbiconbig.png' ));
+ $msg .= "<p>".wfMsgExt('fbconnect-connect-error-msg', 'parse', array("$1" => $pref->getFullUrl() ))."</p>";
+ }
+ }
+ return true;
+ }
+}
401 FBConnectLanguage.i18n.php
@@ -0,0 +1,401 @@
+<?php
+/*
+ * @author Sean Colombo
+ */
+
+/*
+ * Not a valid entry pointx, skip unless MEDIAWIKI is defined.
+ */
+if ( !defined( 'MEDIAWIKI' ) ) {
+ die( 'This file is a MediaWiki extension, it is not a valid entry point' );
+}
+
+/**
+ * FBConnectLanguage.i18n.php
+ *
+ * Contains a message which represents a mapping of MediaWiki language codes to Facebook Locales.
+ */
+
+
+$messages = array();
+
+/** English */
+$messages['en'] = array(
+
+ 'fbconnect-mediawiki-lang-to-fb-locale' => "
+aa, # Qafár af - Afar
+ab, # Аҧсуа - Abkhaz, should possibly add ' бысжѡа'
+ace, # Acèh - Aceh
+af, af_ZA # Afrikaans - Afrikaans
+ak, # Akan - Akan
+aln, # Gegë - Gheg Albanian
+als, # Alemannisch - Alemannic -- not a valid code, for compatibility. See gsw.
+am, # አማርኛ - Amharic
+an, # Aragonés - Aragonese
+ang, # Anglo-Saxon - Old English
+ar, ar_AR # العربية - Arabic
+arc, # ܐܪܡܝܐ - Aramaic
+arn, # Mapudungun - Mapuche, Mapudungu, Araucanian (Araucano)
+arz, # مصرى - Egyptian Spoken Arabic
+as, # অসমীয়া - Assamese
+ast, # Asturianu - Asturian
+av, # Авар - Avar
+avk, # Kotava - Kotava
+ay, ay_BO # Aymar aru - Aymara
+az, # Azərbaycan - Azerbaijani
+ba, # Башҡорт - Bashkir
+bar, # Boarisch - Bavarian (Austro-Bavarian and South Tyrolean)
+bat-smg, # Žemaitėška - Samogitian
+bcc, # بلوچی مکرانی - Southern Balochi
+bcl, # Bikol Central - Bikol: Central Bicolano language
+be, be_BY # Беларуская - Belarusian normative
+be-tarask, be_BY # Беларуская (тарашкевіца) - Belarusian in Taraskievica orthography
+be-x-old, be_BY # Беларуская (тарашкевіца) - Belarusian in Taraskievica orthography; compat link
+bg, bg_BG # Български - Bulgarian
+bh, # भोजपुरी - Bhojpuri
+bi, # Bislama - Bislama
+bm, # Bamanankan - Bambara
+bn, bn_IN # বাংলা - Bengali
+bo, # བོད་ཡིག - Tibetan
+bpy, # ইমার ঠার/বিষ্ণুপ্রিয়া মণিপুরী - Bishnupriya Manipuri
+bqi, # بختياري - Bakthiari
+br, # Brezhoneg - Breton
+bs, bs_BA # Bosanski - Bosnian
+bto, # Iriga Bicolano - Iriga Bicolano/Rinconada Bikol
+bug, # ᨅᨔ ᨕᨘᨁᨗ - Bugis
+bxr, # Буряад - Buryat (Russia)
+ca, ca_ES # Català - Catalan
+cbk-zam, # Chavacano de Zamboanga - Zamboanga Chavacano
+cdo, # Mìng-dĕ̤ng-ngṳ̄ - Min Dong
+ce, # Нохчийн - Chechen
+ceb, # Cebuano - Cebuano
+ch, # Chamoru - Chamorro
+cho, # Choctaw - Choctaw
+chr, ck_US # ᏣᎳᎩ - Cherokee
+chy, # Tsetsêhestâhese - Cheyenne
+co, # Corsu - Corsican
+cr, # Nēhiyawēwin / ᓀᐦᐃᔭᐍᐏᐣ - Cree
+crh, # Qırımtatarca - Crimean Tatar
+crh-latn, # \"\xE2\x80\xAAQırımtatarca (Latin)\xE2\x80\xAC\" - Crimean Tatar (Latin)
+crh-cyrl, # \"\xE2\x80\xAAКъырымтатарджа (Кирилл)\xE2\x80\xAC\" - Crimean Tatar (Cyrillic)
+cs, cs_CZ # Česky - Czech
+csb, # Kaszëbsczi - Cassubian
+cu, # Словѣ́ньскъ / ⰔⰎⰑⰂⰡⰐⰠⰔⰍⰟ - Old Church Slavonic (ancient language)
+cv, # Чăвашла - Chuvash
+cy, cy_GB # Cymraeg - Welsh
+da, da_DK # Dansk - Danish
+de, de_DE # Deutsch - German (\"Du\")
+de-at, # Österreichisches Deutsch - Austrian German
+de-ch, # Schweizer Hochdeutsch - Swiss Standard German
+de-formal, # Deutsch (Sie-Form) - German - formal address (\"Sie\")
+de-weigsbrag, # Deutsch (Weigsbrag) - German - (\"Weigsbrag\")
+diq, # Zazaki - Zazaki
+dk, # Dansk (deprecated:da) - Unused code currently falls back to Danish, 'da' is correct for the language
+dsb, # Dolnoserbski - Lower Sorbian
+dv, # ދިވެހިބަސް - Dhivehi
+dz, # ཇོང་ཁ - Bhutani
+ee, # Eʋegbe - Éwé
+el, el_GR # Ελληνικά - Greek
+eml, # Emiliàn e rumagnòl - Emiliano-Romagnolo / Sammarinese
+en, en_US # English - English
+en-gb, en_GB # British English - British English
+eo, eo_EO # Esperanto - Esperanto
+es, es_ES # Español - Spanish
+et, et_EE # Eesti - Estonian
+eu, eu_ES # Euskara - Basque
+ext, # Estremeñu - Extremaduran
+fa, fa_IR # فارسی - Persian
+ff, # Fulfulde - Fulfulde, Maasina
+fi, fi_FI # Suomi - Finnish
+fiu-vro, # Võro - Võro (deprecated code, 'vro' in ISO 639-3 since 2009-01-16)
+fj, # Na Vosa Vakaviti - Fijian
+fo, fo_FO # Føroyskt - Faroese
+fr, fr_FR # Français - French
+frc, # Français cadien - Cajun French
+frp, # Arpetan - Franco-Provençal/Arpitan
+fur, # Furlan - Friulian
+fy, # Frysk - Frisian
+ga, ga_IE # Gaeilge - Irish
+gag, # Gagauz - Gagauz
+gan, # 贛語 - Gan-hant
+gan-hans, # 赣语(简体) - Gan-hans
+gan-hant, # 贛語(繁體) - Gan-hant
+gd, # Gàidhlig - Scots Gaelic
+gl, gl_ES # Galego - Galician
+glk, # گیلکی - Gilaki
+gn, gn_PY # Avañe\'ẽ - Guaraní, Paraguayan
+got, # 𐌲𐌿𐍄𐌹𐍃𐌺 - Gothic
+grc, # Ἀρχαία ἑλληνικὴ - Ancient Greek
+gsw, # Alemannisch - Alemannic
+gu, gu_IN # ગુજરાતી - Gujarati
+gv, # Gaelg - Manx
+ha, # هَوُسَ - Hausa
+hak, # Hak-kâ-fa - Hakka
+haw, # Hawai`i - Hawaiian
+he, he_IL # עברית - Hebrew
+hi, hi_IN # हिन्दी - Hindi
+hif, # Fiji Hindi - Fijian Hindi (falls back to hif-latn)
+hif-deva, # फ़ीजी हिन्दी - Fiji Hindi (devangari)
+hif-latn, # Fiji Hindi - Fiji Hindi (latin)
+hil, # Ilonggo - Hiligaynon
+ho, # Hiri Motu - Hiri Motu
+hr, hr_HR # Hrvatski - Croatian
+hsb, # Hornjoserbsce - Upper Sorbian
+ht, # Kreyòl ayisyen - Haitian Creole French
+hu, hu_HU # Magyar - Hungarian
+hy, hy_AM # Հայերեն - Armenian
+hz, # Otsiherero - Herero
+ia, # Interlingua - Interlingua (IALA)
+id, id_ID # Bahasa Indonesia - Indonesian
+ie, # Interlingue - Interlingue (Occidental)
+ig, # Igbo - Igbo
+ii, # ꆇꉙ - Sichuan Yi
+ik, # Iñupiak - Inupiak (Inupiatun, Northwest Alaska / Inupiatun, North Alaskan)
+ike-cans, # ᐃᓄᒃᑎᑐᑦ - Inuktitut, Eastern Canadian/Eastern Canadian \"Eskimo\"/\"Eastern Arctic Eskimo\"/Inuit (Unified Canadian Aboriginal Syllabics)
+ike-latn, # inuktitut - Inuktitut, Eastern Canadian (Latin script)
+ilo, # Ilokano - Ilokano
+inh, # ГІалгІай Ğalğaj - Ingush
+io, # Ido - Ido
+is, is_IS # Íslenska - Icelandic
+it, it_IT # Italiano - Italian
+iu, # ᐃᓄᒃᑎᑐᑦ/inuktitut - Inuktitut (macro language - do no localise, see ike/ikt - falls back to ike-cans)
+ja, ja_JP # 日本語 - Japanese
+jbo, # Lojban - Lojban
+jut, # Jysk - Jutish / Jutlandic
+jv, jv_ID # Basa Jawa - Javanese
+ka, ka_GE # ქართული - Georgian
+kaa, # Qaraqalpaqsha - Karakalpak
+kab, # Taqbaylit - Kabyle
+kg, # Kongo - Kongo, (FIXME!) should probaly be KiKongo or KiKoongo
+ki, # Gĩkũyũ - Gikuyu
+kj, # Kwanyama - Kwanyama
+kk, kk_KZ # Қазақша - Kazakh
+kk-arab, # \"\xE2\x80\xABقازاقشا (تٴوتە)\xE2\x80\xAC\" - Kazakh Arabic
+kk-cyrl, # \"\xE2\x80\xAAҚазақша (кирил)\xE2\x80\xAC\" - Kazakh Cyrillic
+kk-latn, # \"\xE2\x80\xAAQazaqşa (latın)\xE2\x80\xAC\" - Kazakh Latin
+kk-cn, # \"\xE2\x80\xABقازاقشا (جۇنگو)\xE2\x80\xAC\" - Kazakh (China)
+kk-kz, # \"\xE2\x80\xAAҚазақша (Қазақстан)\xE2\x80\xAC\" - Kazakh (Kazakhstan)
+kk-tr, # \"\xE2\x80\xAAQazaqşa (Türkïya)\xE2\x80\xAC\" - Kazakh (Turkey)
+kl, # Kalaallisut - Inuktitut, Greenlandic/Greenlandic/Kalaallisut (kal)
+km, km_KH # ភាសាខ្មែរ - Khmer, Central
+kn, kn_IN # ಕನ್ನಡ - Kannada
+ko, ko_KR # 한국어 - Korean
+kr, # Kanuri - Kanuri, Central
+kri, # Krio - Krio
+krj, # Kinaray-a - Kinaray-a
+ks, # कश्मीरी - (كشميري) - Kashmiri
+ksh, # Ripoarisch - Ripuarian
+ku, ku_TR # Kurdî / كوردی - Kurdish
+ku-latn, # \"\xE2\x80\xAAKurdî (latînî)\xE2\x80\xAC\" - Northern Kurdish Latin script
+ku-arab, # \"\xE2\x80\xABكوردي (عەرەبی)\xE2\x80\xAC\" - Northern Kurdish Arabic script
+kv, # Коми - Komi-Zyrian, cyrillic is common script but also written in latin script
+kw, # Kernewek - Cornish
+ky, # Кыргызча - Kirghiz
+la, la_VA # Latina - Latin
+lad, # Ladino - Ladino
+lb, # Lëtzebuergesch - Luxemburguish
+lbe, # Лакку - Lak
+lez, # Лезги - Lezgi
+lfn, # Lingua Franca Nova - Lingua Franca Nova
+lg, # Luganda - Ganda
+li, li_NL # Limburgs - Limburgian
+lij, # Líguru - Ligurian
+lld, # Ladin - Ladin
+lmo, # Lumbaart - Lombard
+ln, # Lingála - Lingala
+lo, # ລາວ',# Laotian
+loz, # Silozi - Lozi
+lt, lt_LT # Lietuvių - Lithuanian
+lv, lv_LV # Latviešu - Latvian
+lzh, # 文言 - Literary Chinese -- (bug 8217) lzh instead of zh-classical, http://www.sil.org/iso639-3/codes.asp?order=639_3&letter=l
+lzz, # Lazuri Nena - Laz
+mai, # मैथिली - Maithili
+map-bms, # Basa Banyumasan - Banyumasan
+mdf, # Мокшень - Moksha
+mg, mg_MG # Malagasy - Malagasy
+mh, # Ebon - Marshallese
+mhr, # Олык Марий - Eastern Mari
+mi, # Māori - Maori
+mk, mk_MK # Македонски - Macedonian
+ml, ml_IN # മലയാളം - Malayalam
+mn, mn_MN # Монгол - Halh Mongolian (Cyrillic) (ISO 639-3: khk)
+mo, # Молдовеняскэ - Moldovan
+mr, mr_IN # मराठी - Marathi
+ms, ms_MY # Bahasa Melayu - Malay
+mt, mt_MT # Malti - Maltese
+mus, # Mvskoke - Muskogee/Creek
+mwl, # Mirandés - Mirandese
+my, # Myanmasa - Burmese
+myv, # Эрзянь - Erzya
+mzn, # مَزِروني - Mazanderani
+na, # Dorerin Naoero - Nauruan
+nah, # Nāhuatl - Nahuatl, en:Wikipedia writes Nahuatlahtolli, while another form is Náhuatl
+nan, # Bân-lâm-gú - Min-nan -- (bug 8217) nan instead of zh-min-nan, http://www.sil.org/iso639-3/codes.asp?order=639_3&letter=n
+nap, # Nnapulitano - Neapolitan
+nb, nb_NO # \"\xE2\x80\xAANorsk (bokmål)\xE2\x80\xAC\" - Norwegian (Bokmal)
+nds, # Plattdüütsch - Low German ''or'' Low Saxon
+nds-nl, # Nedersaksisch - Dutch Low Saxon
+ne, ne_NP # नेपाली - Nepali
+new, # नेपाल भाषा - Newar / Nepal Bhasa
+ng, # Oshiwambo - Ndonga
+niu, # Niuē - Niuean
+nl, nl_NL # Nederlands - Dutch
+nn, nn_NO # \"\xE2\x80\xAANorsk (nynorsk)\xE2\x80\xAC\" - Norwegian (Nynorsk)
+no, nb_NO # \"\xE2\x80\xAANorsk (bokmål)\xE2\x80\xAC\" - Norwegian
+nov, # Novial - Novial
+nrm, # Nouormand - Norman
+nso, # Sesotho sa Leboa - Northern Sotho
+nv, # Diné bizaad - Navajo
+ny, # Chi-Chewa - Chichewa
+oc, # Occitan - Occitan
+om, # Oromoo - Oromo
+or, # ଓଡ଼ିଆ - Oriya
+os, # Иронау - Ossetic
+pa, pa_IN # ਪੰਜਾਬੀ - Eastern Punjabi (pan)
+pag, # Pangasinan - Pangasinan
+pam, # Kapampangan - Pampanga
+pap, # Papiamentu - Papiamentu
+pdc, # Deitsch - Pennsylvania German
+pdt, # Plautdietsch - Plautdietsch/Mennonite Low German
+pfl, # Pfälzisch - Palatinate German
+pi, # पािऴ - Pali
+pih, # Norfuk / Pitkern - Norfuk/Pitcairn/Norfolk
+pl, pl_PL # Polski - Polish
+plm, # Palembang - Palembang
+pms, # Piemontèis - Piedmontese
+pnb, # پنجابی - Western Punjabi
+pnt, # Ποντιακά - Pontic/Pontic Greek
+ps, ps_AF # پښتو - Pashto, Northern/Paktu/Pakhtu/Pakhtoo/Afghan/Pakhto/Pashtu/Pushto/Yusufzai Pashto
+pt, pt_PT # Português - Portuguese
+pt-br, pt_BR # Português do Brasil - Brazilian Portuguese
+qu, qu_PE # Runa Simi - Quechua
+rif, # Tarifit - Tarifit
+rm, # Rumantsch - Raeto-Romance
+rmy, # Romani - Vlax Romany
+rn, # Kirundi - Rundi/Kirundi/Urundi
+ro, ro_RO # Română - Romanian
+roa-rup, # Armãneashce - Aromanian
+roa-tara, # Tarandíne - Tarantino
+ru, ru_RU # Русский - Russian
+ruq, # Vlăheşte - Megleno-Romanian (falls back to ruq-latn)
+ruq-cyrl, # Влахесте - Megleno-Romanian (Cyrillic script)
+ruq-grek, # Βλαεστε - Megleno-Romanian (Greek script)
+ruq-latn, # Vlăheşte - Megleno-Romanian (Latin script)
+rw, # Kinyarwanda - Kinyarwanda, should possibly be Kinyarwandi
+sa, sa_IN # संस्कृत - Sanskrit
+sah, # Саха тыла - Sakha
+sc, # Sardu - Sardinian
+scn, # Sicilianu - Sicilian
+sco, # Scots - Scots
+sd, # سنڌي - Sindhi
+sdc, # Sassaresu - Sassarese
+se, se_NO # Sámegiella - Northern Sami
+sei, # Cmique Itom - Seri
+sg, # Sängö - Sango/Sangho
+sh, # Srpskohrvatski / Српскохрватски - Serbocroatian
+shi, # Tašlḥiyt - Tachelhit
+si, # සිංහල - Sinhalese
+simple, # Simple English - Simple English
+sk, sk_SK # Slovenčina - Slovak
+sl, sl_SI # Slovenščina - Slovenian
+sm, # Gagana Samoa - Samoan
+sma, # Åarjelsaemien - Southern Sami
+sn, # chiShona - Shona
+so, so_SO # Soomaaliga - Somali
+sq, sq_AL # Shqip - Albanian
+sr, sr_RS # Српски / Srpski - Serbian
+sr-ec, # ћирилица - Serbian cyrillic ekavian
+sr-el, # latinica - Serbian latin ekavian
+srn, # Sranantongo - Sranan Tongo
+ss, # SiSwati - Swati
+st, # Sesotho - Southern Sotho
+stq, # Seeltersk - Saterland Frisian
+su, # Basa Sunda - Sundanese
+sv, sv_SE # Svenska - Swedish
+sw, sw_KE # Kiswahili - Swahili
+szl, # Ślůnski - Silesian
+ta, ta_IN # தமிழ் - Tamil
+tcy, # ತುಳು - Tulu
+te, te_IN # తెలుగు - Telugu
+tet, # Tetun - Tetun
+tg, tg_TJ # Тоҷикӣ - Tajiki (falls back to tg-cyrl)
+tg-cyrl, # Тоҷикӣ - Tajiki (Cyrllic script) (default)
+tg-latn, # tojikī - Tajiki (Latin script)
+th, th_TH # ไทย - Thai
+ti, # ትግርኛ - Tigrinya
+tk, # Türkmençe - Turkmen
+tl, tl_PH # Tagalog - Tagalog
+ #tlh, tl_ST # - tlhIngan-Hol - Klingon - no interlanguage links allowed
+tn, # Setswana - Setswana
+to, # lea faka-Tonga - Tonga (Tonga Islands)
+tokipona, # Toki Pona - Toki Pona
+tp, # Toki Pona (deprecated:tokipona) - Toki Pona - non-standard language code
+tpi, # Tok Pisin - Tok Pisin
+tr, tr_TR # Türkçe - Turkish
+ts, # Xitsonga - Tsonga
+tt, tt_RU # Tatarça/Татарча - Tatar (multiple scripts - defaults to Latin)
+tt-cyrl, # Татарча - Tatar (Cyrillic script)
+tt-latn, # Tatarça - Tatar (Latin script)
+tum, # chiTumbuka - Tumbuka
+tw, # Twi - Twi, (FIXME!)
+ty, # Reo Mā`ohi - Tahitian
+tyv, # Тыва дыл - Tyvan
+tzm, # ⵜⴰⵎⴰⵣⵉⵖⵜ - (Central Morocco) Tamazight
+udm, # Удмурт - Udmurt
+ug, # Uyghurche‎ / ئۇيغۇرچە - Uyghur (multiple scripts - defaults to Latin)
+ #'ug-arab, # ئۇيغۇرچە - Uyghur (Arabic script). Disabled until sufficient localisation can be committed
+ug-latn, # Uyghurche‎ - Uyghur (Latin script - default)
+uk, uk_UA # Українська - Ukrainian
+ur, ur_PK # اردو - Urdu
+uz, uz_UZ # O\'zbek - Uzbek
+val, # Valencià - Valencian
+ve, # Tshivenda - Venda
+vec, # Vèneto - Venetian
+vep, # Vepsan kel\' - Veps
+vi, vi_VN # Tiếng Việt - Vietnamese
+vls, # West-Vlams - West Flemish
+vo, # Volapük - Volapük
+vro, # Võro - Võro
+wa, # Walon - Walloon
+war, # Winaray - Waray-Waray
+wo, # Wolof - Wolof
+wuu, # 吴语 - Wu Chinese
+xal, # Хальмг - Kalmyk-Oirat (Kalmuk, Kalmuck, Kalmack, Qalmaq, Kalmytskii Jazyk, Khal:mag, Oirat, Volga Oirat, European Oirat, Western Mongolian)
+xh, xh_ZA # isiXhosa - Xhosan
+xmf, # მარგალური - Mingrelian
+ydd, # מיזרח־ייִדיש - Eastern Yiddish
+yi, yi_DE # ייִדיש - Yiddish
+yo, # Yorùbá - Yoruba
+yue, # 粵語 - Cantonese -- (bug 8217) yue instead of zh-yue, http://www.sil.org/iso639-3/codes.asp?order=639_3&letter=y
+za, # Sawcuengh - Zhuang
+zea, # Zeêuws - Zeeuws/Zeaws
+zh, # 中文 - (Zhōng Wén) - Chinese
+zh-classical, # 文言 - Classical Chinese/Literary Chinese -- (see bug 8217)
+zh-cn, # \"\xE2\x80\xAA中文(中国大陆)\xE2\x80\xAC\" - Chinese (PRC)
+zh-hans, zh_CN # \"\xE2\x80\xAA中文(简体)\xE2\x80\xAC\" - Chinese written using the Simplified Chinese script
+zh-hant, # \"\xE2\x80\xAA中文(繁體)\xE2\x80\xAC\" - Chinese written using the Traditional Chinese script
+zh-hk, zh_HK # \"\xE2\x80\xAA中文(香港)\xE2\x80\xAC\" - Chinese (Hong Kong)
+zh-min-nan, # Bân-lâm-gú - Min-nan -- (see bug 8217)
+zh-mo, # \"\xE2\x80\xAA中文(澳門)\xE2\x80\xAC\" - Chinese (Macau)
+zh-my, # \"\xE2\x80\xAA中文(马来西亚)\xE2\x80\xAC\" - Chinese (Malaysia)
+zh-sg, # \"\xE2\x80\xAA中文(新加坡)\xE2\x80\xAC\" - Chinese (Singapore)
+zh-tw, zh_TW # \"\xE2\x80\xAA中文(台灣)\xE2\x80\xAC\" - Chinese (Taiwan)
+zh-yue, # 粵語 - Cantonese -- (see bug 8217)
+zu, zu_ZA # isiZulu - Zulu
+ # Custom 'languages' for various wikis, each with ticket reference
+kh, # Kingdom Hearts - Kingdom Hearts for de.finalfantasy, trac #2968
+kp, # Kamelopedia - Kamelopedia for de.uncyclopedia, trec #801
+mu, # Mirror Universe - Mirror Universe for Memory-Alpha, trac #2788
+ "
+);
+
+/**
+ * Message documentation.
+ */
+$messages['qqq'] = array(
+ 'fbconnect-mediawiki-lang-to-fb-locale' => 'Do not translate this message. It is a representation of a mapping from MediaWiki language codes to corresponding Facebook locales.
+ Visit http://www.facebook.com/translations/FacebookLocales.xml for a list of all available Facebook Locales.
+ The mapping is expected to be one mapping per line with commas separating the MediaWiki language code on the left and the best Facebook Locale mapping on the right (if available).
+ On each line, anything after the first hash (#) is considered a comment. If no good match for a locale is found, the MediaWiki language code should still be left there (along with the comma) to indicate
+ that this is a spot that needs to be worked on. Any spaces on either side of the comma will be trimmed so spaces are just for readability.'
+);
154 FBConnectLanguage.php
@@ -0,0 +1,154 @@
+<?php
+/**
+ * @author Sean Colombo
+ *
+ * This file helps with languages and internationalization (i18n) for dealing with facebook.
+ * Since MediaWiki has a custom list of languages that differs from the Facebook languages, this class
+ * will help with the conversion.
+ *
+ * No automated conversions are attempted because that could result in much stranger results than just defaulting to english. For instance,
+ * if we just searched for a facebook locale which started with the MediaWiki language code, we could attempt to deliver en_PI (Pirate English)
+ * and there would be false-negatives in the other direction also (such as chr -> ck_US being missed).
+ *
+ * Relevant documentaion:
+ * - Tutorial in making the FBConnect popups from Facebook be internationalized: http://developers.facebook.com/blog/post/264
+ * - XML of Facebook's languages: http://www.facebook.com/translations/FacebookLocales.xml
+ * - Overview of where Facebook's lang-codes come from: http://wiki.developers.facebook.com/index.php/Facebook_Locales
+ * - MediaWiki i18n: http://www.mediawiki.org/wiki/Internationalisation
+ * - List which has MediaWiki fallback languages all on the same page: http://www.mediawiki.org/wiki/Localisation_statistics (will be helpful in building the mapping).
+ * - Comments in /languages/Names.php in MediaWiki has comments next to each mapping which should help. It is approximately RFC 3066
+ */
+
+class FBConnectLanguage{
+
+ // All of the Facebook Locales according to http://www.facebook.com/translations/FacebookLocales.xml as of 20100622
+ private static $allFbLocales = array(
+ 'ca_ES', 'cs_CZ', 'cy_GB', 'da_DK', 'de_DE', 'eu_ES', 'en_PI', 'en_UD', 'ck_US', 'en_US', 'es_LA', 'es_CL', 'es_CO', 'es_ES', 'es_MX',
+ 'es_VE', 'fb_FI', 'fi_FI', 'fr_FR', 'gl_ES', 'hu_HU', 'it_IT', 'ja_JP', 'ko_KR', 'nb_NO', 'nn_NO', 'nl_NL', 'pl_PL', 'pt_BR', 'pt_PT',
+ 'ro_RO', 'ru_RU', 'sk_SK', 'sl_SI', 'sv_SE', 'th_TH', 'tr_TR', 'ku_TR', 'zh_CN', 'zh_HK', 'zh_TW', 'fb_LT', 'af_ZA', 'sq_AL', 'hy_AM',
+ 'az_AZ', 'be_BY', 'bn_IN', 'bs_BA', 'bg_BG', 'hr_HR', 'nl_BE', 'en_GB', 'eo_EO', 'et_EE', 'fo_FO', 'fr_CA', 'ka_GE', 'el_GR', 'gu_IN',
+ 'hi_IN', 'is_IS', 'id_ID', 'ga_IE', 'jv_ID', 'kn_IN', 'kk_KZ', 'la_VA', 'lv_LV', 'li_NL', 'lt_LT', 'mk_MK', 'mg_MG', 'ms_MY', 'mt_MT',
+ 'mr_IN', 'mn_MN', 'ne_NP', 'pa_IN', 'rm_CH', 'sa_IN', 'sr_RS', 'so_SO', 'sw_KE', 'tl_PH', 'ta_IN', 'tt_RU', 'te_IN', 'ml_IN', 'uk_UA',
+ 'uz_UZ', 'vi_VN', 'xh_ZA', 'zu_ZA', 'km_KH', 'tg_TJ', 'ar_AR', 'he_IL', 'ur_PK', 'fa_IR', 'sy_SY', 'yi_DE', 'gn_PY', 'qu_PE', 'ay_BO',
+ 'se_NO', 'ps_AF', 'tl_ST'
+ );
+
+ private static $messageKey = 'fbconnect-mediawiki-lang-to-fb-locale';
+
+ /**
+ * Given a MediaWiki language code, gets a corresponding Facebook locale.
+ */
+ public static function getFbLocaleForLangCode($mediaWikiLangCode){
+ wfProfileIn(__METHOD__);
+ $locale = 'en_US'; // default facebook locale to use
+
+ // See if the mapping is in memcache already. If not, figure out the mapping from the mediawiki message.
+ global $wgMemc;
+ $memkey = wfMemcKey( 'FBConnectLanguage', self::$messageKey);
+ $langMapping = $wgMemc->get($memkey);
+ if(!$langMapping){
+ $langMapping = array();
+ wfLoadExtensionMessages('FBConnectLanguage');
+ $rawMappingText = wfMsg( self::$messageKey );
+
+ // Split the message by line.
+ $lines = explode("\n", $rawMappingText);
+ foreach($lines as $line){
+ // Remove comments
+ $index = strpos($line, "#");
+ if($index !== false){
+ $line = substr($line, 0, $index); // keep only the text before the comment
+ }
+
+ // Split the line into two pieces (if present) for the mapping.
+ $tokens = explode(',', $line, 2);
+ if(count($tokens) == 2){
+ // Trim off whitespace
+ $mwLang = trim($tokens[0]);
+ $fbLocale = trim($tokens[1]);
+
+ if(($mwLang != "") && ($fbLocale != "")){
+ // Verify that this is a valid fb locale before storing (otherwise a typo in the message could break FBConnect javascript by including an invalid fbScript URL).
+ if(self::isValidFacebookLocale($fbLocale)){
+ $langMapping[$mwLang] = $fbLocale;
+ } else {
+ error_log("FBConnect: WARNING: Facebook Locale was found in the wiki-message but does not appear to be a Facebook Locale that we know about: \"$fbLocale\".\n");
+ error_log("FBConnect: Skipping locale for now. If you want this locale to be permitted, please add it to FBConnectLanguage::\$allFbLocales.\n");
+ }
+ }