Version 4.0.13.1

This commit is contained in:
Neo committed 2025-12-19 04:57:54 -08:00
1 parent f9e857d255
commit c6a86f0d9b
7517 files changed
+597400 -668041

No files matched your search

File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+28
View File
@@ -0,0 +1,28 @@
<?php
/**
* @brief Edit History Interface for Content Comments
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @since 14 Oct 2013
* @version SVN_VERSION_NUMBER
*/
namespace IPS\Content;
/* 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;
}
/**
* Edit History Interface for Content Models
*
* @note Content classes will gain special functionality by implementing this interface
*/
interface EditHistory
{
}
+28
View File
@@ -0,0 +1,28 @@
<?php
/**
* @brief Embeddable Interface for Content Models/Comments
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @since 22 Apr 2014
* @version SVN_VERSION_NUMBER
*/
namespace IPS\Content;
/* 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;
}
/**
* Embeddable Interface for Content Models/Comments
*
* @note Content classes will gain special functionality by implementing this interface
*/
interface Embeddable
{
}
+55
View File
@@ -0,0 +1,55 @@
<?php
/**
* @brief Content Extension Generator
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @since 26 Dec 2013
* @version SVN_VERSION_NUMBER
*/
namespace IPS\Content;
/* 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;
}
/**
* Content Extension Generator
*/
abstract class _ExtensionGenerator
{
/**
* @brief If TRUE, will prevent comment classes being included
*/
protected static $contentItemsOnly = FALSE;
/**
* Generate Extensions
*
* @return array
*/
public static function generate()
{
$return = array();
foreach ( \IPS\Content::routedClasses( FALSE, FALSE, static::$contentItemsOnly ) as $_class )
{
$obj = new static;
$obj->class = $_class;
\IPS\Member::loggedIn()->language()->words[ 'ipAddresses__core_Content_' . str_replace( '\\', '_', mb_substr( $_class, 4 ) ) ] = \IPS\Member::loggedIn()->language()->addToStack( $_class::$title . '_pl', FALSE );
$return[ 'Content_' . str_replace( '\\', '_', mb_substr( $_class, 4 ) ) ] = $obj;
}
return $return;
}
/**
* @brief Content Class
*/
public $class;
}
+28
View File
@@ -0,0 +1,28 @@
<?php
/**
* @brief Featurable Interface for Content Models/Comments
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @since 12 Nov 2013
* @version SVN_VERSION_NUMBER
*/
namespace IPS\Content;
/* 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;
}
/**
* Featurable Interface for Content Models/Comments
*
* @note Content classes will gain special functionality by implementing this interface
*/
interface Featurable
{
}
+28
View File
@@ -0,0 +1,28 @@
<?php
/**
* @brief Followable Interface for Content Models/Comments
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @since 02 Dec 2013
* @version SVN_VERSION_NUMBER
*/
namespace IPS\Content;
/* 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;
}
/**
* Followable Interface for Content Models/Comments
*
* @note Content classes will gain special functionality by implementing this interface
*/
interface Followable
{
}
+28
View File
@@ -0,0 +1,28 @@
<?php
/**
* @brief Polls Interface for Content Models
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @since 10 Jan 2013
* @version SVN_VERSION_NUMBER
*/
namespace IPS\Content;
/* 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;
}
/**
* Future publishing Interface for Content Models
*
* @note Content classes will gain special functionality by implementing this interface
*/
interface FuturePublishing
{
}
+28
View File
@@ -0,0 +1,28 @@
<?php
/**
* @brief Hidebale Interface for Content Models/Comments
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @since 6 Nov 2013
* @version SVN_VERSION_NUMBER
*/
namespace IPS\Content;
/* 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;
}
/**
* Hidebale Interface for Content Models/Comments
*
* @note Content classes will gain special functionality by implementing this interface
*/
interface Hideable
{
}
File diff suppressed because it is too large. Load diff
+28
View File
@@ -0,0 +1,28 @@
<?php
/**
* @brief Lockable Interface for Content Models/Comments
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @since 12 Nov 2013
* @version SVN_VERSION_NUMBER
*/
namespace IPS\Content;
/* 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;
}
/**
* Lockable Interface for Content Models/Comments
*
* @note Content classes will gain special functionality by implementing this interface
*/
interface Lockable
{
}
+155
View File
@@ -0,0 +1,155 @@
<?php
/**
* @brief Moderator Permissions Interface for Content Models
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @since 1 Oct 2013
* @version SVN_VERSION_NUMBER
*/
namespace IPS\Content;
/* 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;
}
/**
* Moderator Permissions Interface for Content Models
*/
class _ModeratorPermissions
{
/**
* @brief Actions
*/
public $actions = array();
/**
* @brief Comment Actions
*/
public $commentActions = array();
/**
* @brief Review Actions
*/
public $reviewActions = array();
/**
* Constructor
*
* @return void
*/
public function __construct()
{
$class = static::$class;
if ( in_array( 'IPS\Content\Pinnable', class_implements( $class ) ) )
{
$this->actions[] = 'pin';
$this->actions[] = 'unpin';
}
if ( in_array( 'IPS\Content\Featurable', class_implements( $class ) ) )
{
$this->actions[] = 'feature';
$this->actions[] = 'unfeature';
}
$this->actions[] = 'edit';
if ( in_array( 'IPS\Content\Hideable', class_implements( $class ) ) )
{
$this->actions[] = 'hide';
$this->actions[] = 'unhide';
$this->actions[] = 'view_hidden';
}
if ( isset( $class::$containerNodeClass ) )
{
$this->actions[] = 'move';
}
if ( in_array( 'IPS\Content\Lockable', class_implements( $class ) ) )
{
$this->actions[] = 'lock';
$this->actions[] = 'unlock';
$this->actions[] = 'reply_to_locked';
}
$this->actions[] = 'delete';
if ( isset( $class::$commentClass ) )
{
$this->commentActions = array( 'edit' );
if ( in_array( 'IPS\Content\Hideable', class_implements( $class::$commentClass ) ) )
{
$this->commentActions[] = 'hide';
$this->commentActions[] = 'unhide';
$this->commentActions[] = 'view_hidden';
}
$this->commentActions[] = 'delete';
}
if ( isset( $class::$reviewClass ) )
{
$this->reviewActions = array( 'edit' );
if ( in_array( 'IPS\Content\Hideable', class_implements( $class::$reviewClass ) ) )
{
$this->reviewActions[] = 'hide';
$this->reviewActions[] = 'unhide';
$this->reviewActions[] = 'view_hidden';
}
$this->reviewActions[] = 'delete';
}
}
/**
* Get Permissions
*
* @param array $toggles Toggle data
* @code
return array(
'key' => 'YesNo', // Can just return a string with type
'key' => array( // Or an array for more options
'YesNo', // Type
array( ... ), // Options (as defined by type's class)
'prefix', // Prefix
'suffix', // Suffix
),
...
);
* @endcode
* @return array
*/
public function getPermissions( $toggles )
{
$class = static::$class;
$containerNodeClass = $class::$containerNodeClass;
$return = array();
$return[ $containerNodeClass::$modPerm ] = array( 'Node', array( 'class' => $containerNodeClass, 'zeroVal' => 'all', 'multiple' => TRUE ) );
foreach ( $this->actions as $k )
{
$return[ "can_{$k}_{$class::$title}" ] = 'YesNo';
}
if ( isset( $class::$commentClass ) )
{
$commentClass = $class::$commentClass;
foreach ( $this->commentActions as $k )
{
$return[ "can_{$k}_{$commentClass::$title}" ] = 'YesNo';
}
}
if ( isset( $class::$reviewClass ) )
{
$reviewClass = $class::$reviewClass;
foreach ( $this->reviewActions as $k )
{
$return[ "can_{$k}_{$reviewClass::$title}" ] = 'YesNo';
}
}
return $return;
}
}
+30
View File
@@ -0,0 +1,30 @@
<?php
/**
* @brief Permissions Interface for Content Models
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @since 8 Jul 2013
* @version SVN_VERSION_NUMBER
*/
namespace IPS\Content;
/* 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;
}
/**
* Permissions Interface for Content Models
*
* @note Content classes will gain special functionality by implementing this interface
*/
interface Permissions
{
}
class NoPermissionException extends \OutOfRangeException { }
+28
View File
@@ -0,0 +1,28 @@
<?php
/**
* @brief Pinnable Interface for Content Models/Comments
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @since 12 Nov 2013
* @version SVN_VERSION_NUMBER
*/
namespace IPS\Content;
/* 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;
}
/**
* Pinnable Interface for Content Models/Comments
*
* @note Content classes will gain special functionality by implementing this interface
*/
interface Pinnable
{
}
+28
View File
@@ -0,0 +1,28 @@
<?php
/**
* @brief Polls Interface for Content Models
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @since 10 Jan 2013
* @version SVN_VERSION_NUMBER
*/
namespace IPS\Content;
/* 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;
}
/**
* Polls Interface for Content Models
*
* @note Content classes will gain special functionality by implementing this interface
*/
interface Polls
{
}
+28
View File
@@ -0,0 +1,28 @@
<?php
/**
* @brief Rating Interface for Content Models/Comments
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @since 15 Jan 2013
* @version SVN_VERSION_NUMBER
*/
namespace IPS\Content;
/* 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;
}
/**
* Rating Interface for Content Models/Comments
*
* @note Content classes will gain special functionality by implementing this interface
*/
interface Ratings
{
}
+38
View File
@@ -0,0 +1,38 @@
<?php
/**
* @brief Read/Unread Tracking Interface for Content Models
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @since 8 Jul 2013
* @version SVN_VERSION_NUMBER
*/
namespace IPS\Content;
/* 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;
}
/**
* Read/Unread Tracking Interface for Content Models
*
* @note Content classes will gain special functionality by implementing this interface
*/
interface ReadMarkers
{
/**
* The maximum number of IDs to store in core_item_markers.item_read_array
*
* As a JSON-encoded array of ID => Timestamp values, the storage required
* is (14+l)n + 1 bytes, where l is the length in bytes of any given ID and n is the number of IDs.
* We store the value in a MEDIUMTEXT field, so the maximum length is 16 megabytes. Assuming
* l is always 5 (NB: but it isn't), this means this constant could be increased to a theoretic
* maximum of 883,011.
*/
const STORAGE_CUTOFF = 100000;
}
+26
View File
@@ -0,0 +1,26 @@
<?php
/**
* @brief Interface for Reportable Comment Models
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @since 8 Jul 2013
* @version SVN_VERSION_NUMBER
*/
namespace IPS\Content;
/* 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;
}
/**
* Interface for Reportable Content/Comment Models
*
* @note Content classes will gain special functionality by implementing this interface
*/
interface ReportCenter { }
+28
View File
@@ -0,0 +1,28 @@
<?php
/**
* @brief Reputation interface for Content Models
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @since 27 Nov 2013
* @version SVN_VERSION_NUMBER
*/
namespace IPS\Content;
/* 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;
}
/**
* Reputation Interface for Content Models
*
* @note Content classes will gain special functionality by implementing this interface
*/
interface Reputation
{
}
+366
View File
@@ -0,0 +1,366 @@
<?php
/**
* @brief Content Review Model
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @since 4 Nov 2013
* @version SVN_VERSION_NUMBER
*/
namespace IPS\Content;
/* 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;
}
/**
* Content Review Model
*/
abstract class _Review extends \IPS\Content\Comment
{
/**
* @brief [Content\Comment] Comment Template
*/
public static $commentTemplate = array( array( 'global', 'core', 'front' ), 'reviewContainer' );
/**
* Create first comment (created with content item)
*
* @param \IPS\Content\Item $item The content item just created
* @param string $comment The comment
* @param bool $first Is the first comment?
* @param int $rating The rating (1-5)
* @param string $guestName If author is a guest, the name to use
* @param \IPS\Member|NULL $member The author of this comment. If NULL, uses currently logged in member.
* @param \IPS\DateTime|NULL $time The time
* @return static
* @throws \InvalidArgumentException
*/
public static function create( $item, $comment, $first=FALSE, $rating=NULL, $guestName=NULL, $member=NULL, \IPS\DateTime $time=NULL )
{
if ( !is_int( $rating ) or $rating < 1 or $rating > intval( \IPS\Settings::i()->reviews_rating_out_of ) )
{
throw new \InvalidArgumentException;
}
$obj = parent::create( $item, $comment, $first, $guestName, $member, $time );
foreach ( array( 'rating', 'votes_data' ) as $k )
{
if ( isset( static::$databaseColumnMap[ $k ] ) )
{
$val = NULL;
switch ( $k )
{
case 'rating':
$val = $rating;
break;
case 'votes_data':
$val = json_encode( array() );
break;
}
foreach ( is_array( static::$databaseColumnMap[ $k ] ) ? static::$databaseColumnMap[ $k ] : array( static::$databaseColumnMap[ $k ] ) as $column )
{
$obj->$column = $val;
}
}
}
if( $obj->author()->member_id )
{
$obj->author()->member_last_post = time();
$obj->author()->save();
}
$obj->save();
/* Have to do this AFTER rating is set */
$itemClass = static::$itemClass;
$ratingField = $itemClass::$databaseColumnMap['rating'];
$obj->item()->$ratingField = $obj->item()->averageReviewRating() ?: 0;
$obj->item()->save();
return $obj;
}
/**
* Do stuff after creating (abstracted as comments and reviews need to do different things)
*
* @return void
*/
public function postCreate()
{
$item = $this->item();
if ( !$this->hidden() )
{
if ( isset( $item::$databaseColumnMap['last_review'] ) )
{
$lastReviewField = $item::$databaseColumnMap['last_review'];
if ( is_array( $lastReviewField ) )
{
foreach ( $lastReviewField as $column )
{
$item->$column = time();
}
}
else
{
$item->$lastReviewField = time();
}
}
if ( isset( $item::$databaseColumnMap['last_review_by'] ) )
{
$lastReviewByField = $item::$databaseColumnMap['last_review_by'];
$item->$lastReviewByField = $this->author()->member_id;
}
if ( isset( $item::$databaseColumnMap['num_reviews'] ) )
{
$numReviewsField = $item::$databaseColumnMap['num_reviews'];
$item->$numReviewsField++;
}
if ( !$item->hidden() and $item->containerWrapper() and $item->container()->_reviews !== NULL )
{
$item->container()->_reviews = ( $item->container()->_reviews + 1 );
$item->container()->setLastReview( $this );
$item->container()->save();
}
}
else
{
if ( isset( $item::$databaseColumnMap['unapproved_reviews'] ) )
{
$numReviewsField = $item::$databaseColumnMap['unapproved_reviews'];
$item->$numReviewsField++;
}
if ( $item->containerWrapper() AND $item->container()->_unapprovedReviews !== NULL )
{
$item->container()->_unapprovedReviews = $item->container()->_unapprovedReviews + 1;
$item->container()->save();
}
}
$item->save();
}
/**
* @brief Cached URLs
*/
protected $_url = array();
/**
* Get URL
*
* @param string|NULL $action Action
* @return \IPS\Http\Url
*/
public function url( $action=NULL )
{
$_key = md5( $action );
if( !isset( $this->_url[ $_key ] ) )
{
$itemClass = static::$itemClass;
$itemField = static::$databaseColumnMap['item'];
$idColumn = static::$databaseColumnId;
$this->_url[ $_key ] = $this->item()->url();
if( $action )
{
$this->_url[ $_key ] = $this->_url[ $_key ]->setQueryString( array( 'do' => $action . 'Review', 'review' => $this->$idColumn ) );
}
else
{
$commentPosition = \IPS\Db::i()->select( 'COUNT(*) AS position', static::$databaseTable, array( static::$databasePrefix . static::$databaseColumnMap['item'] . '=? AND ' . static::$databasePrefix . static::$databaseColumnId . '<=?', $this->$itemField, $this->$idColumn ), static::$databasePrefix . static::$databaseColumnMap['date'] . ' asc' )->first();
$page = ceil( $commentPosition / $itemClass::$reviewsPerPage );
if ( $page != 1 )
{
$this->_url[ $_key ] = $this->_url[ $_key ]->setQueryString( 'page', $page );
}
$this->_url[ $_key ] = $this->_url[ $_key ]->setFragment( 'review-' . $this->$idColumn );
}
}
return $this->_url[ $_key ];
}
/**
* Get HTML for search result display
*
* @return callable
*/
public function searchResultHtml()
{
return \IPS\Theme::i()->getTemplate( 'search', 'core', 'front' )->contentReview( $this );
}
/**
* Get line which says how many users found review helpful
*
* @return string
*/
public function helpfulLine()
{
return \IPS\Theme::i()->getTemplate( 'global', 'core', 'front' )->reviewHelpful( $this->mapped('votes_helpful'), $this->mapped('votes_total') );
}
/**
* Syncing to run when hiding
*
* @return void
*/
public function onHide()
{
$item = $this->item();
if ( $item->container()->_reviews !== NULL )
{
$item->container()->_reviews = $item->container()->_reviews - 1;
$item->container()->setLastReview();
$item->container()->save();
}
if ( isset( $item::$databaseColumnMap['num_reviews'] ) )
{
$column = $item::$databaseColumnMap['num_reviews'];
$item->$column = $item->mapped('num_reviews') - 1;
}
$item->resyncLastReview();
$ratingField = $item::$databaseColumnMap['rating'];
$item->$ratingField = $item->averageReviewRating() ?: 0;
$item->save();
}
/**
* Syncing to run when unhiding
*
* @param bool $approving If true, is being approved for the first time
* @return void
*/
public function onUnhide( $approving )
{
$item = $this->item();
if ( $approving )
{
if ( isset( $item::$databaseColumnMap['unapproved_reviews'] ) )
{
$column = $item::$databaseColumnMap['unapproved_reviews'];
$item->$column = $item->mapped('unapproved_reviews') - 1;
}
if ( $item->container()->_unapprovedReviews !== NULL )
{
$item->container()->_unapprovedReviews = $item->container()->_unapprovedReviews - 1;
$item->container()->setLastReview();
$item->container()->save();
}
}
if ( isset( $item::$databaseColumnMap['num_reviews'] ) )
{
$column = $item::$databaseColumnMap['num_reviews'];
$item->$column = $item->mapped('num_reviews') + 1;
}
if ( $item->container()->_reviews !== NULL )
{
$item->container()->_reviews = $item->container()->_reviews + 1;
$item->container()->setLastReview();
$item->container()->save();
}
$ratingField = $item::$databaseColumnMap['rating'];
$item->$ratingField = $item->averageReviewRating() ?: 0;
$item->resyncLastReview();
$item->save();
}
/**
* Can split this comment off?
*
* @param \IPS\Member|NULL $member The member to check for (NULL for currently logged in member)
* @return bool
*/
public function canSplit( $member=NULL )
{
return FALSE;
}
/**
* Warning Reference Key
*
* @return string
*/
public function warningRef()
{
/* If the member cannot warn, return NULL so we're not adding ugly parameters to the profile URL unnecessarily */
if ( !\IPS\Member::loggedIn()->modPermission('mod_can_warn') )
{
return NULL;
}
$itemClass = static::$itemClass;
$idColumn = static::$databaseColumnId;
return base64_encode( json_encode( array( 'app' => $itemClass::$application, 'module' => $itemClass::$module . '-review' , 'id_1' => $this->mapped('item'), 'id_2' => $this->$idColumn ) ) );
}
/**
* Get attachment IDs
*
* @return array
*/
public function attachmentIds()
{
$item = $this->item();
$idColumn = $item::$databaseColumnId;
$commentIdColumn = static::$databaseColumnId;
return array( $this->item()->$idColumn, $this->$commentIdColumn, 'review' );
}
/**
* Create Notification
*
* @param string|NULL $extra Additional data
* @return \IPS\Notification
*/
protected function createNotification( $extra=NULL )
{
return new \IPS\Notification( \IPS\Application::load( 'core' ), 'new_review', $this->item(), array( $this ) );
}
/**
* Delete Review
*
* @return void
*/
public function delete()
{
parent::delete();
$itemClass = static::$itemClass;
$ratingField = $itemClass::$databaseColumnMap['rating'];
$this->item()->$ratingField = $this->item()->averageReviewRating() ?: 0;
}
/* !Embeddable */
/**
* Get content for embed
*
* @param array $params Additional parameters to add to URL
* @return string
*/
public function embedContent( $params )
{
return \IPS\Theme::i()->getTemplate( 'global', 'core' )->embedReview( $this->item(), $this, $this->url()->setQueryString( $params ), $this->item()->embedImage() );
}
}
+231
View File
@@ -0,0 +1,231 @@
<?php
/**
* @brief Abstract Search Index
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @since 21 Aug 2014
* @version SVN_VERSION_NUMBER
*/
namespace IPS\Content\Search;
/* 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;
}
/**
* Abstract Search Index
*/
abstract class _Index extends \IPS\Patterns\Singleton
{
/**
* @brief Singleton Instances
*/
protected static $instance = NULL;
/**
* Get instance
*
* @return static
*/
public static function i()
{
if( static::$instance === NULL )
{
$classname = '\\IPS\\Content\\Search\\' . ucfirst( \IPS\Settings::i()->search_engine ) . '\\Index';
static::$instance = new $classname;
}
return static::$instance;
}
/**
* Clear and rebuild search index
*
* @return void
*/
public function rebuild()
{
/* Delete everything currently in it */
$this->prune();
/* If the queue is already running, clear it out */
\IPS\Db::i()->delete( 'core_queue', array( "`key`=?", 'RebuildSearchIndex' ) );
/* And set the queue in motion to rebuild */
foreach ( \IPS\Content::routedClasses( FALSE ) as $class )
{
try
{
if( is_subclass_of( $class, 'IPS\Content\Searchable' ) )
{
\IPS\Task::queue( 'core', 'RebuildSearchIndex', array( 'class' => $class ) );
}
}
catch( \OutOfRangeException $ex ) {}
}
}
/**
* Get index data
*
* @param \IPS\Content\Searchable $object Item to add
* @return array|NULL
*/
public function indexData( \IPS\Content\Searchable $object )
{
/* Init */
$class = get_class( $object );
$idColumn = $class::$databaseColumnId;
$tags = ( $object instanceof \IPS\Content\Tags ) ? implode( ',', array_filter( array_merge( array( $object->prefix() ), $object->tags() ) ) ) : NULL;
/* If this is an item where the first comment is required, don't index because the comment will serve as both */
if ( $object instanceof \IPS\Content\Item and $class::$firstCommentRequired )
{
return NULL;
}
/* Don't index if this is an item to be published in the future */
if ( $object->isFutureDate() )
{
return NULL;
}
/* Or if this *is* the first comment, add the title and replace the tags */
$title = $object->mapped('title');
$isForItem = FALSE;
if ( $object instanceof \IPS\Content\Comment )
{
$itemClass = $class::$itemClass;
if ( $itemClass::$firstCommentRequired and $object->isFirst() )
{
$title = $object->item()->mapped('title');
$tags = ( $object->item() instanceof \IPS\Content\Tags ) ? implode( ',', array_filter( array_merge( array( $object->item()->prefix() ), $object->item()->tags() ) ) ) : NULL;
$isForItem = TRUE;
}
}
/* Get the last updated date */
$dateUpdated = ( $object instanceof \IPS\Content\Item ) ? $object->mapped('last_comment') : $object->mapped('edit_time');
/* Take the HTML out of the content */
$content = trim( strip_tags( preg_replace( "#<blockquote(.*?)>(.*)</blockquote>#ui", "", preg_replace( "#<script(.*?)>(.*)</script>#ui", "", ' ' . $object->mapped('content') . ' ' ) ) ) );
/* Work out the hidden status */
$hiddenStatus = $object->hidden();
if ( $hiddenStatus === 0 and method_exists( $object, 'item' ) and $object->item()->hidden() )
{
$hiddenStatus = $isForItem ? 1 : 2;
}
if ( $hiddenStatus !== 0 and method_exists( $object, 'item' ) and $object->item()->isFutureDate() )
{
$hiddenStatus = 0;
}
/* Return */
return array(
'index_class' => $class,
'index_object_id' => $object->$idColumn,
'index_item_id' => ( $object instanceof \IPS\Content\Item ) ? $object->$idColumn : $object->mapped('item'),
'index_container_id' => ( $object instanceof \IPS\Content\Item ) ? (int) $object->mapped('container') : (int) $object->item()->mapped('container'),
'index_title' => $title,
'index_content' => $content,
'index_permissions' => $object->searchIndexPermissions(),
'index_date_created' => $object->mapped('date'),
'index_date_updated' => $dateUpdated ?: $object->mapped('date'),
'index_author' => (int) $object->mapped('author'),
'index_tags' => $tags,
'index_hidden' => $hiddenStatus
);
}
/**
* Index an item
*
* @param \IPS\Content\Searchable $object Item to add
* @return void
*/
abstract public function index( \IPS\Content\Searchable $object );
/**
* Remove item
*
* @param \IPS\Content\Searchable $object Item to remove
* @return void
*/
abstract public function removeFromSearchIndex( \IPS\Content\Searchable $object );
/**
* Removes all content for a classs
*
* @param string $class The class
* @param int|NULL $containerId The container ID to update, or NULL
* @return void
*/
abstract public function removeClassFromSearchIndex( $class, $containerId=NULL );
/**
* Removes all content for a specific application from the index (for example, when uninstalling).
*
* @param \IPS\Application $application The application
* @return void
*/
public function removeApplicationContent( \IPS\Application $application )
{
foreach ( $application->extensions( 'core', 'ContentRouter' ) as $router )
{
foreach( $router->classes AS $class )
{
if ( is_subclass_of( $class, 'IPS\Content\Searchable' ) )
{
$this->removeClassFromSearchIndex( $class );
if ( isset( $class::$commentClass ) )
{
$commentClass = $class::$commentClass;
if ( is_subclass_of( $commentClass, 'IPS\Content\Searchable' ) )
{
$this->removeClassFromSearchIndex( $commentClass );
}
}
if ( isset( $class::$reviewClass ) )
{
$reviewClass = $class::$reviewClass;
if ( is_subclass_of( $reviewClass, 'IPS\Content\Searchable' ) )
{
$this->removeClassFromSearchIndex( $reviewClass );
}
}
}
}
}
}
/**
* Mass Update (when permissions change, for example)
*
* @param string $class The class
* @param int|NULL $containerId The container ID to update, or NULL
* @param int|NULL $itemId The item ID to update, or NULL
* @param string|NULL $newPermissions New permissions (if applicable)
* @param int|NULL $newHiddenStatus New hidden status (if applicable) special value 2 can be used to indicate hidden only by parent
* @param int|NULL $newContainer New container ID (if applicable)
* @return void
*/
abstract public function massUpdate( $class, $containerId = NULL, $itemId = NULL, $newPermissions = NULL, $newHiddenStatus = NULL, $newContainer = NULL );
/**
* Prune search index
*
* @param \IPS\DateTime|NULL $cutoff The date to delete index records from, or NULL to delete all
* @return void
*/
abstract public function prune( \IPS\DateTime $cutoff = NULL );
}
+158
View File
@@ -0,0 +1,158 @@
<?php
/**
* @brief MySQL Search Index
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @since 21 Aug 2014
* @version SVN_VERSION_NUMBER
*/
namespace IPS\Content\Search\Mysql;
/* 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;
}
/**
* MySQL Search Index
*/
class _Index extends \IPS\Content\Search\Index
{
/**
* Index an item
*
* @param \IPS\Content\Searchable $object Item to add
* @return void
*/
public function index( \IPS\Content\Searchable $object )
{
$indexData = $this->indexData( $object );
if( $indexData )
{
if ( !$indexData['index_permissions'] )
{
$this->removeFromSearchIndex( $object );
}
else
{
\IPS\Db::i()->replace( 'core_search_index', $indexData );
}
}
}
/**
* Remove item
*
* @param \IPS\Content\Searchable $object Item to remove
* @return void
*/
public function removeFromSearchIndex( \IPS\Content\Searchable $object )
{
$class = get_class( $object );
$idColumn = $class::$databaseColumnId;
\IPS\Db::i()->delete( 'core_search_index', array( 'index_class=? AND index_object_id=?', $class, $object->$idColumn ) );
if ( isset( $class::$commentClass ) )
{
$commentClass = $class::$commentClass;
\IPS\Db::i()->delete( 'core_search_index', array( 'index_class=? AND index_item_id=?', $commentClass, $object->$idColumn ) );
}
if ( isset( $class::$reviewClass ) )
{
$reviewClass = $class::$reviewClass;
\IPS\Db::i()->delete( 'core_search_index', array( 'index_class=? AND index_item_id=?', $reviewClass, $object->$idColumn ) );
}
}
/**
* Removes all content for a classs
*
* @param string $class The class
* @param int|NULL $containerId The container ID to update, or NULL
* @return void
*/
public function removeClassFromSearchIndex( $class, $containerId=NULL )
{
$where = array( array( 'index_class=?', $class ) );
if ( $containerId !== NULL )
{
$where[] = array( 'index_container_id=?', $containerId );
}
\IPS\Db::i()->delete( 'core_search_index', $where );
}
/**
* Mass Update (when permissions change, for example)
*
* @param string $class The class
* @param int|NULL $containerId The container ID to update, or NULL
* @param int|NULL $itemId The item ID to update, or NULL
* @param string|NULL $newPermissions New permissions (if applicable)
* @param int|NULL $newHiddenStatus New hidden status (if applicable) special value 2 can be used to indicate hidden only by parent
* @param int|NULL $newContainer New container ID (if applicable)
* @return void
*/
public function massUpdate( $class, $containerId = NULL, $itemId = NULL, $newPermissions = NULL, $newHiddenStatus = NULL, $newContainer = NULL )
{
$where = array( array( 'index_class=?', $class ) );
if ( $containerId !== NULL )
{
$where[] = array( 'index_container_id=?', $containerId );
}
if ( $itemId !== NULL )
{
$where[] = array( 'index_item_id=?', $itemId );
}
$update = array();
if ( $newPermissions !== NULL )
{
$update['index_permissions'] = $newPermissions;
}
if ( $newHiddenStatus !== NULL )
{
if ( $newHiddenStatus === 2 )
{
$where[] = array( 'index_hidden=0' );
}
else
{
$where[] = array( 'index_hidden=2' );
}
$update['index_hidden'] = $newHiddenStatus;
}
if ( $newContainer )
{
$update['index_container_id'] = $newContainer;
}
\IPS\Db::i()->update( 'core_search_index', $update, $where );
}
/**
* Prune search index
*
* @param \IPS\DateTime|NULL $cutoff The date to delete index records from, or NULL to delete all
* @return void
*/
public function prune( \IPS\DateTime $cutoff = NULL )
{
if ( $cutoff )
{
\IPS\DB::i()->delete( 'core_search_index', array( 'index_date_updated < ?', $cutoff->getTimestamp() ) );
}
else
{
\IPS\DB::i()->delete( 'core_search_index' );
}
}
}
+412
View File
@@ -0,0 +1,412 @@
<?php
/**
* @brief MySQL Search Query
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @since 21 Aug 2014
* @version SVN_VERSION_NUMBER
*/
namespace IPS\Content\Search\Mysql;
/* 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;
}
/**
* MySQL Search Query
*/
class _Query extends \IPS\Content\Search\Query
{
/**
* @brief The WHERE clause
*/
protected $where = array();
/**
* @brief The WHERE clause for hidden/unhidden
*/
protected $hiddenClause = NULL;
/**
* @brief The offset
*/
protected $offset = 0;
/**
* @brief The ORDER BY clause
*/
protected $order = NULL;
/**
* @brief Joins
*/
protected $joins = array();
/**
* Filter by content type
*
* @param string $class The type of content to search (including all comment/review classes)
* @param array|NULL $containers An array of container IDs to filter by, or NULL to not filter by containers
* @param int|NULL $minimumComments The minimum number of comments
* @param int|NULL $minimumReviews The minimum number of reviews
* @param int|NULL $minimumViews The minimum number of views
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
public function filterByContent( $class, $containers = NULL, $minimumComments = NULL, $minimumReviews = NULL, $minimumViews = NULL )
{
$classes = array( $class );
if ( isset( $class::$commentClass ) )
{
$classes[] = $class::$commentClass;
}
if ( isset( $class::$reviewClass ) )
{
$classes[] = $class::$reviewClass;
}
$this->where[] = array( \IPS\Db::i()->in( 'index_class', $classes ) );
if ( $containers !== NULL )
{
$this->where[] = array( \IPS\Db::i()->in( 'index_container_id', $containers ) );
}
if ( $minimumComments !== NULL or $minimumReviews !== NULL or $minimumViews !== NULL )
{
$this->joins[ $class::$databaseTable ] = array( $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnId . '=core_search_index.index_item_id' );
if ( $minimumComments !== NULL )
{
$this->where[] = array( $class::$databasePrefix . $class::$databaseColumnMap['num_comments'] . '>=?', $minimumComments );
}
if ( $minimumReviews !== NULL )
{
$this->where[] = array( $class::$databasePrefix . $class::$databaseColumnMap['num_reviews'] . '>=?', $minimumReviews );
}
if ( $minimumViews !== NULL )
{
$this->where[] = array( $class::$databasePrefix . $class::$databaseColumnMap['views'] . '>=?', $minimumViews );
}
}
return $this;
}
/**
* Filter by content type to exclude
*
* @param array $classes The types of content to exclude (not including all comment/review classes)
* @param bool $allowedContiners If TRUE, $class should be an array of allowed containers for that class - e.g. array( 'IPS\forums\Topic' => array( 1, 2 ) - otherwise, a simple array - e.g. array( 'IPS\forums\Topic' )
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
public function filterByExcludeContent( array $classes, $allowedContiners=FALSE )
{
if ( $allowedContiners )
{
$where = array();
$params = array();
$excludeAll = array();
foreach ( $classes as $class => $allowed )
{
if ( count( $allowed ) )
{
$where[] = '( index_class<>? OR ' . \IPS\Db::i()->in( 'index_container_id', $allowed ) . ' )';
$params[] = $class;
}
else
{
$excludeAll[] = $class;
}
}
$finalWhere = array();
if ( count( $excludeAll ) )
{
$finalWhere[] = \IPS\Db::i()->in( 'index_class', $excludeAll, TRUE );
}
if ( count( $where ) )
{
$finalWhere[] = implode( ' OR ', $where );
}
$this->where[] = array_merge( array( implode( ' AND ', $finalWhere ) ), $params );
}
else
{
$this->where[] = array( \IPS\Db::i()->in( 'index_class', $classes, TRUE ) );
}
return $this;
}
/**
* Filter by content item
*
* @param \IPS\Content\Item $item The item
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
public function filterByItem( \IPS\Content\Item $item )
{
$classes = array();
if ( isset( $item::$commentClass ) )
{
$classes[] = $item::$commentClass;
}
if ( isset( $item::$reviewClass ) )
{
$classes[] = $item::$reviewClass;
}
$this->where[] = array( \IPS\Db::i()->in( 'index_class', $classes ) );
$idColumn = $item::$databaseColumnId;
$this->where[] = array( 'index_item_id=?', $item->$idColumn );
return $this;
}
/**
* Filter by author
*
* @param \IPS\Member $author The author
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
public function filterByAuthor( \IPS\Member $author )
{
$this->where[] = array( 'index_author=?', $author->member_id );
return $this;
}
/**
* Filter by start date
*
* @param \IPS\DateTime|NULL $start The start date (only results AFTER this date will be returned)
* @param \IPS\DateTime|NULL $end The end date (only results BEFORE this date will be returned)
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
public function filterByCreateDate( \IPS\DateTime $start = NULL, \IPS\DateTime $end = NULL )
{
if ( $start )
{
$this->where[] = array( 'index_date_created>?', $start->getTimestamp() );
}
if ( $end )
{
$this->where[] = array( 'index_date_created<?', $end->getTimestamp() );
}
return $this;
}
/**
* Filter by last updated date
*
* @param \IPS\DateTime|NULL $start The start date (only results AFTER this date will be returned)
* @param \IPS\DateTime|NULL $end The end date (only results BEFORE this date will be returned)
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
public function filterByLastUpdatedDate( \IPS\DateTime $start = NULL, \IPS\DateTime $end = NULL )
{
if ( $start )
{
$this->where[] = array( 'index_date_updated>?', $start->getTimestamp() );
}
if ( $end )
{
$this->where[] = array( 'index_date_updated<?', $end->getTimestamp() );
}
return $this;
}
/**
* Set hidden status
*
* @param int|array $statuses The statuses (array of HIDDEN_ constants)
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
public function setHiddenFilter( $statuses )
{
if ( is_null( $statuses ) )
{
$this->hiddenClause = NULL;
}
if ( is_array( $statuses ) )
{
$this->hiddenClause = array( \IPS\Db::i()->in( 'index_hidden', $statuses ) );
}
else
{
$this->hiddenClause = array( 'index_hidden=?', $statuses );
}
return $this;
}
/**
* Set page
*
* @param int $page The page number
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
public function setPage( $page )
{
$this->offset = ( $page - 1 ) * $this->resultsToGet;
return $this;
}
/**
* Set order
*
* @param int $order Order (see ORDER_ constants)
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
public function setOrder( $order )
{
switch ( $order )
{
case static::ORDER_NEWEST_UPDATED:
$this->order = 'index_date_updated DESC';
break;
case static::ORDER_NEWEST_CREATED:
$this->order = 'index_date_created DESC';
break;
case static::ORDER_RELEVANCY:
$this->order = NULL;
break;
}
return $this;
}
/**
* Build where
*
* @param string|null $term The term to search for
* @param array|null $tags The tags to search for
* @param int $method \IPS\Content\Search\Index::i()->TERM_OR_TAGS or \IPS\Content\Search\Index::i()->TERM_AND_TAGS
* @return array
*/
protected function _searchWhereClause( $term = NULL, $tags = NULL, $method = 1 )
{
/* Do we have a term? */
$where = array();
if ( $term !== NULL )
{
if( mb_strpos( $term, ' ' ) !== FALSE AND mb_strpos( $term, '+' ) === FALSE AND mb_strpos( $term, '-' ) === FALSE AND mb_strpos( $term, '"' ) === FALSE )
{
$terms = explode( ' ', $term );
$newTerms = array();
foreach( $terms as $_term )
{
if( mb_strlen( $_term ) > 2 )
{
$newTerms[] = "+" . $_term;
}
else
{
$newTerms[] = $_term;
}
}
$term = implode( ' ', $newTerms );
}
/* Try to guess what mode will be best for this query. If it looks like a MySQL boolean mode search, use that */
if ( mb_substr( $term, 0, 1 ) === '+' or mb_substr( $term, -1, 1 ) === '*' or preg_match( '/^".*"$/', $term ) )
{
$mode = 'IN BOOLEAN MODE';
}
/* Otherwise, we're just using the default mode - we tried using query expansion for short queries, but it didn't work well (lots of irrelevant results) */
else
{
$mode = '';
}
/* If we also have tags, create a combined where */
if ( $tags !== NULL )
{
if ( $method === static::TERM_OR_TAGS )
{
$where[] = array( "( MATCH(index_content,index_title) AGAINST (? {$mode}) OR " . \IPS\Db::i()->findInSet( 'index_tags', $tags ) . ' )', $term );
}
else
{
$where[] = array( "MATCH(index_content,index_title) AGAINST (? {$mode})", $term );
$where[] = array( \IPS\Db::i()->findInSet( 'index_tags', $tags ) );
}
}
/* Or just use the term */
else
{
$where[] = array( "MATCH(index_content,index_title) AGAINST (? {$mode})", $term );
}
}
/* Or do we have tags? */
elseif ( $tags !== NULL )
{
//$this->where[] = array( \IPS\Db::i()->findInSet( 'index_tags', $tags ) );
$tagWhere = array();
$params = array();
foreach( $tags as $tag )
{
array_push( $params, $tag, $tag, $tag, $tag );
$tagWhere[] = "( index_tags=? OR index_tags LIKE CONCAT( ?, ',%' ) OR index_tags LIKE CONCAT( '%,', ? ) OR index_tags LIKE CONCAT( '%,', ?, ',%' ) )";
}
$where[] = array_merge( array( '(' . implode( ' OR ', $tagWhere ) . ')' ), $params );
}
/* Only get stuff we have permission for */
$where[] = array( "( index_permissions = '*' OR " . \IPS\Db::i()->findInSet( 'index_permissions', $this->permissionArray() ) . ' )' );
if ( $this->hiddenClause )
{
$where[] = $this->hiddenClause;
}
/* Return */
return $where;
}
/**
* Search
*
* @param string|null $term The term to search for
* @param array|null $tags The tags to search for
* @param int $method \IPS\Content\Search\Index::i()->TERM_OR_TAGS or \IPS\Content\Search\Index::i()->TERM_AND_TAGS
* @return \IPS\Content\Search\Results
*/
public function search( $term = NULL, $tags = NULL, $method = 1 )
{
$query = \IPS\Db::i()->select( '*', 'core_search_index', array_merge( $this->where, $this->_searchWhereClause( $term, $tags, $method ) ), $this->order, array( $this->offset, $this->resultsToGet ), NULL, NULL, \IPS\Db::SELECT_SQL_CALC_FOUND_ROWS );
foreach ( $this->joins as $table => $where )
{
$query->join( $table, $where );
}
$count = $query->count( TRUE );
return new \IPS\Content\Search\Results( iterator_to_array( $query ), $count );
}
/**
* Get count
*
* @param string|null $term The term to search for
* @param array|null $tags The tags to search for
* @param int $method \IPS\Content\Search\Index::i()->TERM_OR_TAGS or \IPS\Content\Search\Index::i()->TERM_AND_TAGS
* @return int
*/
public function count( $term = NULL, $tags = NULL, $method = 1 )
{
return \IPS\Db::i()->select( 'COUNT(*)', 'core_search_index', array_merge( $this->where, $this->_searchWhereClause( $term, $tags, $method ) ) )->first();
}
}
Whitespace-only changes.
+282
View File
@@ -0,0 +1,282 @@
<?php
/**
* @brief Abstract Search Query
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @since 21 Aug 2014
* @version SVN_VERSION_NUMBER
*/
namespace IPS\Content\Search;
/* 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;
}
/**
* Abstract Search Query
*/
abstract class _Query
{
const TERM_OR_TAGS = 1;
const TERM_AND_TAGS = 2;
const HIDDEN_VISIBLE = 0;
const HIDDEN_UNAPPROVED = 1;
const HIDDEN_HIDDEN = -1;
const HIDDEN_PARENT_HIDDEN = 2;
const ORDER_NEWEST_UPDATED = 1;
const ORDER_NEWEST_CREATED = 2;
const ORDER_RELEVANCY = 3;
const SUPPORTS_JOIN_FILTERS = TRUE;
/**
* Create new query
*
* @param \IPS\Member $member The member performing the search (NULL for currently logged in member)
* @return \IPS\Content\Search
*/
public static function init( \IPS\Member $member = NULL )
{
$class = '\\IPS\\Content\\Search\\' . ucfirst( \IPS\Settings::i()->search_engine ) . '\\Query';
return new $class( $member ?: \IPS\Member::loggedIn() );
}
/**
* @brief Number of results to get
*/
public $resultsToGet = 25;
/**
* @brief The member performing the search
*/
protected $member;
/**
* Constructor
*
* @param \IPS\Member $member The member performing the search
* @return void
*/
public function __construct( \IPS\Member $member )
{
$this->member = $member;
if ( !$member->modPermission('can_view_hidden_content') )
{
$this->setHiddenFilter( static::HIDDEN_VISIBLE );
}
}
/**
* Exclude "first post" content items
* Useful for cases such as activity streams where you do not want both the topic and the first post to show
*
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
public function excludeFirstPostContentItems()
{
$classes = array();
foreach ( \IPS\Content::routedClasses( TRUE, FALSE, TRUE ) as $class )
{
if ( in_array( 'IPS\Content\Searchable', class_implements( $class ) ) and $class::$firstCommentRequired )
{
$classes[] = $class;
}
}
if ( !empty( $classes ) )
{
return $this->filterByExcludeContent( $classes );
}
else
{
return $this;
}
}
/**
* Exclude disabled applications
*
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
public function excludeDisabledApps()
{
$includeClasses = array();
foreach ( \IPS\Content::routedClasses( TRUE, FALSE, TRUE ) as $class )
{
if ( in_array( 'IPS\Content\Searchable', class_implements( $class ) ) )
{
$includeClasses[] = $class;
if ( isset( $class::$commentClass ) )
{
$includeClasses[] = $class::$commentClass;
}
if ( isset( $class::$reviewClass ) )
{
$includeClasses[] = $class::$reviewClass;
}
}
}
$allClasses = array();
foreach ( \IPS\Content::routedClasses( FALSE, FALSE, TRUE ) as $class )
{
if ( in_array( 'IPS\Content\Searchable', class_implements( $class ) ) )
{
$allClasses[] = $class;
if ( isset( $class::$commentClass ) )
{
$allClasses[] = $class::$commentClass;
}
if ( isset( $class::$reviewClass ) )
{
$allClasses[] = $class::$reviewClass;
}
}
}
$classes = array_diff( $allClasses, $includeClasses );
if ( !empty( $classes ) )
{
return $this->filterByExcludeContent( $classes );
}
else
{
return $this;
}
}
/**
* Filter by content type
*
* @param string $class The type of content to search (including all comment/review classes)
* @param array|NULL $containers An array of container IDs to filter by, or NULL to not filter by containers
* @param int|NULL $minimumComments The minimum number of comments
* @param int|NULL $minimumReviews The minimum number of reviews
* @param int|NULL $minimumViews The minimum number of views
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
abstract public function filterByContent( $class, $containers = NULL, $minimumComments = NULL, $minimumReviews = NULL, $minimumViews = NULL );
/**
* Filter by content type to exclude
*
* @param string $classes The types of content to search (not including all comment/review classes)
* @param bool $allowedContiners If TRUE, $class should be an array of allowed containers for that class - e.g. array( 'IPS\forums\Topic' => array( 1, 2 ) - otherwise, a simple array - e.g. array( 'IPS\forums\Topic' )
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
abstract public function filterByExcludeContent( array $classes, $allowedContiners=FALSE );
/**
* Filter by content item
*
* @param \IPS\Content\Item $item The item
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
abstract public function filterByItem( \IPS\Content\Item $item );
/**
* Filter by author
*
* @param \IPS\Member $author The author
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
abstract public function filterByAuthor( \IPS\Member $author );
/**
* Filter by start date
*
* @param \IPS\DateTime|NULL $start The start date (only results AFTER this date will be returned)
* @param \IPS\DateTime|NULL $end The end date (only results BEFORE this date will be returned)
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
abstract public function filterByCreateDate( \IPS\DateTime $start = NULL, \IPS\DateTime $end = NULL );
/**
* Filter by last updated date
*
* @param \IPS\DateTime|NULL $start The start date (only results AFTER this date will be returned)
* @param \IPS\DateTime|NULL $end The end date (only results BEFORE this date will be returned)
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
abstract public function filterByLastUpdatedDate( \IPS\DateTime $start = NULL, \IPS\DateTime $end = NULL );
/**
* Set hidden status
*
* @param int|array|NULL $statuses The statuses (see HIDDEN_ constants) or NULL for any
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
abstract public function setHiddenFilter( $statuses );
/**
* Set limit
*
* @param int $limit Number per page
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
public function setLimit( $limit )
{
$this->resultsToGet = $limit;
return $this;
}
/**
* Set page
*
* @param int $page The page number
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
abstract public function setPage( $page );
/**
* Set order
*
* @param int $order Order (see ORDER_ constants)
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
abstract public function setOrder( $order );
/**
* Permission Array
*
* @return array
*/
public function permissionArray()
{
return $this->member->permissionArray();
}
/**
* Search
*
* @param string|null $term The term to search for
* @param array|null $tags The tags to search for
* @param int $method \IPS\Content\Search\Index::i()->TERM_OR_TAGS or \IPS\Content\Search\Index::i()->TERM_AND_TAGS
* @return \Traversable
*/
abstract public function search( $term = NULL, $tags = NULL, $method = 1 );
/**
* Get count
*
* @param string|null $term The term to search for
* @param array|null $tags The tags to search for
* @param int $method \IPS\Content\Search\Index::i()->TERM_OR_TAGS or \IPS\Content\Search\Index::i()->TERM_AND_TAGS
* @return int
*/
public function count( $term = NULL, $tags = NULL, $method = 1 )
{
return $this->search( $term, $tags, $method )->count( TRUE );
}
}
+182
View File
@@ -0,0 +1,182 @@
<?php
/**
* @brief Search Results
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @since 21 Aug 2014
* @version SVN_VERSION_NUMBER
*/
namespace IPS\Content\Search;
/* 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;
}
/**
* Search Results
*/
class _Results extends \ArrayIterator
{
/**
* @brief Count
*/
protected $countAllRows;
/**
* @brief Has this been initated?
*/
protected $initated = FALSE;
/**
* Constructor
*
* @param array $results The results
* @param int $countAllRows Count for all rows
* @return void
*/
public function __construct( $results, $countAllRows )
{
$this->countAllRows = $countAllRows;
parent::__construct( $results );
}
/**
* Init
*
* @return void
*/
public function init( $loadReputation=FALSE )
{
$classesToLoad = array( 'IPS\Member' => array() );
$reputationIds = array();
foreach ( $this->getArrayCopy() as $row )
{
$classesToLoad[ 'IPS\Member' ][ $row['index_author'] ] = $row['index_author'];
$classesToLoad[ $row['index_class'] ][ $row['index_object_id'] ] = $row['index_object_id'];
$itemClass = NULL;
if ( $row['index_item_id'] and in_array( 'IPS\Content\Comment', class_parents( $row['index_class'] ) ) )
{
$itemClass = $row['index_class']::$itemClass;
$classesToLoad[ $itemClass ][ $row['index_item_id'] ] = $row['index_item_id'];
}
if ( $row['index_container_id'] )
{
$containerClass = $itemClass ? $itemClass::$containerNodeClass : $row['index_class']::$containerNodeClass;
$classesToLoad[ $containerClass ][ $row['index_container_id'] ] = $row['index_container_id'];
}
if ( $loadReputation and in_array( 'IPS\Content\Reputation', class_implements( $row['index_class'] ) ) )
{
$reputationIds[ $row['index_class'] ][ $row['index_object_id'] ] = $row['index_object_id'];
}
}
$reputation = array();
if ( count( $reputationIds ) )
{
$clause = array();
$binds = array();
foreach ( $reputationIds as $class => $ids )
{
$clause[] = "( app=? AND type=? AND " . \IPS\Db::i()->in( 'type_id', $ids ) . " )";
$binds[] = $class::$application;
$binds[] = $class::$reputationType;
}
$where = array( array_merge( array( implode( ' OR ', $clause ) ), $binds ) );
switch( \IPS\Settings::i()->reputation_point_types )
{
case 'positive':
case 'like':
$where[] = array( 'rep_rating=?', "1" );
break;
case 'negative':
$where[] = array( 'rep_rating=?', "-1" );
break;
}
foreach ( \IPS\Db::i()->select( '*', 'core_reputation_index', $where ) as $rep )
{
$reputation[ $rep['app'] ][ $rep['type'] ][ $rep['type_id'] ][ $rep['member_id'] ] = $rep;
}
}
foreach ( $classesToLoad as $class => $ids )
{
$ids = array_diff( $ids, $class::multitonIds() );
$idColumn = $class::$databaseColumnId;
if ( !empty( $ids ) )
{
foreach ( \IPS\Db::i()->select( '*', $class::$databaseTable, \IPS\Db::i()->in( $class::$databasePrefix . $class::$databaseColumnId, $ids ) ) as $row )
{
$obj = $class::constructFromData( $row );
if ( $loadReputation and $obj instanceof \IPS\Content\Reputation )
{
if ( isset( $reputation[ $class::$application ][ $class::$reputationType ][ $obj->$idColumn ] ) )
{
$obj->reputation = $reputation[ $class::$application ][ $class::$reputationType ][ $obj->$idColumn ];
}
else
{
$obj->reputation = array();
}
}
}
}
}
$this->initated = TRUE;
}
/**
* Get current
*
* @return \IPS\Patterns\ActiveRecord
*/
public function current()
{
$row = parent::current();
return call_user_func( array( $row['index_class'], 'load' ), $row['index_object_id'] );
}
/**
* Rewind
*
* @return void
*/
public function rewind()
{
if ( !$this->initated )
{
$this->init();
}
return parent::rewind();
}
/**
* Get count
*
* @param bool $allRows If TRUE, will get the number of rows ignoring the limit
* @return int
*/
public function count( $allRows = FALSE )
{
if ( $allRows )
{
return $this->countAllRows;
}
else
{
return parent::count();
}
}
}
+301
View File
@@ -0,0 +1,301 @@
<?php
/**
* @brief Sphinx Search Index
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @since 26 Aug 2014
* @version SVN_VERSION_NUMBER
*/
namespace IPS\Content\Search\Sphinx;
/* 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;
}
/**
* Sphinx Search Index
*/
class _Index extends \IPS\Content\Search\Index
{
/**
* @brief The index table
*/
protected $table;
/**
* Constructor
*
* @return void
*/
public function __construct()
{
\IPS\Db::i( 'sphinx', array(
'sql_host' => \IPS\Settings::i()->search_sphinx_server,
'sql_user' => NULL,
'sql_pass' => NULL,
'sql_database' => NULL,
'sql_port' => (int) \IPS\Settings::i()->search_sphinx_port,
'sql_socket' => NULL,
'sql_utf8mb4' => \IPS\Settings::i()->sql_utf8mb4
) );
$this->table = \IPS\Settings::i()->sphinx_prefix . 'ips';
}
/**
* Index an item
*
* @param \IPS\Content\Searchable $object Item to add
* @return void
*/
public function index( \IPS\Content\Searchable $object )
{
$indexData = $this->indexData( $object );
if ( $indexData )
{
$data = array_merge( array( 'id' => static::_getId( $object ), 'index_class_id' => static::_getClassId( get_class( $object ) ) ), $indexData );
$data['index_permissions'] = json_encode( explode( ',', $data['index_permissions'] ) );
if( !$data['index_permissions'] )
{
$this->removeFromSearchIndex( $object );
}
else
{
$result = \IPS\Db::i('sphinx')->query( "REPLACE INTO {$this->table} ( " . implode( ', ', array_keys( $data ) ) . ' ) VALUES ( ' . implode( ', ', array_map( array( $this, '_escapeValue' ), $data ) ) . ' )' );
}
}
}
/**
* Remove item
*
* @param \IPS\Content\Searchable $object Item to remove
* @return void
*/
public function removeFromSearchIndex( \IPS\Content\Searchable $object )
{
\IPS\Db::i('sphinx')->query( "DELETE FROM {$this->table} WHERE id=" . static::_getId( $object ) );
$class = get_class( $object );
$idColumn = $class::$databaseColumnId;
if ( isset( $class::$commentClass ) )
{
$commentClass = $class::$commentClass;
foreach ( \IPS\Db::i()->select( $commentClass::$databasePrefix . $commentClass::$databaseColumnId, $commentClass::$databaseTable, array( $commentClass::$databasePrefix . $commentClass::$databaseColumnMap['item'] . '=?', $object->$idColumn ) ) as $commentid )
{
\IPS\Db::i('sphinx')->query( "DELETE FROM {$this->table} WHERE id=" . static::_getId( $commentid, $commentClass ) );
}
}
if ( isset( $class::$reviewClass ) )
{
$reviewClass = $class::$reviewClass;
foreach ( \IPS\Db::i()->select( $reviewClass::$databasePrefix . $reviewClass::$databaseColumnId, $reviewClass::$databaseTable, array( $reviewClass::$databasePrefix . $reviewClass::$databaseColumnMap['item'] . '=?', $object->$idColumn ) ) as $reviewid )
{
\IPS\Db::i('sphinx')->query( "DELETE FROM {$this->table} WHERE id=" . static::_getId( $reviewid, $reviewClass ) );
}
}
}
/**
* Removes all content for a classs
*
* @param string $class The class
* @param int|NULL $containerId The container ID to update, or NULL
* @return void
*/
public function removeClassFromSearchIndex( $class, $containerId=NULL )
{
$where = array( 'index_class_id=' . static::_getClassId( $class ) );
if ( $containerId !== NULL )
{
$where[] = "index_container_id={$containerId}";
}
$where = implode( ' AND ', $where );
\IPS\Db::i('sphinx')->query( "DELETE FROM {$this->table} WHERE {$where}" );
}
/**
* Mass Update (when permissions change, for example)
*
* @param string $class The class
* @param int|NULL $containerId The container ID to update, or NULL
* @param int|NULL $itemId The item ID to update, or NULL
* @param string|NULL $newPermissions New permissions (if applicable)
* @param int|NULL $newHiddenStatus New hidden status (if applicable) special value 2 can be used to indicate hidden only by parent
* @param int|NULL $newContainer New container ID (if applicable)
* @return void
*/
public function massUpdate( $class, $containerId = NULL, $itemId = NULL, $newPermissions = NULL, $newHiddenStatus = NULL, $newContainer = NULL )
{
$where = array( 'index_class_id=' . static::_getClassId( $class ) );
if ( $containerId !== NULL )
{
$where[] = "index_container_id={$containerId}";
}
if ( $itemId !== NULL )
{
$where[] = "index_item_id={$itemId}";
}
$update = array();
if ( $newPermissions !== NULL )
{
$update['index_permissions'] = explode( ',', str_replace( '*', 0, $newPermissions ) );
}
if ( $newHiddenStatus !== NULL )
{
if ( $newHiddenStatus === 2 )
{
$where[] = 'index_hidden=0';
}
else
{
$where[] = 'index_hidden=2';
}
$update['index_hidden'] = $newHiddenStatus;
}
if ( $newContainer )
{
$update['index_container_id'] = $newContainer;
}
$set = array();
foreach ( $update as $k => $v )
{
$set[] = "{$k}={$this->_escapeValue($v)}";
}
$set = implode( ', ', $set );
$where = implode( ' AND ', $where );
try
{
\IPS\Db::i('sphinx')->query( "UPDATE {$this->table} SET {$set} WHERE {$where}" );
}
catch ( \Exception $e )
{
}
}
/**
* Prune search index
*
* @param \IPS\DateTime|NULL $cutoff The date to delete index records from, or NULL to delete all
* @return void
*/
public function prune( \IPS\DateTime $cutoff = NULL )
{
if ( $cutoff === NULL )
{
try
{
\IPS\Db::i('sphinx')->query( "TRUNCATE RTINDEX {$this->table}" );
}
catch( \IPS\Db\Exception $e )
{
/* If the index does not exist, we should not exit at this point in time */
}
}
else
{
throw new \UnexpectedValueException;
}
}
/**
* Get an ID for an object
*
* @param \IPS\Content\Searchable|int $object Item to get ID for
* @param string|NULL $class If $object is an ID rather than an object, provide the class
* @return int
*/
public static function _getId( $object, $class = NULL )
{
if ( is_object( $object ) )
{
$class = get_class( $object );
$idColumn = $class::$databaseColumnId;
$id = $object->$idColumn;
}
else
{
$id = $object;
}
return ( static::_getClassId( $class ) * 100000000 ) + $id;
}
/**
* Get an ID for an class
*
* @param string $class The class
* @return int
*/
public static function _getClassId( $class )
{
$updateMap = FALSE;
/* Get the map (Older versions stored this in datastore, so we look there if we don't have it or use a blank array) */
if ( !\IPS\Settings::i()->sphinx_id_map )
{
$map = isset( \IPS\Data\Store::i()->sphinxIdMap ) ? \IPS\Data\Store::i()->sphinxIdMap : array();
$updateMap = TRUE;
}
else
{
$map = json_decode( \IPS\Settings::i()->sphinx_id_map, TRUE );
}
/* Add it if we don't have it */
if ( !in_array( $class, $map ) )
{
$map[] = $class;
$updateMap = TRUE;
}
/* Update the map if necessary */
if ( $updateMap )
{
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => json_encode( $map ) ), array( 'conf_key=?', 'sphinx_id_map' ) );
unset( \IPS\Data\Store::i()->settings );
}
/* Return */
return intval( array_search( $class, $map ) );
}
/**
* Escape a value for the query (SphinxQL doesn't support prepared statements)
*
* @param mixed $val The value
* @return mixed
*/
protected function _escapeValue( $val )
{
if ( is_numeric( $val ) )
{
return $val;
}
elseif ( is_array( $val ) )
{
return '(' . implode( ',', array_map( array( $this, '_escapeValue' ), $val ) ) . ')';
}
else
{
return ( '\'' . \IPS\Db::i( 'sphinx' )->real_escape_string( $val ) . '\'' );
}
}
}
+375
View File
@@ -0,0 +1,375 @@
<?php
/**
* @brief Sphinx Search Query
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @since 26 Aug 2014
* @version SVN_VERSION_NUMBER
*/
namespace IPS\Content\Search\Sphinx;
/* 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;
}
/**
* Sphinx Search Query
*/
class _Query extends \IPS\Content\Search\Query
{
const SUPPORTS_JOIN_FILTERS = FALSE;
/**
* @brief The index table
*/
protected $table;
/**
* @brief The WHERE clause
*/
protected $where = array();
/**
* @brief The WHERE clause for hidden/unhidden
*/
protected $hiddenClause = NULL;
/**
* @brief The ORDER BY clause
*/
protected $order = NULL;
/**
* @brief The offset
*/
protected $offset = 0;
/**
* Constructor
*
* @param \IPS\Member $member The member performing the search
* @return void
*/
public function __construct( \IPS\Member $member )
{
\IPS\Db::i( 'sphinx', array(
'sql_host' => \IPS\Settings::i()->search_sphinx_server,
'sql_user' => NULL,
'sql_pass' => NULL,
'sql_database' => NULL,
'sql_port' => (int) \IPS\Settings::i()->search_sphinx_port,
'sql_socket' => NULL,
'sql_utf8mb4' => \IPS\Settings::i()->sql_utf8mb4
) );
$this->table = \IPS\Settings::i()->sphinx_prefix . 'ips';
parent::__construct( $member );
}
/**
* Filter by content type
*
* @param string $class The type of content to search (including all comment/review classes)
* @param array|NULL $containers An array of container IDs to filter by, or NULL to not filter by containers
* @param int|NULL $minimumComments The minimum number of comments
* @param int|NULL $minimumReviews The minimum number of reviews
* @param int|NULL $minimumViews The minimum number of views
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
public function filterByContent( $class, $containers = NULL, $minimumComments = NULL, $minimumReviews = NULL, $minimumViews = NULL )
{
/* Classes */
$classes = array( \IPS\Content\Search\Sphinx\Index::_getClassId( $class ) );
if ( isset( $class::$commentClass ) )
{
$classes[] = \IPS\Content\Search\Sphinx\Index::_getClassId( $class::$commentClass );
}
if ( isset( $class::$reviewClass ) )
{
$classes[] = \IPS\Content\Search\Sphinx\Index::_getClassId( $class::$reviewClass );
}
$this->where[] = 'index_class_id IN(' . implode( ',', $classes ) . ')';
/* Containers */
if ( $containers !== NULL )
{
$this->where[] = 'index_container_id IN(' . implode( ',', array_map( 'intval', $containers ) ) . ' )';
}
/* We don't support anything else */
if ( $minimumComments !== NULL or $minimumReviews !== NULL or $minimumViews !== NULL )
{
throw new \UnexpectedValueException;
}
return $this;
}
/**
* Filter by content type to exclude
*
* @param array $classes The types of content to exclude (not including all comment/review classes)
* @param bool $allowedContiners If TRUE, $class should be an array of allowed containers for that class - e.g. array( 'IPS\forums\Topic' => array( 1, 2 ) - otherwise, a simple array - e.g. array( 'IPS\forums\Topic' )
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
public function filterByExcludeContent( array $classes, $allowedContiners=FALSE )
{
if ( $allowedContiners )
{
$where = array();
foreach ( $classes as $class => $allowed )
{
if ( count( $allowed ) )
{
$where[] = '( index_class<>' . \IPS\Content\Search\Sphinx\Index::_getClassId( $class ) . ' OR index_container_id IN( ' . implode( ',', $allowed ) . ' ) )';
}
else
{
$excludeAll[] = $class;
}
}
$finalWhere = array();
if ( count( $excludeAll ) )
{
$finalWhere[] = 'index_class NOT IN('. implode( ',', array_map( array( 'IPS\Content\Search\Sphinx\Index', '_getClassId' ), $excludeAll ) ) . ')';
}
if ( count( $where ) )
{
$finalWhere[] = implode( ' OR ', $where );
}
$this->where[] = array_merge( array( implode( ' AND ', $finalWhere ) ) );
}
else
{
$this->where[] = 'index_class NOT IN('. implode( ',', array_map( array( 'IPS\Content\Search\Sphinx\Index', '_getClassId' ), $classes ) ) . ')';
}
return $this;
}
/**
* Filter by content item
*
* @param \IPS\Content\Item $item The item
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
public function filterByItem( \IPS\Content\Item $item )
{
$class = get_class( $item );
$classes = array();
if ( isset( $class::$commentClass ) )
{
$classes[] = \IPS\Content\Search\Sphinx\Index::_getClassId( $class::$commentClass );
}
if ( isset( $class::$reviewClass ) )
{
$classes[] = \IPS\Content\Search\Sphinx\Index::_getClassId( $class::$reviewClass );
}
$this->where[] = 'index_class_id IN(' . implode( ',', $classes ) . ')';
$idColumn = $item::$databaseColumnId;
$this->where[] = "index_item_id={$item->$idColumn}";
return $this;
}
/**
* Filter by author
*
* @param \IPS\Member $author The author
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
public function filterByAuthor( \IPS\Member $author )
{
$this->where[] = "index_author={$author->member_id}";
return $this;
}
/**
* Filter by start date
*
* @param \IPS\DateTime|NULL $start The start date (only results AFTER this date will be returned)
* @param \IPS\DateTime|NULL $end The end date (only results BEFORE this date will be returned)
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
public function filterByCreateDate( \IPS\DateTime $start = NULL, \IPS\DateTime $end = NULL )
{
if ( $start )
{
$this->where[] = 'index_date_created>' . $start->getTimestamp();
}
if ( $end )
{
$this->where[] = 'index_date_created<' . $end->getTimestamp();
}
return $this;
}
/**
* Filter by last updated date
*
* @param \IPS\DateTime|NULL $start The start date (only results AFTER this date will be returned)
* @param \IPS\DateTime|NULL $end The end date (only results BEFORE this date will be returned)
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
public function filterByLastUpdatedDate( \IPS\DateTime $start = NULL, \IPS\DateTime $end = NULL )
{
if ( $start )
{
$this->where[] = 'index_date_updated>' . $start->getTimestamp();
}
if ( $end )
{
$this->where[] = 'index_date_updated<' . $end->getTimestamp();
}
return $this;
}
/**
* Set hidden status
*
* @param int|array $statuses The statuses (array of HIDDEN_ constants)
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
public function setHiddenFilter( $statuses )
{
if ( is_null( $statuses ) )
{
$this->hiddenClause = NULL;
}
if ( is_array( $statuses ) )
{
$this->hiddenClause = 'index_hidden IN(' . implode( ',', array_map( 'intval', $statuses ) ) . ' )';
}
else
{
$this->hiddenClause = 'index_hidden=' . intval( $statuses );
}
return $this;
}
/**
* Set page
*
* @param int $page The page number
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
public function setPage( $page )
{
$page = $page ?: 1;
$this->offset = ( $page - 1 ) * $this->resultsToGet;
return $this;
}
/**
* Set order
*
* @param int $order Order (see ORDER_ constants)
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
public function setOrder( $order )
{
switch ( $order )
{
case static::ORDER_NEWEST_UPDATED:
$this->order = 'index_date_updated DESC';
break;
case static::ORDER_NEWEST_CREATED:
$this->order = 'index_date_created DESC';
break;
case static::ORDER_RELEVANCY:
$this->order = NULL;
break;
}
return $this;
}
/**
* Search
*
* @param string|null $term The term to search for
* @param array|null $tags The tags to search for
* @param int $method \IPS\Content\Search\Index::i()->TERM_OR_TAGS or \IPS\Content\Search\Index::i()->TERM_AND_TAGS
* @return \IPS\Content\Search\Results
*/
public function search( $term = NULL, $tags = NULL, $method = 1 )
{
/* Build the match term */
if ( $term !== NULL )
{
$match[] = "( @(index_title,index_content) {$this->escape( $term )} )";
}
if ( $tags !== NULL )
{
$match[] = "( @index_tags " . implode( ' | ', array_map( array( $this, 'escape' ), $tags ) ) . ' )';
}
if ( !empty( $match ) )
{
array_unshift( $this->where, 'MATCH( \'' . implode( ( $method === static::TERM_OR_TAGS ? ' | ' : ' ' ), $match ) . '\' )' );
}
/* Only get stuff we have permission for */
$permissions = array( '\'' . $this->escape( '*' ) . '\'' );
foreach ( $this->permissionArray() as $id )
{
$permissions[] = '\'' . $this->escape( $id ) . '\'';
}
$permCheck = 'IN(index_permissions,' . implode( ',', $permissions ) . ') AS perm_check';
$this->where[] = 'perm_check=1';
/* Add the hidden clause */
if ( $this->hiddenClause )
{
$this->where[] = $this->hiddenClause;
}
/* Query */
$query = \IPS\Db::i('sphinx')->query( "SELECT *, {$permCheck} FROM {$this->table} WHERE " . implode( ' AND ', $this->where ) . ( $this->order ? " ORDER BY {$this->order}" : '' ) . " LIMIT {$this->offset}, {$this->resultsToGet}" );
/* Get count */
$meta = \IPS\Db::i('sphinx')->query('SHOW META');
while ( $row = $meta->fetch_assoc() )
{
if ( $row['Variable_name'] === 'total' )
{
$count = $row['Value'];
}
break;
}
/* Convert to array */
$results = array();
while ( $row = $query->fetch_assoc() )
{
$results[] = $row;
}
/* Return */
return new \IPS\Content\Search\Results( $results, $count );
}
/**
* Escape string for SphinxQL query
*
* @param string $term Search term
* @return string
*/
protected function escape( $term )
{
return str_replace( array( '\\', '(',')','|','-','!','@','~','"','&', '/', '^', '$', '=', "'", "\x00", "\n", "\r", "\x1a" ), array( '\\\\', '\\\(','\\\)','\\\|','\\\-','\\\!','\\\@','\\\~','\\\"', '\\\&', '\\\/', '\\\^', '\\\$', '\\\=', "\\'", "\\x00", "\\n", "\\r", "\\x1a" ), $term );
}
}
Whitespace-only changes.
View File
Whitespace-only changes.
+28
View File
@@ -0,0 +1,28 @@
<?php
/**
* @brief Interface for Searchable Content/Comment Models
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @since 8 Jul 2013
* @version SVN_VERSION_NUMBER
*/
namespace IPS\Content;
/* 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;
}
/**
* Interface for Searchable Content/Comment Models
*
* @note Content classes will gain special functionality by implementing this interface
*/
interface Searchable
{
}
@@ -0,0 +1,92 @@
<?php
/**
* @brief Delicious share link
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @since 11 Sept 2013
* @version SVN_VERSION_NUMBER
* @see <a href='https://delicious.com/tools'>Delicious button documentation</a>
*/
namespace IPS\Content\ShareServices;
/* 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;
}
/**
* Delicious share link
* @note Delicious does not provide any method to control the locale/language
*/
class _Delicious
{
/**
* @brief URL to the content item
*/
protected $url = NULL;
/**
* @brief Title of the content item
*/
protected $title = NULL;
/**
* Constructor
*
* @param \IPS\Http\Url $url URL to the content [optional - if omitted, some services will figure out on their own]
* @param string $title Default text for the content, usually the title [optional - if omitted, some services will figure out on their own]
* @return void
*/
public function __construct( \IPS\Http\Url $url=NULL, $title=NULL )
{
$this->url = $url;
$this->title = $title;
}
/**
* Determine whether the logged in user has the ability to autoshare
*
* @return boolean
*/
public static function canAutoshare()
{
return false;
}
/**
* Add any additional form elements to the configuration form. These must be setting keys that the service configuration form can save as a setting.
*
* @param \IPS\Helpers\Form $form Configuration form for this service
* @return void
*/
public static function modifyForm( \IPS\Helpers\Form &$form )
{
}
/**
* Return the HTML code to show the share link
*
* @return string
*/
public function __toString()
{
$url = \IPS\Http\Url::external( "http://delicious.com/save?jump=close&noui=1&v=5" )->setQueryString( 'provider', urlencode( \IPS\Settings::i()->board_name ) );
if( $this->url !== NULL )
{
$url = $url->setQueryString( 'url', (string) $this->url );
}
if( $this->title !== NULL )
{
$url = $url->setQueryString( 'title', $this->title );
}
return \IPS\Theme::i()->getTemplate( 'sharelinks', 'core' )->delicious( $url );
}
}
+81
View File
@@ -0,0 +1,81 @@
<?php
/**
* @brief Digg share link
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @since 11 Sept 2013
* @version SVN_VERSION_NUMBER
* @see <a href='http://ftllc.com/articles/social-media-buttons.php'>Digg button</a> (This was the best I could find...Digg has removed most of their button documentation it seems)
*/
namespace IPS\Content\ShareServices;
/* 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;
}
/**
* Digg share link
* @note Digg does not provide any method to control the locale/language
* @note Digg does not provide any method to control the URL or specify any title
*/
class _Digg
{
/**
* @brief URL to the content item
*/
protected $url = NULL;
/**
* @brief Title of the content item
*/
protected $title = NULL;
/**
* Constructor
*
* @param \IPS\Http\Url $url URL to the content [optional - if omitted, some services will figure out on their own]
* @param string $title Default text for the content, usually the title [optional - if omitted, some services will figure out on their own]
* @return void
*/
public function __construct( \IPS\Http\Url $url=NULL, $title=NULL )
{
$this->url = $url;
$this->title = $title;
}
/**
* Determine whether the logged in user has the ability to autoshare
*
* @return boolean
*/
public static function canAutoshare()
{
return false;
}
/**
* Add any additional form elements to the configuration form. These must be setting keys that the service configuration form can save as a setting.
*
* @param \IPS\Helpers\Form $form Configuration form for this service
* @return void
*/
public static function modifyForm( \IPS\Helpers\Form &$form )
{
}
/**
* Return the HTML code to show the share link
*
* @return string
*/
public function __toString()
{
return \IPS\Theme::i()->getTemplate( 'sharelinks', 'core' )->digg( \IPS\Http\Url::external( 'http://digg.com/submit' )->setQueryString( 'url', (string) $this->url ) );
}
}
+78
View File
@@ -0,0 +1,78 @@
<?php
/**
* @brief Email share link
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @since 11 Sept 2013
* @version SVN_VERSION_NUMBER
*/
namespace IPS\Content\ShareServices;
/* 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;
}
/**
* Email share link
*/
class _Email
{
/**
* @brief URL to the content item
*/
protected $url = NULL;
/**
* @brief Title of the content item
*/
protected $title = NULL;
/**
* Constructor
*
* @param \IPS\Http\Url $url URL to the content [optional - if omitted, some services will figure out on their own]
* @param string $title Default text for the content, usually the title [optional - if omitted, some services will figure out on their own]
* @return void
*/
public function __construct( \IPS\Http\Url $url=NULL, $title=NULL )
{
$this->url = $url;
$this->title = $title;
}
/**
* Determine whether the logged in user has the ability to autoshare
*
* @return boolean
*/
public static function canAutoshare()
{
return false;
}
/**
* Add any additional form elements to the configuration form. These must be setting keys that the service configuration form can save as a setting.
*
* @param \IPS\Helpers\Form $form Configuration form for this service
* @return void
*/
public static function modifyForm( \IPS\Helpers\Form &$form )
{
}
/**
* Return the HTML code to show the share link
*
* @return string
*/
public function __toString()
{
return \IPS\Theme::i()->getTemplate( 'sharelinks', 'core' )->email( (string) $this->url->setQueryString( array( 'do' => 'email' ) ) );
}
}
+133
View File
@@ -0,0 +1,133 @@
<?php
/**
* @brief Facebook share link
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @since 11 Sept 2013
* @version SVN_VERSION_NUMBER
* @see <a href='https://developers.facebook.com/docs/reference/plugins/like/'>Facebook like button documentation</a>
*/
namespace IPS\Content\ShareServices;
/* 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;
}
/**
* Facebook share link
*/
class _Facebook
{
/**
* @brief URL to the content item
*/
protected $url = NULL;
/**
* @brief Title of the content item
*/
protected $title = NULL;
/**
* Constructor
*
* @param \IPS\Http\Url $url URL to the content [optional - if omitted, some services will figure out on their own]
* @param string $title Default text for the content, usually the title [optional - if omitted, some services will figure out on their own]
* @return void
*/
public function __construct( \IPS\Http\Url $url=NULL, $title=NULL )
{
$this->url = $url;
$this->title = $title;
}
/**
* Determine whether the logged in user has the ability to autoshare
*
* @return boolean
*/
public static function canAutoshare()
{
return (boolean) \IPS\Member::loggedIn()->fb_token AND \IPS\Member::loggedIn()->fb_uid;
}
/**
* Publish text or a URL to this service
*
* @param string $content Text to publish
* @param string $url [URL to publish]
* @return @void
*/
public static function publish( $content, $url=null )
{
if ( static::canAutoshare() )
{
try
{
/* If we include a URL, we'll post it as a link to the wall, otherwise we'll just update the user's status */
if ( $url !== NULL )
{
$response = \IPS\Http\Url::external( "https://graph.facebook.com/" . \IPS\Member::loggedIn()->fb_uid . "/links" )->request()->post( array(
'message' => $content,
'link' => (string) $url,
'access_token' => \IPS\Member::loggedIn()->fb_token,
) )->decodeJson();
if ( ! isset( $response['id'] ) )
{
throw new \InvalidArgumentException( \IPS\Member::loggedIn()->language()->addToStack('facebook_publish_exception') );
}
}
else
{
$response = \IPS\Http\Url::external( "https://graph.facebook.com/" . \IPS\Member::loggedIn()->fb_uid . "/feed" )->request()->post( array(
'message' => $content,
'access_token' => \IPS\Member::loggedIn()->fb_token,
) )->decodeJson();
if ( ! isset( $response['id'] ) )
{
throw new \InvalidArgumentException( \IPS\Member::loggedIn()->language()->addToStack('facebook_publish_exception') );
}
}
}
catch( \IPS\Http\Request\Exception $e )
{
IPS\Log::i( LOG_WARNING )->write( \IPS\Member::loggedIn()->id . ': '. $e->getMessage(), 'facebook' );
throw new \InvalidArgumentException( \IPS\Member::loggedIn()->language()->addToStack('facebook_publish_exception') );
}
}
else
{
throw new \InvalidArgumentException( \IPS\Member::loggedIn()->language()->addToStack('facebook_publish_no_user') );
}
}
/**
* Add any additional form elements to the configuration form. These must be setting keys that the service configuration form can save as a setting.
*
* @param \IPS\Helpers\Form $form Configuration form for this service
* @return void
*/
public static function modifyForm( \IPS\Helpers\Form &$form )
{
$form->add( new \IPS\Helpers\Form\Select( 'fbc_bot_group', \IPS\Settings::i()->fbc_bot_group, TRUE, array( 'options' => array_combine( array_keys( \IPS\Member\Group::groups() ), array_map( function( $_group ) { return (string) $_group; }, \IPS\Member\Group::groups() ) ) ) ) );
}
/**
* Return the HTML code to show the share link
*
* @return string
*/
public function __toString()
{
return \IPS\Theme::i()->getTemplate( 'sharelinks', 'core' )->facebook( urlencode( $this->url ) );
}
}
@@ -0,0 +1,81 @@
<?php
/**
* @brief Google +1 share link
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @since 11 Sept 2013
* @version SVN_VERSION_NUMBER
* @see <a href='https://developers.google.com/+/web/+1button/'>Google +1 button documentation</a>
*/
namespace IPS\Content\ShareServices;
/* 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;
}
/**
* Google +1 share link
*/
class _Googleplusone
{
/**
* @brief URL to the content item
*/
protected $url = NULL;
/**
* @brief Title of the content item
*/
protected $title = NULL;
/**
* Constructor
*
* @param \IPS\Http\Url $url URL to the content [optional - if omitted, some services will figure out on their own]
* @param string $title Default text for the content, usually the title [optional - if omitted, some services will figure out on their own]
* @return void
*/
public function __construct( \IPS\Http\Url $url=NULL, $title=NULL )
{
$this->url = $url;
$this->title = $title;
}
/**
* Determine whether the logged in user has the ability to autoshare
*
* @return boolean
*/
public static function canAutoshare()
{
return false;
}
/**
* Add any additional form elements to the configuration form. These must be setting keys that the service configuration form can save as a setting.
*
* @param \IPS\Helpers\Form $form Configuration form for this service
* @return void
*/
public static function modifyForm( \IPS\Helpers\Form &$form )
{
}
/**
* Return the HTML code to show the share link
*
* @return string
* @note Google +1 automatically determines title/description based on page microdata
* @see <a href='https://developers.google.com/+/web/snippet/'>Snippet documentation</a>
*/
public function __toString()
{
return \IPS\Theme::i()->getTemplate( 'sharelinks', 'core' )->google( urlencode( $this->url ) );
}
}
+79
View File
@@ -0,0 +1,79 @@
<?php
/**
* @brief Linked In share link
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @since 11 Sept 2013
* @version SVN_VERSION_NUMBER
* @see <a href='http://developer.linkedin.com/plugins/share-plugin-generator'>Linked In button documentation</a>
*/
namespace IPS\Content\ShareServices;
/* 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;
}
/**
* Linked In share link
*/
class _Linkedin
{
/**
* @brief URL to the content item
*/
protected $url = NULL;
/**
* @brief Title of the content item
*/
protected $title = NULL;
/**
* Constructor
*
* @param \IPS\Http\Url $url URL to the content [optional - if omitted, some services will figure out on their own]
* @param string $title Default text for the content, usually the title [optional - if omitted, some services will figure out on their own]
* @return void
*/
public function __construct( \IPS\Http\Url $url=NULL, $title=NULL )
{
$this->url = $url;
$this->title = $title;
}
/**
* Determine whether the logged in user has the ability to autoshare
*
* @return boolean
*/
public static function canAutoshare()
{
return false;
}
/**
* Add any additional form elements to the configuration form. These must be setting keys that the service configuration form can save as a setting.
*
* @param \IPS\Helpers\Form $form Configuration form for this service
* @return void
*/
public static function modifyForm( \IPS\Helpers\Form &$form )
{
}
/**
* Return the HTML code to show the share link
*
* @return string
*/
public function __toString()
{
return \IPS\Theme::i()->getTemplate( 'sharelinks', 'core' )->linkedin( urlencode( $this->url ), urlencode( $this->title ) );
}
}
+80
View File
@@ -0,0 +1,80 @@
<?php
/**
* @brief Reddit share link
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @since 11 Sept 2013
* @version SVN_VERSION_NUMBER
* @see <a href='http://www.reddit.com/buttons/‎'>Reddit button documentation</a>
*/
namespace IPS\Content\ShareServices;
/* 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;
}
/**
* Reddit share link
* @note Reddit does not provide any method to control the locale/language
*/
class _Reddit
{
/**
* @brief URL to the content item
*/
protected $url = NULL;
/**
* @brief Title of the content item
*/
protected $title = NULL;
/**
* Constructor
*
* @param \IPS\Http\Url $url URL to the content [optional - if omitted, some services will figure out on their own]
* @param string $title Default text for the content, usually the title [optional - if omitted, some services will figure out on their own]
* @return void
*/
public function __construct( \IPS\Http\Url $url=NULL, $title=NULL )
{
$this->url = $url;
$this->title = $title;
}
/**
* Determine whether the logged in user has the ability to autoshare
*
* @return boolean
*/
public static function canAutoshare()
{
return false;
}
/**
* Add any additional form elements to the configuration form. These must be setting keys that the service configuration form can save as a setting.
*
* @param \IPS\Helpers\Form $form Configuration form for this service
* @return void
*/
public static function modifyForm( \IPS\Helpers\Form &$form )
{
}
/**
* Return the HTML code to show the share link
*
* @return string
*/
public function __toString()
{
return \IPS\Theme::i()->getTemplate( 'sharelinks', 'core' )->reddit( urlencode( $this->url ), $this->title ? str_replace( '"', '\\"', $this->title ) : NULL );
}
}
+80
View File
@@ -0,0 +1,80 @@
<?php
/**
* @brief StumbleUpon share link
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @since 11 Sept 2013
* @version SVN_VERSION_NUMBER
* @see <a href='http://www.stumbleupon.com/dt/badges/create'>StumbleUpon button documentation</a>
*/
namespace IPS\Content\ShareServices;
/* 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;
}
/**
* StumbleUpon share link
* @note StumbleUpon does not provide a way to specify the language/locale
*/
class _Stumble
{
/**
* @brief URL to the content item
*/
protected $url = NULL;
/**
* @brief Title of the content item
*/
protected $title = NULL;
/**
* Constructor
*
* @param \IPS\Http\Url $url URL to the content [optional - if omitted, some services will figure out on their own]
* @param string $title Default text for the content, usually the title [optional - if omitted, some services will figure out on their own]
* @return void
*/
public function __construct( \IPS\Http\Url $url=NULL, $title=NULL )
{
$this->url = $url;
$this->title = $title;
}
/**
* Determine whether the logged in user has the ability to autoshare
*
* @return boolean
*/
public static function canAutoshare()
{
return false;
}
/**
* Add any additional form elements to the configuration form. These must be setting keys that the service configuration form can save as a setting.
*
* @param \IPS\Helpers\Form $form Configuration form for this service
* @return void
*/
public static function modifyForm( \IPS\Helpers\Form &$form )
{
}
/**
* Return the HTML code to show the share link
*
* @return string
*/
public function __toString()
{
return \IPS\Theme::i()->getTemplate( 'sharelinks', 'core' )->stumble( urlencode( $this->url ) );
}
}
+165
View File
@@ -0,0 +1,165 @@
<?php
/**
* @brief Twitter share link
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @since 11 Sept 2013
* @version SVN_VERSION_NUMBER
* @see <a href='https://dev.twitter.com/docs/tweet-button'>Tweet button documentation</a>
*/
namespace IPS\Content\ShareServices;
/* 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;
}
/**
* Twitter share link
*/
class _Twitter
{
/**
* @brief URL to the content item
*/
protected $url = NULL;
/**
* @brief Title of the content item
*/
protected $title = NULL;
/**
* Constructor
*
* @param \IPS\Http\Url $url URL to the content [optional - if omitted, some services will figure out on their own]
* @param string $title Default text for the content, usually the title [optional - if omitted, some services will figure out on their own]
* @return void
*/
public function __construct( \IPS\Http\Url $url=NULL, $title=NULL )
{
$this->url = $url;
$this->title = $title;
}
/**
* Determine whether the logged in user has the ability to autoshare
*
* @return boolean
*/
public static function canAutoshare()
{
return (boolean) \IPS\Member::loggedIn()->twitter_token AND \IPS\Member::loggedIn()->twitter_secret;
}
/**
* Publish text or a URL to this service
*
* @param string $content Text to publish
* @param string $url [URL to publish]
* @return @void
*/
public static function publish( $content, $url=null )
{
if ( static::canAutoshare() )
{
$loginHandler = \IPS\Login\LoginAbstract::load('twitter');
if ( isset( \IPS\Data\Store::i()->twitter_config ) AND isset( \IPS\Data\Store::i()->twitter_config['time'] ) AND ( \IPS\Data\Store::i()->twitter_config['time'] <= time() - 86400 ) )
{
$maxUrlLen = \IPS\Data\Store::i()->twitter_config['short_url_length'];
}
else
{
try
{
$response = $loginHandler->sendRequest( 'get', "https://api.twitter.com/1.1/help/configuration.json", array(), \IPS\Member::loggedIn()->twitter_token, \IPS\Member::loggedIn()->twitter_secret )->decodeJson();
if ( isset( $response['short_url_length'] ) )
{
\IPS\Data\Store::i()->twitter_config = array_merge( $response, array( 'time' => time() ) );
$maxUrlLen = $response['short_url_length'];
}
else
{
throw new \InvalidArgumentException( \IPS\Member::loggedIn()->language()->addToStack('twitter_publish_exception') );
}
}
catch ( \IPS\Http\Request\Exception $e )
{
\IPS\Log::i( LOG_WARNING )->write( \IPS\Member::loggedIn()->id . ': '. $e->getMessage(), 'twitter' );
throw new \InvalidArgumentException( \IPS\Member::loggedIn()->language()->addToStack('twitter_publish_exception') );
}
}
if ( $url !== null )
{
$content = mb_substr( $content, 0, ( 140 - ( $maxUrlLen + 1 ) ) ) . ' ' . $url;
}
try
{
$response = $loginHandler->sendRequest( 'post', "https://api.twitter.com/1.1/statuses/update.json", array( 'status' => $content ), \IPS\Member::loggedIn()->twitter_token, \IPS\Member::loggedIn()->twitter_secret )->decodeJson();
if ( isset( $response['id_str'] ) )
{
return $response['id_str'];
}
else
{
throw new \InvalidArgumentException( \IPS\Member::loggedIn()->language()->addToStack('twitter_publish_exception') );
}
}
catch ( \IPS\Http\Request\Exception $e )
{
\IPS\Log::i( LOG_WARNING )->write( \IPS\Member::loggedIn()->id . ': '. $e->getMessage(), 'twitter' );
throw new \InvalidArgumentException( \IPS\Member::loggedIn()->language()->addToStack('twitter_publish_exception') );
}
}
else
{
throw new \InvalidArgumentException( \IPS\Member::loggedIn()->language()->addToStack('twitter_publish_no_user') );
}
}
/**
* Add any additional form elements to the configuration form. These must be setting keys that the service configuration form can save as a setting.
*
* @param \IPS\Helpers\Form $form Configuration form for this service
* @return void
*/
public static function modifyForm( \IPS\Helpers\Form &$form )
{
$form->add( new \IPS\Helpers\Form\Text( 'twitter_hashtag', \IPS\Settings::i()->twitter_hashtag, FALSE ) );
}
/**
* Return the HTML code to show the share link
*
* @return string
*/
public function __toString()
{
try
{
$title = $this->title ?: NULL;
if ( \IPS\Settings::i()->twitter_hashtag !== '')
{
$title .= ' ' . \IPS\Settings::i()->twitter_hashtag;
}
return \IPS\Theme::i()->getTemplate( 'sharelinks', 'core' )->twitter( urlencode( $this->url ), urlencode( $title ) );
}
catch ( \Exception $e )
{
\IPS\IPS::exceptionHandler( $e );
}
}
}
Whitespace-only changes.
+26
View File
@@ -0,0 +1,26 @@
<?php
/**
* @brief Interface for Shareable Comment Models
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @since 8 Jul 2013
* @version SVN_VERSION_NUMBER
*/
namespace IPS\Content;
/* 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;
}
/**
* Interface for Shareable Content/Comment Models
*
* @note Content classes will gain special functionality by implementing this interface
*/
interface Shareable { }
+28
View File
@@ -0,0 +1,28 @@
<?php
/**
* @brief Tags interface for Content Models
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @since 14 Nov 2013
* @version SVN_VERSION_NUMBER
*/
namespace IPS\Content;
/* 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;
}
/**
* Tags Interface for Content Models
*
* @note Content classes will gain special functionality by implementing this interface
*/
interface Tags
{
}
+27
View File
@@ -0,0 +1,27 @@
<?php
/**
* @brief Interface for Tracking Views
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @since 8 Jan 2014
* @version SVN_VERSION_NUMBER
*/
namespace IPS\Content;
/* 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;
}
/**
* Interface for Tracking Views
*
* @note Content classes will gain special functionality by implementing this interface
* @note Originally we were just checking for a mapped 'views' column, but decided to use an interface for consistency
*/
interface Views { }
+295
View File
@@ -0,0 +1,295 @@
<?php
/**
* @brief Content Item Feed Widget
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @subpackage forums
* @since 16 Oct 2014
* @version SVN_VERSION_NUMBER
*/
namespace IPS\Content;
/* 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;
}
/**
* Content Item Feed Widget
*/
abstract class _Widget extends \IPS\Widget\PermissionCache
{
/**
* Class
*/
protected static $class;
/**
* Specify widget configuration
*
* @param null|\IPS\Helpers\Form $form Form object
* @return \IPS\Helpers\Form
*/
public function configuration( &$form=null )
{
/* Init */
$class = static::$class;
if ( $form === null )
{
$form = new \IPS\Helpers\Form;
}
/* Block title */
$form->add( new \IPS\Helpers\Form\Text( 'widget_feed_title', isset( $this->configuration['widget_feed_title'] ) ? $this->configuration['widget_feed_title'] : \IPS\Member::loggedIn()->language()->addToStack( $class::$title . '_pl' ) ) );
/* Container */
if ( isset( $class::$containerNodeClass ) )
{
$form->add( new \IPS\Helpers\Form\Node( 'widget_feed_container_' . $class::$title, isset( $this->configuration['widget_feed_container'] ) ? $this->configuration['widget_feed_container'] : 0, FALSE, array(
'class' => $class::$containerNodeClass,
'zeroVal' => 'all',
'permissionCheck' => 'view',
'multiple' => true
) ) );
}
/* Use permissions? */
if ( in_array( 'IPS\Content\Permissions', class_implements( $class ) ) )
{
$form->add( new \IPS\Helpers\Form\YesNo( 'widget_feed_use_perms', isset( $this->configuration['widget_feed_use_perms'] ) ? $this->configuration['widget_feed_use_perms'] : TRUE, FALSE ) );
}
/* Types */
if ( in_array( 'IPS\Content\Lockable', class_implements( $class ) ) or in_array( 'IPS\Content\Pinnable', class_implements( $class ) ) or in_array( 'IPS\Content\Featurable', class_implements( $class ) ) )
{
$types = array();
if ( in_array( 'IPS\Content\Lockable', class_implements( $class ) ) )
{
$types['open'] = 'mod_confirm_unlock';
$types['closed'] = 'mod_confirm_lock';
}
if ( in_array( 'IPS\Content\Pinnable', class_implements( $class ) ) )
{
$types['pinned'] = 'mod_confirm_pin';
$types['notpinned'] = 'mod_confirm_unpin';
}
if ( in_array( 'IPS\Content\Featurable', class_implements( $class ) ) )
{
$types['featured'] = 'mod_confirm_feature';
$types['notfeatured'] = 'mod_confirm_unfeature';
}
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'widget_feed_status', isset( $this->configuration['widget_feed_status'] ) ? $this->configuration['widget_feed_status'] : array_keys( $types ), FALSE, array( 'options' => $types ) ) );
}
/* Author */
$author = NULL;
try
{
if ( isset( $this->configuration['widget_feed_author'] ) and is_array( $this->configuration['widget_feed_author'] ) )
{
foreach( $this->configuration['widget_feed_author'] as $id )
{
$author[ $id ] = \IPS\Member::load( $id );
}
}
}
catch( \OutOfRangeException $ex ) { }
$form->add( new \IPS\Helpers\Form\Member( 'widget_feed_author', $author, FALSE, array( 'multiple' => true ) ) );
/* Minimum comments/reviews */
if ( isset( $class::$commentClass ) )
{
if ( $class::$firstCommentRequired )
{
$form->add( new \IPS\Helpers\Form\Number( 'widget_feed_min_posts', isset( $this->configuration['widget_feed_min_posts'] ) ? $this->configuration['widget_feed_min_posts'] : 0, FALSE, array( 'unlimitedLang' => 'any', 'unlimited' => 0 ) ) );
}
else
{
$form->add( new \IPS\Helpers\Form\Number( 'widget_feed_min_comments', isset( $this->configuration['widget_feed_min_comments'] ) ? $this->configuration['widget_feed_min_comments'] : 0, FALSE, array( 'unlimitedLang' => 'any', 'unlimited' => 0 ) ) );
}
}
if ( isset( $class::$reviewClass ) )
{
$form->add( new \IPS\Helpers\Form\Number( 'widget_feed_min_reviews', isset( $this->configuration['widget_feed_min_reviews'] ) ? $this->configuration['widget_feed_min_reviews'] : 0, FALSE, array( 'unlimitedLang' => 'any', 'unlimited' => 0 ) ) );
}
/* Rating */
if ( in_array( 'IPS\Content\Ratings', class_implements( $class ) ) and isset( $class::$databaseColumnMap['rating_average'] ) )
{
$form->add( new \IPS\Helpers\Form\Rating( 'widget_feed_min_rating', isset( $this->configuration['widget_feed_min_rating'] ) ? $this->configuration['widget_feed_min_rating'] : 0, FALSE, array() ) );
}
/* Number to show */
$form->add( new \IPS\Helpers\Form\Number( 'widget_feed_show', isset( $this->configuration['widget_feed_show'] ) ? $this->configuration['widget_feed_show'] : 5, TRUE ) );
/* Sort */
$sortOptions = array();
foreach ( array( 'updated', 'title', 'num_comments', 'date', 'views', 'rating_average' ) as $k )
{
if ( isset( $class::$databaseColumnMap[ $k ] ) )
{
$sortOptions[ $class::$databaseColumnMap[ $k ] ] = 'sort_' . $k;
}
}
$form->add( new \IPS\Helpers\Form\Select( 'widget_feed_sort_on', isset( $this->configuration['widget_feed_sort_on'] ) ? $this->configuration['widget_feed_sort_on'] : $class::$databaseColumnMap['updated'], FALSE, array( 'options' => $sortOptions ) ), NULL, NULL, NULL, 'widget_feed_sort_on' );
$form->add( new \IPS\Helpers\Form\Select( 'widget_feed_sort_dir', isset( $this->configuration['widget_feed_sort_dir'] ) ? $this->configuration['widget_feed_sort_dir'] : 'desc', FALSE, array(
'options' => array(
'desc' => 'descending',
'asc' => 'ascending'
)
) ) );
return $form;
}
/**
* Ran before saving widget configuration
*
* @param array $values Values from form
* @return array
*/
public function preConfig( $values )
{
$class = static::$class;
if ( is_array( $values[ 'widget_feed_container_' . $class::$title ] ) )
{
$values['widget_feed_container'] = array_keys( $values[ 'widget_feed_container_' . $class::$title ] );
}
if ( is_array( $values['widget_feed_author'] ) )
{
$members = array();
foreach( $values['widget_feed_author'] as $member )
{
$members[] = $member->member_id;
}
$values['widget_feed_author'] = $members;
}
return $values;
}
/**
* Get where clause
*
* @return array
*/
protected function buildWhere()
{
$class = static::$class;
$where = array();
/* Container */
if ( isset( $class::$containerNodeClass ) and !empty( $this->configuration['widget_feed_container'] ) )
{
$where[] = array( \IPS\Db::i()->in( $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnMap['container'], $this->configuration['widget_feed_container'] ) );
}
/* Status */
if ( isset( $this->configuration['widget_feed_status'] ) and in_array( 'IPS\Content\Lockable', class_implements( $class ) ) )
{
if ( in_array( 'closed', $this->configuration['widget_feed_status'] ) AND !in_array( 'open', $this->configuration['widget_feed_status'] ) )
{
$where[] = isset( $class::$databaseColumnMap['locked'] ) ? array( $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnMap['locked'] . '=?', 1 ) : array( $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnMap['status'] . '=?', 'closed' );
}
elseif ( in_array( 'open', $this->configuration['widget_feed_status'] ) AND !in_array( 'closed', $this->configuration['widget_feed_status'] ) )
{
$where[] = isset( $class::$databaseColumnMap['locked'] ) ? array( $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnMap['locked'] . '=?', 0 ) : array( $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnMap['status'] . '=?', 'open' );
}
}
if ( isset( $this->configuration['widget_feed_status'] ) and in_array( 'IPS\Content\Featurable', class_implements( $class ) ) )
{
if ( in_array( 'notfeatured', $this->configuration['widget_feed_status'] ) AND !in_array( 'featured', $this->configuration['widget_feed_status'] ) )
{
$where[] = array( $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnMap['featured'] . '=0' );
}
elseif ( in_array( 'featured', $this->configuration['widget_feed_status'] ) AND !in_array( 'notfeatured', $this->configuration['widget_feed_status'] ) )
{
$where[] = array( $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnMap['featured'] . '=1' );
}
}
if ( isset( $this->configuration['widget_feed_status'] ) and in_array( 'IPS\Content\Pinnable', class_implements( $class ) ) )
{
if ( in_array( 'notpinned', $this->configuration['widget_feed_status'] ) AND !in_array( 'pinned', $this->configuration['widget_feed_status'] ) )
{
$where[] = array( $class::$databasePrefix . $class::$databaseColumnMap['pinned'] . '=0' );
}
elseif ( in_array( 'pinned', $this->configuration['widget_feed_status'] ) AND !in_array( 'notpinned', $this->configuration['widget_feed_status'] ) )
{
$where[] = array( $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnMap['pinned'] . '=1' );
}
}
/* Author */
if ( isset( $this->configuration['widget_feed_author'] ) and is_array( $this->configuration['widget_feed_author'] ) )
{
$where[] = array( \IPS\Db::i()->in( $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnMap['author'], $this->configuration['widget_feed_author'] ) );
}
/* Min comments/reviews */
if ( isset( $this->configuration['widget_feed_min_posts'] ) and $this->configuration['widget_feed_min_posts'] )
{
$where[] = array( $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnMap['num_comments'] . '>=?', (int) $this->configuration['widget_feed_min_posts'] );
}
if ( isset( $this->configuration['widget_feed_min_comments'] ) and $this->configuration['widget_feed_min_comments'] )
{
$where[] = array( $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnMap['num_comments'] . '>?', (int) $this->configuration['widget_feed_min_comments'] );
}
if ( isset( $this->configuration['widget_feed_min_reviews'] ) and $this->configuration['widget_feed_min_reviews'] )
{
$where[] = array( $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnMap['num_reviews'] . '>?', (int) $this->configuration['widget_feed_min_reviews'] );
}
/* Rating */
if ( isset( $this->configuration['widget_feed_min_rating'] ) and $this->configuration['widget_feed_min_rating'] )
{
$where[] = array( $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnMap['rating_average'] . '>?', (int) $this->configuration['widget_feed_min_rating'] );
}
/* Start date */
if ( isset( $this->configuration['widget_feed_start_date'] ) and isset( $this->configuration['widget_feed_start_date'] ) and $this->configuration['widget_feed_start_date'] > 0 )
{
$where[] = array( $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnMap['date'] . '>?', \IPS\DateTime::create()->sub( new \DateInterval( 'P' . $this->configuration['widget_feed_start_date'] . 'D' ) )->getTimestamp() );
}
return $where;
}
/**
* Render a widget
*
* @return string
*/
public function render()
{
$class = static::$class;
$items = $class::getItemsWithPermission(
$this->buildWhere(),
( isset( $this->configuration['widget_feed_sort_on'] ) and isset( $this->configuration['widget_feed_sort_dir'] ) ) ? ( $class::$databasePrefix . $this->configuration['widget_feed_sort_on'] . ' ' . $this->configuration['widget_feed_sort_dir'] ) : NULL,
isset( $this->configuration['widget_feed_show'] ) ? $this->configuration['widget_feed_show'] : 5,
( !isset( $this->configuration['widget_feed_use_perms'] ) or $this->configuration['widget_feed_use_perms'] ) ? 'read' : NULL,
FALSE
);
if ( count( $items ) )
{
return $this->output( $items, isset( $this->configuration['widget_feed_title'] ) ? $this->configuration['widget_feed_title'] : \IPS\Member::loggedIn()->language()->addToStack( $class::$title . '_pl' ) );
}
else
{
return '';
}
}
}
View File
Whitespace-only changes.