Version 4.2.0
This commit is contained in:
1 parent
0dc2aee5ed
commit
96997ddd8e
2074 files changed
+125454
-55780
No files matched your search
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Base API endpoint for Nodes
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
* @package Invision Community
|
||||
* @since 3 Apr 2017
|
||||
*/
|
||||
|
||||
namespace IPS\Node\Api;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Base API endpoint for Nodes
|
||||
*/
|
||||
class _NodeController extends \IPS\Api\Controller
|
||||
{
|
||||
/**
|
||||
* List
|
||||
*
|
||||
* @param array $where Extra WHERE clause
|
||||
* @return \IPS\Api\PaginatedResponse
|
||||
*/
|
||||
protected function _list( $where = array() )
|
||||
{
|
||||
$class = $this->class;
|
||||
|
||||
/* Return */
|
||||
return new \IPS\Api\PaginatedResponse(
|
||||
200,
|
||||
\IPS\Db::i()->select( '*', $class::$databaseTable, $where, $class::$databaseColumnOrder ? $class::$databaseColumnOrder . " asc" : NULL ),
|
||||
isset( \IPS\Request::i()->page ) ? \IPS\Request::i()->page : 1,
|
||||
$class,
|
||||
\IPS\Db::i()->select( 'COUNT(*)', $class::$databaseTable, $where )->first()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* View
|
||||
*
|
||||
* @param int $id ID Number
|
||||
* @return \IPS\Api\Response
|
||||
*/
|
||||
protected function _view( $id )
|
||||
{
|
||||
$class = $this->class;
|
||||
return new \IPS\Api\Response( 200, $class::load( $id )->apiOutput() );
|
||||
}
|
||||
|
||||
/**
|
||||
* View
|
||||
*
|
||||
* @param int $id ID Number
|
||||
* @throws 1S359/1 INVALID_ID The member ID does not exist
|
||||
* @return \IPS\Api\Response
|
||||
*/
|
||||
protected function _delete( $id )
|
||||
{
|
||||
$class = $this->class;
|
||||
|
||||
try
|
||||
{
|
||||
$class::load( $id )->delete();
|
||||
|
||||
return new \IPS\Api\Response( 200, NULL );
|
||||
}
|
||||
catch ( \OutOfRangeException $e )
|
||||
{
|
||||
throw new \IPS\Api\Exception( 'INVALID_ID', '1S359/1', 404 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create or update node
|
||||
*
|
||||
* @param \IPS\node\Model $node The node
|
||||
* @return \IPS\node\Model
|
||||
*/
|
||||
protected function _createOrUpdate( \IPS\node\Model $node )
|
||||
{
|
||||
$node->save();
|
||||
|
||||
/* Return */
|
||||
return $node;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create
|
||||
*
|
||||
* @return \IPS\Content\Node
|
||||
*/
|
||||
protected function _create()
|
||||
{
|
||||
$class = $this->class;
|
||||
|
||||
/* Create item */
|
||||
$node = new $class;
|
||||
|
||||
if( isset( $node::$databaseColumnOrder ) AND $node::$automaticPositionDetermination === TRUE )
|
||||
{
|
||||
$orderColumn = $node::$databaseColumnOrder;
|
||||
$node->$orderColumn = \IPS\Db::i()->select( 'MAX(' . $node::$databasePrefix . $orderColumn . ')', $node::$databaseTable )->first() + 1;
|
||||
}
|
||||
|
||||
$node->save();
|
||||
$node = $this->_createOrUpdate( $node );
|
||||
|
||||
/* Output */
|
||||
return $node;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Colorize Node Titles Trait
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
* @package Invision Community
|
||||
* @since 5 April 2017
|
||||
*/
|
||||
|
||||
namespace IPS\Node;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Colorize Trait
|
||||
*/
|
||||
trait Colorize
|
||||
{
|
||||
/**
|
||||
* @brief Table column name that holds the feature color hex
|
||||
*/
|
||||
public static $featureColumnName = 'feature_color';
|
||||
|
||||
/**
|
||||
* @brief Cache the calculated formatted title for this node
|
||||
*/
|
||||
protected $formattedTitle = NULL;
|
||||
|
||||
/**
|
||||
* @brief Cache the formatted text colour for this node
|
||||
*/
|
||||
protected $featureTextColor = NULL;
|
||||
|
||||
/**
|
||||
* Get HTML formatted title. Allows apps or nodes to format the title, such as adding different colours, etc
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get__formattedTitle()
|
||||
{
|
||||
if ( $this->formattedTitle === NULL )
|
||||
{
|
||||
$columnName = static::$featureColumnName;
|
||||
|
||||
if ( ! $this->$columnName )
|
||||
{
|
||||
$this->formattedTitle = $this->_title;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->formattedTitle = \IPS\Theme::i()->getTemplate( 'global', 'core', 'front' )->formattedTitle( $this );
|
||||
}
|
||||
}
|
||||
|
||||
return $this->formattedTitle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the featured text color for use in CSS
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get__featureTextColor()
|
||||
{
|
||||
if ( $this->featureTextColor === NULL )
|
||||
{
|
||||
if ( ! $this->feature_color )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
$columnName = static::$featureColumnName;
|
||||
$this->featureTextColor = static::featureTextColor( $this->$columnName );
|
||||
}
|
||||
}
|
||||
|
||||
return $this->featureTextColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Static method to fetch the text colour based on the the contrast from the background color
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function featureTextColor( $featureColor )
|
||||
{
|
||||
$hexColor = str_replace( '#', '', $featureColor );
|
||||
$blackColor = '000000';
|
||||
$textColor = '#ffffff';
|
||||
|
||||
/* Use luminosity contrast algorithm to determine font color */
|
||||
$r1 = hexdec( \substr( $hexColor, 0, 2 ) );
|
||||
$g1 = hexdec( \substr( $hexColor, 2, 2 ) );
|
||||
$b1 = hexdec( \substr( $hexColor, 4, 2 ) );
|
||||
|
||||
$r2 = hexdec( \substr( $blackColor, 0, 2 ) );
|
||||
$g2 = hexdec( \substr( $blackColor, 2, 2 ) );
|
||||
$b2 = hexdec( \substr( $blackColor, 4, 2 ) );
|
||||
|
||||
$l1 = 0.2126 * pow( $r1 / 255, 2.2 ) + 0.7152 * pow( $g1 / 255, 2.2 ) + 0.0722 * pow( $b1 / 255, 2.2 );
|
||||
$l2 = 0.2126 * pow( $r2 / 255, 2.2 ) + 0.7152 * pow( $g2 / 255, 2.2 ) + 0.0722 * pow( $b2 / 255, 2.2 );
|
||||
|
||||
$contrastRatio = 0;
|
||||
if ( $l1 > $l2 )
|
||||
{
|
||||
$contrastRatio = (int) ( ( $l1 + 0.05 ) / ( $l2 + 0.05 ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
$contrastRatio = (int) ( ( $l2 + 0.05 ) / ( $l1 + 0.05 ) );
|
||||
}
|
||||
|
||||
if ( $contrastRatio > 5 )
|
||||
{
|
||||
$textColor = '#000000';
|
||||
}
|
||||
|
||||
return $textColor;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get URL from index data
|
||||
*
|
||||
* @param array $indexData Data from the search index
|
||||
* @param array $itemData Basic data about the item. Only includes columns returned by item::basicDataColumns()
|
||||
* @param array|NULL $containerData Basic data about the container. Only includes columns returned by container::basicDataColumns()
|
||||
* @return \IPS\Http\Url
|
||||
*/
|
||||
public static function titleFromIndexData( $indexData, $itemData, $containerData )
|
||||
{
|
||||
if ( ! isset( $containerData[ static::$featureColumnName ] ) )
|
||||
{
|
||||
return parent::titleFromIndexData( $indexData, $itemData, $containerData );
|
||||
}
|
||||
|
||||
$node = $containerData;
|
||||
$node['title'] = \IPS\Member::loggedIn()->language()->addToStack( static::$titleLangPrefix . $indexData['index_container_id'], 'NULL', array( 'escape' => true ) );
|
||||
$node['text_color'] = static::featureTextColor( $node[ static::$featureColumnName ] );
|
||||
|
||||
/* Normalize */
|
||||
$node['feature_color'] = $node[ static::$featureColumnName ];
|
||||
|
||||
$title = \IPS\Theme::i()->getTemplate( 'global', 'core', 'front' )->formattedTitle( $node );
|
||||
|
||||
if ( $indexData['index_club_id'] and isset( $containerData['_club'] ) )
|
||||
{
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'club_container_title', FALSE, array( 'htmlsprintf' => array( $containerData['_club']['name'], $title ) ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
return $title;
|
||||
}
|
||||
}
|
||||
}
|
||||
+209
-11
@@ -1,12 +1,11 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Node Controller
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
* @package Invision Community
|
||||
* @since 18 Feb 2013
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\Node;
|
||||
@@ -121,12 +120,21 @@ class _Controller extends \IPS\Dispatcher\Controller
|
||||
|
||||
if ( $nodeClass::canAddRoot() )
|
||||
{
|
||||
return array( 'add' => array(
|
||||
$add = array(
|
||||
'icon' => 'plus',
|
||||
'title' => 'add',
|
||||
'link' => $this->url->setQueryString( 'do', 'form' ),
|
||||
'data' => ( $nodeClass::$modalForms ? array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('add') ) : array() )
|
||||
) );
|
||||
);
|
||||
|
||||
if ( \IPS\Request::i()->isAjax() )
|
||||
{
|
||||
return array( 'add' => $add );
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Output::i()->sidebar['actions']['add'] = ( array( 'primary' => true ) + $add );
|
||||
}
|
||||
}
|
||||
return array();
|
||||
}
|
||||
@@ -270,7 +278,7 @@ class _Controller extends \IPS\Dispatcher\Controller
|
||||
/* Load the node being edited */
|
||||
try
|
||||
{
|
||||
$node = call_user_func( "{$nodeClass}::load", \IPS\Request::i()->id );
|
||||
$node = $nodeClass::load( \IPS\Request::i()->id );
|
||||
\IPS\Output::i()->title = $node->_title;
|
||||
}
|
||||
catch ( \OutOfRangeException $e )
|
||||
@@ -368,7 +376,7 @@ class _Controller extends \IPS\Dispatcher\Controller
|
||||
try
|
||||
{
|
||||
$new = !$node->_id;
|
||||
if ( $new and isset( $node::$databaseColumnOrder ) )
|
||||
if ( $new and isset( $node::$databaseColumnOrder ) AND $node::$automaticPositionDetermination === TRUE )
|
||||
{
|
||||
$orderColumn = $node::$databaseColumnOrder;
|
||||
$node->$orderColumn = \IPS\Db::i()->select( 'MAX(' . $node::$databasePrefix . $orderColumn . ')', $node::$databaseTable )->first() + 1;
|
||||
@@ -1181,10 +1189,10 @@ class _Controller extends \IPS\Dispatcher\Controller
|
||||
/* Do we have any children or content? */
|
||||
if ( $node->hasChildren( NULL, NULL, TRUE ) or $node->showDeleteOrMoveForm() )
|
||||
{
|
||||
$form = $node->deleteOrMoveForm();
|
||||
$form = $node->deleteOrMoveForm( FALSE, TRUE );
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
$node->deleteOrMoveFormSubmit( $values, \IPS\Request::i()->deleteNode );
|
||||
$node->deleteOrMoveFormSubmit( $values );
|
||||
\IPS\Output::i()->redirect( $this->url->setQueryString( array( 'root' => ( $node->parent() ? $node->parent()->_id : '' ) ) ), 'deleted' );
|
||||
}
|
||||
else
|
||||
@@ -1221,6 +1229,196 @@ class _Controller extends \IPS\Dispatcher\Controller
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mass Move/Delete Content
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function massManageContent()
|
||||
{
|
||||
/* Get node */
|
||||
$nodeClass = $this->nodeClass;
|
||||
if ( \IPS\Request::i()->subnode )
|
||||
{
|
||||
$nodeClass = $nodeClass::$subnodeClass;
|
||||
}
|
||||
try
|
||||
{
|
||||
$node = call_user_func( "{$nodeClass}::load", \IPS\Request::i()->id );
|
||||
}
|
||||
catch ( \OutOfRangeException $e )
|
||||
{
|
||||
\IPS\Output::i()->error( 'node_error', '2S101/X', 404, '' );
|
||||
}
|
||||
|
||||
/* Permission check */
|
||||
if( !$node->canMassManageContent() )
|
||||
{
|
||||
\IPS\Output::i()->error( 'node_noperm_delete', '2S101/Y', 403, '' );
|
||||
}
|
||||
|
||||
/* Is there any content? */
|
||||
if ( !isset( $nodeClass::$contentItemClass ) or !$node->getContentItemCount() )
|
||||
{
|
||||
\IPS\Output::i()->error( 'node_mass_content_none', '2S101/Z' );
|
||||
}
|
||||
$contentItemClass = $nodeClass::$contentItemClass;
|
||||
|
||||
/* Build Wizard */
|
||||
\IPS\Output::i()->title = $node->_title;
|
||||
\IPS\Output::i()->output = new \IPS\Helpers\Wizard(
|
||||
array(
|
||||
'node_mass_content_form' => function( $data ) use( $nodeClass, $node, $contentItemClass )
|
||||
{
|
||||
/* Build Form */
|
||||
$form = new \IPS\Helpers\Form( 'delete_node_form', 'continue', $this->url->setQueryString( array( 'do' => 'massManageContent', 'id' => $node->_id, '_step' => 'node_mass_content_form' ) ) );
|
||||
$form->addHeader('node_mass_move_delete_if');
|
||||
$form->add( new \IPS\Helpers\Form\Member( 'node_move_author', isset( $data['additional']['author'] ) ? array_map( function( $id ) {
|
||||
return \IPS\Member::load( $id );
|
||||
}, $data['additional']['author'] ) : NULL, FALSE, array( 'nullLang' => 'node_mass_move_anyone', 'multiple' => NULL ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Date( 'node_move_date', isset( $data['additional']['date'] ) ? $data['additional']['date'] : 0, FALSE, array( 'unlimited' => 0, 'unlimitedLang' => 'node_mass_move_any_time' ) ) );
|
||||
if ( isset( $contentItemClass::$commentClass ) )
|
||||
{
|
||||
$form->add( new \IPS\Helpers\Form\Number( 'node_move_comments', isset( $data['additional']['num_comments'] ) ? $data['additional']['num_comments'] : -1, FALSE, array( 'unlimited' => -1 ), NULL, NULL, NULL, 'node_move_comments_less' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Date( 'node_move_last_post', isset( $data['additional']['last_post'] ) ? $data['additional']['last_post'] : 0, FALSE, array( 'unlimited' => 0, 'unlimitedLang' => 'node_mass_move_any_time' ) ) );
|
||||
}
|
||||
if ( in_array( 'IPS\Content\Lockable', class_implements( $contentItemClass ) ) )
|
||||
{
|
||||
$form->add( new \IPS\Helpers\Form\Radio( 'node_move_state', isset( $data['additional']['state'] ) ? $data['additional']['state'] : 'any', FALSE, array( 'options' => array( 'locked' => 'locked', 'open' => 'unlocked', 'any' => 'node_mass_move_either' ) ) ) );
|
||||
}
|
||||
if ( in_array( 'IPS\Content\Pinnable', class_implements( $contentItemClass ) ) )
|
||||
{
|
||||
$form->add( new \IPS\Helpers\Form\Radio( 'node_move_pinned', isset( $data['additional']['pinned'] ) ? $data['additional']['pinned'] : 'any', FALSE, array( 'options' => array( '1' => 'pinned', '0' => 'node_move_pinned_not_pinned', 'any' => 'node_mass_move_either' ) ) ) );
|
||||
}
|
||||
if ( in_array( 'IPS\Content\Featurable', class_implements( $contentItemClass ) ) )
|
||||
{
|
||||
$form->add( new \IPS\Helpers\Form\Radio( 'node_move_featured', isset( $data['additional']['featured'] ) ? $data['additional']['featured'] : 'any', FALSE, array( 'options' => array( '1' => 'featured', '0' => 'node_move_pinned_not_featured', 'any' => 'node_mass_move_either' ) ) ) );
|
||||
}
|
||||
$form->addHeader('node_mass_move_delete_then');
|
||||
$form->add( new \IPS\Helpers\Form\Node( 'node_move_content', isset( $data['moveTo'] ) ? call_user_func( array( $data['moveToClass'], 'load' ), $data['moveTo'] ) : 0, TRUE, array( 'class' => $nodeClass, 'disabled' => array( $node->_id ), 'disabledLang' => 'node_move_delete', 'zeroVal' => 'node_delete_content', 'subnodes' => FALSE, 'permissionCheck' => function( $node )
|
||||
{
|
||||
return array_key_exists( 'add', $node->permissionTypes() );
|
||||
} ) ) );
|
||||
|
||||
/* Handle submissions */
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
$data['deleteWhenDone'] = FALSE;
|
||||
$data['class'] = get_class( $node );
|
||||
$data['id'] = $node->_id;
|
||||
|
||||
if ( is_object( $values['node_move_content'] ) )
|
||||
{
|
||||
$data['moveToClass'] = get_class( $values['node_move_content'] );
|
||||
$data['moveTo'] = $values['node_move_content']->_id;
|
||||
}
|
||||
else
|
||||
{
|
||||
unset( $data['moveToClass'] );
|
||||
unset( $data['moveTo'] );
|
||||
}
|
||||
|
||||
if ( count( $values['node_move_author'] ) )
|
||||
{
|
||||
$data['additional']['author'] = array_keys( $values['node_move_author'] );
|
||||
}
|
||||
else
|
||||
{
|
||||
unset( $data['additional']['author'] );
|
||||
}
|
||||
|
||||
if ( $values['node_move_date'] )
|
||||
{
|
||||
$data['additional']['date'] = $values['node_move_date']->setTime( 23, 59, 59 )->getTimestamp();
|
||||
}
|
||||
else
|
||||
{
|
||||
unset( $data['additional']['date'] );
|
||||
}
|
||||
|
||||
if ( isset( $values['node_move_comments'] ) and $values['node_move_comments'] > -1 )
|
||||
{
|
||||
$data['additional']['num_comments'] = $values['node_move_comments'];
|
||||
}
|
||||
else
|
||||
{
|
||||
unset( $data['additional']['num_comments'] );
|
||||
}
|
||||
|
||||
if ( isset( $values['node_move_last_post'] ) and $values['node_move_last_post'] )
|
||||
{
|
||||
$data['additional']['last_post'] = $values['node_move_last_post']->setTime( 23, 59, 59 )->getTimestamp();
|
||||
}
|
||||
else
|
||||
{
|
||||
unset( $data['additional']['last_post'] );
|
||||
}
|
||||
|
||||
if ( isset( $values['node_move_state'] ) and $values['node_move_state'] != 'any' )
|
||||
{
|
||||
$data['additional']['state'] = $values['node_move_state'];
|
||||
}
|
||||
else
|
||||
{
|
||||
unset( $data['additional']['state'] );
|
||||
}
|
||||
|
||||
if ( isset( $values['node_move_pinned'] ) and $values['node_move_pinned'] != 'any' )
|
||||
{
|
||||
$data['additional']['pinned'] = $values['node_move_pinned'];
|
||||
}
|
||||
else
|
||||
{
|
||||
unset( $data['additional']['pinned'] );
|
||||
}
|
||||
|
||||
if ( isset( $values['node_move_featured'] ) and $values['node_move_featured'] != 'any' )
|
||||
{
|
||||
$data['additional']['featured'] = $values['node_move_featured'];
|
||||
}
|
||||
else
|
||||
{
|
||||
unset( $data['additional']['featured'] );
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/* Display Form */
|
||||
return (string) $form;
|
||||
},
|
||||
'node_mass_content_confirm' => function( $data ) use ( $node, $contentItemClass )
|
||||
{
|
||||
if ( isset( \IPS\Request::i()->confirm ) )
|
||||
{
|
||||
\IPS\Task::queue( 'core', 'DeleteOrMoveContent', $data );
|
||||
|
||||
if ( isset( $data['moveTo'] ) )
|
||||
{
|
||||
$newNode = $data['moveToClass']::load( $data['moveTo'] );
|
||||
\IPS\Session::i()->log( 'acplog__node_mass_move', array( $this->title => TRUE, $node->titleForLog() => FALSE, $newNode->titleForLog() => FALSE ) );
|
||||
\IPS\Output::i()->redirect( $this->url->setQueryString( array( 'root' => ( $node->parent() ? $node->parent()->_id : '' ) ) ), 'node_mass_content_moving' );
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Session::i()->log( 'acplog__node_mass_delete', array( $this->title => TRUE, $node->titleForLog() => FALSE ) );
|
||||
\IPS\Output::i()->redirect( $this->url->setQueryString( array( 'root' => ( $node->parent() ? $node->parent()->_id : '' ) ) ), 'node_mass_content_deleting' );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$number = $node->getContentItems( NULL, NULL, $node->massMoveorDeleteWhere( $data ), TRUE );
|
||||
|
||||
return \IPS\Theme::i()->getTemplate( 'global', 'core' )->nodeMoveDeleteContent( $this->url->setQueryString( array( 'do' => 'massManageContent', 'id' => $node->_id ) ), \IPS\Member::loggedIn()->language()->addToStack( $contentItemClass::$title . '_pl_lc' ), $number, isset( $data['moveTo'] ) ? call_user_func( array( $data['moveToClass'], 'load' ), $data['moveTo'] ) : NULL );
|
||||
}
|
||||
}
|
||||
),
|
||||
$this->url->setQueryString( array( 'do' => 'massManageContent', 'id' => $node->_id ) )
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Search
|
||||
*
|
||||
|
||||
+393
-177
@@ -1,12 +1,11 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Node Model
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
* @package Invision Community
|
||||
* @since 18 Feb 2013
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\Node;
|
||||
@@ -41,6 +40,11 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
|
||||
*/
|
||||
public static $databaseColumnOrder = NULL;
|
||||
|
||||
/**
|
||||
* @brief [Node] Automatically set position for new nodes
|
||||
*/
|
||||
public static $automaticPositionDetermination = TRUE;
|
||||
|
||||
/**
|
||||
* @brief [Node] Enabled/Disabled Column
|
||||
*/
|
||||
@@ -103,13 +107,14 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
|
||||
'app' => 'core', // The application key which holds the restrictrions
|
||||
'module' => 'foo', // The module key which holds the restrictions
|
||||
'map' => array( // [Optional] The key for each restriction - can alternatively use "prefix"
|
||||
'add' => 'foo_add',
|
||||
'edit' => 'foo_edit',
|
||||
'permissions' => 'foo_perms',
|
||||
'delete' => 'foo_delete'
|
||||
'add' => 'foo_add',
|
||||
'edit' => 'foo_edit',
|
||||
'permissions' => 'foo_perms',
|
||||
'massManageContent' => 'foo_massManageContent',
|
||||
'delete' => 'foo_delete'
|
||||
),
|
||||
'all' => 'foo_manage', // [Optional] The key to use for any restriction not provided in the map (only needed if not providing all 4)
|
||||
'prefix' => 'foo_', // [Optional] Rather than specifying each key in the map, you can specify a prefix, and it will automatically look for restrictions with the key "[prefix]_add/edit/permissions/delete"
|
||||
'prefix' => 'foo_', // [Optional] Rather than specifying each key in the map, you can specify a prefix, and it will automatically look for restrictions with the key "[prefix]_add/edit/permissions/massManageContent/delete"
|
||||
* @endcode
|
||||
*/
|
||||
protected static $restrictions = NULL;
|
||||
@@ -131,40 +136,69 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
|
||||
*/
|
||||
public static function roots( $permissionCheck='view', $member=NULL, $where=array() )
|
||||
{
|
||||
/* Will we need to check permissions? */
|
||||
$usingPermssions = ( in_array( 'IPS\Node\Permissions', class_implements( get_called_class() ) ) and $permissionCheck !== NULL );
|
||||
|
||||
if ( $usingPermssions )
|
||||
{
|
||||
$member = $member ?: \IPS\Member::loggedIn();
|
||||
}
|
||||
|
||||
/* Have we got a cached result we can use? */
|
||||
if ( $usingPermssions )
|
||||
{
|
||||
$cacheKey = md5( get_called_class() . $permissionCheck . $member->member_id . json_encode( $where ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
$cacheKey = md5( get_called_class() . $permissionCheck . json_encode( $where ) );
|
||||
}
|
||||
|
||||
/* Check the cache first */
|
||||
if( !$where and isset( static::$rootsResult[ $cacheKey ] ) )
|
||||
if( isset( static::$rootsResult[ $cacheKey ] ) )
|
||||
{
|
||||
return static::$rootsResult[ $cacheKey ];
|
||||
}
|
||||
|
||||
|
||||
/* Specify that we only want the ones without a parent */
|
||||
if( static::$databaseColumnParent !== NULL )
|
||||
{
|
||||
$where[] = array( static::$databasePrefix . static::$databaseColumnParent . '=?', static::$databaseColumnParentRootValue );
|
||||
}
|
||||
|
||||
/* Permission check? */
|
||||
if ( $usingPermssions )
|
||||
|
||||
/* And aren't in clubs */
|
||||
if ( \IPS\IPS::classUsesTrait( get_called_class(), 'IPS\Content\ClubContainer' ) )
|
||||
{
|
||||
$where[] = array( '(' . \IPS\Db::i()->findInSet( 'core_permission_index.perm_' . static::$permissionMap[ $permissionCheck ], $member->groups ) . ' OR ' . 'core_permission_index.perm_' . static::$permissionMap[ $permissionCheck ] . '=? )', '*' );
|
||||
$where[] = array( static::$databasePrefix . static::clubIdColumn() . ' IS NULL' );
|
||||
}
|
||||
|
||||
/* Fetch */
|
||||
$nodes = static::nodesWithPermission( $usingPermssions ? $permissionCheck : NULL, $member, $where );
|
||||
|
||||
/* Set cache */
|
||||
static::$rootsResult[ $cacheKey ] = $nodes;
|
||||
|
||||
/* Return */
|
||||
return static::$rootsResult[ $cacheKey ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch All Root Nodes
|
||||
*
|
||||
* @param string|NULL $permissionCheck The permission key to check for or NULl to not check permissions
|
||||
* @param \IPS\Member|NULL $member The member to check permissions for or NULL for the currently logged in member
|
||||
* @param mixed $where Additional WHERE clause
|
||||
* @return array
|
||||
*/
|
||||
protected static function nodesWithPermission( $permissionCheck, $member, $where=array() )
|
||||
{
|
||||
/* Permission check? */
|
||||
if ( $permissionCheck )
|
||||
{
|
||||
$where[] = array( '(' . \IPS\Db::i()->findInSet( 'core_permission_index.perm_' . static::$permissionMap[ $permissionCheck ], $member->permissionArray() ) . ' OR ' . 'core_permission_index.perm_' . static::$permissionMap[ $permissionCheck ] . '=? )', '*' );
|
||||
if ( static::$databaseColumnEnabledDisabled )
|
||||
{
|
||||
$where[] = array( static::$databasePrefix . static::$databaseColumnEnabledDisabled . '=1' );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Specify the order */
|
||||
$order = NULL;
|
||||
if( static::$databaseColumnOrder !== NULL )
|
||||
@@ -174,8 +208,7 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
|
||||
|
||||
/* Select */
|
||||
$select = \IPS\Db::i()->select( '*', static::$databaseTable, $where, $order );
|
||||
|
||||
if ( $usingPermssions )
|
||||
if ( $permissionCheck )
|
||||
{
|
||||
$select->join( 'core_permission_index', array( "core_permission_index.app=? AND core_permission_index.perm_type=? AND core_permission_index.perm_type_id=" . static::$databaseTable . "." . static::$databasePrefix . static::$databaseColumnId, static::$permApp, static::$permType ) );
|
||||
}
|
||||
@@ -191,12 +224,9 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
|
||||
}
|
||||
catch ( \Exception $e ) { }
|
||||
}
|
||||
|
||||
/* Set cache */
|
||||
static::$rootsResult[ $cacheKey ] = $nodes;
|
||||
|
||||
|
||||
/* Return */
|
||||
return static::$rootsResult[ $cacheKey ];
|
||||
return $nodes;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -389,7 +419,7 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
|
||||
$order = static::$databaseColumnOrder !== NULL ? static::$databasePrefix . static::$databaseColumnOrder : NULL;
|
||||
if ( in_array( 'IPS\Node\Permissions', class_implements( get_called_class() ) ) and $permissionCheck !== NULL )
|
||||
{
|
||||
$where[] = array( '(' . \IPS\Db::i()->findInSet( 'perm_' . static::$permissionMap[ $permissionCheck ], $member->groups ) . ' OR ' . 'perm_' . static::$permissionMap[ $permissionCheck ] . '=? )', '*' );
|
||||
$where[] = array( '(' . \IPS\Db::i()->findInSet( 'perm_' . static::$permissionMap[ $permissionCheck ], $member->permissionArray() ) . ' OR ' . 'perm_' . static::$permissionMap[ $permissionCheck ] . '=? )', '*' );
|
||||
|
||||
$select = \IPS\Db::i()->select( '*', static::$databaseTable, $where, $order, NULL, NULL, NULL, \IPS\Db::SELECT_MULTIDIMENSIONAL_JOINS )
|
||||
->join( 'core_permission_index', array( "core_permission_index.app=? AND core_permission_index.perm_type=? AND core_permission_index.perm_type_id=" . static::$databaseTable . "." . static::$databasePrefix . static::$databaseColumnId, static::$permApp, static::$permType ) );
|
||||
@@ -426,7 +456,7 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
|
||||
|
||||
/* Put into tree */
|
||||
$obj->_childrenResults[ $cacheKey ] = array();
|
||||
if ( $row[ static::$databaseTable ][ static::$databasePrefix . static::$databaseColumnParent ] === static::$databaseColumnParentRootValue )
|
||||
if ( static::$databaseColumnParent === NULL or $row[ static::$databaseTable ][ static::$databasePrefix . static::$databaseColumnParent ] === static::$databaseColumnParentRootValue )
|
||||
{
|
||||
static::$rootsResult[ $rootsCacheKey ][ $obj->_id ] = $obj;
|
||||
}
|
||||
@@ -473,7 +503,15 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
|
||||
*/
|
||||
public static function basicDataColumns()
|
||||
{
|
||||
return array( static::$databasePrefix . static::$databaseColumnId, static::$databasePrefix . static::$seoTitleColumn );
|
||||
$return = array( static::$databasePrefix . static::$databaseColumnId, static::$databasePrefix . static::$seoTitleColumn );
|
||||
|
||||
/* Using colorize trait? We can't check the object but we can check the added class variable */
|
||||
if ( isset( static::$featureColumnName ) )
|
||||
{
|
||||
$return[] = static::$featureColumnName;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -499,20 +537,62 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
|
||||
*/
|
||||
public static function titleFromIndexData( $indexData, $itemData, $containerData )
|
||||
{
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( static::$titleLangPrefix . $indexData['index_container_id'] );
|
||||
if ( $indexData['index_club_id'] and isset( $containerData['_club'] ) )
|
||||
{
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'club_container_title', FALSE, array( 'sprintf' => array( $containerData['_club']['name'], \IPS\Member::loggedIn()->language()->addToStack( static::$titleLangPrefix . $indexData['index_container_id'], 'NULL', array( 'escape' => true ) ) ) ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( static::$titleLangPrefix . $indexData['index_container_id'], 'NULL', array( 'escape' => true ) );
|
||||
}
|
||||
}
|
||||
|
||||
/* !Getters */
|
||||
|
||||
/**
|
||||
* [Node] Get ID Number
|
||||
* [Node] Fetches the only node if only one exists
|
||||
* One Node to rule them all, One Node to find them, One Node to bring them all and in the darkness bind them
|
||||
*
|
||||
* @return int
|
||||
* @param $properties Array of property=>value i.e. array( 'redirect_url' => FALSE, 'password' => FALSE );
|
||||
* @param $returnRoots Enable to check and return the root node
|
||||
* @param $subNodes Enable to check subnodes
|
||||
* @return \IPS\Node\Model|NULL
|
||||
*/
|
||||
protected function get__id()
|
||||
public static function theOnlyNode( $properties=array(), $returnRoots=TRUE, $subNodes=TRUE )
|
||||
{
|
||||
$idColumn = static::$databaseColumnId;
|
||||
return $this->$idColumn;
|
||||
if ( count( static::roots() ) === 1 )
|
||||
{
|
||||
foreach ( static::roots() as $root )
|
||||
{
|
||||
if ( $root->childrenCount( 'view', NULL, $subNodes ) === 1 )
|
||||
{
|
||||
foreach ( $root->children( 'view', NULL, $subNodes ) as $node )
|
||||
{
|
||||
/* Check properties */
|
||||
foreach( $properties as $name => $bool )
|
||||
{
|
||||
if( $node->$name != $bool )
|
||||
{
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we're just checking root objects, we don't want to return the child */
|
||||
if( $returnRoots )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
}
|
||||
/* There are no children */
|
||||
elseif( $root->childrenCount( 'view', NULL, $subNodes ) === 0 and $returnRoots )
|
||||
{
|
||||
return $root;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -539,6 +619,19 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
|
||||
}
|
||||
}
|
||||
|
||||
/* !Getters */
|
||||
|
||||
/**
|
||||
* [Node] Get ID Number
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function get__id()
|
||||
{
|
||||
$idColumn = static::$databaseColumnId;
|
||||
return $this->$idColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* [Node] Get Title
|
||||
*
|
||||
@@ -580,6 +673,16 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get HTML formatted title. Allows apps or nodes to format the title, such as adding different colours, etc
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get__formattedTitle()
|
||||
{
|
||||
return $this->_title;
|
||||
}
|
||||
|
||||
/**
|
||||
* [Node] Get Node Description
|
||||
@@ -952,7 +1055,6 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
|
||||
$working = $this;
|
||||
while ( $working = $working->parent() )
|
||||
{
|
||||
|
||||
if( ! $working instanceof \IPS\Node\Model )
|
||||
{
|
||||
return $stack;
|
||||
@@ -1223,24 +1325,23 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
|
||||
);
|
||||
}
|
||||
|
||||
if( $this->canMassManageContent() )
|
||||
{
|
||||
$buttons['content'] = array(
|
||||
'icon' => 'arrow-right',
|
||||
'title' => 'mass_manage_content',
|
||||
'link' => $url->setQueryString( array( 'do' => 'massManageContent', 'id' => $this->_id, '_new' => 1 ) ),
|
||||
'hotkey'=> 'm'
|
||||
);
|
||||
}
|
||||
|
||||
if( $this->canDelete() )
|
||||
{
|
||||
if ( isset( static::$contentItemClass ) AND $this->getContentItemCount() )
|
||||
{
|
||||
$buttons['empty'] = array(
|
||||
'icon' => 'trash-o',
|
||||
'title' => 'empty',
|
||||
'link' => $url->setQueryString( array( 'do' => 'delete', 'id' => $this->_id, 'deleteNode' => 0 ) ),
|
||||
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('empty') ),
|
||||
'hotkey'=> 'd'
|
||||
);
|
||||
}
|
||||
|
||||
$buttons['delete'] = array(
|
||||
'icon' => 'times-circle',
|
||||
'title' => 'delete',
|
||||
'link' => $url->setQueryString( array( 'do' => 'delete', 'id' => $this->_id, 'deleteNode' => 1 ) ),
|
||||
'data' => ( $this->hasChildren( NULL, NULL, TRUE ) or ( isset( static::$contentItemClass ) AND $this->getContentItemCount() ) ) ? array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('delete') ) : array( 'delete' => '' ),
|
||||
'link' => $url->setQueryString( array( 'do' => 'delete', 'id' => $this->_id ) ),
|
||||
'data' => ( $this->hasChildren( NULL, NULL, TRUE ) or $this->showDeleteOrMoveForm() ) ? array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('delete') ) : array( 'delete' => '' ),
|
||||
'hotkey'=> 'd'
|
||||
);
|
||||
}
|
||||
@@ -1283,7 +1384,7 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
return \IPS\Member::loggedIn()->hasAcpRestriction( static::$restrictions['app'], static::$restrictions['module'], $_key );
|
||||
}
|
||||
|
||||
@@ -1391,6 +1492,31 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
|
||||
|
||||
return ( static::$permApp !== NULL and static::$permType !== NULL and static::restrictionCheck( 'permissions' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* [Node] Does the currently logged in user have permission to mass move/delete content in this node?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function canMassManageContent()
|
||||
{
|
||||
if ( !isset( static::$contentItemClass ) )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ( $this->deleteOrMoveQueued() === TRUE )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if( static::restrictionCheck( 'massManageContent' ) )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* [Node] Does the currently logged in user have permission to delete this node?
|
||||
@@ -1777,7 +1903,7 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
|
||||
'perm_view' => ''
|
||||
) );
|
||||
|
||||
$this->_permissions = array( 'perm_id' => $permId, 'perm_view' => '' );
|
||||
$this->_permissions = array( 'perm_id' => $permId, 'perm_view' => '', 'perm_2' => NULL, 'perm_3' => NULL, 'perm_4' => NULL, 'perm_5' => NULL, 'perm_6' => NULL, 'perm_7' => NULL );
|
||||
}
|
||||
}
|
||||
return $this->_permissions;
|
||||
@@ -1907,6 +2033,19 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
|
||||
$level--;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Can View
|
||||
*
|
||||
* @param \IPS\Member|\IPS\Member\Group|NULL $member The member or group to check (NULL for currently logged in member)
|
||||
* @return bool
|
||||
* @throws \OutOfRangeException
|
||||
* @note This is just a quick wrapper to brings things consistent between Content Items and Nodes for things like Reactions, which may support both
|
||||
*/
|
||||
public function canView( $member=NULL )
|
||||
{
|
||||
return $this->can( 'view', $member );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check permissions
|
||||
@@ -1995,10 +2134,39 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
|
||||
return ( $permissions[ 'perm_' . static::$permissionMap[ $permission ] ] === '*' or ( $permissions[ 'perm_' . static::$permissionMap[ $permission ] ] and in_array( $member->g_id, explode( ',', $permissions[ 'perm_' . static::$permissionMap[ $permission ] ] ) ) ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
return ( $permissions[ 'perm_' . static::$permissionMap[ $permission ] ] === '*' or ( $permissions[ 'perm_' . static::$permissionMap[ $permission ] ] and $member->inGroup( explode( ',', $permissions[ 'perm_' . static::$permissionMap[ $permission ] ] ) ) ) );
|
||||
{
|
||||
return ( $permissions[ 'perm_' . static::$permissionMap[ $permission ] ] === '*' or ( $permissions[ 'perm_' . static::$permissionMap[ $permission ] ] and array_intersect( explode( ',', $permissions[ 'perm_' . static::$permissionMap[ $permission ] ] ), $member->permissionArray() ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check Moderator Permission
|
||||
*
|
||||
* @param string $type 'edit', 'hide', 'unhide', 'delete', etc.
|
||||
* @param \IPS\Member|NULL $member The member to check for or NULL for the currently logged in member
|
||||
* @return bool
|
||||
*/
|
||||
public function modPermission( $type, \IPS\Member $member )
|
||||
{
|
||||
if ( isset( static::$modPerm ) )
|
||||
{
|
||||
$itemClass = static::$contentItemClass;
|
||||
$title = $itemClass::$title;
|
||||
if ( $member->modPermission( "can_{$type}_{$title}" ) )
|
||||
{
|
||||
if ( $member->modPermission( static::$modPerm ) === -1 )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
if ( is_array( $member->modPermission( static::$modPerm ) ) and in_array( $this->_id, $member->modPermission( static::$modPerm ) ) )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disable the copy button - useful when the forms are very distinctly different
|
||||
@@ -2034,6 +2202,102 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get where clause for a mass move/delete
|
||||
*
|
||||
* @param array|null $data Additional filters to mass move by
|
||||
* @return array
|
||||
*/
|
||||
public function massMoveorDeleteWhere( $data=NULL )
|
||||
{
|
||||
$contentItemClass = static::$contentItemClass;
|
||||
|
||||
$where = array();
|
||||
if ( isset( $data['additional'] ) AND count( $data['additional'] ) )
|
||||
{
|
||||
/* Author */
|
||||
if ( isset( $data['additional']['author'] ) )
|
||||
{
|
||||
if ( is_array( $data['additional']['author'] ) )
|
||||
{
|
||||
if ( count( $data['additional']['author'] ) )
|
||||
{
|
||||
$where[] = array( \IPS\Db::i()->in( $contentItemClass::$databasePrefix . $contentItemClass::$databaseColumnMap['author'], $data['additional']['author'] ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$where[] = array( $contentItemClass::$databasePrefix . $contentItemClass::$databaseColumnMap['author'] . '=?', $data['additional']['author'] );
|
||||
}
|
||||
}
|
||||
|
||||
/* Posted before */
|
||||
if ( isset( $data['additional']['date'] ) AND $data['additional']['date'] )
|
||||
{
|
||||
$where[] = array( $contentItemClass::$databasePrefix . $contentItemClass::$databaseColumnMap['date'] . '<=?', $data['additional']['date'] );
|
||||
}
|
||||
|
||||
/* Number of comments is less than */
|
||||
if ( isset( $data['additional']['num_comments'] ) AND $data['additional']['num_comments'] > 0 )
|
||||
{
|
||||
$where[] = array( $contentItemClass::$databasePrefix . $contentItemClass::$databaseColumnMap['num_comments'].'<=?', $data['additional']['num_comments'] );
|
||||
}
|
||||
|
||||
/* Last post was before */
|
||||
$lastCommentField = $contentItemClass::$databaseColumnMap['last_comment'];
|
||||
$field = is_array( $lastCommentField ) ? array_pop( $lastCommentField ) : $lastCommentField;
|
||||
if ( isset( $data['additional']['no_comments'] ) AND $data['additional']['no_comments'] > 0 ) // Legacy, may still be in queue
|
||||
{
|
||||
$where[] = array( $contentItemClass::$databasePrefix . $contentItemClass::$databaseColumnMap['num_comments'] . '<=? AND ' . $contentItemClass::$databasePrefix . $field . '<?', $contentItemClass::$firstCommentRequired ? 1 : 0, $data['additional']['no_comments'] );
|
||||
}
|
||||
if ( isset( $data['additional']['last_post'] ) AND $data['additional']['last_post'] )
|
||||
{
|
||||
$where[] = array( $contentItemClass::$databasePrefix . $field . '<=?', $data['additional']['last_post'] );
|
||||
}
|
||||
|
||||
/* Locked/Unlocked */
|
||||
if ( isset( $data['additional']['state'] ) )
|
||||
{
|
||||
if ( isset( $contentItemClass::$databaseColumnMap['locked'] ) )
|
||||
{
|
||||
$where[] = array( $contentItemClass::$databasePrefix . $contentItemClass::$databaseColumnMap['locked'].'=?', $data['additional']['state'] == 'locked' ? 1 : 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
$where[] = array( $contentItemClass::$databasePrefix . $contentItemClass::$databaseColumnMap['status'].'=?', $data['additional']['state'] == 'locked' ? 'closed' : 'open' );
|
||||
}
|
||||
}
|
||||
|
||||
/* Pinned/Unpinned */
|
||||
if ( isset( $data['additional']['pinned'] ) )
|
||||
{
|
||||
if ( $data['additional']['pinned'] === TRUE ) // Legacy, may still be in queue
|
||||
{
|
||||
$where[] = array( $contentItemClass::$databasePrefix . $contentItemClass::$databaseColumnMap['pinned'].'!=?', 1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
$where[] = array( $contentItemClass::$databasePrefix . $contentItemClass::$databaseColumnMap['pinned'].'=?', $data['additional']['pinned'] );
|
||||
}
|
||||
}
|
||||
|
||||
/* Featured/Unfeatured */
|
||||
if ( isset( $data['additional']['featured'] ) )
|
||||
{
|
||||
if ( $data['additional']['featured'] === TRUE ) // Legacy, may still be in queue
|
||||
{
|
||||
$where[] = array( $contentItemClass::$databasePrefix . $contentItemClass::$databaseColumnMap['featured'].'!=?', 1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
$where[] = array( $contentItemClass::$databasePrefix . $contentItemClass::$databaseColumnMap['featured'].'=?', $data['additional']['featured'] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $where;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mass move content items in this node to another node
|
||||
@@ -2044,52 +2308,7 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
|
||||
*/
|
||||
public function massMoveorDelete( $node=NULL, $data=NULL )
|
||||
{
|
||||
$contentItemClass = static::$contentItemClass;
|
||||
|
||||
$where = array();
|
||||
if ( isset( $data['additional'] ) AND count( $data['additional'] ) )
|
||||
{
|
||||
if ( isset( $data['additional']['author'] ) )
|
||||
{
|
||||
$where[] = array( $contentItemClass::$databasePrefix . $contentItemClass::$databaseColumnMap['author'] . '=?', $data['additional']['author'] );
|
||||
}
|
||||
|
||||
if ( isset( $data['additional']['no_comments'] ) AND $data['additional']['no_comments'] > 0 )
|
||||
{
|
||||
$lastCommentField = $contentItemClass::$databaseColumnMap['last_comment'];
|
||||
$field = is_array( $lastCommentField ) ? array_pop( $lastCommentField ) : $lastCommentField;
|
||||
$where[] = array( $contentItemClass::$databasePrefix . $contentItemClass::$databaseColumnMap['num_comments'] . '<=? AND ' . $contentItemClass::$databasePrefix . $field . '<?', $contentItemClass::$firstCommentRequired ? 1 : 0, $data['additional']['no_comments'] );
|
||||
}
|
||||
|
||||
if ( isset( $data['additional']['num_comments'] ) AND $data['additional']['num_comments'] > 0 )
|
||||
{
|
||||
$where[] = array( $contentItemClass::$databasePrefix . $contentItemClass::$databaseColumnMap['num_comments'].'<?', $data['additional']['num_comments'] );
|
||||
}
|
||||
|
||||
if ( isset( $data['additional']['state'] ) )
|
||||
{
|
||||
if ( isset( $contentItemClass::$databaseColumnMap['locked'] ) )
|
||||
{
|
||||
$where[] = array( $contentItemClass::$databasePrefix . $contentItemClass::$databaseColumnMap['locked'].'=?', $data['additional']['state'] == 'locked' ? 1 : 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
$where[] = array( $contentItemClass::$databasePrefix . $contentItemClass::$databaseColumnMap['status'].'=?', $data['additional']['state'] == 'locked' ? 'closed' : 'open' );
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $data['additional']['pinned'] ) AND $data['additional']['pinned'] === TRUE )
|
||||
{
|
||||
$where[] = array( $contentItemClass::$databasePrefix . $contentItemClass::$databaseColumnMap['pinned'].'!=?', 1 );
|
||||
}
|
||||
|
||||
if ( isset( $data['additional']['featured'] ) AND $data['additional']['featured'] === TRUE )
|
||||
{
|
||||
$where[] = array( $contentItemClass::$databasePrefix . $contentItemClass::$databaseColumnMap['featured'].'!=?', 1 );
|
||||
}
|
||||
}
|
||||
|
||||
$select = $this->getContentItems( 100, 0, $where );
|
||||
$select = $this->getContentItems( 100, 0, $this->massMoveorDeleteWhere( $data ) );
|
||||
|
||||
if ( count( $select ) )
|
||||
{
|
||||
@@ -2168,6 +2387,18 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
|
||||
array( $itemClass::$databaseTable . '.' . $itemClass::$databasePrefix . $itemClass::$databaseColumnMap['container'] . '=?', $this->_id )
|
||||
);
|
||||
|
||||
if ( in_array( 'IPS\Content\Hideable', class_implements( $itemClass ) ) )
|
||||
{
|
||||
if ( isset( $itemClass::$databaseColumnMap['approved'] ) )
|
||||
{
|
||||
$commentWhere[] = array( $itemClass::$databasePrefix . $itemClass::$databaseColumnMap['approved'] . '=?', 1 );
|
||||
}
|
||||
elseif ( isset( $itemClass::$databaseColumnMap['hidden'] ) )
|
||||
{
|
||||
$commentWhere[] = array( $itemClass::$databasePrefix . $itemClass::$databaseColumnMap['hidden'] . '=?', 0 );
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $commentClass::$databaseColumnMap['approved'] ) )
|
||||
{
|
||||
$commentWhere[] = array( $commentClass::$databaseTable . '.' . $commentClass::$databasePrefix . $commentClass::$databaseColumnMap['approved'] . '=?', 1 );
|
||||
@@ -2183,6 +2414,28 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
|
||||
), $commentWhere )->first();
|
||||
}
|
||||
|
||||
if ( $this->_reviews !== NULL AND $reviewClass !== NULL )
|
||||
{
|
||||
$reviewWhere = array(
|
||||
array( $reviewClass::$databaseTable . '.' . $reviewClass::$databasePrefix . $reviewClass::$databaseColumnMap['item'] . ' = ' . $itemClass::$databaseTable . '.' . $itemClass::$databasePrefix . $itemIdColumn ),
|
||||
array( $itemClass::$databaseTable . '.' . $itemClass::$databasePrefix . $itemClass::$databaseColumnMap['container'] . '=?', $this->_id )
|
||||
);
|
||||
|
||||
if ( isset( $reviewClass::$databaseColumnMap['approved'] ) )
|
||||
{
|
||||
$reviewWhere[] = array( $reviewClass::$databaseTable . '.' . $reviewClass::$databasePrefix . $reviewClass::$databaseColumnMap['approved'] . '=?', 1 );
|
||||
}
|
||||
elseif ( isset( $reviewClass::$databaseColumnMap['hidden'] ) )
|
||||
{
|
||||
$reviewWhere[] = array( $reviewClass::$databaseTable . '.' . $reviewClass::$databasePrefix . $reviewClass::$databaseColumnMap['hidden'] . '=?', 0 );
|
||||
}
|
||||
|
||||
$this->_reviews = \IPS\Db::i()->select( 'COUNT(*)', array(
|
||||
array( $reviewClass::$databaseTable, $reviewClass::$databaseTable ),
|
||||
array( $itemClass::$databaseTable, $itemClass::$databaseTable )
|
||||
), $reviewWhere )->first();
|
||||
}
|
||||
|
||||
if ( in_array( 'IPS\Content\Hideable', class_implements( $itemClass ) ) )
|
||||
{
|
||||
if ( $this->_unapprovedItems !== NULL )
|
||||
@@ -2279,13 +2532,14 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
|
||||
/**
|
||||
* Retrieve content items (if applicable) for a node.
|
||||
*
|
||||
* @param int $limit The limit
|
||||
* @param int $offset The offset
|
||||
* @param array $additionalWhere Additional where clauses
|
||||
* @return \IPS\Patterns\ActiveRecordIterator
|
||||
* @param int $limit The limit
|
||||
* @param int $offset The offset
|
||||
* @param array $additional Where Additional where clauses
|
||||
* @param int $countOnly If TRUE, will get the number of results
|
||||
* @return \IPS\Patterns\ActiveRecordIterator|int
|
||||
* @throws \BadMethodCallException
|
||||
*/
|
||||
public function getContentItems( $limit, $offset, $additionalWhere = array() )
|
||||
public function getContentItems( $limit, $offset, $additionalWhere = array(), $countOnly=FALSE )
|
||||
{
|
||||
if ( !isset( static::$contentItemClass ) )
|
||||
{
|
||||
@@ -2304,10 +2558,17 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
|
||||
$where[] = $clause;
|
||||
}
|
||||
}
|
||||
|
||||
$contentItemClass = static::$contentItemClass;
|
||||
$limit = ( $offset !== NULL ) ? array( $offset, $limit ) : NULL;
|
||||
return new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', $contentItemClass::$databaseTable, $where, $contentItemClass::$databasePrefix . $contentItemClass::$databaseColumnId, $limit ), $contentItemClass );
|
||||
|
||||
if ( $countOnly )
|
||||
{
|
||||
return \IPS\Db::i()->select( 'COUNT(*)', $contentItemClass::$databaseTable, $where )->first();
|
||||
}
|
||||
else
|
||||
{
|
||||
$contentItemClass = static::$contentItemClass;
|
||||
$limit = ( $offset !== NULL ) ? array( $offset, $limit ) : NULL;
|
||||
return new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', $contentItemClass::$databaseTable, $where, $contentItemClass::$databasePrefix . $contentItemClass::$databaseColumnId, $limit ), $contentItemClass );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2525,7 +2786,18 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
|
||||
$this->updateSearchIndexPermissions();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Can promote this comment/item?
|
||||
*
|
||||
* @param \IPS\Member|NULL $member The member to check for (NULL for currently logged in member)
|
||||
* @return boolean
|
||||
*/
|
||||
public function canPromoteToSocialMedia( $member=NULL )
|
||||
{
|
||||
return \IPS\core\Promote::canPromote( $member );
|
||||
}
|
||||
|
||||
/**
|
||||
* [ActiveRecord] Duplicate
|
||||
*
|
||||
@@ -2614,7 +2886,10 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
|
||||
{
|
||||
\IPS\Lang::deleteCustom( ( static::$permApp !== NULL ) ? static::$permApp : 'core', static::$titleLangPrefix . $this->_id . static::$descriptionLangSuffix );
|
||||
}
|
||||
|
||||
|
||||
/* Remove any entries in the promotions table */
|
||||
\IPS\Db::i()->delete( 'core_social_promote', array( 'promote_class=? AND promote_class_id=?', get_called_class(), $this->_id ) );
|
||||
|
||||
return parent::delete();
|
||||
}
|
||||
|
||||
@@ -2776,7 +3051,7 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
|
||||
$hasContent = (bool) $this->getContentItemCount();
|
||||
}
|
||||
|
||||
$form = new \IPS\Helpers\Form( 'form', 'delete' );
|
||||
$form = new \IPS\Helpers\Form( 'delete_node_form', 'delete' );
|
||||
$form->addMessage( 'node_delete_blurb' );
|
||||
if ( $showMoveToChildren or $this->hasChildren( NULL, NULL, TRUE ) )
|
||||
{
|
||||
@@ -2790,32 +3065,6 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
|
||||
{
|
||||
return array_key_exists( 'add', $node->permissionTypes() );
|
||||
} ) ) );
|
||||
|
||||
if ( \IPS\Request::i()->deleteNode != 1 )
|
||||
{
|
||||
$form->add( new \IPS\Helpers\Form\Member( 'node_move_author', NULL, FALSE ) );
|
||||
|
||||
if ( isset( $contentItemClass::$commentClass ) )
|
||||
{
|
||||
$form->add( new \IPS\Helpers\Form\Date( 'node_move_no_comments', NULL, FALSE ) );
|
||||
$form->add( new \IPS\Helpers\Form\Number( 'node_move_comments_less', NULL, FALSE ) );
|
||||
}
|
||||
|
||||
if ( in_array( 'IPS\Content\Lockable', class_implements( $contentItemClass ) ) )
|
||||
{
|
||||
$form->add( new \IPS\Helpers\Form\Select( 'node_move_state', 'any', FALSE, array( 'options' => array( 'open' => 'open', 'locked' => 'locked', 'any' => 'any' ) ) ) );
|
||||
}
|
||||
|
||||
if ( in_array( 'IPS\Content\Pinnable', class_implements( $contentItemClass ) ) )
|
||||
{
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'node_move_not_pinned', FALSE, FALSE ) );
|
||||
}
|
||||
|
||||
if ( in_array( 'IPS\Content\Featurable', class_implements( $contentItemClass ) ) )
|
||||
{
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'node_move_not_featured', FALSE, FALSE ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $form;
|
||||
@@ -2825,10 +3074,9 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
|
||||
* Handle submissions of form to delete or move content
|
||||
*
|
||||
* @param array $values Values from form
|
||||
* @param bool $deleteWhenDone Delete the node when done?
|
||||
* @return void
|
||||
*/
|
||||
public function deleteOrMoveFormSubmit( $values, $deleteWhenDone=TRUE )
|
||||
public function deleteOrMoveFormSubmit( $values )
|
||||
{
|
||||
$nodesToQueue = array( $this );
|
||||
|
||||
@@ -2866,52 +3114,20 @@ abstract class _Model extends \IPS\Patterns\ActiveRecord
|
||||
|
||||
foreach ( $nodesToQueue as $_node )
|
||||
{
|
||||
if ( $deleteWhenDone and in_array( 'IPS\Node\Permissions', class_implements( $_node ) ) )
|
||||
if ( in_array( 'IPS\Node\Permissions', class_implements( $_node ) ) )
|
||||
{
|
||||
\IPS\Db::i()->update( 'core_permission_index', array( 'perm_view' => '' ), array( "app=? AND perm_type=? AND perm_type_id=?", $_node::$permApp, $_node::$permType, $_node->_id ) );
|
||||
}
|
||||
|
||||
$additional = array();
|
||||
if ( !$deleteWhenDone )
|
||||
{
|
||||
if ( isset( $values['node_move_author'] ) AND $values['node_move_author'] instanceof \IPS\Member )
|
||||
{
|
||||
$additional['author'] = $values['node_move_author']->member_id;
|
||||
}
|
||||
|
||||
if ( isset( $values['node_move_no_comments'] ) AND $values['node_move_no_comments'] instanceof \IPS\DateTime )
|
||||
{
|
||||
$additional['no_comments'] = $values['node_move_no_comments']->getTimestamp();
|
||||
}
|
||||
|
||||
if ( isset( $values['node_move_comments_less'] ) AND $values['node_move_comments_less'] > 0 )
|
||||
{
|
||||
$additional['num_comments'] = $values['node_move_comments_less'];
|
||||
}
|
||||
|
||||
if ( isset( $values['node_move_state'] ) AND $values['node_move_state'] != 'any' )
|
||||
{
|
||||
$additional['state'] = $values['node_move_state'];
|
||||
}
|
||||
|
||||
if ( isset( $values['node_move_not_pinned'] ) AND $values['node_move_not_pinned'] )
|
||||
{
|
||||
$additional['pinned'] = TRUE;
|
||||
}
|
||||
|
||||
if ( isset( $values['node_move_not_featured'] ) AND $values['node_move_not_featured'] )
|
||||
{
|
||||
$additional['featured'] = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $values['node_move_content'] ) and $values['node_move_content'] )
|
||||
{
|
||||
\IPS\Task::queue( 'core', 'DeleteOrMoveContent', array( 'class' => get_class( $_node ), 'id' => $_node->_id, 'moveToClass' => get_class( $values['node_move_content'] ), 'moveTo' => $values['node_move_content']->_id, 'deleteWhenDone' => $deleteWhenDone, 'additional' => $additional ) );
|
||||
\IPS\Task::queue( 'core', 'DeleteOrMoveContent', array( 'class' => get_class( $_node ), 'id' => $_node->_id, 'moveToClass' => get_class( $values['node_move_content'] ), 'moveTo' => $values['node_move_content']->_id, 'deleteWhenDone' => TRUE, 'additional' => $additional ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Task::queue( 'core', 'DeleteOrMoveContent', array( 'class' => get_class( $_node ), 'id' => $_node->_id, 'deleteWhenDone' => $deleteWhenDone, 'additional' => $additional ) );
|
||||
\IPS\Task::queue( 'core', 'DeleteOrMoveContent', array( 'class' => get_class( $_node ), 'id' => $_node->_id, 'deleteWhenDone' => TRUE, 'additional' => $additional ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Permissions Interface for Nodes
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
* @package Invision Community
|
||||
* @since 2 Aug 2013
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\Node;
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Rating Interface for Nodes
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
* @package Invision Community
|
||||
* @since 25 Mar 2014
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\Node;
|
||||
|
||||
Reference in new issue
Block a user