therItems = array(); foreach ( $ids as $id ) { if ( $id != $values['merge_master'] ) { $otherItems[] = $class::load( $id ); } } $class::load( $values['merge_master'] )->mergeIn( $otherItems, isset( $values['move_keep_link'] ) ? $values['move_keep_link'] : FALSE ); } else { \IPS\Output::i()->output = $form->customTemplate( array( call_user_func_array( array( \IPS\Theme::i(), 'getTemplate' ), array( 'forms', 'core' ) ), 'popupTemplate' ) ); if ( \IPS\Request::i()->isAjax() ) { \IPS\Output::i()->sendOutput( \IPS\Output::i()->output ); } else { \IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->globalTemplate( \IPS\Output::i()->title, \IPS\Output::i()->output, array( 'app' => \IPS\Dispatcher::i()->application->directory, 'module' => \IPS\Dispatcher::i()->module->key, 'controller' => \IPS\Dispatcher::i()->controller ) ), 200, 'text/html', \IPS\Output::i()->httpHeaders ); } return; } } } /* Everything else: just do it */ else { foreach ( array_keys( \IPS\Request::i()->moderate ) as $id ) { try { $object = $class::loadAndCheckPerms( $id ); $object->modAction( \IPS\Request::i()->modaction, \IPS\Member::loggedIn(), $params ); } catch ( \Exception $e ) {} } } \IPS\Output::i()->redirect( $this->baseUrl ); } /** * Get the form to move items * * @return string|array */ protected function getMoveForm() { $class = $this->class; $params = array(); $form = new \IPS\Helpers\Form( 'form', 'move' ); $form->class = 'ipsForm_vertical'; $form->hiddenValues['modaction'] = 'move'; $form->hiddenValues['moderate'] = \IPS\Request::i()->moderate; $currentContainer = $this->container; $form->add( new \IPS\Helpers\Form\Node( 'move_to', NULL, TRUE, array( 'class' => $class::$containerNodeClass, 'url' => \IPS\Request::i()->url()->setQueryString( 'modaction', 'move' ), 'permissionCheck' => function( $node ) use ( $currentContainer, $class ) { if( $currentContainer->id != $node->id ) { try { /* If the item is in a club, only allow moving to other clubs that you moderate */ if ( $currentContainer and \IPS\IPS::classUsesTrait( $currentContainer, 'IPS\Content\ClubContainer' ) and $currentContainer->club() ) { return $class::modPermission( 'move', \IPS\Member::loggedIn(), $node ) and $node->can( 'add' ) ; } if ( $node->can( 'add' ) ) { return true; } } catch( \OutOfBoundsException $e ) { } } return false; }, 'clubs' => TRUE ) ) ); if ( isset( $class::$databaseColumnMap['moved_to'] ) ) { $form->add( new \IPS\Helpers\Form\Checkbox( 'move_keep_link' ) ); if ( \IPS\Settings::i()->topic_redirect_prune ) { \IPS\Member::loggedIn()->language()->words['move_keep_link_desc'] = \IPS\Member::loggedIn()->language()->addToStack('_move_keep_link_desc', FALSE, array( 'pluralize' => array( \IPS\Settings::i()->topic_redirect_prune ) ) ); } } if ( $values = $form->values() ) { $params[] = $values['move_to']; $params[] = ( isset( $values['move_keep_link'] ) and $values['move_keep_link'] ); return $params; } else { \IPS\Output::i()->output = $form->customTemplate( array( call_user_func_array( array( \IPS\Theme::i(), 'getTemplate' ), array( 'forms', 'core' ) ), 'popupTemplate' ) ); if ( \IPS\Request::i()->isAjax() ) { \IPS\Output::i()->sendOutput( \IPS\Output::i()->output ); } else { \IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->globalTemplate( \IPS\Output::i()->title, \IPS\Output::i()->output, array( 'app' => \IPS\Dispatcher::i()->application->directory, 'module' => \IPS\Dispatcher::i()->module->key, 'controller' => \IPS\Dispatcher::i()->controller ) ), 200, 'text/html', \IPS\Output::i()->httpHeaders ); } return; } } /** * Return the table headers * * @param array|NULL $advancedSearchValues Advanced search values * @return array */ public function getHeaders( $advancedSearchValues ) { return array(); } /** * Return the container * * @return \IPS\Node\Model */ public function container() { return $this->container; } }obje`VNffff * @)`%-Time|NULL */ public function get_objectDatePosted() { $object = $this->object(); if ( $object instanceof \IPS\Content ) { if ( isset( $object::$databaseColumnMap['date'] ) ) { return \IPS\DateTime::ts( $object->mapped('date') ); } /* Valid object, but there isn't any date data available */ return NULL; } else if ( $object instanceof \IPS\Node\Model ) { /* Valid object, but there isn't any date data available */ return NULL; } throw new \OutofRangeException('object_not_valid'); } /** * Get the object author * * @return \IPS\Member|NULL */ public function get_objectAuthor() { $object = $this->object(); if ( $object instanceof \IPS\Content ) { return $object->author(); } else if ( $object instanceof \IPS\Node\Model ) { try { return $object->owner(); } catch( \Exception $e ) { return NULL; } } throw new \OutofRangeException('object_not_valid'); } /** * Get the object unread status * * @return bool|null */ public function get_objectIsUnread() { $object = $this->object(); if ( $object instanceof \IPS\Content\Item ) { return $object->unread(); } else if ( $object instanceof \IPS\Content\Comment ) { return $object->item()->unread(); } else if ( $object instanceof \IPS\Node\Model ) { if ( $object::$contentItemClass ) { $contentItemClass = $object::$contentItemClass; return $contentItemClass::containerUnread( $object ); } return NULL; } throw new \OutofRangeException('object_not_valid'); } /** * Get the number and indefinite article for replies/children where applicable * * @return array|null */ public function get_objectDataCount() { $object = $this->object(); if ( $object instanceof \IPS\Content\Item ) { try { $container = $object->container(); } catch( \Exception $e ){} if ( $object::supportsComments( NULL, $container ) ) { $count = $object->mapped('num_comments'); if ( $count AND isset( $object::$firstCommentRequired ) ) { $count--; } return array( 'count' => $count, 'words' => \IPS\Member::loggedIn()->language()->addToStack( 'num_replies', NULL, array( 'pluralize' => array( $count ) ) ) ); } if ( $object::supportsReviews( NULL, $container ) ) { $count = $object->mapped('num_reviews'); return array( 'count' => $count, 'words' => \IPS\Member::loggedIn()->language()->addToStack( 'num_reviews', NULL, array( 'pluralize' => array( $count ) ) ) ); } /* Valid object, but there isn't any date data available */ return NULL; } else if ( $object instanceof \IPS\Content\Comment ) { return NULL; } else if ( $object instanceof \IPS\Node\Model ) { if( $object->_items !== NULL ) { $count = $object->_items; return array( 'count' => $count, 'words' => \IPS\Member::loggedIn()->language()->addToStack( $object->_countLanguageString, NULL, array( 'pluralize' => array( $count ) ) ) ); } return NULL; } throw new \OutofRangeException('object_not_valid'); } /** * Returns "Foo posted {{indefart}} in {{container}}, {{date}} * * @return array|null */ public function get_objectMetaDescription() { $object = $this->object(); $author = $this->objectAuthor; if ( $object instanceof \IPS\Content\Item ) { $container = $object->containerWrapper(); if ( $container ) { return \IPS\Member::loggedIn()->language()->addToStack( 'promote_metadescription_container', NULL, array( 'htmlsprintf' => array( $author->link(), $this->objectDatePosted->html( FALSE ) ), 'sprintf' => array( $object->indefiniteArticle(), $container->url(), $container->_title ), ) ); } else { return \IPS\Member::loggedIn()->language()->addToStack( 'promote_metadescription_nocontainer', NULL, array( 'htmlsprintf' => array( $author->link(), $this->objectDatePosted->html( FALSE ) ), 'sprintf' => array( $object->indefiniteArticle() ) ) ); } } else if ( $object instanceof \IPS\Content\Comment ) { return \IPS\Member::loggedIn()->language()->addToStack( 'promote_metadescription_nocontainer', NULL, array( 'htmlsprintf' => array( $author->link(), $this->objectDatePosted->html( FALSE ) ), 'sprintf' => array( $object->indefiniteArticle() ) ) ); } else if ( $object instanceof \IPS\Node\Model ) { return \IPS\Member::loggedIn()->language()->addToStack( 'promote_metadescription_node', NULL, array( 'htmlsprintf' => array( $author->link() ), 'sprintf' => array( $object->url(), $object->_title ) ) ); } throw new \OutofRangeException('object_not_valid'); } /** * Get reactions class for this object * * @return array|null */ public function get_objectReactionClass() { $object = $this->object(); $class = NULL; if ( ! \IPS\Settings::i()->reputation_enabled ) { return NULL; } if ( $object instanceof \IPS\Content\Item ) { /* The first post has the reactions for this item */ if ( $object::$firstCommentRequired ) { try { $class = $object->comments( 1, NULL, 'date', 'asc' ); } catch( \Exception $e ) { $class = NULL; } } else { $class = $object; } } else if ( $object instanceof \IPS\Content\Comment ) { $class = $object; } else if ( $object instanceof \IPS\Node\Model ) { return NULL; } return ( $class and \IPS\IPS::classUsesTrait( $class, 'IPS\Content\Reactable' ) ) ? $class : NULL; } /** * Send to networks now * * @return void */ public function send() { /* Race condition possible, so flag as sent now */ $this->sent = time(); $this->save(); $returned = $this->returned; $time = time(); $hasFailed = false; foreach( $this->share_to as $service ) { if ( $this->failed ) { /* It failed, but some services may have sent */ if ( ! $this->serviceFailed( $service ) ) { /* This service did sent, so skip */ continue; } } $response = array( 'response_promote_id' => $this->id, 'response_promote_key' => $service, 'response_date' => time(), 'response_sent_date' => $time ); try { $serviceObject = $this->getPromoter( $service )->setMember( \IPS\Member::load( $this->added_by ) ); /* Successful post ID returned */ $returned[ $service ] = $serviceObject->post( $this ); } catch( \Exception $ex ) { $hasFailed = true; $this->scheduled = time() + 600; $this->sent = 0; $response['response_failed'] = 1; } /* Full response stored */ $response['response_json'] = json_encode( $serviceObject->response, TRUE ); \IPS\Db::i()->insert( 'core_social_promote_content', $response ); } $this->failed = ( $hasFailed ) ? $this->failed + 1 : 0; $this->returned = $returned; $this->save(); } /** * Save Changed Columns * * @return void */ public function save() { parent::save(); /* Enable the task again */ \IPS\Db::i()->update( 'core_tasks', array( 'enabled' => 1 ), array( '`key`=?', 'promote' ) ); } /** * Return an array of File objects * * @return array|null */ public function imageObjects() { $photos = array(); if ( count( $this->images ) ) { foreach( $this->images as $image ) { foreach( $image as $ext => $url ) { $photos[] = \IPS\File::get( $ext, $url ); } } } if ( count( $this->media ) ) { foreach( $this->media as $media ) { $photos[] = \IPS\File::get( 'core_Promote', $media ); } } return ( count( $photos ) ) ? $photos : NULL; } /** * Look for a specific image * * @param string $path Image path monthly_x_x/foo.gif * @param string $extension Storage extension * @return boolean */ public function hasImage( $path, $extension='core_Attachment' ) { foreach( $this->images as $image ) { foreach( $image as $ext => $url ) { if ( $ext == $extension and $path == $url ) { return TRUE; } } } return FALSE; } /** * Returns a \IPS\DateTime object for the scheduled timestamp * * @return \IPS\DateTime */ public function scheduledDateTime() { $timezone = new \DateTimeZone( \IPS\Settings::i()->promote_tz ); return \IPS\DateTime::ts( $this->scheduled )->setTimezone( $timezone ); } /** * Returns a \IPS\DateTime object for the sent timestamp * * @return \IPS\DateTime */ public function sentDateTime() { $timezone = new \DateTimeZone( \IPS\Settings::i()->promote_tz ); return \IPS\DateTime::ts( $this->sent )->setTimezone( $timezone ); } /** * Shorten URL. * * @param string $service Service key, (such as facebook or twitter) * @returns boolean */ public function serviceFailed( $service ) { if ( ! $this->failed ) { return FALSE; } $returned = $this->returned; return isset( $returned[ $service ] ) ? FALSE : TRUE; } /** * Returns text sent to a named service * * @param string $service Service key (twitter, facebook) * @param boolean $forDisplay Is this for display in output? * @return string|NULL */ public function getText( $service, $forDisplay=false ) { if ( in_array( $service, $this->share_to ) ) { $text = $this->text; return isset( $text[ $service ] ) ? ( $forDisplay ? nl2br( htmlspecialchars( $text[ $service ], ENT_QUOTES | \IPS\HTMLENTITIES, 'UTF-8', FALSE ) ) : $text[ $service ] ) : NULL; } return NULL; } /** * Sets text for a named service * * @param string $service Service key (twitter, facebook) * @param boolean $text Text to save * @return string|NULL */ public function setText( $service, $text ) { $allText = $this->text; $allText[ $service ] = $text; $this->text = $allText; } /** * Return the published URL for this post * * @param string $service Service key, (such as facebook or twitter) * @returns string|NULL */ public function getPublishedUrl( $service ) { $returned = $this->returned; if ( isset( $returned[ $service ] ) ) { try { if ( $url = static::getPromoter( $service )->getUrl( $returned[ $service ] ) ) { return $url; } return $this->object()->url(); } catch ( \InvalidArgumentException $e ) { return NULL; } } } /** * Attempt to get all responses for this ID * * @param string $service Service to return (twitter, facebook, etc) * @return array|NULL array( response_id => data ) */ public function responses( $service ) { $responses = array(); try { foreach( \IPS\Db::i()->select( '*', 'core_social_promote_content', array( 'response_promote_id=? and response_promote_key=?', $this->id, $service ) ) as $row ) { $responses[ $row['response_id'] ] = json_decode( $row['response_json'], TRUE ); } } catch( \Exception $e ) { return NULL; } return count( $responses ) ? $responses : NULL; } /** * Fetch successful sent history for this promoted item * * @return array|NULL array( timestamp => array( service => response_time, ... ) */ public function history() { if ( ! isset( $this->history[ $this->id ] ) ) { $this->history[ $this->id ] = array(); foreach( \IPS\Db::i()->select( '*', 'core_social_promote_content', array( 'response_promote_id=? and response_failed=0', $this->id ) ) as $row ) { $this->history[ $this->id ][ $row['response_sent_date'] ][ $row['response_promote_key'] ] = $row['response_date']; } } return count( $this->history[ $this->id ] ) ? $this->history[ $this->id ] : NULL; } /** * [ActiveRecord] Delete Record * * @return void */ public function delete() { try { \IPS\Db::i()->delete( 'core_social_promote_content', array( 'response_promote_id=?', $this->id ) ); } catch( \Exception $e ) { } return parent::delete(); } /** * Promote stream of internally promoted items * * @param array $services Array of services by key (internal, facebook, etc) * @param int|array $limit Number of items to fetch * @param string $sortField Sort by field * @param string $sortDirection Sort by direction (asc, desc) * @return array */ public static function internalStream( $limit=10, $sortField='promote_sent', $sortDirection='desc') { $items = array(); foreach( \IPS\Db::i()->select( '*', 'core_social_promote', array( 'promote_sent > 0 and promote_internal=1' ), $sortField . ' ' . $sortDirection, $limit ) as $row ) { $items[ $row['promote_id'] ] = static::constructFromData( $row ); } return $items; } /** * Can a member promote anything? * * @param NULL|\IPS\Member $member Member object or NULL for current member * @return bool */ public static function canPromote( $member=NULL ) { $member = $member ? $member : \IPS\Member::loggedIn(); /* Got any services enabled? */ if ( static::promoters() === NULL ) { return FALSE; } if ( \IPS\Settings::i()->promote_members ) { if ( \IPS\Settings::i()->promote_members and in_array( $member->member_id , explode( "\n", \IPS\Settings::i()->promote_members ) ) ) { return TRUE; } } if ( $member->group['gbw_promote'] ) { return TRUE; } return FALSE; } /** * Can View wrapper for items and nodes * * @param object $object Object (node or content item) * @param NULL|\IPS\Member $member Member object or NULL for current member * @return boolean */ public static function objectCanView( $object, $member=NULL ) { $member ?: \IPS\Member::loggedIn(); if ( $object instanceof \IPS\Content ) { return $object->canView( $member ); } else if ( $object instanceof \IPS\Node\Model ) { return $object->can( 'view', $member ); } throw new \OutofRangeException('object_not_valid'); } /** * Return a list of groups that cannot see this item * * @param object $object Object (node or content item) * @return NULL|Array */ public static function objectCannotViewGroups( $object ) { $groups = array(); foreach( \IPS\Member\Group::groups() as $group ) { if ( $object instanceof \IPS\Content\Comment ) { if ( ! $object->item()->can( 'view', $group ) ) { $groups[] = $group->name; } } else { if ( ! $object->can( 'view', $group ) ) { $groups[] = $group->name; } } } return count( $groups ) ? $groups : NULL; } /** * Get title wrapper for items and nodes * * @param object $object Object (node or content item) * @return string */ public static function objectTitle( $object ) { if ( $object instanceof \IPS\Content\Item ) { return $object->mapped('title'); } else if ( $object instanceof \IPS\Content\Comment ) { try { return \IPS\Member::loggedIn()->language()->addToStack( 'promote_thing_in_thing', NULL, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->addToStack( $object::$title ), $object->item()->mapped('title') ) ) ); } catch( \Exception $e ) { return $object->item()->mapped('title'); } } else if ( $object instanceof \IPS\Node\Model ) { return $object->_title; } throw new \OutofRangeException('object_not_valid'); } /** * Get content wrapper for items and nodes * * @param object $object Object (node or content item) * @return string */ public static function objectContent( $object ) { $result = NULL; if ( $object instanceof \IPS\Content\Item ) { if ( isset( $object::$databaseColumnMap['content'] ) ) { $result = $object->truncated(); } else if ( $object::$firstCommentRequired ) { $firstComment = $object->comments( 1, NULL, 'date', 'asc' ); $result = $firstComment->truncated(); } } else if ( $object instanceof \IPS\Content\Comment ) { $result = $object->truncated(); } else if ( $object instanceof \IPS\Node\Model ) { $result = $object->description; } /* If result was not set, throw exception now */ if( $result === NULL ) { throw new \OutofRangeException('object_not_valid'); } /* If we treat enter key as newline instead of paragraph, we need to clean up a bit further */ if( !\IPS\Settings::i()->editor_paragraph_padding ) { $result = str_replace( "\n", '', $result ); } /* Clean up excess newlines */ $result = trim( preg_replace( "#(
){1,}#", "\n", preg_replace( '#(
)\s+#', "\n", $result ) ) ); /* If this is a node, strip HTML tags for security reasons */ if ( $object instanceof \IPS\Node\Model ) { $result = strip_tags( $result ); } return $result; } /** * Load promote row for this class and id */ protected static $classAndIdLookup = array(); /** * Construct ActiveRecord from database row * * @param array $data Row from database table * @param bool $updateMultitonStoreIfExists Replace current object in multiton store if it already exists there? * @return static */ public static function constructFromData( $data, $updateMultitonStoreIfExists = TRUE ) { $object = parent::constructFromData( $data, $updateMultitonStoreIfExists ); static::$classAndIdLookup[ $object->class ][ $object->class_id ] = $object->id; return $object; } /** * Load promote row for this class and id * * @param string $class Class name * @param integer $id Item ID * @param boolean $sent Only look for sent items * @param boolean $futureScheduled Only look for future scheduled items * @return \IPS\core\Promote item */ public static function loadByClassAndId( $class, $id, $sent=FALSE, $futureScheduled=FALSE ) { if ( !isset( static::$classAndIdLookup[ $class ][ $id ] ) ) { try { $object = static::constructFromData( \IPS\Db::i()->select( '*', 'core_social_promote', array( 'promote_class=? and promote_class_id=?', $class, $id ) )->first() ); } catch( \UnderflowException $e ) { static::$classAndIdLookup[ $class ][ $id ] = 0; return; } } else { if ( static::$classAndIdLookup[ $class ][ $id ] ) { $object = static::load( static::$classAndIdLookup[ $class ][ $id ] ); } else { return; } } if ( $futureScheduled and $object->scheduled < time() ) { return; } if ( $sent and $object->sent > time() ) { return; } return $object; } /** * Shorten URL. * We only have bit.ly as a shortener at the moment. * * @param string $longUrl The original URL * @returns string NULL if no shortnener available or it fails * @throws RuntimeException if shortener fails * @throws UnderflowException if no shortener available */ public static function shortenUrl( $longUrl ) { if ( ! \IPS\Settings::i()->bitly_enabled or ! \IPS\Settings::i()->bitly_token ) { throw new \UnderflowException; } /* Have a bash at it like */ try { $response = \IPS\Http\Url::external( "https://api-ssl.bitly.com/v3/shorten" )->setQueryString( array( 'access_token' => \IPS\Settings::i()->bitly_token, 'longUrl' => $longUrl ) )->request()->get()->decodeJson(); if ( $response['status_code'] !== 200 ) { throw new \RuntimeException; } return $response['data']['url']; } catch ( \IPS\Http\Request\Exception $e ) { throw new \RuntimeException; } } /** * Get the next auto schedule timestamp * * @return null|DateTime object */ public static function getNextAutoSchedule() { if ( ! \IPS\Settings::i()->promote_scheduled ) { return NULL; } $latest = \IPS\Db::i()->select( 'MAX(promote_scheduled)', 'core_social_promote', array( 'promote_schedule_auto=1 and promote_sent=0' ) )->first(); $timezone = new \DateTimeZone( \IPS\Settings::i()->promote_tz ); $current = \IPS\DateTime::create()->setTimezone( $timezone ); $times = explode( ',', \IPS\Settings::i()->promote_scheduled ); $time = NULL; if ( $latest ) { $current = \IPS\DateTime::ts( $latest )->setTimezone( $timezone ); } /* Fetch the next scheduled time */ $test = $current; foreach( $times as $entry ) { list( $h, $m ) = explode( ':', $enN $test = \IPS\DateTime::create()->setTimezone( $timezone )->setTime( $h, $m ); if ( $current->getTimeStamp() < $test->getTimeStamp() ) { $time = $test; break; } } /* Still here? Then pick the earliest time for the next day */ if ( $time === NULL ) { $firstTime = array_shift( $times ); list( $h, $m ) = explode( ':', $firstTime ); $time = $current->add( new \DateInterval( 'P1D' ) )->setTime( $h, $m ); } return $time; } /** * Return a single promote object * * @throws UnderflowException * @return \IPS\Login */ public static function getPromoter( $key ) { /* Try and get this from the datastore first */ $promoters = static::promoters(); if ( $promoters !== NULL ) { foreach( $promoters as $promoterKey => $object ) { if ( mb_strtolower( $promoterKey ) == mb_strtolower( $key ) ) { return $object; } } } /* Fall back */ return \IPS\Content\Promote\PromoteAbstract::constructFromData( \IPS\Db::i()->select( '*', 'core_social_promote_sharers', array( 'sharer_key=?', $key ) )->first() ); } /** * Get Promoter objects * * @return array */ public static function promoters() { /* Fetch the appropriate promoters */ if ( static::$promoters === NULL ) { if ( isset( \IPS\Data\Store::i()->promoters ) ) { $rows = \IPS\Data\Store::i()->promoters; } else { $rows = iterator_to_array( \IPS\Db::i()->select( '*', 'core_social_promote_sharers', 'sharer_enabled=1' ) ); \IPS\Data\Store::i()->promoters = $rows; } foreach ( $rows as $row ) { try { static::$promoters[ $row['sharer_key'] ] = \IPS\Content\Promote\PromoteAbstract::constructFromData( $row ); } catch ( \RuntimeException $e ) { } } } return static::$promoters; } /** * Return the promotable services this user has access to * * @return NULL|array of promote classes */ public static function promoteServices() { $services = array(); $promoters = static::promoters(); if ( $promoters === NULL ) { return NULL; } foreach( $promoters as $key => $object ) { if ( $object->setMember( \IPS\Member::loggedIn() )->canPromote() ) { $services[] = $object; } } return count( $services ) ? $services : NULL; } /** * Process any queued items * * @return void */ public static function processQueue() { $processed = 0; foreach( \IPS\Db::i()->select( '*', 'core_social_promote', array( 'promote_sent=0 and promote_failed < 4 and ( promote_scheduled < ' . time() . ' and promote_scheduled > 0 )' ), 'promote_scheduled asc', array( 0, 5 ) ) as $row ) { $processed++; $promote = static::constructFromData( $row ); $promote->send(); } if ( ! $processed ) { /* Disable the task for now, but only if there is nothing to send later */ try { $future = \IPS\Db::i()->select( 'COUNT(*)', 'core_social_promote', array( "promote_sent=?", 0 ) )->first(); } catch( \Exception $e ) { $future = 0; } if ( !$future ) { \IPS\Db::i()->update( 'core_tasks', array( 'enabled' => 0 ), array( '`key`=?', 'promote' ) ); } } } /** * Reschedule queued items * * @return void */ public static function rescheduleQueue() { /* Reset times */ if ( \IPS\Settings::i()->promote_scheduled ) { \IPS\Db::i()->update( 'core_social_promote', array( 'promote_scheduled' => 0 ), array( 'promote_schedule_auto=1 and promote_sent=0' ) ); foreach( \IPS\Db::i()->select( '*', 'core_social_promote', array( 'promote_scheduled=0 and promote_schedule_auto=1 and promote_sent=0' ), 'promote_added asc' ) as $row ) { \IPS\Db::i()->update( 'core_social_promote', array( 'promote_scheduled' => static::getNextAutoSchedule()->getTimeStamp() ), array( 'promote_id=?', $row['promote_id'] ) ); } } } } }ks8gD37z;ykcgsfg@Pl+qϩ~v R,'cf&%ht7F8u4yCC\{XuXJ1`Q~Sɓ?e3":ak |7y^tH BGn6E{:JE.4 ٛƛh+c kE^ad<&4ZBkXt)Ym @~5aae;>` xyWbaµfKԫG ߡ`#~r@m&JGC70aqr64F%>A)Q'v j3?GF@\ &&KD 1 ǵ27 h4x? J sP^a% @!fUP`Hֵ ,BO(|rԻ~[m>Ajԯ/8Oݨ^]_n0c;Me6? ؝ zumP׌3]iz-8]q*@sܵy*`" T%1 TA7ZNj)ߢ~CxJ$3D,UVovVju֦^_Rev[ݢ^93nfca22 gRGAXft63POUyO밾ntjcv}[c`hK9@zGtt6`z1mcihPhouۚmvϱ{{!rlYNq.f&34}uufSM-2AtrZV=ٜb<887mo[mk dk0;965{`1@mѻc'kZ=Cr`5IC{fn~.V9;j,wI⊝e36@ѻm Rz삚i~˲[f $f-B3f>31о =zq];fWے<cUn>}=IJh} ?.%>HJ7f4{ս>}T{?/;jx.9!rFvkЪ?6]F 6 R݀*g`LqiZ7jF'^;Vos ˦0@K!؋{9m{V`X*,:DYfIzNS B'\LqBpSL;'Ϟ;:HނA}СSa*Gv14+ %qZ)Eɋ =vgW/@]QmƢNXĮ#*3i"/7QMz?TGο%QòԦ5Lkrl0V3Ԛ2iv#jzBiq^h +Rxj7Ч*SglfI$ E6 D ) o[xuAOsA<\3}Ѷ[gjXzs=(S+V$}IDcȎucIuޱ-M3ie Фa Y5,x͎0j}40I0*ձf7 [`S5׏/Fmь^AY 0lgvO_1Iy `z(X|N[k5 gѮaPy0 CIwKwkkgN5iQubxqd4 NWj}cҺaZέWL*z3:GjzN2eu4 L15JyQ,=RE_`9ǽN?۫VfssT O6BG,RXF!1ohCss=@ăc\Nl3I /Z fhr)̡_MFsJŦg~fvq?2>;M4e3 qN'nK =@u"nH0_+ST_}Mhq:r򊽡l[煡|-uK?]"YJgKpY5B};oIRU!,ag˺`dJ!?#qg_PρeyEL@ow95v8sFa5&@Ƌ!']PB'4 ljĄvF<@{IA1 ]׌[rv=[O ?NEBe7~ Ģ>4Hm.A-z.kʅ+Y -@Str)t0a P(.yݟT=ǀVlݒ0=7nWaSiLCo+O ^9]ۈvs> n50I療xОPy03Ȣ9]㖏GVߦbZ&*i6+وgb. q%ϙY`_Ԋdɴ]2 b#(%NZ}syp`]/ kehOHɰ[ dJ_q.%qÎNJjHlTY~FU]\2R1)pzFZ훁@H΀@w|h|TQQ6>rVyƬHmN Ü`$ʙ,g%2Zӕ+:?_pB A>y<sz"JxE ' !WOi@%,OМ&{NW%o=,-˿d?'wQn`8zr:~2pΑ^oic9[:zuV (ZTP2E1+K :}/D 9o8ڪ BzoTF6=VY]12cCRGEq^ @sa/wE蓎 \>0Kqf0iL;WxGK=˽$^co)P7L?` Q7wƣ~4#vEdd2Y={,a^ʣ1?.ac@ < XܓM" 5'OnX[`g۞| ճe 8 F d$0y,[0*hK3h 40A]#j)`ǞC 9NBjԼ {U3} ګ\_lP"x/Wɽ}DI^2zѺٔ4 "oȈxǽ'*U k{F V[@Ipm6Uzǎ"w@4*c(.dG]>`OyV59K7Bf9\pEm[[yt6e|5r%Ʉv|QBtSW`qp*/.J|qa0cbD2aQ* pO;8k"qOC {9}ֵ}ݥ uÝU)Д<}H#eBsw"V=jXaE]!kz~!5v ȴC{y(gb\B:ꐅ\ya Q^uD;~$ZۈNy}ILr)c)F%ȓzʫkD46RFbZ^^b'ՠqU鵴b53EUrF!RrS* µt{n-jy}A.=D^UNj䰴 vVj~ӐmI]'a1Aͯun5{;umOK!yMu@Р(ZR_*Y wá BֈBBPS)wCpq^3 Үs4Nv&|ꀆ_πԳ@:L$u. o{iVk~-5a 3:? ôk6BLVߒac=gXE(G8x\F0Ȑ%Rg$H'5oFXTqc|( Di{ *o+Ywh8'[~ 9t{+u3$yzd --B~Cxm1CsEC :t:nܟ_PKZf;H 'KjtEuNhoI@bYKOc[ 4"*>pſze'ȿ-yP z~-C3Nm},g`8C*XR#LW!aӻң(w2[j]mH^Z {ee$H]o_&%l+\lPioV؎q ?" D B)3Wř/J]|˸lC:^V7~#zm]JE}rp|)ʱ~xmQ1߫jf(DkDYMOIXs"8f#O1RYcE:RӰR1<ѭfO'WHy40V9VP;I!w,XPU JxaaŘpKt@9jeauV۟PA=ldb=\D0XFѣ>v} $|O1, Xʿ1zgY-aEW% Jl3I8ɉ9?J`乣 ,DOwQnTG+2rx&+0;l.ح$8P,LBD(ٺ_X$*tK^6RJNy] 3)s6~OWK(jn*3"Sp-];Iav/1viQ1!Ĥ{Z J;~=1~"P)haԝ~Yl0ꨶ$1J8I,aHFz+dˊR c 0&MT 5o߬:_8>_l| Sъ.?X.Oÿ"Ea%}8Dd*PmgS; b~!O@N,-muǃ$X*HA"S0FBalb|v/smssi U61Glha#I~0faTv_$VvgM1DU`=vޖ`ݨo!WRU" / `ٞ2[xJ/wKjmOE$Q ӿ2fMϋ67I:sրdfQ]RZ^&`Aqb 6ʚU, u0˲1cļCeBu~> %Ϧ\礂$ܩ Ƕ~P>휡 <,Xoi.ou)6 tQi|$v䝍+lZ֫SrBMêLm^gR>J]K9E^EX"_AsB<đ|̄W^ʟ(c[}*ëCH)_R> 4 LOr, R >@?'5ʒbpEf@FfjBek[fxE;gDF.-pmAKB:bc*q^gu8r`ɑ}N} su/qϊ攓Vq"syTśFQ>_X,^E9/ʈ\j^*@+*[::;L@.$)P[[zUsx pb͢]=e-Z@_tx*}ׂTMw!ؒ u0U[}Ƞ|K#r)SQ‡[[Q:c+Ai0_aL(Aƶ3I6ijZUXG:yħzC;gH/owqBJ`Tc0'%|:詢BNZKڠUJv'#[ޜw4AmPӵZU[nftjzkAկpGuZWkAvm)eP7cl~'G!4(XM(d3}&Ჩc( a<tʔϽ W߅BRrQ\=m^sgy.ln)kuE&>z"ӷ\wQbZS]9:OӪ[tISJ:z'd \C (Lr. E&,UΑěT8V!xbָ+SԌI/G"=#$/iDd6+n _C:;rLBUJea/r̟L0vUtU;P!l^&֣P0'>Θ}jc/g>1c|Pb1:H'kT5*fRBUZJ`zI6yS*n>rNiGJT>#k& RACO/B:OͼdRsipVR0Wr(Fr @cJ }˓1yRi$AvL2e`:HD(o&v~8%Cϭl>-&%F]5S.EH+*,+^9BG2CwtC3BG1S N7{}IIOϕU\7uvaW8r)bk˽q B K*C!%\+>k混&UNjKf1< 0MP,O;ɷ ";,ӎ"\e@Y\ S ]2+ z2j<7i:ǘ}2L#~X>1ĘTO1 r&rL&0f_#}KdkHp@EX#a! B㛿0%5*EE q,^J>*-qpɓ)]l2 OYrf#.XI s ("o(jQEĦW%5+ʌ|'Pb  غ冖=W>'sDyqgaH{KA!.f4?)cLQO ,VWrJ'ɾvYq 噼ώsD۰W9"'d&ʡ J@H®fg|>'/؁44tO?S.Dy(^s7T0:,!3D=)|<-(Cxvd\Jhkt@Q4"/29oMtHC%\Gq[]d`6P'5: 6_Hc1Wْ0Xl'l͝*U^@wʕaųP3DȻHvJ#O׾8*uFTZKCvre_bdJ_eER_|R1:WaX-$錫 {"&,3cqVبv{1[ BRbko@9o#~IE{t~,F6W :LKZ ['eޔÅ 虐7 a%Q`Jv4y@~+Kh(ZeR~hc* QsfMK= S jy&@ _ C9y*OPuVb4b\KN ZUֺ8b%FzvQoߪZo߮fiߩ: 2kY)9l啣˕!炃#ͿO1w86kJ#; MZ@%q5oU.b%X<ŸR)YFlz70+@-Ws$ɹYO?ǗxWHj(;[#=ȟ5g`Rnh)i,`.^Mz83CG"u^n`&$wQ0LF$WLGJDG`6P(bՇ2fBqȩ(MO`0\=0M}i%LdD-I2Nz+F^̕(ܒuqib1XxpT\J}< J9L@U%[H5e%HGҍGm7~į`EQ>EƂ*GnGPzeط6ڂuV,Kܘg )m),o/-n-՜wWM[ר*cd"{w˯ށ8jTex9bpMTIR5a rJ$ aUZp,os alj bhe<9etth: ́>0;֠v,i-PVYwwѩߔTYFx_O(Xݱf4EI.X^ ݎ6{AӬ6`!,e"񲡡+`"]~O(ց%xJ='Nt9g0Z Ts mǴ}Pfm&v: M|bRG H#g/h8P5Ik%\Q9'x Ȫ;axPQi^\\4H5MбTL#i=!.c:H<8XlVow|{f>IW/ @"jQv$nyP"p>:A_:AИ$NZֿQ>⤜ i9 M.SeM2qZCJ6gUr hzDHhe$Ʊ4%F*&<Ȉha TðF?ݒݦoQlq-|ì?˸E3Y@1~3Y%4B@#Z1]1" 7O?"w"q9.mͣ2yt{8Tq 3Fes,GR9Y#t19ir%W%:,کG8R3ˆF&c4b%aƀ:)8Y#x.+G {"&)=g0,|vAČ-!=M5^a c"qZ> q¼%cL3h£8B\Z=ţL g[념S҉:Xi|)&bt|AckHr.&h1HytLEQ9S5@՛@ q`4,(11\v9emz)wˬee 2K)ő"e=|)Rh ӈ\8ug|d2WT~ x?.axz-- 7xŤ[5*2;%[y4#l|Xз|VY33q~.ߕl6m(fzv.8̝7#RqjFO#~JA^`q4~0آw@8dQc(钼 LqdF{C*(a_EQɆaN7(g\lqߛ  #bFDyE,l`"65`!? ?w3ߚUOvUʍPͥQ,0(B{KO+&V5>a_wEJ(j|͊+wB9Gr/r^q?&6ke;*˯=lX/TY&:W-Ă W%APcQ_]80xv'OJvL%e`z1б1R\yP͇߬/y%{o !ܿ rj]%q; DK^z^6pnb})orـ?ydﶚ$SQOm+e3zۺX|Jˡly},eY 1R a="!:ܥϒd9; 0&<]XDDW ^$>`euXnq< 5* &MMOfA28U [aJo$ӕ)?`/ +-z y/bJ"$zɽWpu{/a5UӔ(1LԵA[CKmr0k&yTA0SB4qH5m\cb.mq.BRW<ɩ8Cÿo%6r))<މzQ+:dc2b7*g}EȹxՈJrJ\ޙZD=y}97֏-zOWǯ:iëW&?y'}3띛f(?y٣SǭyhE&)027 zgEV&[g; 7-1Yפ^T_MeǺw+U}~/vBNyo{=o{(o|?b?NmFJ'J+-Ʀz\p}>hH$}l)g[$K}$o{],]@mvnf,G_ )J7]s%m+Pe_7wD)Vz΢XxZ X/BZK"ERTxM|uWIb_;OYž3OVgFq8FdV^ L^*لBeӠ1fz>9'jɓA}G~`v#۹KwUQJDUAmPӵZU[nftjzkAկ׺Zӫx6}~*,i{yQ0T Z}VZYH6 Kҋn*# '&9Ŧtvۙt %9>Xªbv9:ι5*I.:ЮԮg̓ C'kQ6i