Version 4.3.0

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

No files matched your search

+97 -24
View File
@@ -25,7 +25,6 @@ class _File extends \IPS\Content\Item implements
\IPS\Content\Permissions,
\IPS\Content\Tags,
\IPS\Content\Followable,
\IPS\Content\ReportCenter,
\IPS\Content\ReadMarkers,
\IPS\Content\Views,
\IPS\Content\Hideable, \IPS\Content\Featurable, \IPS\Content\Pinnable, \IPS\Content\Lockable,
@@ -34,7 +33,7 @@ class _File extends \IPS\Content\Item implements
\IPS\Content\Embeddable,
\IPS\Content\MetaData
{
use \IPS\Content\Reactable;
use \IPS\Content\Reactable, \IPS\Content\Reportable;
/**
* @brief Application
@@ -252,7 +251,7 @@ class _File extends \IPS\Content\Item implements
*/
public static function contentTableTemplate()
{
return array( \IPS\Theme::i()->getTemplate( 'browse', 'downloads' ), 'rows' );
return array( \IPS\Theme::i()->getTemplate( 'browse', 'downloads', 'front' ), 'rows' );
}
/**
@@ -359,6 +358,57 @@ class _File extends \IPS\Content\Item implements
return FALSE;
}
/**
* Is Purchasable?
*
* @param bool $checkPaid Check if the file is a paid file
* @return bool
*/
public function isPurchasable( $checkPaid = TRUE )
{
/* If it's not a paid file, then it's not purchasable */
if ( $checkPaid and !$this->isPaid() )
{
return FALSE;
}
return (bool) $this->purchasable;
}
/**
* Can enable purchases?
*
* @param \IPS\Member|NULL $member Member to check, or NULL for currently logged in member
* @return bool
*/
public function canEnablePurchases( \IPS\Member $member = NULL )
{
if ( !$this->isPaid() )
{
return FALSE;
}
$member = $member ?: \IPS\Member::loggedIn();
return (bool) $member->modPermission('can_make_purchasable');
}
/**
* Can disable purchases?
*
* @param \IPS\Member|NULL $member Member to check, or NULL for currently logged in member
* @return bool
*/
public function canDisablePurchases( \IPS\Member $member = NULL )
{
if ( !$this->isPaid() )
{
return FALSE;
}
$member = $member ?: \IPS\Member::loggedIn();
return (bool) $member->modPermission('can_make_unpurchasable');
}
/**
* Get Price
*
@@ -396,7 +446,8 @@ class _File extends \IPS\Content\Item implements
}
}
catch ( \OutOfRangeException $e ) { }
catch ( \OutOfBoundsException $e ) { }
return NULL;
}
@@ -572,14 +623,14 @@ class _File extends \IPS\Content\Item implements
{
$images = array();
$count = 0;
foreach( $this->screenshots() as $image )
foreach( $this->screenshots( 0, FALSE ) as $image )
{
if ( $count == $limit )
{
break;
}
$images[] = array( 'downloads_Screenshots' => (string) $image);
$images[] = array( 'downloads_Screenshots' => (string) $image );
$count++;
}
@@ -719,13 +770,13 @@ class _File extends \IPS\Content\Item implements
if( isset( $this->_fieldData[ $fieldId ] ) )
{
$v = str_replace( '{content}', htmlspecialchars( $v, \IPS\HTMLENTITIES, 'UTF-8', FALSE ), $thisField->displayValue( $v ) );
$v = str_replace( '{content}', htmlspecialchars( $v, ENT_DISALLOWED, 'UTF-8', FALSE ), $thisField->displayValue( $v ) );
$v = str_replace( '{member_id}', \IPS\Member::loggedIn()->member_id, $v );
$v = str_replace( '{title}', \IPS\Member::loggedIn()->language()->addToStack( 'downloads_field_' . $fieldId ), $v );
}
else
{
$v = htmlspecialchars( $v, \IPS\HTMLENTITIES, 'UTF-8', FALSE );
$v = htmlspecialchars( $v, ENT_DISALLOWED, 'UTF-8', FALSE );
}
}
@@ -789,7 +840,7 @@ class _File extends \IPS\Content\Item implements
{
if ( $container and $container->bitoptions['moderation'] and !$member->group['g_avoid_q'] )
{
return TRUE;
return !static::modPermission( 'approve', $member, $container );
}
return parent::moderateNewItems( $member, $container );
@@ -1238,7 +1289,7 @@ class _File extends \IPS\Content\Item implements
$return = parent::formElements( $item, $container );
/* Description */
$return['description'] = new \IPS\Helpers\Form\Editor( 'file_desc', $item ? $item->desc : NULL, TRUE, array( 'app' => 'downloads', 'key' => 'Downloads', 'autoSaveKey' => 'downloads-new-file', 'attachIds' => ( $item === NULL ? NULL : array( $item->id, NULL, 'desc' ) ) ) );
$return['description'] = new \IPS\Helpers\Form\Editor( 'file_desc', $item ? $item->desc : NULL, TRUE, array( 'app' => 'downloads', 'key' => 'Downloads', 'autoSaveKey' => 'downloads-new-file', 'attachIds' => ( $item === NULL ? NULL : array( $item->id, NULL, 'desc' ) ) ), '\IPS\Helpers\Form::floodCheck' );
/* Primary screenshot */
if ( $item )
@@ -1351,8 +1402,13 @@ class _File extends \IPS\Content\Item implements
public function processForm( $values )
{
parent::processForm( $values );
$new = FALSE;
if ( !$this->_new )
if ( $this->_new )
{
$new = TRUE;
}
else
{
$oldContent = $this->desc;
}
@@ -1412,7 +1468,14 @@ class _File extends \IPS\Content\Item implements
foreach ( $this->container()->cfields as $field )
{
$helper = $field->buildHelper();
$cfields[ "field_{$field->id}" ] = $helper::stringValue( $values[ "downloads_field_{$field->id}" ] );
if ( $helper instanceof \IPS\Helpers\Form\Upload )
{
$cfields[ "field_{$field->id}" ] = (string) $values[ "downloads_field_{$field->id}" ];
}
else
{
$cfields[ "field_{$field->id}" ] = $helper::stringValue( $values[ "downloads_field_{$field->id}" ] );
}
if ( $helper instanceof \IPS\Helpers\Form\Editor )
{
@@ -1426,7 +1489,7 @@ class _File extends \IPS\Content\Item implements
}
/* Update Category */
$this->container()->setLastFile();
$this->container()->setLastFile( $new ? $this : NULL );
$this->container()->save();
}
@@ -1787,7 +1850,7 @@ class _File extends \IPS\Content\Item implements
$queueData['category_id'] = $category->$categoryIdColumn;
$queueData['member_id'] = $member->member_id;
\IPS\Task::queue( 'downloads', 'Follow', $queueData );
\IPS\Task::queue( 'downloads', 'Follow', $queueData, 2 );
}
else
{
@@ -1936,7 +1999,7 @@ class _File extends \IPS\Content\Item implements
if ( $count > static::NOTIFICATIONS_PER_BATCH )
{
$idColumn = static::$databaseColumnId;
\IPS\Task::queue( 'core', 'Follow', array( 'class' => get_class( $this ), 'item' => $this->$idColumn, 'extra' => 'update', 'exclude' => array() ) );
\IPS\Task::queue( 'core', 'Follow', array( 'class' => get_class( $this ), 'item' => $this->$idColumn, 'extra' => 'update', 'exclude' => array() ), 2 );
}
else
{
@@ -2180,7 +2243,7 @@ class _File extends \IPS\Content\Item implements
*
* @return void
*/
protected function syncTopic()
public function syncTopic()
{
/* Existing topic */
if ( $this->topicid )
@@ -2202,6 +2265,13 @@ class _File extends \IPS\Content\Item implements
}
$topic->save();
$firstPost = $topic->comments( 1 );
/* If the first post of the topic is missing, NULL will be returned */
if( $firstPost === NULL )
{
throw new \OutOfRangeException;
}
$content = \IPS\Theme::i()->getTemplate( 'submit', 'downloads', 'front' )->topic( $this );
\IPS\Member::loggedIn()->language()->parseOutputForDisplay( $content );
$firstPost->post = $content;
@@ -2326,6 +2396,7 @@ class _File extends \IPS\Content\Item implements
/**
* Get output for API
*
* @param \IPS\Member|NULL $authorizedMember The member making the API request or NULL for API Key / client_credentials
* @param array $backup If provided, will output for a particular version - provide row from downloads_filebackup
* @return array
* @apiresponse int id ID number
@@ -2350,25 +2421,26 @@ class _File extends \IPS\Content\Item implements
* @apiresponse bool pinned File is pinned
* @apiresponse bool featured File is featured
* @apiresponse string url URL
* @apiresponse \IPS\forums\Topic topic The topic
*/
public function apiOutput( $backup=NULL )
public function apiOutput( \IPS\Member $authorizedMember = NULL, $backup=NULL )
{
return array(
'id' => $this->id,
'title' => $backup ? $backup['b_filetitle'] : $this->name,
'category' => $this->container()->apiOutput(),
'author' => $this->author()->apiOutput(),
'category' => $this->container()->apiOutput( $authorizedMember ),
'author' => $this->author()->apiOutput( $authorizedMember ),
'date' => \IPS\DateTime::ts( $backup ? $backup['b_backup'] : $this->submitted )->rfc3339(),
'description' => $backup ? $backup['b_filedesc'] : $this->content(),
'version' => $backup ? $backup['b_version'] : $this->version,
'changelog' => $backup ? $backup['b_changelog'] : $this->changelog,
'files' => array_values( array_map( function( $file ) {
return $file->apiOutput();
'files' => array_values( array_map( function( $file ) use ( $authorizedMember ) {
return $file->apiOutput( $authorizedMember );
}, iterator_to_array( $this->files( $backup ? $backup['b_id'] : NULL ) ) ) ),
'screenshots' => array_values( array_map( function( $file ) {
return $file->apiOutput();
'screenshots' => array_values( array_map( function( $file ) use ( $authorizedMember ) {
return $file->apiOutput( $authorizedMember );
}, iterator_to_array( $this->screenshots( 0, TRUE, $backup ? $backup['b_id'] : NULL ) ) ) ),
'primaryScreenshot' => $this->primary_screenshot ? ( $this->primary_screenshot->apiOutput() ) : null,
'primaryScreenshot' => $this->primary_screenshot ? ( $this->primary_screenshot->apiOutput( $authorizedMember ) ) : null,
'downloads' => $this->downloads,
'comments' => $this->comments,
'reviews' => $this->reviews,
@@ -2380,6 +2452,7 @@ class _File extends \IPS\Content\Item implements
'pinned' => (bool) $this->mapped('pinned'),
'featured' => (bool) $this->mapped('featured'),
'url' => (string) $this->url(),
'topic' => $this->topicid ? $this->topic()->apiOutput( $authorizedMember ) : NULL,
);
}