Swidmann has uploaded a new change for review. https://gerrit.wikimedia.org/r/148354
Change subject: Cache support ...................................................................... Cache support Added CacheHelper class Added LCStore_MC for memcached stuff Added cache support for ArticleHelper Added cache support for WidgetListHelper Change-Id: I4b7f6830493a213e07440189656eb0a95f9f2c77 --- A BlueSpice.hooks.php M BlueSpiceFoundation.php M includes/AutoLoader.php M includes/Config.class.php A includes/cache/LCStore_MC.php M includes/utility/ArticleHelper.class.php A includes/utility/CacheHelper.class.php M includes/utility/WidgetListHelper.class.php M resources/bluespice.extjs/Ext.ux/form/ItemSelector.js 9 files changed, 283 insertions(+), 62 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/BlueSpiceFoundation refs/changes/54/148354/1 diff --git a/BlueSpice.hooks.php b/BlueSpice.hooks.php new file mode 100644 index 0000000..9f2067e --- /dev/null +++ b/BlueSpice.hooks.php @@ -0,0 +1,27 @@ +<?php +$wgHooks['SetupAfterCache'][] = 'BsCoreHooks::onSetupAfterCache'; +$wgHooks['SoftwareInfo'][] = 'BsCoreHooks::onSoftwareInfo'; +$wgHooks['BeforePageDisplay'][] = 'BsCoreHooks::onBeforePageDisplay'; +$wgHooks['LinkEnd'][] = 'BsCoreHooks::LinkEnd'; +$wgHooks['MakeGlobalVariablesScript'][] = 'BsCoreHooks::onMakeGlobalVariablesScript'; +$wgHooks['LoadExtensionSchemaUpdates'][] = 'BsCoreHooks::onLoadExtensionSchemaUpdates'; +$wgHooks['ApiCheckCanExecute'][] = 'BsCoreHooks::onApiCheckCanExecute'; +$wgHooks['UserGetRights'][] = 'BsCoreHooks::onUserGetRights'; +$wgHooks['userCan'][] = 'BsCoreHooks::onUserCan'; +$wgHooks['UploadVerification'][] = 'BsCoreHooks::onUploadVerification'; +$wgHooks['SkinTemplateOutputPageBeforeExec'][] = 'BsCoreHooks::onSkinTemplateOutputPageBeforeExec'; + +$wgHooks['UserAddGroup'][] = 'BsGroupHelper::addTemporaryGroupToUserHelper'; + +// START cache invalidation hooks +$wgHooks['PageContentSaveComplete'][] = 'BsCacheHelper::onPageContentSaveComplete'; +$wgHooks['BsSettingsAfterSaveSettings'][] = 'BsCacheHelper::onBsSettingsAfterSaveSettings'; +// START cache invalidation hooks + +if ( !isset( $wgHooks['EditPage::showEditForm:initial'] ) ) { + $wgHooks['EditPage::showEditForm:initial'] = array(); +} + +if ( $wgDBtype == 'oracle' ) { + $wgHooks['ArticleDelete'][] = 'BSOracleHooks::onArticleDelete'; +} diff --git a/BlueSpiceFoundation.php b/BlueSpiceFoundation.php index 7e2760c..e4f77fd 100644 --- a/BlueSpiceFoundation.php +++ b/BlueSpiceFoundation.php @@ -90,29 +90,10 @@ $wgSpecialPageGroups['SpecialCredits'] = 'bluespice'; $wgSpecialPages['SpecialCredits'] = 'SpecialCredits'; -$wgHooks['SetupAfterCache'][] = 'BsCoreHooks::onSetupAfterCache'; -$wgHooks['SoftwareInfo'][] = 'BsCoreHooks::onSoftwareInfo'; -$wgHooks['BeforePageDisplay'][] = 'BsCoreHooks::onBeforePageDisplay'; -$wgHooks['LinkEnd'][] = 'BsCoreHooks::LinkEnd'; -$wgHooks['MakeGlobalVariablesScript'][] = 'BsCoreHooks::onMakeGlobalVariablesScript'; -$wgHooks['LoadExtensionSchemaUpdates'][] = 'BsCoreHooks::onLoadExtensionSchemaUpdates'; -$wgHooks['ApiCheckCanExecute'][] = 'BsCoreHooks::onApiCheckCanExecute'; -$wgHooks['UserGetRights'][] = 'BsCoreHooks::onUserGetRights'; -$wgHooks['userCan'][] = 'BsCoreHooks::onUserCan'; -$wgHooks['UploadVerification'][] = 'BsCoreHooks::onUploadVerification'; -$wgHooks['SkinTemplateOutputPageBeforeExec'][] = 'BsCoreHooks::onSkinTemplateOutputPageBeforeExec'; - -$wgHooks['UserAddGroup'][] = 'BsGroupHelper::addTemporaryGroupToUserHelper'; - -if ( !isset( $wgHooks['EditPage::showEditForm:initial'] ) ) { - $wgHooks['EditPage::showEditForm:initial'] = array(); -} - -if ( $wgDBtype == 'oracle' ) { - $wgHooks['ArticleDelete'][] = 'BSOracleHooks::onArticleDelete'; -} - +// Register hooks +require_once( 'BlueSpice.hooks.php' ); //Setup + $wgExtensionFunctions[] = 'BsCoreHooks::setup'; // initalise BlueSpice as first extension in a fully initialised environment diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php index 84196fb..fcc5622 100644 --- a/includes/AutoLoader.php +++ b/includes/AutoLoader.php @@ -15,6 +15,7 @@ $GLOBALS['wgAutoloadClasses']['LCStore_BSDB'] = __DIR__."/cache/LCStore_BSDB.php"; $GLOBALS['wgAutoloadClasses']['LCStore_SHM'] = __DIR__."/cache/LCStore_SHM.php"; +$GLOBALS['wgAutoloadClasses']['LCStore_MC'] = __DIR__."/cache/LCStore_MC.php"; $GLOBALS['wgAutoloadClasses']['BsValidator'] = __DIR__."/validator/Validator.class.php"; $GLOBALS['wgAutoloadClasses']['BsValidatorPlugin'] = __DIR__."/validator/Validator.class.php"; @@ -57,6 +58,7 @@ //utility $GLOBALS['wgAutoloadClasses']['BsArticleHelper'] = __DIR__."/utility/ArticleHelper.class.php"; +$GLOBALS['wgAutoloadClasses']['BsCacheHelper'] = __DIR__."/utility/CacheHelper.class.php"; $GLOBALS['wgAutoloadClasses']['BsConnectionHelper'] = __DIR__."/utility/ConnectionHelper.class.php"; $GLOBALS['wgAutoloadClasses']['BsDOMHelper'] = __DIR__."/utility/DOMHelper.class.php"; $GLOBALS['wgAutoloadClasses']['BsFormatConverter'] = __DIR__."/utility/FormatConverter.class.php"; diff --git a/includes/Config.class.php b/includes/Config.class.php index 3813e82..1af870e 100644 --- a/includes/Config.class.php +++ b/includes/Config.class.php @@ -273,11 +273,29 @@ * loads all settings from the database and saves the instances for every variable internal. */ public static function loadSettings() { - $dbr = wfGetDB ( DB_READ ); - # query the settings from bs_settings - $res = $dbr->select ( 'bs_settings', array ( $dbr->addIdentifierQuotes('key'), $dbr->addIdentifierQuotes('value') ) ); + $oCache = wfGetCache( CACHE_ANYTHING ); + $sKey = wfMemcKey( 'BlueSpice', 'BsConfig', 'loadSettings' ); + $aData = $oCache->get( $sKey ); + + if( $aData !== false ) { + wfDebugLog( 'BsMemcached' , __CLASS__.': Fetching settings from cache' ); + $aRows = $aData; + } else { + wfDebugLog( 'BsMemcached' , __CLASS__.': Fetching settings from DB' ); + $dbr = wfGetDB ( DB_READ ); + # query the settings from bs_settings + $res = $dbr->select ( 'bs_settings', array ( $dbr->addIdentifierQuotes('key'), $dbr->addIdentifierQuotes('value') ) ); + $aRows = array(); + + while( $row = $res->fetchObject() ) { + $aRows[] = $row; + } + + // TODO SW: invalidate cache in BsCacheHelper + $oCache->set( $sKey, $aRows, 60*5 );//invalidate cache after 5 minutes + } # unserialize and save every setting in the config class - while ( $row = $res->fetchObject () ) { + foreach( $aRows as $row ) { self::set ( $row->key, unserialize ( $row->value ) ); } } @@ -321,7 +339,10 @@ } # write the settings array to the database - return $dbw->insert('bs_settings', $aSettings); + wfRunHooks( 'BsSettingsBeforeSaveSettings', array( $this, &$aSettings ) ); + $bReturn = $dbw->insert('bs_settings', $aSettings); + wfRunHooks( 'BsSettingsAfterSaveSettings', array( $this, $aSettings ) ); + return $bReturn; } // TODO RBV (02.06.11 16:06): Core-Kontamination! Keine MediaWiki Funktionen im Core! diff --git a/includes/cache/LCStore_MC.php b/includes/cache/LCStore_MC.php new file mode 100644 index 0000000..cc8d35a --- /dev/null +++ b/includes/cache/LCStore_MC.php @@ -0,0 +1,57 @@ +<?php + +/** + * This file is part of blue spice for MediaWiki. + * + * @copyright Copyright (c) 2014, HalloWelt! Medienwerkstatt GmbH, All rights reserved. + * @author Stefan Widmann <widm...@hallowelt.biz> + * @version 0.1.0 beta + */ +/** + * LCStore implementation which uses memcache to store data. + */ +class LCStore_MC implements LCStore { + private $currentLang; + private $keys; + + public function __construct() { + $this->cache = wfGetCache( CACHE_MEMCACHED ); + } + + public $counter = 0; + public function get( $code, $key ) { + $k = wfMemcKey( 'l10n', $code, 'k', $key ); + $r = $this->cache->get( $k ); + + return $r === false ? null : $r; + } + + public function startWrite( $code ) { + $k = wfMemcKey( 'l10n', $code, 'l' ); + $keys = $this->cache->get( $k ); + if ( $keys ) { + foreach ( $keys as $k ) { + $this->cache->delete( $k ); + } + } + $this->currentLang = $code; + $this->keys = array(); + } + + public function finishWrite() { + if ( $this->currentLang ) { + $k = wfMemcKey( 'l10n', $this->currentLang, 'l' ); + $this->cache->set( $k, array_keys( $this->keys ) ); + } + $this->currentLang = null; + $this->keys = array(); + } + + public function set( $key, $value ) { + if ( $this->currentLang ) { + $k = wfMemcKey( 'l10n', $this->currentLang, 'k', $key ); + $this->keys[$k] = true; + $this->cache->set( $k, $value ); + } + } +} \ No newline at end of file diff --git a/includes/utility/ArticleHelper.class.php b/includes/utility/ArticleHelper.class.php index d041ddc..c255689 100644 --- a/includes/utility/ArticleHelper.class.php +++ b/includes/utility/ArticleHelper.class.php @@ -13,7 +13,7 @@ protected static $aInstances = array(); /** - * Protected constructor. Instances can be obtained through the factory + * Protected constructor. Instances can be obtained through the factory * method of this class * @param Title $oTitle */ @@ -43,20 +43,32 @@ $oTalkPage = $this->oTitle->getTalkPage(); $iTalkPageId = $oTalkPage->getArticleID(); - $dbr = wfGetDB( DB_SLAVE ); - // a new revision (rev_id) is also created on page move. So use rev_text_id - $res = $dbr->select( - 'revision', - 'DISTINCT rev_text_id', - array( 'rev_page' => $iTalkPageId ), - __METHOD__ - ); - $iCount = $dbr->numRows( $res ); + $sKey = BsCacheHelper::getCacheKey( 'BlueSpice', 'ArticleHelper', 'getDiscussionAmount', $iTalkPageId ); + $aData = BsCacheHelper::get( $sKey ); + + if( $aData !== false ) { + wfDebugLog( 'BsMemcached', __CLASS__.': Fetching discussion amounts from cache' ); + $iCount = $aData; + } else { + wfDebugLog( 'BsMemcached', __CLASS__.': Fetching discussion amounts from cache' ); + $dbr = wfGetDB( DB_SLAVE ); + // a new revision (rev_id) is also created on page move. So use rev_text_id + $res = $dbr->select( + 'revision', + 'DISTINCT rev_text_id', + array( 'rev_page' => $iTalkPageId ), + __METHOD__ + ); + $iCount = $dbr->numRows( $res ); + + BsCacheHelper::set( $sKey, $iCount ); + } + return $iCount; } /** - * + * * @param string $sPropName * @param bool $bDoLoad * @return string|null @@ -74,13 +86,13 @@ } /** - * + * * @param string $sPropName * @param bool $bDoLoad * @return sdtClass|true|false|null See PHP documentation for json_decode() */ public function getJSONPageProp( $sPropName, $bDoLoad = false ) { - return json_decode( + return json_decode( $this->getPageProp( $sPropName, $bDoLoad ) ); } @@ -117,18 +129,28 @@ */ public function loadPageProps() { $iArticleId = $this->oTitle->getArticleID(); - $dbr = wfGetDB( DB_SLAVE ); - $res = $dbr->select( - 'page_props', - array('pp_propname', 'pp_value'), - array('pp_page' => $iArticleId ), - __METHOD__ - ); - foreach( $res as $row ) { - $this->aPageProps[$row->pp_propname] = $row->pp_value; + $sKey = BsCacheHelper::getCacheKey( 'BlueSpice', 'ArticleHelper', 'loadPageProps', $iArticleId ); + $aData = BsCacheHelper::get( $sKey ); + + if( $aData !== false ) { + wfDebugLog( 'BsMemcached', __CLASS__.': Fetching page props from cache' ); + $this->aPageProps = $aData; + } else { + wfDebugLog( 'BsMemcached', __CLASS__.': Fetching page props from DB' ); + $dbr = wfGetDB( DB_SLAVE ); + $res = $dbr->select( + 'page_props', + array('pp_propname', 'pp_value'), + array('pp_page' => $iArticleId ), + __METHOD__ + ); + + foreach( $res as $row ) { + $this->aPageProps[$row->pp_propname] = $row->pp_value; + } + BsCacheHelper::set( $sKey, $this->aPageProps, 60*15 );// invalidates cache after 15 minutes } - $this->bIsLoaded = true; } diff --git a/includes/utility/CacheHelper.class.php b/includes/utility/CacheHelper.class.php new file mode 100644 index 0000000..4ca8317 --- /dev/null +++ b/includes/utility/CacheHelper.class.php @@ -0,0 +1,101 @@ +<?php +/** + * This class contains methods working with mediawiki's cache . + * + * @copyright Copyright (c) 2007-2014, HalloWelt! Medienwerkstatt GmbH, All rights reserved. + * @author Stefan Widmann <widm...@hallowelt.biz> + * @version 0.1.0 beta + */ +class BsCacheHelper { + + protected static $aCacheKeys = array(); + protected static $oCache = null; + + public static function getCache() { + if( self::$oCache === null ) { + self::$oCache = wfGetCache( CACHE_ANYTHING ); + } + + return self::$oCache; + } + + public static function getCacheKey( /*...*/ ) { + $args = func_get_args(); + $sKey = call_user_func_array( 'wfMemcKey', $args ); + self::$aCacheKeys[] = $sKey; + + return $sKey; + } + + public static function get( $sKey ) { + return self::getCache()->get( $sKey ); + } + + public static function set( $sKey, $mData, $iExpiryTime = 0 ) { + return self::getCache()->set( $sKey, $mData, $iExpiryTime ); + } + + public static function invalidateCache( $sKey ) { + if( is_array( $sKey ) ) { + foreach( $sKey as $key ) { + self::getCache()->delete( $key ); + } + return true; + } + return self::getCache()->delete( $sKey ); + } + + public static function clearAllCaches() { + foreach( self::$aCacheKeys as $sKey ) { + self::getCache()->delete( $sKey ); + } + } + + /** + * cache invalidation + * @param Article $article + * @param User $user + * @param Content $content + * @param type $summary + * @param bool $isMinor + * @param bool $isWatch + * @param type $section + * @param type $flags + * @param Revision $revision + * @param Status $status + * @param type $baseRevId + * @return boolean + */ + public static function onPageContentSaveComplete( $article, $user, $content, $summary, $isMinor, $isWatch, $section, $flags, $revision, $status, $baseRevId ) { + //invalidate cache ArticleHelper::getDiscussionAmount + if( $article->getTitle()->isTalkPage() ) { + BsCacheHelper::invalidateCache( BsCacheHelper::getCacheKey( 'BlueSpice', 'ArticleHelper', 'getDiscussionAmount', $article->getTitle()->getArticleID() ) ); + } else { + $aKeys = array( + // invalidate cache ArticleHelper::loadPageProps + BsCacheHelper::getCacheKey( 'BlueSpice', 'ArticleHelper', 'loadPageProps', $article->getTitle()->getArticleID() ), + // invalidate cache WidgetListHelper::getWidgets + BsCacheHelper::getCacheKey( 'BlueSpice', 'WidgetListHelper', $article->getTitle()->getPrefixedDBkey() ) + ); + + BsCacheHelper::invalidateCache( $aKeys ); + } + return true; + } + + /** + * + * @param BsConfig $oBsConfig + * @param type $aSettings + */ + public static function onBsSettingsAfterSaveSettings( $oBsConfig, $aSettings ) { + $aKeys = array( + BsCacheHelper::getCacheKey( 'BlueSpice', 'BsConfig', 'loadSettings' ) + ); + + BsCacheHelper::invalidateCache( $aKeys ); + + return true; + } + +} \ No newline at end of file diff --git a/includes/utility/WidgetListHelper.class.php b/includes/utility/WidgetListHelper.class.php index 4b7275a..5533d88 100644 --- a/includes/utility/WidgetListHelper.class.php +++ b/includes/utility/WidgetListHelper.class.php @@ -50,7 +50,7 @@ /** * N-glton pattern implementation to prevent multiple parsing. * @param Title $oTitle - * @return BsWidgetListHelper + * @return BsWidgetListHelper */ public static function getInstanceForTitle( $oTitle ){ $sTitleKey = $oTitle->getPrefixedDBkey(); @@ -76,11 +76,21 @@ return $this->aWidgetList; } - // TODO MRG (01.07.11 02:20): Maybe this could be stored in an object cache. Question is, however, when to invalidate. - $oArticle = new Article( $this->oTitle, 0 ); //New: Fetch current revision - $sArticleContent = $oArticle->fetchContent( 0 ); //Old (<1.18): Fetch current revision - // TODO MRG (01.07.11 02:16): Careful: this does not allow nested noincludes. However, these dont make sense anyways - $sArticleContent = preg_replace( '#<noinclude>.*?<\/noinclude>#si', '', $sArticleContent ); + $sKey = BsCacheHelper::getCacheKey( 'BlueSpice', 'WidgetListHelper', $this->oTitle->getPrefixedDBkey() ); + $aData = BsCacheHelper::get( $sKey ); + + if( $aData !== false ) { + wfDebugLog( 'BsMemcached', __CLASS__.': Fetching WidgetList page content from cache' ); + $sArticleContent = $aData; + } else { + wfDebugLog( 'BsMemcached', __CLASS__.': Fetching WidgetList page content from DB' ); + $oWikiPage = WikiPage::factory( $this->oTitle ); + $sArticleContent = $oWikiPage->getContent()->getNativeData(); + $sArticleContent = preg_replace( '#<noinclude>.*?<\/noinclude>#si', '', $sArticleContent ); + + BsCacheHelper::set( $sKey, $sArticleContent ); + } + if( empty ( $sArticleContent ) ) { $this->aWidgetList = array(); return $this->aWidgetList; diff --git a/resources/bluespice.extjs/Ext.ux/form/ItemSelector.js b/resources/bluespice.extjs/Ext.ux/form/ItemSelector.js index a0efb0d..6a55109 100644 --- a/resources/bluespice.extjs/Ext.ux/form/ItemSelector.js +++ b/resources/bluespice.extjs/Ext.ux/form/ItemSelector.js @@ -70,7 +70,7 @@ return null; }, getModelData: function(){ - return null; + return null; }, flex: 1, dragGroup: me.ddGroup, @@ -137,11 +137,11 @@ /** * Get the selected records from the specified list. - * + * * Records will be returned *in store order*, not in order of selection. * @param {Ext.view.BoundList} list The list to read selections from. * @return {Ext.data.Model[]} The selected records in store order. - * + * */ getSelections: function(list) { var store = list.getStore(); @@ -169,7 +169,7 @@ store.insert(0, selected); store.resumeEvents(); list.refresh(); - this.syncValue(); + this.syncValue(); list.getSelectionModel().select(selected); }, @@ -270,7 +270,7 @@ // Synchronizes the submit value with the current state of the toStore syncValue: function() { - var me = this; + var me = this; me.mixins.field.setValue.call(me, me.setupValue(me.toField.store.getRange())); }, @@ -327,7 +327,7 @@ Ext.suspendLayouts(); fromField.boundList.refresh(); toField.boundList.refresh(); - Ext.resumeLayouts(true); + Ext.resumeLayouts(true); }, onBindStore: function(store, initial) { -- To view, visit https://gerrit.wikimedia.org/r/148354 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I4b7f6830493a213e07440189656eb0a95f9f2c77 Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/BlueSpiceFoundation Gerrit-Branch: master Gerrit-Owner: Swidmann <widm...@hallowelt.biz> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits