r106843 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r106842‎ | r106843 | r106844 >
Date:19:30, 20 December 2011
Author:bsitu
Status:deferred (Comments)
Tags:markashelpful 
Comment:
Mark As Helpful APIs and backend
Modified paths:
  • /trunk/extensions/MarkAsHelpful/MarkAsHelpful.hooks.php (modified) (history)
  • /trunk/extensions/MarkAsHelpful/MarkAsHelpful.php (modified) (history)
  • /trunk/extensions/MarkAsHelpful/api (added) (history)
  • /trunk/extensions/MarkAsHelpful/api/ApiGetMarkAsHelpfulItem.php (added) (history)
  • /trunk/extensions/MarkAsHelpful/api/ApiMarkAsHelpful.php (added) (history)
  • /trunk/extensions/MarkAsHelpful/includes (added) (history)
  • /trunk/extensions/MarkAsHelpful/includes/MarkAsHelpfulItem.php (added) (history)
  • /trunk/extensions/MarkAsHelpful/includes/MarkAsHelpfulUtil.php (added) (history)
  • /trunk/extensions/MarkAsHelpful/sql (added) (history)
  • /trunk/extensions/MarkAsHelpful/sql/mark_as_helpful.sql (added) (history)

Diff [purge]

Index: trunk/extensions/MarkAsHelpful/sql/mark_as_helpful.sql
@@ -0,0 +1,21 @@
 2+CREATE TABLE /*_*/mark_as_helpful (
 3+ mah_id int unsigned NOT NULL PRIMARY KEY auto_increment, -- Primary key
 4+
 5+ mah_type varbinary(32) NOT NULL, -- the object type that is being marked as helpful
 6+ mah_item int unsigned NOT NULL, -- the object item that is being marked as helpful
 7+ mah_user_id int unsigned NOT NULL, -- User ID, or zero
 8+ mah_user_ip varbinary(40) NULL, -- If anonymous, user's IP address
 9+ mah_user_editcount int unsigned NOT NULL, -- number of edit for the user
 10+
 11+ mah_namespace int,
 12+ mah_title varchar(255) binary,
 13+ mah_active tinyint unsigned not null default 1, -- is this an active 'mark as helpful'
 14+
 15+ -- Options and context
 16+ mah_timestamp varchar(14) binary NOT NULL, -- When response was received
 17+ mah_system_type varchar(64) binary NULL, -- Operating System
 18+ mah_user_agent varchar(255) binary NULL, -- User-Agent header
 19+ mah_locale varchar(32) binary NULL -- The locale of the user's browser
 20+) /*$wgDBTableOptions*/;
 21+
 22+CREATE INDEX /*i*/mah_type_item ON /*_*/mark_as_helpful (mah_type, mah_item);
Index: trunk/extensions/MarkAsHelpful/MarkAsHelpful.php
@@ -15,14 +15,19 @@
1616
1717
1818 // Object model
 19+$wgAutoloadClasses['MarkAsHelpfulItem'] = dirname(__FILE__).'/includes/MarkAsHelpfulItem.php';
 20+$wgAutoloadClasses['MarkAsHelpfulUtil'] = dirname(__FILE__).'/includes/MarkAsHelpfulUtil.php';
1921
20 -
2122 // API
 23+$wgAutoloadClasses['ApiMarkAsHelpful'] = dirname(__FILE__).'/api/ApiMarkAsHelpful.php';
 24+$wgAPIModules['markashelpful'] = 'ApiMarkAsHelpful';
 25+$wgAutoloadClasses['ApiGetMarkAsHelpfulItem'] = dirname(__FILE__).'/api/ApiGetMarkAsHelpfulItem.php';
 26+$wgAPIModules['getmarkashelpfulitem'] = 'ApiGetMarkAsHelpfulItem';
2227
23 -
2428 // Hooks
2529 $wgAutoloadClasses['MarkAsHelpfulHooks'] = dirname(__FILE__).'/MarkAsHelpful.hooks.php';
2630 $wgHooks['BeforePageDisplay'][] = 'MarkAsHelpfulHooks::onPageDisplay';
 31+$wgHooks['LoadExtensionSchemaUpdates'][] = 'MarkAsHelpfulHooks::onLoadExtensionSchemaUpdates';
2732 $wgHooks['MakeGlobalVariablesScript'][] = 'MoodBarHooks::makeGlobalVariablesScript';
2833
2934 // Special pages
@@ -53,4 +58,4 @@
5459 'dependencies' => array(
5560 'mediawiki.util'
5661 ),
57 -);
\ No newline at end of file
 62+);
Index: trunk/extensions/MarkAsHelpful/MarkAsHelpful.hooks.php
@@ -27,10 +27,25 @@
2828 return true;
2929 }
3030
 31+
 32+ /**
 33+ * Runs MarkAsHelpful schema updates#
 34+ *
 35+ * @param $updater DatabasEUpdater
 36+ */
 37+ public static function onLoadExtensionSchemaUpdates( $updater = null ) {
 38+ $updater->addExtensionUpdate( array( 'addTable', 'mark_as_helpful',
 39+ dirname(__FILE__).'/sql/mark_as_helpful.sql', true ) );
 40+
 41+ return true;
 42+ }
 43+
 44+
3145 public static function makeGlobalVariablesScript( &$vars ) {
3246 global $wgUser;
3347 $vars['mahEditToken'] = $wgUser->editToken();
3448 return true;
3549 }
3650
37 -}
\ No newline at end of file
 51+
 52+}
Index: trunk/extensions/MarkAsHelpful/includes/MarkAsHelpfulUtil.php
@@ -0,0 +1,66 @@
 2+<?php
 3+
 4+class MarkAsHelpfulUtil {
 5+
 6+
 7+ public static function getMarkAsHelpfulTemplate( $User, $isAbleToMark, $HelpfulUserList, $type, $item ) {
 8+
 9+
 10+
 11+ if ( $User->isAnon() ) {
 12+
 13+ $userList = '';
 14+
 15+ foreach ( $HelpfulUserList AS $val ) {
 16+ $userList .= $val['user_name'] . ' ';
 17+ }
 18+
 19+ $data = '';
 20+
 21+ if ( $userList ) {
 22+
 23+ $data = wfMessage( 'mah-someone-marked-text' )->params( $userList )->escape();
 24+
 25+ }
 26+
 27+ return <<<HTML
 28+ <div id="markashelpful-$type-$item">
 29+ $data
 30+ </div>
 31+HTML;
 32+
 33+ }
 34+ else {
 35+
 36+ $userList = '';
 37+ $userId = $User->getId();
 38+ if ( isset( $HelpfulUserList[$userId] ) ) {
 39+
 40+ $data = wfMessage( 'mah-you-marked-text' )->escaped();
 41+ $undo = wfMessage( 'mah-undo-mark-text' )->escaped();
 42+
 43+ return <<<HTML
 44+ <div id="markashelpful-$type-$item">
 45+ $data | $undo
 46+ </div>
 47+HTML;
 48+ }
 49+ else {
 50+ if ( $isAbleToMark ) {
 51+ $linkText = wfMessage( 'mah-mark-text' )->escaped();
 52+ return <<<HTML
 53+ <div id="markashelpful-$type-$item">
 54+ $linkText
 55+ </div>
 56+HTML;
 57+ }
 58+
 59+ }
 60+
 61+ }
 62+
 63+
 64+ }
 65+
 66+
 67+}
Index: trunk/extensions/MarkAsHelpful/includes/MarkAsHelpfulItem.php
@@ -0,0 +1,271 @@
 2+<?php
 3+
 4+
 5+/**
 6+ * An Enity that handles 'Mark As Helpful' items
 7+ */
 8+class MarkAsHelpfulItem {
 9+
 10+ //database field definition
 11+ protected $property = array('mah_id' => null,
 12+ 'mah_type' => null,
 13+ 'mah_item' => null,
 14+ 'mah_user_id' => null,
 15+ 'mah_user_ip' => null,
 16+ 'mah_user_editcount' => null,
 17+ 'mah_namespace' => null,
 18+ 'mah_title' => null,
 19+ 'mah_active' => null,
 20+ 'mah_timestamp' => null,
 21+ 'mah_system_type' => null,
 22+ 'mah_user_agent' => null,
 23+ 'mah_locale' => null );
 24+
 25+ protected $User;
 26+ protected $loadFromDatabase = false;
 27+
 28+ /**
 29+ * Constructor
 30+ */
 31+ public function __construct( $mah_id = null ) {
 32+ if ( $mah_id == intval( $mah_id ) ) {
 33+ $this->property['mah_id'] = $mah_id;
 34+ }
 35+ }
 36+
 37+ public function getProperty( $key ) {
 38+ if( isset( $key, $this->property) ) {
 39+ return $this->property[$key];
 40+ }
 41+ }
 42+
 43+ public function setProperty( $key, $value ) {
 44+ if( isset( $key, $this->property) ) {
 45+ $this->property[$key] = $value;
 46+ }
 47+ }
 48+
 49+ public function getUser() {
 50+ if ( !$this->User ) {
 51+
 52+ if ( $this->loadFromDatabase ) {
 53+ if ( $this->getProperty('mah_user_id') ) {
 54+ $this->User = User::newFromId( $this->getProperty( 'mah_user_id' ) );
 55+ }
 56+ else if ( $this->getProperty('mah_user_ip') ) {
 57+ $this->User = User::newFromName( $this->getProperty( 'mah_user_ip' ) );
 58+ }
 59+
 60+ }
 61+ else {
 62+ global $wgUser;
 63+
 64+ $this->User = $wgUser;
 65+ }
 66+
 67+ }
 68+
 69+ return $this->User;
 70+ }
 71+
 72+ public function loadFromRequest( $params ) {
 73+
 74+ global $wgUser, $wgMarkAsHelpfulType;
 75+
 76+ if ( isset( $params['type'] ) && in_array( $params['type'], $wgMarkAsHelpfulType ) ) {
 77+ $this->setProperty( 'mah_type', $params['type'] );
 78+ }
 79+ else {
 80+ throw new MWMarkAsHelpFulItemPropertyException( 'Unsupported type!' );
 81+ }
 82+
 83+ if ( isset( $params['item'] ) && $params['item'] == intval( $params['item'] ) ) {
 84+ $this->setProperty( 'mah_item', $params['item'] );
 85+ }
 86+ else {
 87+ throw new MWMarkAsHelpFulItemPropertyException( 'Invalid item!' );
 88+ }
 89+
 90+ if ( $wgUser->isAnon() ) {
 91+ $this->setProperty( 'mah_user_ip', $wgUser->getName() );
 92+ $this->setProperty( 'mah_user_editcount', 0 );
 93+ }
 94+ else {
 95+ $this->setProperty( 'mah_user_id', $wgUser->getId() );
 96+ $this->setProperty( 'mah_user_editcount', $wgUser->getEditCount() );
 97+ }
 98+
 99+ if ( isset( $params['page'] ) ) {
 100+ $Page = Title::newFromText( $params['page'] );
 101+
 102+ if ( $Page ) {
 103+ $this->setProperty( 'mah_namespace', $Page->getNamespace() );
 104+ $this->setProperty( 'mah_title', $Page->getDBkey() );
 105+ }
 106+ else {
 107+ throw new MWMarkAsHelpFulItemPropertyException( 'Invalid page!' );
 108+ }
 109+ }
 110+
 111+ $this->setProperty( 'mah_active', '1' );
 112+ $this->setProperty( 'mah_timestamp', wfTimestampNow() );
 113+
 114+ if ( isset( $params['system'] ) ) {
 115+ $this->setProperty( 'mah_system_type', $params['system'] );
 116+ }
 117+ if ( isset ( $params['useragent'] ) ) {
 118+ $this->setProperty( 'mah_user_agent', $params['useragent'] );
 119+ }
 120+ if ( isset ( $params['locale'] ) ) {
 121+ $this->setProperty( 'mah_locale', $params['locale'] );
 122+ }
 123+
 124+ }
 125+
 126+ /**
 127+ * Load from Database
 128+ * @param $conds array - keys to load unique item from database
 129+ */
 130+ public function loadFromDatabase( $conds = array() ) {
 131+
 132+ $dbr = wfGetDB( DB_SLAVE );
 133+
 134+ $res = $dbr->selectRow( array( 'mark_as_helpful' ),
 135+ array( '*' ),
 136+ $conds,
 137+ __METHOD__ );
 138+
 139+ if( $res !== false ) {
 140+ foreach( $this->property AS $key => $val) {
 141+ $this->setProperty( $key, $res->$key );
 142+ }
 143+
 144+ $Item->setLoadFromDatabase = true;
 145+
 146+ return true;
 147+ }
 148+ else {
 149+ return false;
 150+ }
 151+
 152+ }
 153+
 154+ /**
 155+ * this function should be called after either prepareDataBeforeMark() or setProperty()
 156+ * data must be valiated if called from setProperty()
 157+ */
 158+ public function mark() {
 159+
 160+ if ( $this->userHasMarked() ) {
 161+ return;
 162+ }
 163+
 164+
 165+ $dbw = wfGetDB( DB_MASTER );
 166+
 167+ $row = array();
 168+
 169+ foreach ( $this->property AS $key => $value ) {
 170+ if ( !is_null ( $value ) ) {
 171+ $row[$key] = $value;
 172+ }
 173+ }
 174+
 175+
 176+ $this->property['mah_id'] = $dbw->nextSequenceValue( 'mark_as_helpful_mah_id' );
 177+ $dbw->insert( 'mark_as_helpful', $row, __METHOD__ );
 178+ $this->setProperty( 'mah_id', $dbw->insertId() );
 179+
 180+ }
 181+
 182+ public function unmark( $CurrentUser ) {
 183+
 184+ if ( $this->getProperty( 'mah_id' ) ) {
 185+ if ( !$this->getProperty( 'mah_type' ) ) {
 186+ if( !$this->loadFromDatabase() ) {
 187+ return;
 188+ }
 189+ }
 190+
 191+ //for sure this is an invalid item
 192+ if( !$this->getProperty( 'mah_item' ) ) {
 193+ return;
 194+ }
 195+
 196+ $User = $this->getUser();
 197+
 198+ if ( $User ) {
 199+ if ( $CurrentUser->getId() == $User->getId() || $CurrentUser->getName() == $User->getName() ) {
 200+ $dbw = wfGetDB( DB_MASTER );
 201+
 202+ $dbw->delete( 'mark_as_helpful', array( 'mah_id' => $this->getProperty( 'mah_id' ) ), __METHOD__ );
 203+ }
 204+ }
 205+
 206+
 207+ }
 208+
 209+ }
 210+
 211+ public function userHasMarked() {
 212+
 213+ $dbr = wfGetDB( DB_SLAVE );
 214+
 215+ $conds = array( 'mah_type' => $this->getProperty( 'mah_type' ),
 216+ 'mah_item' => intval( $this->getProperty( 'mah_item' ) ) );
 217+
 218+ $User = $this->getUser();
 219+
 220+ if ( $User ) {
 221+ if ( $User->isAnon() ) {
 222+ $conds['mah_user_ip'] = $User->getName();
 223+ }
 224+ else {
 225+ $conds['mah_user_id'] = $User->getId();
 226+ }
 227+ }
 228+ // Invalid User object, we can't treat this user has marked an item
 229+ else {
 230+ return true;
 231+ }
 232+
 233+ $res = $dbr->selectRow( array( 'mark_as_helpful' ),
 234+ array( 'mah_id' ),
 235+ $conds,
 236+ __METHOD__ );
 237+
 238+ //user has not marked this item
 239+ if ( $res === false ) {
 240+ return false;
 241+ }
 242+ else {
 243+ return true;
 244+ }
 245+
 246+ }
 247+
 248+
 249+ public static function getMarkAsHelpfulList( $type, $item ) {
 250+ $dbr = wfGetDB( DB_SLAVE );
 251+
 252+ $conds = array( 'mah_type' => $type,
 253+ 'mah_item' => intval( $item ) );
 254+
 255+ $res = $dbr->select( array( 'mark_as_helpful', 'user' ),
 256+ array( 'mah_id', 'user_id', 'user_name', 'mah_user_ip' ),
 257+ $conds,
 258+ __METHOD__,
 259+ array(),
 260+ array( 'user' => array( 'LEFT JOIN', 'mah_user_id=user_id' ) ) );
 261+
 262+ $list = array();
 263+
 264+ foreach( $res AS $val ) {
 265+ $list[$val['user_id']] = array( 'user_name' => $val['user_name'], 'user_id' => $val['user_id'], 'user_ip' => $val['mah_user_ip'] );
 266+ }
 267+
 268+ return $list;
 269+ }
 270+}
 271+
 272+class MWMarkAsHelpFulItemPropertyException extends MWException {};
Index: trunk/extensions/MarkAsHelpful/api/ApiGetMarkAsHelpfulItem.php
@@ -0,0 +1,68 @@
 2+<?php
 3+
 4+class ApiGetMarkAsHelpfulItem extends ApiBase {
 5+
 6+ public function execute() {
 7+ global $wgUser;
 8+
 9+ $params = $this->extractRequestParams();
 10+
 11+ // check if current user has permission to mark this item,
 12+ $isAbleToMark = wfRunHooks( 'onMarkItemAsHelpful', array( 'mark', $params['type'], $params['item'], $wgUser ) );
 13+
 14+ $HelpfulUserList = MarkAsHelpfulItem::getMarkAsHelpfulList( $params['type'], $params['item'] );
 15+
 16+ if ( $params['prop'] == 'metadata') {
 17+ $data = $HelpfulUserList;
 18+ $format = 'metadata';
 19+ }
 20+ else {
 21+ $data = MarkAsHelpfulUtil::getMarkAsHelpfulTemplate( $wgUser, $isAbleToMark, $HelpfulUserList, $params['type'], $params['item'] );
 22+ $format = 'formatted';
 23+ }
 24+
 25+
 26+ $result = array( 'result' => 'success', $format => $data );
 27+ $this->getResult()->addValue( null, $this->getModuleName(), $result );
 28+ }
 29+
 30+ public function getAllowedParams() {
 31+ return array(
 32+ 'type' => array(
 33+ ApiBase::PARAM_REQUIRED => true,
 34+ ApiBase::PARAM_TYPE => 'string',
 35+ ),
 36+ 'item' => array(
 37+ ApiBase::PARAM_REQUIRED => true,
 38+ ApiBase::PARAM_TYPE => 'integer'
 39+ ),
 40+ 'useragent' => null,
 41+ 'system' => null,
 42+ 'locale' => null,
 43+ 'prop' => array(
 44+ ApiBase::PARAM_TYPE => array( 'metadata', 'formatted' ),
 45+ ),
 46+ );
 47+ }
 48+
 49+ public function getVersion() {
 50+ return __CLASS__ . ': $Id: ApiGetMarkAsHelpfulItem.php 104827 2011-12-19 02:13:46Z bsitu $';
 51+ }
 52+
 53+ public function getParamDescription() {
 54+ return array(
 55+ 'type' => 'The object type that is being marked as helpful',
 56+ 'item' => 'The object item that is being marked as helpful',
 57+ 'useragent' => 'The User-Agent header of the browser',
 58+ 'system' => 'The operating system being used',
 59+ 'locale' => 'The locale in use',
 60+ );
 61+ }
 62+
 63+ public function getDescription() {
 64+ return 'Get a list of all helpful status for an object item';
 65+ }
 66+
 67+}
 68+
 69+class MWApiGetMarkAsHelpfulItemInvalidActionException extends MWException {};
Index: trunk/extensions/MarkAsHelpful/api/ApiMarkAsHelpful.php
@@ -0,0 +1,114 @@
 2+<?php
 3+
 4+class ApiMarkAsHelpful extends ApiBase {
 5+
 6+ public function execute() {
 7+ global $wgUser;
 8+
 9+ if ( $wgUser->isBlocked( false ) ) {
 10+ $this->dieUsageMsg( array( 'blockedtext' ) );
 11+ }
 12+
 13+ $params = $this->extractRequestParams();
 14+
 15+ // Gives other extension the last chance to speicfy mark as helpful permission rules
 16+ if ( !wfRunHooks( 'onMarkItemAsHelpful', array( $params['mahaction'], $params['type'], $params['item'], $wgUser ) ) ) {
 17+ $this->dieUsage( "You don't have permission to do that", 'permission-denied' );
 18+ }
 19+
 20+ switch ( $params['mahaction'] ) {
 21+
 22+ case 'mark':
 23+ $Item = new MarkAsHelpfulItem();
 24+ $Item->loadFromRequest( $params );
 25+ $Item->mark();
 26+ break;
 27+
 28+ case 'unmark':
 29+ $Item = new MarkAsHelpfulItem( );
 30+
 31+ if( $wgUser->isAnon() ) {
 32+ $Item->loadFromDatabase( array( 'mah_type' => $params['type'], 'mah_item' => $params['item'], 'mah_user_ip' => $wgUser->getName() ) );
 33+ }
 34+ else {
 35+ $Item->loadFromDatabase( array( 'mah_type' => $params['type'], 'mah_item' => $params['item'], 'mah_user_id' => $wgUser->getId() ) );
 36+ }
 37+
 38+ $Item->unmark( $wgUser );
 39+ break;
 40+
 41+ default:
 42+ throw new MWApiMarkAsHelpfulInvalidActionException( "Action {$params['mbaction']} not implemented" );
 43+ break;
 44+
 45+ }
 46+
 47+
 48+ $result = array( 'result' => 'success' );
 49+ $this->getResult()->addValue( null, $this->getModuleName(), $result );
 50+ }
 51+
 52+ public function needsToken() {
 53+ return true;
 54+ }
 55+
 56+ public function getTokenSalt() {
 57+ return '';
 58+ }
 59+
 60+ public function getAllowedParams() {
 61+ return array(
 62+ 'mahaction' => array(
 63+ ApiBase::PARAM_REQUIRED => true,
 64+ ApiBase::PARAM_TYPE => array( 'mark', 'unmark' ),
 65+ ),
 66+ 'page' => array(
 67+ ApiBase::PARAM_REQUIRED => true,
 68+ ),
 69+ 'type' => array(
 70+ ApiBase::PARAM_REQUIRED => true,
 71+ ApiBase::PARAM_TYPE => 'string',
 72+ ),
 73+ 'item' => array(
 74+ ApiBase::PARAM_REQUIRED => true,
 75+ ApiBase::PARAM_TYPE => 'integer'
 76+ ),
 77+ 'useragent' => null,
 78+ 'system' => null,
 79+ 'locale' => null,
 80+ 'token' => null,
 81+ );
 82+ }
 83+
 84+ public function mustBePosted() {
 85+ return true;
 86+ }
 87+
 88+ public function isWriteMode() {
 89+ return true;
 90+ }
 91+
 92+ public function getVersion() {
 93+ return __CLASS__ . ': $Id: ApiMarkAsHelpful.php 104827 2011-12-19 02:13:46Z bsitu $';
 94+ }
 95+
 96+ public function getParamDescription() {
 97+ return array(
 98+ 'mahaction' => 'the mark or unmark an item as helpful',
 99+ 'page' => 'The page which the item to be marked is on',
 100+ 'type' => 'The object type that is being marked as helpful',
 101+ 'item' => 'The object item that is being marked as helpful',
 102+ 'useragent' => 'The User-Agent header of the browser',
 103+ 'system' => 'The operating system being used',
 104+ 'locale' => 'The locale in use',
 105+ 'token' => 'An edit token',
 106+ );
 107+ }
 108+
 109+ public function getDescription() {
 110+ return 'Allows users to mark/unmark an object item in the site as helpful';
 111+ }
 112+
 113+}
 114+
 115+class MWApiMarkAsHelpfulInvalidActionException extends MWException {};

Follow-up revisions

RevisionCommit summaryAuthorDate
r106845followup to -r106843 - adding svn eol-stylebsitu19:41, 20 December 2011
r106869updated html message with language file and replace db row access with stdcla...bsitu21:37, 20 December 2011
r106874Remove undefined variable and repalce class variable with a more intuitive namebsitu21:55, 20 December 2011
r106877Fix a typo escape -> escapedbsitu21:59, 20 December 2011
r106889follow up to -r106843 - added code comment, allowable search keys and error m...bsitu23:17, 20 December 2011
r106906updated the template and moved template generation for different cases to ind...bsitu00:35, 21 December 2011
r106966follow up to -r106843 - Use addExtensionTable() instead of addExtensionUpdate...bsitu19:07, 21 December 2011
r107010follow up to -r106843 - Fix the logic in "Mark As Helpful" Hookbsitu23:50, 21 December 2011

Comments

#Comment by Reedy (talk | contribs)   19:32, 20 December 2011

Missing svn:eol-stylve native on new files

FYI, there is also a nicer "$updater->addExtensionTable()" you might want to use

#Comment by Bsitu (talk | contribs)   19:32, 21 December 2011

Fixed svn:eol-style nativeon in -r106845 Replace addExtensionUpdate() with addExtensionTable() in -r106966

Status & tagging log