Version 4.3.0

This commit is contained in:
Neo committed 2025-12-19 05:47:12 -08:00
1 parent 96997ddd8e
commit fe1acf984a
1349 files changed
+116159 -67711

No files matched your search

+152 -74
View File
@@ -197,15 +197,15 @@ class _Content extends Table
{
if ( isset( $class::$databaseColumnMap['updated'] ) )
{
$this->sortBy = $class::$databasePrefix . $class::$databaseColumnMap['updated'];
$this->sortBy = $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnMap['updated'];
}
elseif ( isset( $class::$databaseColumnMap['last_comment'] ) )
{
$this->sortBy = $class::$databasePrefix . ( is_array( $class::$databaseColumnMap['last_comment'] ) ? $class::$databaseColumnMap['last_comment'][0] : $class::$databaseColumnMap['last_comment'] );
$this->sortBy = $class::$databaseTable . '.' . $class::$databasePrefix . ( is_array( $class::$databaseColumnMap['last_comment'] ) ? $class::$databaseColumnMap['last_comment'][0] : $class::$databaseColumnMap['last_comment'] );
}
else
{
$this->sortBy = $class::$databasePrefix . $class::$databaseColumnMap['date'];
$this->sortBy = $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnMap['date'];
}
}
@@ -246,20 +246,17 @@ class _Content extends Table
}
}
if ( ! mb_stristr( $this->sortBy, '`' ) )
$compareSortBy = $this->sortBy;
$lookFor = $class::$databaseTable . '.' . $class::$databasePrefix;
if ( mb_substr( $this->sortBy, 0, mb_strlen( $lookFor ) ) === $lookFor )
{
$compareSortBy = $this->sortBy;
$lookFor = $class::$databaseTable . '.' . $class::$databasePrefix;
if ( mb_substr( $this->sortBy, 0, mb_strlen( $lookFor ) ) === $lookFor )
{
$compareSortBy = mb_substr( $this->sortBy, mb_strlen( $lookFor ) );
}
$this->sortBy = in_array( $compareSortBy, $this->sortOptions ) ? $this->sortBy :
( array_key_exists( $compareSortBy, $this->sortOptions ) ? $this->sortOptions[ $compareSortBy ] : $defaultSort );
$compareSortBy = mb_substr( $this->sortBy, mb_strlen( $lookFor ) );
}
$this->sortBy = in_array( $compareSortBy, $this->sortOptions ) ? $this->sortBy :
( array_key_exists( $compareSortBy, $this->sortOptions ) ? $this->sortOptions[ $compareSortBy ] : $defaultSort );
/* Callback? */
if ( $this->advancedSearchCallback and !empty( $advancedSearchValues ) )
{
@@ -514,60 +511,41 @@ class _Content extends Table
$class = $this->class;
$params = array();
/* Permission check for the items we have specific actions for here, modActions will take care of permissions for the rest */
if( in_array( \IPS\Request::i()->modaction, array( 'hide', 'move', 'merge' ) ) )
{
$options = array();
$ids = array_keys( \IPS\Request::i()->moderate );
foreach ( $ids as $id )
{
$item = $class::load( $id );
$action = 'can' . ucwords( \IPS\Request::i()->modaction );
if( $item->$action() )
{
$options[ $id ] = $item->mapped('title');
$descriptions[ $id ] = \IPS\Member::loggedIn()->language()->addToStack( 'byline', FALSE, array( 'sprintf' => array( $item->author()->name ) ) ) . \IPS\DateTime::ts( $item->mapped('date') )->html();
}
}
/* The user doesn't have permission to perform the action on any of the content */
if ( !count( $options ) )
{
throw new \OutOfRangeException;
}
}
/* Move: to where? */
if ( \IPS\Request::i()->modaction == 'move' )
{
$form = new \IPS\Helpers\Form( 'form', 'move' );
$form->class = 'ipsForm_vertical';
$form->hiddenValues['modaction'] = 'move';
$form->hiddenValues['moderate'] = \IPS\Request::i()->moderate;
$form->add( new \IPS\Helpers\Form\Node( 'move_to', NULL, TRUE, array(
'class' => $class::$containerNodeClass,
'url' => \IPS\Request::i()->url()->setQueryString( 'modaction', 'move' ),
'permissionCheck' => function( $node )
{
try
{
if ( $node->can( 'add' ) )
{
return true;
}
}
catch( \OutOfBoundsException $e ) { }
return false;
},
'clubs' => TRUE
) ) );
if ( isset( $class::$databaseColumnMap['moved_to'] ) )
/* The method will return an HTML string, or an array of parameters to pass to modAction */
$params = $this->getMoveForm();
/* This is the form instead */
if( !is_array( $params ) )
{
$form->add( new \IPS\Helpers\Form\Checkbox( 'move_keep_link' ) );
if ( \IPS\Settings::i()->topic_redirect_prune )
{
\IPS\Member::loggedIn()->language()->words['move_keep_link_desc'] = \IPS\Member::loggedIn()->language()->addToStack('_move_keep_link_desc', FALSE, array( 'pluralize' => array( \IPS\Settings::i()->topic_redirect_prune ) ) );
}
}
if ( $values = $form->values() )
{
$params[] = $values['move_to'];
$params[] = ( isset( $values['move_keep_link'] ) and $values['move_keep_link'] );
}
else
{
\IPS\Output::i()->output = $form->customTemplate( array( call_user_func_array( array( \IPS\Theme::i(), 'getTemplate' ), array( 'forms', 'core' ) ), 'popupTemplate' ) );
if ( \IPS\Request::i()->isAjax() )
{
\IPS\Output::i()->sendOutput( \IPS\Output::i()->output );
}
else
{
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->globalTemplate( \IPS\Output::i()->title, \IPS\Output::i()->output, array( 'app' => \IPS\Dispatcher::i()->application->directory, 'module' => \IPS\Dispatcher::i()->module->key, 'controller' => \IPS\Dispatcher::i()->controller ) ), 200, 'text/html', \IPS\Output::i()->httpHeaders );
}
return;
return $params;
}
}
@@ -602,21 +580,31 @@ class _Content extends Table
/* Merge: what's the master? */
if ( \IPS\Request::i()->modaction == 'merge' )
{
$ids = array_keys( \IPS\Request::i()->moderate );
$options = array();
$descriptions = array();
foreach ( $ids as $id )
{
$item = $class::load( $id );
$options[ $id ] = $item->mapped('title');
$descriptions[ $id ] = \IPS\Member::loggedIn()->language()->addToStack( 'byline', FALSE, array( 'sprintf' => array( $item->author()->name ) ) ) . \IPS\DateTime::ts( $item->mapped('date') )->html();
}
if ( count( $options ) === 1 )
{
foreach ( $options as $id => $title )
{
\IPS\Output::i()->redirect( $class::load( $id )->url()->setQueryString( 'do', 'merge' ) );
$item = $class::load( $id );
$form = $item->mergeForm();
if ( $values = $form->values() )
{
$item->mergeIn( array( $class::loadFromUrl( $values['merge_with'] ) ), isset( $values['move_keep_link'] ) ? $values['move_keep_link'] : FALSE );
}
else
{
\IPS\Output::i()->output = $form->customTemplate( array( call_user_func_array( array( \IPS\Theme::i(), 'getTemplate' ), array( 'forms', 'core' ) ), 'popupTemplate' ) );
if ( \IPS\Request::i()->isAjax() )
{
\IPS\Output::i()->sendOutput( \IPS\Output::i()->output );
}
else
{
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->globalTemplate( \IPS\Output::i()->title, \IPS\Output::i()->output, array( 'app' => \IPS\Dispatcher::i()->application->directory, 'module' => \IPS\Dispatcher::i()->module->key, 'controller' => \IPS\Dispatcher::i()->controller ) ), 200, 'text/html', \IPS\Output::i()->httpHeaders );
}
}
}
}
else
@@ -673,7 +661,20 @@ class _Content extends Table
try
{
$object = $class::loadAndCheckPerms( $id );
/* If this item is read, we need to re-mark it as such after moving */
if( $object instanceof \IPS\Content\ReadMarkers )
{
$unread = $object->unread();
}
$object->modAction( \IPS\Request::i()->modaction, \IPS\Member::loggedIn(), $params );
/* Mark it as read */
if( $object instanceof \IPS\Content\ReadMarkers and \IPS\Request::i()->modaction == 'move' AND $unread == 0 )
{
$object->markRead();
}
}
catch ( \Exception $e ) {}
}
@@ -681,6 +682,83 @@ class _Content extends Table
\IPS\Output::i()->redirect( $this->baseUrl );
}
/**
* Get the form to move items
*
* @return string|array
*/
protected function getMoveForm()
{
$class = $this->class;
$params = array();
$form = new \IPS\Helpers\Form( 'form', 'move' );
$form->class = 'ipsForm_vertical';
$form->hiddenValues['modaction'] = 'move';
$form->hiddenValues['moderate'] = \IPS\Request::i()->moderate;
$currentContainer = $this->container;
$form->add( new \IPS\Helpers\Form\Node( 'move_to', NULL, TRUE, array(
'class' => $class::$containerNodeClass,
'url' => \IPS\Request::i()->url()->setQueryString( 'modaction', 'move' ),
'permissionCheck' => function( $node ) use ( $currentContainer, $class )
{
if( $currentContainer->id != $node->id )
{
try
{
/* If the item is in a club, only allow moving to other clubs that you moderate */
if ( $currentContainer and \IPS\IPS::classUsesTrait( $currentContainer, 'IPS\Content\ClubContainer' ) and $currentContainer->club() )
{
return $class::modPermission( 'move', \IPS\Member::loggedIn(), $node ) and $node->can( 'add' ) ;
}
if ( $node->can( 'add' ) )
{
return true;
}
}
catch( \OutOfBoundsException $e ) { }
}
return false;
},
'clubs' => TRUE
) ) );
if ( isset( $class::$databaseColumnMap['moved_to'] ) )
{
$form->add( new \IPS\Helpers\Form\Checkbox( 'move_keep_link' ) );
if ( \IPS\Settings::i()->topic_redirect_prune )
{
\IPS\Member::loggedIn()->language()->words['move_keep_link_desc'] = \IPS\Member::loggedIn()->language()->addToStack('_move_keep_link_desc', FALSE, array( 'pluralize' => array( \IPS\Settings::i()->topic_redirect_prune ) ) );
}
}
if ( $values = $form->values() )
{
$params[] = $values['move_to'];
$params[] = ( isset( $values['move_keep_link'] ) and $values['move_keep_link'] );
return $params;
}
else
{
\IPS\Output::i()->output = $form->customTemplate( array( call_user_func_array( array( \IPS\Theme::i(), 'getTemplate' ), array( 'forms', 'core' ) ), 'popupTemplate' ) );
if ( \IPS\Request::i()->isAjax() )
{
\IPS\Output::i()->sendOutput( \IPS\Output::i()->output );
}
else
{
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->globalTemplate( \IPS\Output::i()->title, \IPS\Output::i()->output, array( 'app' => \IPS\Dispatcher::i()->application->directory, 'module' => \IPS\Dispatcher::i()->module->key, 'controller' => \IPS\Dispatcher::i()->controller ) ), 200, 'text/html', \IPS\Output::i()->httpHeaders );
}
return;
}
}
/**
* Return the table headers
+1 -1
View File
@@ -139,7 +139,7 @@ class _Custom extends Table
}
else
{
$v = htmlspecialchars( $v, ENT_QUOTES | \IPS\HTMLENTITIES, 'UTF-8', FALSE );
$v = htmlspecialchars( $v, ENT_QUOTES | ENT_DISALLOWED, 'UTF-8', FALSE );
}
$rows[ $i ][ $k ] = $v;
+41 -41
View File
@@ -36,16 +36,21 @@ class _Db extends Table
* @brief Initial WHERE clause
*/
public $where;
/**
* @brief Initial GROUP BY clause
*/
protected $group;
/**
* @brief Force index clause
*/
protected $index;
/**
* @brief Primary sort column
*/
public $primarySortBy;
/**
* @brief Direction of primary sort
*/
public $primarySortDirection;
/**
* @brief Joins
@@ -57,31 +62,26 @@ class _Db extends Table
*/
public $keyField = NULL;
/**
* @brief Restrict select columns to only those listed - useful when grouping
*/
public $onlySelected = NULL;
/**
* Constructor
*
* @param array|string $table Database table
* @param string $table Database table
* @param \IPS\Http\Url $baseUrl Base URL
* @param array|null $where WHERE clause
* @param string|null $group GROUP clause
* @param array|null $forceIndex Index to force
* @return void
*/
public function __construct( $table, \IPS\Http\Url $baseUrl, $where=NULL, $group=NULL, $forceIndex=NULL )
public function __construct( $table, \IPS\Http\Url $baseUrl, $where=NULL, $forceIndex=NULL )
{
$this->table = $table;
$this->where = $where;
$this->group = $group;
$this->index = $forceIndex;
return parent::__construct( $baseUrl );
}
protected $onlySelected = false;
/**
* Get rows
*
@@ -285,29 +285,13 @@ class _Db extends Table
$selects = $this->selects;
if( $this->onlySelected !== NULL )
if ( count( $this->joins ) )
{
foreach( $this->onlySelected as $column )
foreach( $this->joins as $join )
{
$selects[] = $column;
}
if( $this->group !== NULL )
{
$this->group = is_array( $this->group ) ? $this->group : array( $this->group );
$this->group = array_unique( array_merge( $this->group, $this->onlySelected ) );
}
}
else
{
if ( count( $this->joins ) )
{
foreach( $this->joins as $join )
if ( isset( $join['select'] ) )
{
if ( isset( $join['select'] ) )
{
$selects[] = $join['select'];
}
$selects[] = $join['select'];
}
}
}
@@ -324,8 +308,8 @@ class _Db extends Table
$count = $count->first();
/* Now get column headers */
$query = \IPS\Db::i()->select( $this->onlySelected ? implode( ', ', $selects ) : '*', $this->table, NULL, NULL, array( 0, 1 ) );
$query = \IPS\Db::i()->select( '*', $this->table, NULL, NULL, array( 0, 1 ) );
if ( count( $this->joins ) )
{
foreach( $this->joins as $join )
@@ -368,17 +352,22 @@ class _Db extends Table
}, explode( ',', $this->sortBy ) ) );
$orderBy .= $this->sortDirection == 'asc' ? ' asc' : ' desc';
/* Primary sorting effectively creates a way to 'pin' records regardless of the user selected sort */
if( $this->primarySortBy !== NULL AND $this->primarySortDirection !== NULL )
{
$orderBy = "{$this->primarySortBy} {$this->primarySortDirection}, {$orderBy}";
}
}
/* Run query */
$rows = array();
$select = \IPS\Db::i()->select(
$this->onlySelected ? implode( ', ', $selects ) : ( ( count( $selects ) ) ? $this->table . '.*, ' . implode( ', ', $selects ) : '*' ),
( count( $selects ) ) ? $this->table . '.*, ' . implode( ', ', $selects ) : '*',
$this->table,
$where,
$orderBy,
array( ( $this->limit * ( $this->page - 1 ) ), $this->limit ),
$this->group
array( ( $this->limit * ( $this->page - 1 ) ), $this->limit )
);
if ( $this->index )
@@ -429,7 +418,7 @@ class _Db extends Table
}
else
{
$v = htmlspecialchars( $v, ENT_QUOTES | \IPS\HTMLENTITIES, 'UTF-8', FALSE );
$v = htmlspecialchars( $v, ENT_QUOTES | ENT_DISALLOWED, 'UTF-8', FALSE );
}
/* Are we including this one? */
@@ -449,6 +438,12 @@ class _Db extends Table
$row['_buttons'] = call_user_func( $this->rowButtons, $_row );
}
/* Highlighting? */
if ( isset( $this->parsers['_highlight'] ) and $class = call_user_func( $this->parsers['_highlight'], $_row ) )
{
$this->highlightRows[ $rowId ] = $class;
}
$rows[ $rowId ] = $row;
}
@@ -485,6 +480,11 @@ class _Db extends Table
{
return false;
}
if( !is_array( $count ) )
{
$count = array( $count );
}
if ( mb_strstr( $this->sortBy, ',' ) )
{
+25 -23
View File
@@ -167,6 +167,11 @@ abstract class _Table
*/
public $rowClasses = array();
/**
* @brief Rows to highlight
*/
public $highlightRows = array();
/**
* @brief Buttons to show on the "root row"
* @code
@@ -281,7 +286,7 @@ abstract class _Table
$this->rowsTemplate = array( \IPS\Theme::i()->getTemplate( 'tables', 'core' ), 'rows' );
/* Create a unique id used by the template - can be overriden manually if desired */
$this->uniqueId = md5( uniqid() );
$this->uniqueId = md5( mt_rand() );
}
/**
@@ -478,7 +483,7 @@ abstract class _Table
/* Get rows */
$rows = $this->getRows( $advancedSearchValues );
/* Check we're on a valid page (must come after getRows() as this is where $this->pages is set) */
if ( $this->page )
{
@@ -700,6 +705,7 @@ abstract class _Table
/* Store these to prevent URL from being rebuilt on subsequent calls */
if ( $this->advancedSearchValues === NULL )
{
$this->advancedSearchValues = array();
foreach ( $values as $k => $v )
{
if( array_key_exists( $k, $form->hiddenValues ) )
@@ -709,26 +715,6 @@ abstract class _Table
if ( $v !== NULL )
{
if( \IPS\Request::i()->isAjax() )
{
if ( is_array( $v ) )
{
$newVal = array();
foreach( $v as $key => $val )
{
$newVal[ $key ] = urldecode( $val );
}
$v = $newVal;
}
else
{
if ( ! is_object( $v ) )
{
$v = urldecode( $v );
}
}
}
$advancedSearchValuesQuery[ $k ] = $v;
$this->advancedSearchValues[ mb_substr( $k, mb_strlen( $this->langPrefix ) ) ] = $v;
}
@@ -744,5 +730,21 @@ abstract class _Table
return $this->advancedSearchValues;
}
}
/**
* Return the sort direction to use for links
*
* @note Abstracted so other table helper instances can adjust as needed
* @param string $column Sort by string
* @return string [asc|desc]
*/
public function getSortDirection( $column )
{
if( $column == 'title' )
{
return 'asc';
}
return 'desc';
}
}