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