'DynamicPageList', 'description' => 'outputs a bulleted list of the most recent items residing in a category, or a union of several categories', 'url' => 'http://meta.wikimedia.org/wiki/DynamicPageList' ); function wfDynamicPageList() { global $wgParser, $wgMessageCache; $wgMessageCache->addMessages( array( 'dynamicpagelist_toomanycats' => 'DynamicPageList: Too many categories!', 'dynamicpagelist_toofewcats' => 'DynamicPageList: Too few categories!', 'dynamicpagelist_noresults' => 'DynamicPageList: No results!', 'dynamicpagelist_noincludecats' => 'DynamicPageList: You need to include at least one category, or specify a namespace!', 'dynamicpagelist_noincludedcatsbutcatdate' => 'DynamicPageList: You need to include at least one category to use \'addfirstcategorydate=true\' or \'ordermethod=categoryadd\'!', 'dynamicpagelist_morethanonecatbutcatdate' => 'DynamicPageList: If you include more than one category you cannot use \'addfirstcategorydate=true\' or \'ordermethod=categoryadd\'!', 'dynamicpagelist_catoutputwithwrongordermethod' => 'DynamicPageList: You need to use \'ordermethod=title\' when using category-style output!', ) ); $wgParser->setHook( "DynamicPageList", "DynamicPageList" ); } /* ** * function: setParams * ** * Purpose: constructor, parse parameters * parameters * aCategories ARRAY category names * aExcludeCategories ARRAY notcategory names * bShowNamespace BOOL true = show namespace in link * bNamespace BOOL true = only from namespace * iNamspace INTEGER namespace id * sOrder STRING sorting direction * sOrderMethod STRING method of sorting to use * bSuppressErrors BOOL true = do not return errors * sRedirects STRING show, show only, or exclude redirects * bAddFirstCategoryDate BOOL display date added to 1st category * sStartList STRING HTML list type opening tag * sEndList STRING HTML list type closing tag * sStartItem STRING HTML list item opening tag * sEndItem STRING HTML list item closing tag * iCatCount INTEGER count of include categories * iExcludeCatCount INTEGER count of exclued categories * iTotalCatCount INTEGER count of both included and excluded * bCountSet BOOL true = final category count determined * iCount INTEGER total categories * ** * @param STRING $input * @return ARRAY $aParameters * ** */ function setParams ( $input ) { // Globals global $wgContLang, $wgDLPminCategories; global $wgDLPmaxCategories, $wgDLPMinResultCount, $wgDLPMaxResultCount; global $wgDLPAllowUnlimitedResults, $wgDLPAllowUnlimitedCategories; $aParameters = array( 'aCategories' => array(), 'aExcludeCategories' => array(), 'bShowNamespace' => true, 'aNamespace' => array(), 'abNamespace' => array(), 'sOrder' => 'descending', 'sOrderMethod' => 'lastedit', 'bSuppressErrors' => false, 'sRedirects' => 'exclude', 'bAddFirstCategoryDate' => false, 'sStartList' => '', 'sStartItem' => '
  • ', 'sEndItem' => '
  • ' ); $aParams = explode("\n", $input); // Loop through passed parameters foreach ($aParams as $sParam) { $aParam = explode('=', $sParam); // if improper, skip if ( count( $aParam ) < 2 ) continue; $sType = trim($aParam[0]); $sArg = trim($aParam[1]); switch ( $sType ) { case 'category': $aCats = explode(',', $sArg); $aaCats = array(); foreach($aCats as $sParam) { $sParam=trim($sParam); $title = Title::newFromText( $sParam ); if( $title != NULL ) $aaCats[] = $title; } if (!empty($aaCats)) $aParameters['aCategories'][] = $aaCats; break; case 'notcategory': $title = Title::newFromText( $sArg ); // if not a real category, skip if( is_null( $title ) ) continue; $aParameters['aExcludeCategories'][] = $title; break; case 'namespace': $aNS = explode(',', $sArg); if (count($aNS)) { // If second namespace assignment, reset the array $aParameters['aNamespace'] = array(); $aParameters['abNamespace'] = array(); foreach ($aNS as $key => $sNS) { $sNS = trim($sNS); // check valid namespace value $ns = $wgContLang->getNsIndex($sNS); if (NULL != $ns) { $aParameters['aNamespace'][$key] = $ns; $aParameters['abNamespace'][$key] = true; } else { $aParameters['aNamespace'][$key] = intval($sNS); if ($aParameters['aNamespace'][$key] >= 0) { $aParameters['abNamespace'][$key] = true; } else { $aParameters['abNamespace'][$key] = false; } } } } break; case 'shownamespace': if ('false' == $sArg) $aParameters['bShowNamespace'] = false; else $aParameters['bShowNamespace'] = true; break; case 'mode': switch ($sArg) { case 'none': $aParameters['sStartList'] = ''; $aParameters['sEndList'] = ''; $aParameters['sStartItem'] = ''; $aParameters['sEndItem'] = '
    '; $aParameters['bLastEndItem'] = false; break; case 'inline': $aParameters['sStartList'] = ''; $aParameters['sEndList'] = ''; $aParameters['sStartItem'] = ''; $aParameters['sEndItem'] = ' - '; $aParameters['bLastEndItem'] = false; break; case 'ordered': $aParameters['sStartList'] = '
      '; $aParameters['sEndList'] = '
    '; $aParameters['sStartItem'] = '
  • '; $aParameters['sEndItem'] = '
  • '; $aParameters['bLastEndItem'] = true; break; case 'unordered': default: $aParameters['sStartList'] = ''; $aParameters['sStartItem'] = '
  • '; $aParameters['sEndItem'] = '
  • '; $aParameters['bLastEndItem'] = true; } break; case 'order': switch ($sArg) { case 'ascending': $aParameters['sOrder'] = 'ascending'; break; case 'descending': default: $aParameters['sOrder'] = 'descending'; } break; case 'ordermethod': switch ($sArg) { case 'lastedit': $aParameters['sOrderMethod'] = 'lastedit'; break; case 'categoryadd': default: $aParameters['sOrderMethod'] = 'categoryadd'; } break; case 'count': $aParameters['iCount'] = IntVal( $sArg ); $aParameters['bCountSet'] = true; break; case 'suppresserrors': if ('true' == $sArg) $aParameters['bSuppressErrors'] = true; else $aParameters['bSuppressErrors'] = false; break; case 'redirects': switch ($sArg) { case 'include': $aParameters['sRedirects'] = 'include'; break; case 'only': $aParameters['sRedirects'] = 'only'; break; case 'exclude': default: $aParameters['sRedirects'] = 'exclude'; } break; case 'addfirstcategorydate': if ('true' == $sArg) $aParameters['bAddFirstCategoryDate'] = true; else $aParameters['bAddFirstCategoryDate'] = false; break; default: } } // Calculate and check category counts against configuration limits $aParameters['iCatCount'] = count($aParameters['aCategories']); $aParameters['iTotalIncludeCatCount'] = count($aParameters['aCategories'], COUNT_RECURSIVE) - $aParameters['iCatCount']; $aParameters['iExcludeCatCount'] = count($aParameters['aExcludeCategories']); $aParameters['iTotalCatCount'] = $aParameters['iCatCount'] + $aParameters['iExcludeCatCount']; // 'DynamicPageList: You need to include at least one category, or specify a namespace!' if ($aParameters['iCatCount'] < 1 && !count($aParameters['abNamespace'])) { if (false == $aParameters['bSuppressErrors']) { return htmlspecialchars( wfMsg( 'dynamicpagelist_noincludecats' ) ); } else return ''; } // 'DynamicPageList: Too few categories!' if ($aParameters['iTotalIncludeCatCount'] < $wgDLPminCategories && !count($aParameters['abNamespace'])) { if (false == $aParameters['bSuppressErrors']) { echo $aParameters['iTotalIncludeCatCount'], $wgDLPminCategories; return htmlspecialchars( wfMsg( 'dynamicpagelist_toofewcats' ) ); } else return ''; } // 'DynamicPageList: Too many categories!' if ( $aParameters['iTotalCatCount'] > $wgDLPmaxCategories && !$wgDLPAllowUnlimitedCategories ) { if (false == $aParameters['bSuppressErrors']) return htmlspecialchars( wfMsg( 'dynamicpagelist_toomanycats' ) ); else return ''; } // DynamicPageList: You need to include at least one category to use \'addfirstcategorydate=true\' or \'ordermethod=categoryadd\'! if ( $aParameters['iCatCount'] == 0 && ($aParameters['sOrderMethod'] == 'categoryadd' || $aParameters['bAddFirstCategoryDate'] == true) ) { if (false == $aParameters['bSuppressErrors']) return htmlspecialchars( wfMsg( 'dynamicpagelist_noincludedcatsbutcatdate' ) ); else return ''; } // DynamicPageList: If you include more than one category you cannot use \'addfirstcategorydate=true\' or \'ordermethod=categoryadd\'! if ($aParameters['iTotalCatCount'] > 1 && ($aParameters['sOrderMethod'] == 'categoryadd' || $aParameters['bAddFirstCategoryDate'] == true) ) { if (false == $aParameters['bSuppressErrors']) return htmlspecialchars( wfMsg( 'dynamicpagelist_morethanonecatbutcatdate' ) ); else return ''; } // DynamicPageList: You need to use \'ordermethod=title\' when using category-style output! if ($aParameters['sOutputMode'] == 'category' && $aParameters['sOrderMethod'] != 'title') { if (false == $aParameters['bSuppressErrors']) return htmlspecialchars( wfMsg( 'dpl2_catoutputwithwrongordermethod' ) ); else return ''; } if ($aParameters['bCountSet']) { if ($aParameters['iCount'] < $wgDLPMinResultCount) $aParameters['iCount'] = $wgDLPMinResultCount; if ($aParameters['iCount'] > $wgDLPMaxResultCount) $aParameters['iCount'] = $wgDLPMaxResultCount; } else { if (!$wgDLPAllowUnlimitedResults) { $aParameters['iCount'] = $wgDLPMaxResultCount; $aParameters['bCountSet'] = true; } } // prevent display of date when no include category defined if ($aParameters['iCatCount'] < 1) $aParameters['bAddFirstCategoryDate'] = false; return $aParameters; } /* ** * function: BuildSQL * ** * Purpose: build the necessary SQL statement for the given parameters * ** * @return STRING SQL query * ** */ function BuildSQL ( $aParameters ) { // Globals $dbr =& wfGetDB( DB_SLAVE ); //build the SQL query $sPageTable = $dbr->tableName( 'page' ); $categorylinks = $dbr->tableName( 'categorylinks' ); if ($aParameters['iTotalIncludeCatCount'] == 1) $sSqlSelectFrom = "SELECT DISTINCT page_namespace, page_touched, page_title, c1.cl_timestamp FROM $sPageTable"; else $sSqlSelectFrom = "SELECT DISTINCT page_namespace, page_touched, page_title FROM $sPageTable"; // Set the selectfrom $iCurrentTableNumber = 0; for ($i = 0; $i < $aParameters['iCatCount']; $i++) { $sSqlSelectFrom .= " INNER JOIN $categorylinks AS c" . ($iCurrentTableNumber+1); $sSqlSelectFrom .= ' ON page_id = c'.($iCurrentTableNumber+1).'.cl_from'; $sSqlSelectFrom .= ' AND (c'.($iCurrentTableNumber+1).'.cl_to='. $dbr->addQuotes( $aParameters['aCategories'][$i][0]->getDbKey() ); for ($j = 1; $j < count($aParameters['aCategories'][$i]); $j++) { $sSqlSelectFrom .= ' OR c' . ($iCurrentTableNumber+1) . '.cl_to=' . $dbr->addQuotes( $aParameters['aCategories'][$i][$j]->getDbKey() ); } $sSqlSelectFrom .= ') '; $iCurrentTableNumber++; } // if namespace $i = count($aParameters['abNamespace']); if ($i > 0) { $sSqlWhere = ' WHERE (page_namespace=' . $aParameters['aNamespace'][0] . ' '; if ($i > 1) { for ($i = 1; $i < count($aParameters['aNamespace']); $i++ ) { if (true == $aParameters['abNamespace'][$i]) $sSqlWhere .= ' OR page_namespace=' . $aParameters['aNamespace'][$i]; } } $sSqlWhere .= ') '; } // if only or exclude redirects... switch ($aParameters['sRedirects']) { case 'only': if (isset($sSqlWhere)) $sSqlWhere .= ' AND page_is_redirect = 1 '; else $sSqlWhere = ' WHERE page_is_redirect = 1 '; break; case 'exclude': if (isset($sSqlWhere)) $sSqlWhere .= ' AND page_is_redirect = 0 '; else $sSqlWhere = ' WHERE page_is_redirect = 0 '; break; } if (!isset($sSqlWhere)) $sSqlWhere = ' WHERE 1=1 '; for ($i = 0; $i < $aParameters['iExcludeCatCount']; $i++) { $sSqlSelectFrom .= " LEFT OUTER JOIN $categorylinks AS c" . ($iCurrentTableNumber+1); $sSqlSelectFrom .= ' ON page_id = c'.($iCurrentTableNumber+1).'.cl_from'; $sSqlSelectFrom .= ' AND c'.($iCurrentTableNumber+1).'.cl_to='. $dbr->addQuotes( $aParameters['aExcludeCategories'][$i]->getDbKey() ); $sSqlWhere .= ' AND c'.($iCurrentTableNumber+1).'.cl_to IS NULL'; $iCurrentTableNumber++; } // Set the sort method, direction switch ($aParameters['sOrderMethod']) { case 'lastedit': $sSqlWhere .= ' ORDER BY page_touched '; break; case 'categoryadd': $sSqlWhere .= ' ORDER BY c1.cl_timestamp '; break; case 'title': default: $sSqlWhere .= ' ORDER BY page_title '; break; } if ('descending' == $aParameters['sOrder']) $sSqlWhere .= 'DESC'; else $sSqlWhere .= 'ASC'; // if count parameter set if ($aParameters['bCountSet']) $sSqlWhere .= ' LIMIT ' . $aParameters['iCount']; return $sSqlSelectFrom . $sSqlWhere; } function processQuery ( $sSql, $aParameters ) { // Globals global $wgUser, $wgLang; $dbr =& wfGetDB( DB_SLAVE ); //DEBUG: output SQL query $output .= "QUERY: [{$sSql}]

    "; // process the query $res = $dbr->query($sSql); $sk =& $wgUser->getSkin(); // if no results returned if ($dbr->numRows( $res ) == 0) { if (false == $aParameters['bSuppressErrors']) return $output . htmlspecialchars( wfMsg( 'dynamicpagelist_noresults' ) ); else return ''; } // Display the output //start list $output .= $aParameters['sStartList'] . "\n"; //process results of query, outputing equivalent of
  • [[Article]]
  • for each result, //or something similar if the list uses other startlist/endlist while ($row = $dbr->fetchObject( $res ) ) { $title = Title::makeTitle( $row->page_namespace, $row->page_title); $output .= $aParameters['sStartItem']; if (true == $aParameters['bAddFirstCategoryDate']) $output .= $wgLang->date($row->cl_timestamp) . ': '; if (true == $aParameters['bShowNamespace']) $output .= $sk->makeKnownLinkObj($title); else $output .= $sk->makeKnownLinkObj($title, htmlspecialchars($title->getText())); $output .= $aParameters['sEndItem'] . "\n"; } //end unordered list $output .= $aParameters['sEndList'] . "\n"; return $output; } // The callback function for converting the input text to HTML output function DynamicPageList( $input ) { // Disable page cache global $wgTitle; $dbw =& wfGetDB( DB_MASTER ); $dbw->update( 'page', array( 'page_touched' => $dbw->timestamp( time() + 120 )), array( 'page_namespace' => $wgTitle->getNamespace(), 'page_title' => $wgTitle->getDBkey() ), 'DynamicPageList' ); // parse the parameters $aParameters = setParams( $input ); if (!is_array($aParameters)) return $aParameters; // build the query $sSql = BuildSql( $aParameters ); // Get output $output = processQuery( $sSql, $aParameters ); return $output; } ?>