Paladox has uploaded a new change for review. ( 
https://gerrit.wikimedia.org/r/330165 )

Change subject: Initial import from the SocialProfile extension, adds support 
for avatars
......................................................................

Initial import from the SocialProfile extension, adds support for avatars

This is not a social feature instead this just adds supports for avatars in 
users profiles. This is similar to what we do in phabricator.

Change-Id: I82d9c57e5a143a5dc8a3e089f0536bc338e5e968
---
M autoload.php
A includes/specials/SpecialUploadAvatar.php
A includes/upload/UploadAvatar.php
M resources/Resources.php
A resources/src/mediawiki/mediawiki.avatar.css
5 files changed, 1,274 insertions(+), 0 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core 
refs/changes/65/330165/1

diff --git a/autoload.php b/autoload.php
index cdbdf1f..442266c 100644
--- a/autoload.php
+++ b/autoload.php
@@ -1380,6 +1380,7 @@
        'SpecialUnlinkAccounts' => __DIR__ . 
'/includes/specials/SpecialUnlinkAccounts.php',
        'SpecialUnlockdb' => __DIR__ . '/includes/specials/SpecialUnlockdb.php',
        'SpecialUpload' => __DIR__ . '/includes/specials/SpecialUpload.php',
+       'SpecialUploadAvatar' => __DIR__ . 
'/includes/specials/SpecialUploadAvatar.php',
        'SpecialUploadStash' => __DIR__ . 
'/includes/specials/SpecialUploadStash.php',
        'SpecialUploadStashTooLargeException' => __DIR__ . 
'/includes/specials/SpecialUploadStash.php',
        'SpecialUserLogin' => __DIR__ . 
'/includes/specials/SpecialUserLogin.php',
@@ -1482,6 +1483,7 @@
        'UpdateRestrictions' => __DIR__ . '/maintenance/updateRestrictions.php',
        'UpdateSearchIndex' => __DIR__ . '/maintenance/updateSearchIndex.php',
        'UpdateSpecialPages' => __DIR__ . '/maintenance/updateSpecialPages.php',
+       'UploadAvatar' => __DIR__ . '/includes/upload/UploadAvatar.php',
        'UploadBase' => __DIR__ . '/includes/upload/UploadBase.php',
        'UploadChunkFileException' => __DIR__ . 
'/includes/upload/UploadFromChunks.php',
        'UploadChunkVerificationException' => __DIR__ . 
'/includes/upload/UploadFromChunks.php',
diff --git a/includes/specials/SpecialUploadAvatar.php 
b/includes/specials/SpecialUploadAvatar.php
new file mode 100644
index 0000000..3759bdf
--- /dev/null
+++ b/includes/specials/SpecialUploadAvatar.php
@@ -0,0 +1,254 @@
+<?php
+/**
+ * A special page for uploading avatars
+ * This page is a big hack -- it's just the image upload page with some changes
+ * to upload the actual avatar files.
+ * The avatars are not held as MediaWiki images, but
+ * rather based on the user_id and in multiple sizes
+ *
+ * Requirements: Need writable directory $wgUploadPath/avatars
+ *
+ * @file
+ * @ingroup Extensions
+ * @author David Pean <david.p...@gmail.com>
+ * @copyright Copyright © 2007, Wikia Inc.
+ * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 
2.0 or later
+ */
+
+class SpecialUploadAvatar extends SpecialUpload {
+       public $avatarUploadDirectory;
+
+       /**
+        * Constructor
+        */
+       public function __construct( $request = null ) {
+               SpecialPage::__construct( 'UploadAvatar', 'upload', false/* 
listed? */ );
+       }
+
+       /**
+        * Let the parent handle most of the request, but specify the Upload
+        * class ourselves
+        */
+       protected function loadRequest() {
+               $request = $this->getRequest();
+               parent::loadRequest( $request );
+               $this->mUpload = new UploadAvatar();
+               $this->mUpload->initializeFromRequest( $request );
+       }
+
+       /**
+        * Show the special page. Let the parent handle most stuff, but handle a
+        * successful upload ourselves
+        *
+        * @param $params Mixed: parameter(s) passed to the page or null
+        */
+       public function execute( $params ) {
+               $out = $this->getOutput();
+
+               // Add CSS
+               $out->addModuleStyles( array(
+                       'mediawiki.avatar.clearfix',
+                       'mediawiki.avatar.userprofile.css'
+               ) );
+
+               // Let the parent class do most of the heavy lifting.
+               parent::execute( $params );
+
+               if ( $this->mUploadSuccessful ) {
+                       // Cancel redirect
+                       $out->redirect( '' );
+
+                       $this->showSuccess( $this->mUpload->mExtension );
+                       // Run a hook on avatar change
+                       Hooks::run( 'NewAvatarUploaded', array( 
$this->getUser() ) );
+               }
+       }
+
+       /**
+        * Show some text and linkage on successful upload.
+        *
+        * @param $ext String: file extension (gif, jpg or png)
+        */
+       private function showSuccess( $ext ) {
+               global $wgAvatarKey, $wgUploadPath, 
$wgUploadAvatarInRecentChanges;
+
+               $user = $this->getUser();
+               $log = new LogPage( 'avatar' );
+               if ( !$wgUploadAvatarInRecentChanges ) {
+                       $log->updateRecentChanges = false;
+               }
+               $log->addEntry(
+                       'avatar',
+                       $user->getUserPage(),
+                       $this->msg( 'user-profile-picture-log-entry' 
)->inContentLanguage()->text()
+               );
+
+               $uid = $user->getId();
+
+               $output = '<h1>' . $this->msg( 'uploadavatar' )->plain() . 
'</h1>';
+               $output .= UserProfile::getEditProfileNav( $this->msg( 
'user-profile-section-picture' )->plain() );
+               $output .= '<div class="profile-info">';
+               $output .= '<p class="profile-update-title">' .
+                       $this->msg( 'user-profile-picture-yourpicture' 
)->plain() . '</p>';
+               $output .= '<p>' . $this->msg( 
'user-profile-picture-yourpicturestext' )->plain() . '</p>';
+
+               $output .= '<table class="avatar-success-page">';
+               $output .= '<tr>
+                       <td class="title-cell" valign="top">' .
+                               $this->msg( 'user-profile-picture-large' 
)->plain() .
+                       '</td>
+                       <td class="image-cell">
+                               <img src="' . $wgUploadPath . '/avatars/' . 
$wgAvatarKey . '_' . $uid . '_l.' . $ext . '?ts=' . rand() . '" alt="" />
+                       </td>
+               </tr>';
+               $output .= '<tr>
+                       <td class="title-cell" valign="top">' .
+                               $this->msg( 'user-profile-picture-medlarge' 
)->plain() .
+                       '</td>
+                       <td class="image-cell">
+                               <img src="' . $wgUploadPath . '/avatars/' . 
$wgAvatarKey . '_' . $uid . '_ml.' . $ext . '?ts=' . rand() . '" alt="" />
+                       </td>
+               </tr>';
+               $output .= '<tr>
+                       <td class="title-cell" valign="top">' .
+                               $this->msg( 'user-profile-picture-medium' 
)->plain() .
+                       '</td>
+                       <td class="image-cell">
+                               <img src="' . $wgUploadPath . '/avatars/' . 
$wgAvatarKey . '_' . $uid . '_m.' . $ext . '?ts=' . rand() . '" alt="" />
+                       </td>
+               </tr>';
+               $output .= '<tr>
+                       <td class="title-cell" valign="top">' .
+                               $this->msg( 'user-profile-picture-small' 
)->plain() .
+                       '</td>
+                       <td class="image-cell">
+                               <img src="' . $wgUploadPath . '/avatars/' . 
$wgAvatarKey . '_' . $uid . '_s.' . $ext . '?ts=' . rand() . '" alt="" />
+                       </td>
+               </tr>';
+               $output .= '<tr>
+                       <td>
+                               <input type="button" 
onclick="javascript:history.go(-1)" class="site-button" value="' . $this->msg( 
'user-profile-picture-uploaddifferent' )->plain() . '" />
+                       </td>
+               </tr>';
+               $output .= '</table>';
+               $output .= '</div>';
+
+               $this->getOutput()->addHTML( $output );
+       }
+
+       /**
+        * Displays the main upload form, optionally with a highlighted
+        * error message up at the top.
+        *
+        * @param $msg String: error message as HTML
+        * @param $sessionKey String: session key in case this is a stashed 
upload
+        * @param $hideIgnoreWarning Boolean: whether to hide "ignore warning" 
check box
+        * @return HTML output
+        */
+       protected function getUploadForm( $message = '', $sessionKey = '', 
$hideIgnoreWarning = false ) {
+               global $wgUseCopyrightUpload, $wgUserProfileDisplay;
+
+               if ( $wgUserProfileDisplay['avatar'] === false ) {
+                       $message = $this->msg( 'socialprofile-uploads-disabled' 
)->plain();
+               }
+
+               if ( $message != '' ) {
+                       $sub = $this->msg( 'uploaderror' )->plain();
+                       $this->getOutput()->addHTML( "<h2>{$sub}</h2>\n" .
+                               "<h4 class='error'>{$message}</h4>\n" );
+               }
+
+               if ( $wgUserProfileDisplay['avatar'] === false ) {
+                       return '';
+               }
+
+               $ulb = $this->msg( 'uploadbtn' );
+
+               $source = null;
+
+               if ( $wgUseCopyrightUpload ) {
+                       $source = "
+                               <td align='right' nowrap='nowrap'>" . 
$this->msg( 'filestatus' )->plain() . "</td>
+                               <td><input tabindex='3' type='text' 
name=\"wpUploadCopyStatus\" value=\"" .
+                               htmlspecialchars( $this->mUploadCopyStatus ) . 
"\" size='40' /></td>
+                               </tr><tr>
+                               <td align='right'>" . $this->msg( 'filesource' 
)->plain() . "</td>
+                               <td><input tabindex='4' type='text' 
name='wpUploadSource' id='wpUploadSource' value=\"" .
+                               htmlspecialchars( $this->mUploadSource ) . "\" 
/></td>
+                               ";
+               }
+
+               $output = '<h1>' . $this->msg( 'uploadavatar' )->plain() . 
'</h1>';
+               $output .= UserProfile::getEditProfileNav( $this->msg( 
'user-profile-section-picture' )->plain() );
+               $output .= '<div class="profile-info">';
+
+               if ( $this->getAvatar( 'l' ) != '' ) {
+                       $output .= '<table>
+                               <tr>
+                                       <td>
+                                               <p 
class="profile-update-title">' .
+                                                       $this->msg( 
'user-profile-picture-currentimage' )->plain() .
+                                               '</p>
+                                       </td>
+                               </tr>';
+                               $output .= '<tr>
+                                       <td>' . $this->getAvatar( 'l' ) . '</td>
+                               </tr>
+                       </table>';
+               }
+
+               $output .= '<form id="upload" method="post" 
enctype="multipart/form-data" action="">';
+               // The following two lines are delicious copypasta from 
HTMLForm.php,
+               // function getHiddenFields() and they are required; 
wpEditToken is, as
+               // of MediaWiki 1.19, checked _unconditionally_ in
+               // SpecialUpload::loadRequest() and having the hidden title 
doesn't
+               // hurt either
+               // @see https://phabricator.wikimedia.org/T32953
+               $output .= Html::hidden( 'wpEditToken', 
$this->getUser()->getEditToken(), array( 'id' => 'wpEditToken' ) ) . "\n";
+               $output .= Html::hidden( 'title', 
$this->getPageTitle()->getPrefixedText() ) . "\n";
+               $output .= '<table>
+                               <tr>
+                                       <td>
+                                               <p 
class="profile-update-title">' .
+                                                       $this->msg( 
'user-profile-picture-choosepicture' )->plain() .
+                                               '</p>
+                                               <p 
style="margin-bottom:10px;">' .
+                                                       $this->msg( 
'user-profile-picture-picsize' )->plain() .
+                                               '</p>
+                                               <input tabindex="1" type="file" 
name="wpUploadFile" id="wpUploadFile" size="36"/>
+                                       </td>
+                               </tr>
+                               <tr>' . $source . '</tr>
+                               <tr>
+                                       <td>
+                                               <input tabindex="5" 
type="submit" name="wpUpload" class="site-button" value="' . $ulb . '" />
+                                       </td>
+                               </tr>
+                       </table>
+                       </form>' . "\n";
+
+               $output .= '</div>';
+
+               return $output;
+       }
+
+       /**
+        * Gets an avatar image with the specified size
+        *
+        * @param $size String: size of the image ('s' for small, 'm' for 
medium,
+        * 'ml' for medium-large and 'l' for large)
+        * @return String: full img HTML tag
+        */
+       function getAvatar( $size ) {
+               global $wgAvatarKey, $wgUploadDirectory, $wgUploadPath;
+               $files = glob(
+                       $wgUploadDirectory . '/avatars/' . $wgAvatarKey . '_' .
+                       $this->getUser()->getID() . '_' . $size . '*'
+               );
+               if ( isset( $files[0] ) && $files[0] ) {
+                       return "<img src=\"{$wgUploadPath}/avatars/" .
+                               basename( $files[0] ) . '" alt="" border="0" 
/>';
+               }
+       }
+
+}
diff --git a/includes/upload/UploadAvatar.php b/includes/upload/UploadAvatar.php
new file mode 100644
index 0000000..631a592
--- /dev/null
+++ b/includes/upload/UploadAvatar.php
@@ -0,0 +1,257 @@
+<?php
+
+class UploadAvatar extends UploadFromFile {
+       public $mExtension;
+
+       function createThumbnail( $imageSrc, $imageInfo, $imgDest, $thumbWidth 
) {
+               global $wgUseImageMagick, $wgImageMagickConvertCommand;
+
+               if ( $wgUseImageMagick ) { // ImageMagick is enabled
+                       list( $origWidth, $origHeight, $typeCode ) = $imageInfo;
+
+                       if ( $origWidth < $thumbWidth ) {
+                               $thumbWidth = $origWidth;
+                       }
+                       $thumbHeight = ( $thumbWidth * $origHeight / $origWidth 
);
+                       $border = ' -bordercolor white  -border  0x';
+                       if ( $thumbHeight < $thumbWidth ) {
+                               $border = ' -bordercolor white  -border  0x' . 
( ( $thumbWidth - $thumbHeight ) / 2 );
+                       }
+                       if ( $typeCode == 2 ) {
+                               exec(
+                                       $wgImageMagickConvertCommand . ' -size 
' . $thumbWidth . 'x' . $thumbWidth .
+                                       ' -resize ' . $thumbWidth . ' -crop ' . 
$thumbWidth . 'x' .
+                                       $thumbWidth . '+0+0   -quality 100 ' . 
$border . ' ' .
+                                       $imageSrc . ' ' . 
$this->avatarUploadDirectory . '/' . $imgDest . '.jpg'
+                               );
+                       }
+                       if ( $typeCode == 1 ) {
+                               exec(
+                                       $wgImageMagickConvertCommand . ' -size 
' . $thumbWidth . 'x' . $thumbWidth .
+                                       ' -resize ' . $thumbWidth . ' -crop ' . 
$thumbWidth . 'x' .
+                                       $thumbWidth . '+0+0 ' . $imageSrc . ' ' 
. $border . ' ' .
+                                       $this->avatarUploadDirectory . '/' . 
$imgDest . '.gif'
+                               );
+                       }
+                       if ( $typeCode == 3 ) {
+                               exec(
+                                       $wgImageMagickConvertCommand . ' -size 
' . $thumbWidth . 'x' . $thumbWidth .
+                                       ' -resize ' . $thumbWidth . ' -crop ' . 
$thumbWidth . 'x' .
+                                       $thumbWidth . '+0+0 ' . $imageSrc . ' ' 
.
+                                       $this->avatarUploadDirectory . '/' . 
$imgDest . '.png'
+                               );
+                       }
+                       if ( $typeCode == 4 ) {
+                               exec(
+                                       $wgImageMagickConvertCommand . ' -size 
' . $thumbWidth . 'x' . $thumbWidth .
+                                       ' -resize ' . $thumbWidth . ' -crop ' . 
$thumbWidth . 'x' .
+                                       $thumbWidth . '+0+0 ' . $imageSrc . ' ' 
.
+                                       $this->avatarUploadDirectory . '/' . 
$imgDest . '.svg'
+                               );
+                       }
+               } else { // ImageMagick is not enabled, so fall back to PHP's 
GD library
+                       // Get the image size, used in calculations later.
+                       list( $origWidth, $origHeight, $typeCode ) = 
getimagesize( $imageSrc );
+
+                       switch( $typeCode ) {
+                               case '1':
+                                       $fullImage = imagecreatefromgif( 
$imageSrc );
+                                       $ext = 'gif';
+                                       break;
+                               case '2':
+                                       $fullImage = imagecreatefromjpeg( 
$imageSrc );
+                                       $ext = 'jpg';
+                                       break;
+                               case '3':
+                                       $fullImage = imagecreatefrompng( 
$imageSrc );
+                                       $ext = 'png';
+                                       break;
+                               case '4':
+                                       $fullImage = imagecreatefrompng( 
$imageSrc );
+                                       $ext = 'svg';
+                                       break;
+                       }
+
+                       $scale = ( $thumbWidth / $origWidth );
+
+                       // Create our thumbnail size, so we can resize to this, 
and save it.
+                       $tnImage = imagecreatetruecolor(
+                               $origWidth * $scale,
+                               $origHeight * $scale
+                       );
+
+                       // Resize the image.
+                       imagecopyresampled(
+                               $tnImage,
+                               $fullImage,
+                               0, 0, 0, 0,
+                               $origWidth * $scale,
+                               $origHeight * $scale,
+                               $origWidth,
+                               $origHeight
+                       );
+
+                       // Create a new image thumbnail.
+                       if ( $typeCode == 1 ) {
+                               imagegif( $tnImage, $imageSrc );
+                       } elseif ( $typeCode == 2 ) {
+                               imagejpeg( $tnImage, $imageSrc );
+                       } elseif ( $typeCode == 3 ) {
+                               imagepng( $tnImage, $imageSrc );
+                       }
+
+                       // Clean up.
+                       imagedestroy( $fullImage );
+                       imagedestroy( $tnImage );
+
+                       // Copy the thumb
+                       copy(
+                               $imageSrc,
+                               $this->avatarUploadDirectory . '/' . $imgDest . 
'.' . $ext
+                       );
+               }
+       }
+
+       /**
+        * Create the thumbnails and delete old files
+        */
+       public function performUpload( $comment, $pageText, $watch, $user, 
$tags = [] ) {
+               global $wgUploadDirectory, $wgAvatarKey, $wgMemc;
+
+               $this->avatarUploadDirectory = $wgUploadDirectory . '/avatars';
+
+               $imageInfo = getimagesize( $this->mTempPath );
+               if ( empty( $imageInfo[2] ) ) {
+                       return Status::newFatal( 'empty-file' );
+               }
+
+               switch ( $imageInfo[2] ) {
+                       case 1:
+                               $ext = 'gif';
+                               break;
+                       case 2:
+                               $ext = 'jpg';
+                               break;
+                       case 3:
+                               $ext = 'png';
+                               break;
+                       case 4:
+                               $ext = 'svg';
+                               break;
+                       default:
+                               return Status::newFatal( 'filetype-banned' );
+               }
+
+               $dest = $this->avatarUploadDirectory;
+
+               $uid = $user->getId();
+               $avatar = new wAvatar( $uid, 'l' );
+               // If this is the user's first custom avatar, update statistics 
(in
+               // case if we want to give out some points to the user for 
uploading
+               // their first avatar)
+               if ( strpos( $avatar->getAvatarImage(), 'default_' ) !== false 
) {
+                       $stats = new UserStatsTrack( $uid, $user->getName() );
+                       $stats->incStatField( 'user_image' );
+               }
+
+               $this->createThumbnail( $this->mTempPath, $imageInfo, 
$wgAvatarKey . '_' . $uid . '_l', 75 );
+               $this->createThumbnail( $this->mTempPath, $imageInfo, 
$wgAvatarKey . '_' . $uid . '_ml', 50 );
+               $this->createThumbnail( $this->mTempPath, $imageInfo, 
$wgAvatarKey . '_' . $uid . '_m', 30 );
+               $this->createThumbnail( $this->mTempPath, $imageInfo, 
$wgAvatarKey . '_' . $uid . '_s', 16 );
+
+               if ( $ext != 'jpg' ) {
+                       if ( is_file( $this->avatarUploadDirectory . '/' . 
$wgAvatarKey . '_' . $uid . '_s.jpg' ) ) {
+                               unlink( $this->avatarUploadDirectory . '/' . 
$wgAvatarKey . '_' . $uid . '_s.jpg' );
+                       }
+                       if ( is_file( $this->avatarUploadDirectory . '/' . 
$wgAvatarKey . '_' . $uid . '_m.jpg' ) ) {
+                               unlink( $this->avatarUploadDirectory . '/' . 
$wgAvatarKey . '_' . $uid . '_m.jpg' );
+                       }
+                       if ( is_file( $this->avatarUploadDirectory . '/' . 
$wgAvatarKey . '_' . $uid . '_l.jpg' ) ) {
+                               unlink( $this->avatarUploadDirectory . '/' . 
$wgAvatarKey . '_' . $uid . '_l.jpg' );
+                       }
+                       if ( is_file( $this->avatarUploadDirectory . '/' . 
$wgAvatarKey . '_' . $uid . '_ml.jpg' ) ) {
+                               unlink( $this->avatarUploadDirectory . '/' . 
$wgAvatarKey . '_' . $uid . '_ml.jpg' );
+                       }
+               }
+               if ( $ext != 'gif' ) {
+                       if ( is_file( $this->avatarUploadDirectory . '/' . 
$wgAvatarKey . '_' . $uid . '_s.gif' ) ) {
+                               unlink( $this->avatarUploadDirectory . '/' . 
$wgAvatarKey . '_' . $uid . '_s.gif' );
+                       }
+                       if ( is_file( $this->avatarUploadDirectory . '/' . 
$wgAvatarKey . '_' . $uid . '_m.gif' ) ) {
+                               unlink( $this->avatarUploadDirectory . '/' . 
$wgAvatarKey . '_' . $uid . '_m.gif' );
+                       }
+                       if ( is_file( $this->avatarUploadDirectory . '/' . 
$wgAvatarKey . '_' . $uid . '_l.gif' ) ) {
+                               unlink( $this->avatarUploadDirectory . '/' . 
$wgAvatarKey . '_' . $uid . '_l.gif' );
+                       }
+                       if ( is_file( $this->avatarUploadDirectory . '/' . 
$wgAvatarKey . '_' . $uid . '_ml.gif' ) ) {
+                               unlink( $this->avatarUploadDirectory . '/' . 
$wgAvatarKey . '_' . $uid . '_ml.gif' );
+                       }
+               }
+               if ( $ext != 'png' ) {
+                       if ( is_file( $this->avatarUploadDirectory . '/' . 
$wgAvatarKey . '_' . $uid . '_s.png' ) ) {
+                               unlink( $this->avatarUploadDirectory . '/' . 
$wgAvatarKey . '_' . $uid . '_s.png' );
+                       }
+                       if ( is_file( $this->avatarUploadDirectory . '/' . 
$wgAvatarKey . '_' . $uid . '_m.png' ) ) {
+                               unlink( $this->avatarUploadDirectory . '/' . 
$wgAvatarKey . '_' . $uid . '_m.png' );
+                       }
+                       if ( is_file( $this->avatarUploadDirectory . '/' . 
$wgAvatarKey . '_' . $uid . '_l.png' ) ) {
+                               unlink( $this->avatarUploadDirectory . '/' . 
$wgAvatarKey . '_' . $uid . '_l.png' );
+                       }
+                       if ( is_file( $this->avatarUploadDirectory . '/' . 
$wgAvatarKey . '_' . $uid . '_ml.png' ) ) {
+                               unlink( $this->avatarUploadDirectory . '/' . 
$wgAvatarKey . '_' . $uid . '_ml.png' );
+                       }
+               }
+
+               if ( $ext != 'svg' ) {
+                       if ( is_file( $this->avatarUploadDirectory . '/' . 
$wgAvatarKey . '_' . $uid . '_s.svg' ) ) {
+                               unlink( $this->avatarUploadDirectory . '/' . 
$wgAvatarKey . '_' . $uid . '_s.svg' );
+                       }
+                       if ( is_file( $this->avatarUploadDirectory . '/' . 
$wgAvatarKey . '_' . $uid . '_m.svg' ) ) {
+                               unlink( $this->avatarUploadDirectory . '/' . 
$wgAvatarKey . '_' . $uid . '_m.svg' );
+                       }
+                       if ( is_file( $this->avatarUploadDirectory . '/' . 
$wgAvatarKey . '_' . $uid . '_l.svg' ) ) {
+                               unlink( $this->avatarUploadDirectory . '/' . 
$wgAvatarKey . '_' . $uid . '_l.svg' );
+                       }
+                       if ( is_file( $this->avatarUploadDirectory . '/' . 
$wgAvatarKey . '_' . $uid . '_ml.svg' ) ) {
+                               unlink( $this->avatarUploadDirectory . '/' . 
$wgAvatarKey . '_' . $uid . '_ml.svg' );
+                       }
+               }
+
+               $key = wfMemcKey( 'user', 'profile', 'avatar', $uid, 's' );
+               $data = $wgMemc->delete( $key );
+
+               $key = wfMemcKey( 'user', 'profile', 'avatar', $uid, 'm' );
+               $data = $wgMemc->delete( $key );
+
+               $key = wfMemcKey( 'user', 'profile', 'avatar', $uid , 'l' );
+               $data = $wgMemc->delete( $key );
+
+               $key = wfMemcKey( 'user', 'profile', 'avatar', $uid, 'ml' );
+               $data = $wgMemc->delete( $key );
+
+               $this->mExtension = $ext;
+               return Status::newGood();
+       }
+
+       /**
+        * Don't verify the upload, since it all dangerous stuff is killed by
+        * making thumbnails
+        */
+       public function verifyUpload() {
+               return array( 'status' => self::OK );
+       }
+
+       /**
+        * Only needed for the redirect; needs fixage
+        */
+       public function getTitle() {
+               return Title::makeTitle( NS_FILE, 'Avatar-placeholder' . 
uniqid() . '.jpg' );
+       }
+
+       /**
+        * We don't overwrite stuff, so don't care
+        */
+       public function checkWarnings() {
+               return array();
+       }
+}
diff --git a/resources/Resources.php b/resources/Resources.php
index ae95c78..9bf0f31 100644
--- a/resources/Resources.php
+++ b/resources/Resources.php
@@ -858,6 +858,14 @@
                'styles' => 'resources/src/mediawiki/mediawiki.apihelp.css',
                'targets' => [ 'desktop' ],
        ],
+       'mediawiki.avatar.css' => [
+               'styles' => 'resources/src/mediawiki/mediawiki.avatar.css',
+               'targets' => [ 'desktop', 'mobile' ],
+       ],
+       'mediawiki.avatar.clearfix' => [
+               'styles' => 
'resources/src/mediawiki/mediawiki.avatar-clearfix.css',
+               'targets' => [ 'desktop' , 'mobile'],
+       ],
        'mediawiki.template' => [
                'scripts' => 'resources/src/mediawiki/mediawiki.template.js',
                'targets' => [ 'desktop', 'mobile' ],
diff --git a/resources/src/mediawiki/mediawiki.avatar.css 
b/resources/src/mediawiki/mediawiki.avatar.css
new file mode 100644
index 0000000..899ecb2
--- /dev/null
+++ b/resources/src/mediawiki/mediawiki.avatar.css
@@ -0,0 +1,753 @@
+.firstHeading {
+       border-bottom: none;
+       margin: 0px !important;
+       display: none;
+}
+
+.pagetitle {
+       display: inline !important;
+}
+
+#main {
+       padding: 0px 0px 0px 0px !important;
+}
+
+#user-page-left {
+       width: 35%;
+       float: left;
+}
+
+#user-page-right {
+       float: right;
+       width: 60%;
+}
+
+#user-page-left h2, #user-page-right h2 {
+       border-bottom: 1px solid #dcdcdc;
+       padding: 0px 0px 3px 0px;
+       margin: 0px 0px 5px 0px;
+       font-size: 17px;
+       letter-spacing: -1px;
+       color: #333;
+}
+
+.no-info-container {
+       margin: 10px 0px 20px 0px;
+       background-color: #F2F4F7;
+       padding: 20px;
+       text-align: center;
+       color: #777;
+}
+
+.item-small {
+       color: #999;
+       font-size: 9px;
+       margin: 0px 0px 0px 6px;
+}
+
+.user-section-heading {
+       border-bottom: 1px solid #DCDCDC;
+       width: 100%;
+       line-height: 22px;
+       margin: 0px 0px 8px 0px;
+}
+
+.user-section-title {
+       float: left;
+       font-size: 20px;
+       letter-spacing: -1px;
+       padding: 0px 10px 0px 0px;
+}
+
+.user-section-actions {
+       position: relative;
+       right: 0px;
+       top: -2px;
+}
+
+.action-right {
+       float: right;
+       font-size: 10px;
+       padding: 0px 5px 0px 0px;
+}
+
+.action-left {
+       float: left;
+       font-size: 10px;
+}
+
+.user-section-actions a {
+       text-decoration: none;
+}
+
+/* Profile Top */
+#profile-top {
+       background-color: #fff;
+       border-bottom: 1px solid #D4DFD7;
+       padding: 10px 0px;
+       margin: 0px 0px 20px 0px;
+       position: relative;
+}
+
+#profile-title-container {
+       padding: 0px 0px 6px 6px;
+}
+
+#profile-title {
+       float: left;
+       font-size: 2em;
+       line-height: 26px;
+       font-weight: bold;
+       margin: 0px 15px 0px 0px;
+}
+
+#points-level {
+       float: left;
+       font-size: 11px;
+       background-color: #CC0000;
+       padding: 1px 5px;
+       margin: 4px 8px 0px 0px;
+       text-align: center;
+}
+
+#honorific-level {
+       float: left;
+       margin: 4px 0px 0px 0px;
+       font-size: 12px;
+}
+
+#honorific-level a {
+       text-decoration: none;
+}
+
+#points-level a {
+       text-decoration: none;
+       font-weight: bold;
+       color: #fff;
+}
+
+#profile-image {
+       position: relative;
+       float: left;
+       margin: 0px 8px 0px 0px;
+}
+
+#profile-image img {
+       border: 1px solid #dcdcdc;
+       background-color: #fff;
+       padding: 3px;
+}
+
+#profile-right {
+       float: left;
+       position: relative;
+}
+
+.profile-actions a {
+       text-decoration: none;
+       font-weight: bold;
+       margin: 0px 6px;
+}
+
+/* User-Relationship */
+.user-relationship-container {
+       padding: 0px 0px 25px 0px;
+}
+
+.user-relationship-container a {
+       text-decoration: none;
+       display: block;
+       float: left;
+       margin: 5px 10px 0px 0px;
+}
+
+.user-relationship-container img {
+       padding: 3px;
+       border: 1px solid #dcdcdc;
+       background-color: #fff;
+}
+
+/* User Gift / User Awards */
+.user-gift-container {
+       padding: 0px 0px 25px 0px;
+}
+
+.user-gift-container a {
+       text-decoration: none;
+       display: block;
+       float: left;
+       margin: 5px 10px 0px 0px;
+}
+
+.user-gift-container img {
+       padding: 3px;
+       border: 1px solid #dcdcdc;
+       background-color: #fff;
+}
+
+/* Fanboxes */
+.user-fanbox-container {
+       padding: 0px 0px 25px 0px;
+}
+
+/* User Activity */
+.activity-item {
+       border-bottom: 1px solid #dcdcdc;
+       padding: 7px 0px;
+}
+
+.activity-item-bottom {
+       padding: 7px 0px 0px 0px;
+}
+
+.activity-item img, .activity-item-bottom img {
+       vertical-align: middle;
+       margin: 0px 5px 0px 0px;
+}
+
+.activity-item a, .activity-item-bottom a {
+       text-decoration: none;
+}
+
+.item {
+       color: #666;
+       margin: 5px 25px;
+}
+
+.item a {
+       font-weight: bold;
+}
+
+#recent-all {
+       padding: 0px 0px 25px 0px;
+}
+
+/* Message Board */
+#user-page-board {
+       margin: 5px 0px 10px 0px;
+}
+
+.user-page-message-form {
+       margin: 5px 0px 15px 0px;
+       font-weight: bold;
+}
+
+.user-board-message {
+       border: 1px solid #e2e2e2;
+       margin: 0px 0px 15px 0px;
+}
+
+.user-board-message-from {
+       background-color: #F8F5F5;
+       padding: 3px 0px 5px 5px;
+}
+
+.user-board-message-from a {
+       font-size: 14px;
+       text-decoration: none;
+       font-weight: bold;
+}
+
+.user-board-message-content {
+       padding: 5px;
+}
+
+.user-board-message-time {
+       font-size: 11px;
+       color: #797979;
+       margin: 2px 0px 0px 5px;
+}
+
+.user-board-message-image {
+       float: left;
+       padding: 0px 10px 5px 0px;
+}
+
+.user-board-message-image img {
+       border: 1px solid #dcdcdc;
+}
+
+.user-board-message-body {
+       margin: 0px 10px 0px 0px;
+}
+
+.user-board-message-body p {
+       margin: 0px 0px 0px 0px !important;
+}
+
+.user-board-message-links {
+       padding: 0px 0px 5px 5px;
+}
+
+.user-board-message-links a {
+       font-size: 11px;
+       text-decoration: none;
+       margin: 0px 10px 0px 0px;
+}
+
+.user-board-red a {
+       color: #ff0000;
+}
+
+/* Profile Data */
+.profile-info-container {
+       padding: 0px 0px 17px 0px;
+}
+
+.profile-info-container b {
+       color: #555;
+       margin: 0px 10px 0px 0px;
+}
+
+.profile-info-container div {
+       margin: 0px 0px 8px 0px;
+       color: #666;
+}
+
+.bold-fix b {
+       margin: 0px 5px 0px 0px !important;
+}
+
+/* Casual Games */
+.casual-game-container {
+       padding: 0px 0px 18px 0px;
+}
+
+.casual-game-container p {
+       margin: 0px 0px 0px 0px !important;
+       padding: 7px 0px !important;
+       border-top: 1px solid #dcdcdc;
+}
+
+.casual-game-container .item-top {
+       border: none;
+}
+
+.casual-game-container a {
+       text-decoration: none;
+       font-weight: bold;
+}
+
+.casual-game-container .item-type {
+       font-size: 9px;
+       margin: 0px 0px 0px 6px;
+}
+
+.casual-game-container img {
+       border: 1px solid #dcdcdc;
+       padding: 3px;
+       background-color: #fff;
+       vertical-align: middle;
+       margin: 0px 3px 0px 0px;
+}
+
+/* User Articles */
+.user-articles-container {
+       padding: 0px 0px 23px 0px;
+}
+
+.article-item {
+       margin: 0px 0px 0px 0px;
+       padding: 7px 0px 2px 0px;
+       border-top: 1px solid #dcdcdc;
+}
+
+.user-articles-container a {
+       text-decoration: none;
+       font-weight: bold;
+}
+
+.article-item-top {
+       margin: 0px 0px 0px 0px;
+       padding: 7px 0px 2px 0px;
+       border: none;
+}
+
+.number-of-votes {
+       margin: 0px 10px 3px 0px;
+       text-align: center;
+       float: left;
+       width: 25px;
+}
+
+.vote-number {
+       background-color: #89C46F;
+       color: #FFFFFF;
+       font-size: 12px !important;
+       margin: 0px 0px 2px 0px;
+       font-weight: bold;
+       height: 25px;
+       line-height: 25px;
+}
+
+.vote-text {
+       margin: -1px 0px 0px 0px;
+       font-size: 9px;
+       color: #777;
+       line-height: 9px;
+}
+
+/* Pictures */
+.picture-container-spacer {
+       height: 25px;
+       width: 100%;
+}
+
+.picture-container .mini-image {
+       float: left;
+       margin: 5px 10px 0px 0px;
+}
+
+.picture-container img {
+       background-color: #fff;
+       padding: 3px;
+       border: 1px solid #dcdcdc;
+}
+
+.picture-container a {
+       text-decoration: none;
+}
+
+.upload-container {
+       clear: left;
+       height: 90px;
+       margin-left: -5px;
+}
+
+.upload-frame-errors {
+       margin: 10px 0px 5px 0px;
+       color: red;
+       font-size: 14px;
+       font-weight: bold;
+}
+
+.no-pictures-container {
+       color: #777777;
+       margin: 10px 0px 0px;
+       padding: 20px;
+       text-align: center;
+       background-color: #EFF3F0;
+}
+
+.upload-frame-errors {
+       display: none;
+}
+
+.imageUpload-frame {
+       height: 90px;
+       width: 425px;
+}
+
+/* Sports Networks */
+.network-container {
+       padding: 0px 0px 22px 0px;
+}
+
+.network {
+       margin: 0px 0px 3px 0px;
+}
+
+.network img {
+       margin: 0px 5px 0px 0px;
+       vertical-align: middle;
+}
+
+.network a {
+       text-decoration: none;
+}
+
+.status-message-add a {
+       color: #999;
+       font-size: 10px;
+}
+
+.status-update-box {
+       margin: 10px 0px 10px 0px;
+}
+
+#status-text input {
+       font-size: 10px;
+       width: 200px;
+}
+
+/* Status */
+.status-container {
+       padding: 0px 0px 20px 0px;
+}
+
+.status-message img {
+       vertical-align: middle;
+       margin: 0px 5px 0px 0px;
+}
+
+.user-status-date {
+       margin: 0px 6px 0px 0px;
+       color: #999;
+       font-size: 9px;
+}
+
+.user-status-profile-vote {
+       color: #666;
+}
+
+.user-status-profile-vote a {
+       text-decoration: none;
+       font-size: 9px;
+}
+
+/* Avatar upload page (Special:UploadAvatar) */
+#wpUploadSource {
+       width: 100px;
+}
+
+table.avatar-success-page {
+       border-collapse: collapse;
+       margin-top: 20px;
+       padding: 0;
+}
+
+table.avatar-success-page td.title-cell {
+       color: #797979;
+       font-size: 12px;
+       font-weight: bold;
+       padding: 5px;
+       padding-bottom: 20px;
+}
+
+table.avatar-success-page td.image-cell {
+       padding-bottom: 20px;
+}
+
+table.avatar-success-page td.image-cell img {
+       border: 0;
+}
+
+/* Update Profile */
+.profile-info {
+       background-color: #fff;
+       padding: 25px 0px;
+}
+
+.profile-update {
+       padding: 0px;
+}
+
+.profile-tab-bar {
+       margin-top: 20px;
+}
+
+.profile-tab {
+       float: left;
+       background-color: #68BD46;
+       height: 22px;
+       min-width: 72px;
+       padding: 6px 5px 0px 5px;
+       margin: 0px 5px 0px 0px;
+       text-align: center;
+}
+
+.profile-tab-on {
+       background-color: #466C2C;
+       float: left;
+       height: 22px;
+       color: #ffffff;
+       font-size: 12px;
+       min-width: 72px;
+       font-weight: bold;
+       padding: 6px 5px 0px 5px;
+       margin: 0px 5px 0px 0px;
+       text-align: center;
+}
+
+.profile-tab-on a, .profile-tab a {
+       color: #fff;
+       font-weight: normal;
+       text-decoration: none;
+}
+
+.profile-tab a:hover {
+       color: #466C2C;
+}
+
+.profile-tab a:visited {
+       color: #FFFFFF !important;
+}
+
+.profile-tab-on a:visited {
+       color: #FFFFFF !important;
+}
+
+.profile-update-button {
+       background-color: #78BA5D;
+       border: 1px solid #6B6B6B;
+       color: #ffffff;
+       font-size: 13px;
+       padding: 3px;
+}
+
+.profile-update-title {
+       font-size: 16px;
+       color: #333;
+       margin: 0px 0px 5px 0px !important;
+       font-weight: bold;
+}
+
+.profile-update-title img {
+       vertical-align: middle;
+       margin: -3px 0px 0px 5px;
+}
+
+.profile-update-unit-left {
+       color: #797979;
+       font-weight: bold;
+       font-size: 12px;
+       float: left;
+       width: 150px;
+}
+
+.profile-update-unit {
+       float: left;
+       width: 450px;
+}
+
+.profile-update-unit-small {
+       float: left;
+       width: 450px;
+       font-size: 10px;
+       color: #797979;
+}
+
+.profile-update-row {
+       color: #797979;
+       font-weight: bold;
+       font-size: 12px;
+}
+
+.profile-update-links {
+       margin: 0px 0px 10px 0px;
+       font-weight: bold;
+}
+
+.profile-update-links a {
+       text-decoration: none;
+}
+
+.profile-status {
+       background-color: #FFFB9B;
+       font-weight: bold;
+       padding: 0px 5px 0px 5px;
+}
+
+.profile-on {
+       background-color: #FFFB9B;
+       border: 1px solid #FDC745;
+       font-weight: bold;
+       padding: 1px 5px 1px 5px;
+}
+
+#profile-toggle-button {
+       position: absolute;
+       top: 5px;
+       right: 0px;
+       background-color: orange;
+       padding: 2px 10px;
+}
+
+#profile-toggle-button a {
+       color: #fff;
+       font-weight: bold;
+}
+
+/* The text "Message type" on the left side of the message type selector on 
profile page */
+.profile-board-message-type {
+       color: #797979;
+}
+
+/* Responsive stuff for mobile devices */
+@media screen and (max-width: 750px) {
+       /* Special:UpdateProfile & Special:EditProfile */
+       .mw-special-EditProfile textarea,
+       .mw-special-UpdateProfile textarea {
+               min-width: 60%;
+               max-width: 100%;
+       }
+       div.profile-tab {
+               /**
+                * I don't like how the tabs' texts are off-center, but this
+                * is still better than nothing
+                */
+               margin-bottom: 10px;
+       }
+}
+
+@media screen and (max-width: 750px) and (orientation: portrait) {
+       .mw-special-EditProfile textarea,
+       .mw-special-UpdateProfile textarea {
+               width: 30% !important;
+       }
+}
+
+/* Privacy */
+.mw-special-UpdateProfile div#mw-content-text .visualClear {
+       height: 20px;
+}
+
+.eye-container {
+       width: 130px;
+       height: 20px;
+       /* @embed */
+       background: #a3c2d5 url(../images/eye.png) 0 0 no-repeat;
+       border-radius: 4px;
+       opacity: 0.5;
+       overflow: hidden;
+       position: relative;
+       top: -25px;
+       cursor: pointer;
+       box-shadow: 2px 2px 3px -1px darkgray;
+}
+
+/** @todo FIXME: this is an awful hack for Special:UpdateProfile (the 
"personal information" page) */
+div.eye-container[fieldkey="up_real_name"],
+div.eye-container[fieldkey="up_email"],
+div.eye-container[fieldkey="up_location_city"],
+div.eye-container[fieldkey="up_hometown_country"],
+div.eye-container[fieldkey="up_birthday"] {
+       top: 5px;
+}
+
+div.eye-container[fieldkey="up_hometown_city"],
+div.eye-container[fieldkey="up_location_country"] {
+       top: 10px;
+}
+
+.eye-container:hover {
+       opacity: 1.0;
+}
+
+.eye-container .title {
+       font-size: 12px;
+       font-weight: bold;
+       color: white;
+       padding: 0px 0 0 25px;
+}
+
+.eye-container .menu {
+       padding: 10px 0 10px 0;
+}
+
+.eye-container .menu .item {
+       height: 18px;
+       font-size: 12px;
+       font-weight: bold;
+       color: white;
+       margin: 4px 0 4px 0;
+       padding: 0 0 0 30px;
+       /* @embed */
+       background: #a3c2d5 url(images/eye-bw.png) 5px 1px no-repeat;
+}
+
+.eye-container .menu .item:hover {
+       background-color: white;
+       box-shadow: 1px 1px 1px -1px black;
+       color: #a3c2d5;
+}

-- 
To view, visit https://gerrit.wikimedia.org/r/330165
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I82d9c57e5a143a5dc8a3e089f0536bc338e5e968
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Paladox <thomasmulhall...@yahoo.com>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to