芝麻web文件管理V1.00
编辑当前文件:/home/mgatv524/public_html/avenida/views/Storage.zip
PK qYuZq q ApiAuthCodeStorage.phpnu [ . */ namespace Xibo\Storage; use League\OAuth2\Server\Entity\AuthCodeEntity; use League\OAuth2\Server\Entity\ScopeEntity; use League\OAuth2\Server\Storage\AbstractStorage; use League\OAuth2\Server\Storage\AuthCodeInterface; class ApiAuthCodeStorage extends AbstractStorage implements AuthCodeInterface { /** * @var StorageServiceInterface */ private $store; /** * ApiAccessTokenStorage constructor. * @param StorageServiceInterface $store */ public function __construct($store) { if (!$store instanceof StorageServiceInterface) throw new \RuntimeException('Invalid $store'); $this->store = $store; } /** * Get Store * @return StorageServiceInterface */ protected function getStore() { return $this->store; } /** * {@inheritdoc} */ public function get($code) { $result = $this->getStore()->select('SELECT * FROM oauth_auth_codes WHERE auth_code = :auth_code AND expire_time >= :expire_time', array('auth_code' => $code, 'expire_time' => time())); if (count($result) === 1) { $token = new AuthCodeEntity($this->server); $token->setId($result[0]['auth_code']); $token->setRedirectUri($result[0]['client_redirect_uri']); $token->setExpireTime($result[0]['expire_time']); return $token; } return; } public function create($token, $expireTime, $sessionId, $redirectUri) { $this->getStore()->insert(' INSERT INTO oauth_auth_codes (auth_code, client_redirect_uri, session_id, expire_time) VALUES (:auth_code, :client_redirect_uri, :session_id, :expire_time) ', [ 'auth_code' => $token, 'client_redirect_uri' => $redirectUri, 'session_id' => $sessionId, 'expire_time' => $expireTime, ]); } /** * {@inheritdoc} */ public function getScopes(AuthCodeEntity $token) { $result = $this->getStore()->select(' SELECT oauth_scopes.id, oauth_scopes.description FROM oauth_auth_code_scopes INNER JOIN oauth_scopes ON oauth_auth_code_scopes.scope = oauth_scopes.id WHERE auth_code = :auth_code ', [ 'auth_code' => $token->getId() ]); $response = []; if (count($result) > 0) { foreach ($result as $row) { $scope = (new ScopeEntity($this->server))->hydrate([ 'id' => $row['id'], 'description' => $row['description'], ]); $response[] = $scope; } } return $response; } /** * {@inheritdoc} */ public function associateScope(AuthCodeEntity $token, ScopeEntity $scope) { $this->getStore()->insert('INSERT INTO oauth_auth_code_scopes (auth_code, scope) VALUES (:auth_code, :scope)', [ 'auth_code' => $token->getId(), 'scope' => $scope->getId(), ]); } /** * {@inheritdoc} */ public function delete(AuthCodeEntity $token) { $this->getStore()->update('DELETE FROM oauth_auth_codes WHERE auth_code = :auth_code', [ 'auth_code' => $token->getId() ]); } }PK qYoP8 8 TimeSeriesMongoDbResults.phpnu [ . */ namespace Xibo\Storage; /** * Class TimeSeriesMongoDbResults * @package Xibo\Storage */ class TimeSeriesMongoDbResults implements TimeSeriesResultsInterface { /** * Statement * @var \MongoDB\Driver\Cursor */ private $object; /** * Total number of stats */ public $totalCount; /** * Iterator * @var \IteratorIterator */ private $iterator; /** * @inheritdoc */ public function __construct($cursor = null) { $this->object = $cursor; } /** @inheritdoc */ public function getArray() { $result = $this->object->toArray(); $rows = []; foreach ($result as $row) { $entry = []; $entry['id'] = $row['id']; $entry['type'] = $row['type']; $entry['start'] = $row['start']->toDateTime()->format('U'); $entry['end'] = $row['end']->toDateTime()->format('U'); $entry['display'] = isset($row['display']) ? $row['display']: 'No display'; $entry['layout'] = isset($row['layout']) ? $row['layout']: 'No layout'; $entry['media'] = isset($row['media']) ? $row['media'] : 'No media' ; $entry['tag'] = $row['tag']; $entry['duration'] = $row['duration']; $entry['count'] = $row['count']; $entry['displayId'] = isset($row['displayId']) ? $row['displayId']: 0; $entry['layoutId'] = isset($row['layoutId']) ? $row['layoutId']: 0; $entry['campaignId'] = isset($row['campaignId']) ? $row['campaignId']: 0; $entry['widgetId'] = isset($row['widgetId']) ? $row['widgetId']: 0; $entry['mediaId'] = isset($row['mediaId']) ? $row['mediaId']: 0; $entry['statDate'] = isset($row['statDate']) ? $row['statDate']->toDateTime()->format('U') : null; $entry['engagements'] = isset($row['engagements']) ? $row['engagements'] : []; $entry['tagFilter'] = isset($row['tagFilter']) ? $row['tagFilter'] : [ 'dg' => [], 'layout' => [], 'media' => [] ]; $rows[] = $entry; } return ['statData'=> $rows]; } public function getIterator() { if ($this->iterator == null) { $this->iterator = new \IteratorIterator($this->object); $this->iterator->rewind(); } return $this->iterator; } /** @inheritdoc */ public function getNextRow() { $this->getIterator(); if ($this->iterator->valid()) { $document = $this->iterator->current(); $this->iterator->next(); return (array) $document; } return false; } /** @inheritdoc */ public function getTotalCount() { return $this->totalCount; } }PK qY7 7 TimeSeriesResultsInterface.phpnu [ . */ namespace Xibo\Storage; /** * Interface TimeSeriesResultsInterface * @package Xibo\Service */ interface TimeSeriesResultsInterface { /** * Time series results constructor */ public function __construct($object = null); /** * Get statistics array * @return array[array statData] */ public function getArray(); /** * Get next row * @return array|false */ public function getNextRow(); /** * Get total number of stats * @return integer */ public function getTotalCount(); }PK qYz9h h MongoDbTimeSeriesStore.phpnu [ . */ namespace Xibo\Storage; use MongoDB\BSON\ObjectId; use MongoDB\BSON\UTCDateTime; use MongoDB\Client; use Xibo\Exception\GeneralException; use Xibo\Exception\InvalidArgumentException; use Xibo\Exception\NotFoundException; use Xibo\Exception\XiboException; use Xibo\Factory\CampaignFactory; use Xibo\Factory\DisplayFactory; use Xibo\Factory\DisplayGroupFactory; use Xibo\Factory\LayoutFactory; use Xibo\Factory\MediaFactory; use Xibo\Factory\WidgetFactory; use Xibo\Service\DateServiceInterface; use Xibo\Service\LogServiceInterface; /** * Class MongoDbTimeSeriesStore * @package Xibo\Storage */ class MongoDbTimeSeriesStore implements TimeSeriesStoreInterface { /** @var LogServiceInterface */ private $log; /** @var DateServiceInterface */ private $dateService; /** @var array */ private $config; /** @var \MongoDB\Client */ private $client; private $table = 'stat'; private $periodTable = 'period'; // Keep all stats in this array after processing private $stats = []; private $mediaItems = []; private $widgets = []; private $layouts = []; private $displayGroups = []; private $layoutsNotFound = []; private $mediaItemsNotFound = []; /** @var MediaFactory */ protected $mediaFactory; /** @var WidgetFactory */ protected $widgetFactory; /** @var LayoutFactory */ protected $layoutFactory; /** @var DisplayFactory */ protected $displayFactory; /** @var DisplayGroupFactory */ protected $displayGroupFactory; /** @var CampaignFactory */ protected $campaignFactory; /** * @inheritdoc */ public function __construct($config = null) { $this->config = $config; } /** * @inheritdoc */ public function setDependencies($log, $date, $layoutFactory = null, $campaignFactory = null, $mediaFactory = null, $widgetFactory = null, $displayFactory = null, $displayGroupFactory = null) { $this->log = $log; $this->dateService = $date; $this->mediaFactory = $mediaFactory; $this->widgetFactory = $widgetFactory; $this->layoutFactory = $layoutFactory; $this->displayFactory = $displayFactory; $this->displayGroupFactory = $displayGroupFactory; $this->campaignFactory = $campaignFactory; try { $uri = isset($this->config['uri']) ? $this->config['uri'] : 'mongodb://' . $this->config['host'] . ':' . $this->config['port']; $this->client = new Client($uri, [ 'username' => $this->config['username'], 'password' => $this->config['password'] ], (array_key_exists('driverOptions', $this->config) ? $this->config['driverOptions'] : [])); } catch (\MongoDB\Exception\RuntimeException $e) { $this->log->critical($e->getMessage()); } return $this; } /** @inheritdoc */ public function addStat($statData) { // We need to transform string date to UTC date $statData['statDate'] = new UTCDateTime($statData['statDate']->format('U') * 1000); // In mongo collection we save fromDt/toDt as start/end // and tag as eventName // so we unset fromDt/toDt/tag from individual stats array $statData['start'] = new UTCDateTime($statData['fromDt']->format('U') * 1000); $statData['end'] = new UTCDateTime($statData['toDt']->format('U') * 1000); $statData['eventName'] = $statData['tag']; unset($statData['fromDt']); unset($statData['toDt']); unset($statData['tag']); // Make an empty array to collect layout/media/display tags into $tagFilter = []; // Media name $mediaName = null; if (!empty($statData['mediaId'])) { if (array_key_exists($statData['mediaId'], $this->mediaItems)) { $media = $this->mediaItems[$statData['mediaId']]; } else { try { // Media exists in not found cache if (in_array($statData['mediaId'], $this->mediaItemsNotFound)) { return; } $media = $this->mediaFactory->getById($statData['mediaId']); // Cache media $this->mediaItems[$statData['mediaId']] = $media; } catch (NotFoundException $error) { // Cache Media not found, ignore and log the stat if (!in_array($statData['mediaId'], $this->mediaItemsNotFound)) { $this->mediaItemsNotFound[] = $statData['mediaId']; $this->log->error('Media not found. Media Id: '. $statData['mediaId']); } return; } } $mediaName = $media->name; //dont remove used later $mediaTags = $media->tags; $statData['mediaName'] = $mediaName; if (isset($mediaTags)) { $arrayOfTags = explode(',', $mediaTags); for ($i=0; $i
widgets)) { $widget = $this->widgets[$statData['widgetId']]; } else { // We are already doing getWidgetForStat is XMDS, // checking widgetId not found does not require // We should always be able to get the widget try { $widget = $this->widgetFactory->getById($statData['widgetId']); // Cache widget $this->widgets[$statData['widgetId']] = $widget; } catch (\Exception $error) { // Widget not found, ignore and log the stat $this->log->error('Widget not found. Widget Id: '. $statData['widgetId']); return; } } if($widget != null) { $widget->load(); $widgetName = isset($mediaName) ? $mediaName : $widget->getOptionValue('name', $widget->type); // SET widgetName $statData['widgetName'] = $widgetName; } } // Layout data $layoutName = null; $layoutTags = null; // For a type "event" we have layoutid 0 so is campaignId // otherwise we should try and resolve the campaignId $campaignId = 0; if ($statData['type'] != 'event') { if (array_key_exists($statData['layoutId'], $this->layouts)) { $layout = $this->layouts[$statData['layoutId']]; } else { try { // Layout exists in not found cache if (in_array($statData['layoutId'], $this->layoutsNotFound)) { return; } // Get the layout campaignId - this can give us a campaignId of a layoutId which id was replaced when draft to published $layout = $this->layoutFactory->getByLayoutHistory($statData['layoutId']); $this->log->debug('Found layout : '. $statData['layoutId']); // Cache layout $this->layouts[$statData['layoutId']] = $layout; } catch (NotFoundException $error) { // All we can do here is log // we shouldn't ever get in this situation because the campaignId we used above will have // already been looked up in the layouthistory table. // Cache layouts not found if (!in_array($statData['layoutId'], $this->layoutsNotFound)) { $this->layoutsNotFound[] = $statData['layoutId']; $this->log->alert('Error processing statistic into MongoDB. Layout not found. Layout Id: ' . $statData['layoutId']); } return; } catch (XiboException $error) { // Cache layouts not found if (!in_array($statData['layoutId'], $this->layoutsNotFound)) { $this->layoutsNotFound[] = $statData['layoutId']; $this->log->error('Layout not found. Layout Id: '. $statData['layoutId']); } return; } } $campaignId = (int) $layout->campaignId; $layoutName = $layout->layout; $layoutTags = $layout->tags; } // Get layout Campaign ID $statData['campaignId'] = $campaignId; $statData['layoutName'] = $layoutName; // Layout tags if (isset($layoutTags)) { $arrayOfTags = explode(',', $layoutTags); for ($i=0; $i
displayId; unset($statData['display']); // Display name $statData['displayName'] = $display->display; $arrayOfTags = array_filter(explode(',', $display->tags)); $arrayOfTagValues = array_filter(explode(',', $display->tagValues)); for ($i=0; $i
displayGroupId, $this->displayGroups)) { $displayGroup = $this->displayGroups[$display->displayGroupId]; } else { try { $displayGroup = $this->displayGroupFactory->getById($display->displayGroupId); // Cache displaygroup $this->displayGroups[$display->displayGroupId] = $displayGroup; } catch (NotFoundException $notFoundException) { $this->log->error('Display group not found'); return; } } $arrayOfTags = array_filter(explode(',', $displayGroup->tags)); $arrayOfTagValues = array_filter(explode(',', $displayGroup->tagValues)); for ($i=0; $i
stats[] = $statData; } /** @inheritdoc */ public function addStatFinalize() { // Insert statistics if (count($this->stats) > 0) { $collection = $this->client->selectCollection($this->config['database'], $this->table); try { $collection->insertMany($this->stats); } catch (\MongoDB\Exception\RuntimeException $e) { $this->log->error($e->getMessage()); throw new \MongoDB\Exception\RuntimeException($e->getMessage()); } } // Create a period collection if it doesnot exist $collectionPeriod = $this->client->selectCollection($this->config['database'], $this->periodTable); try { $cursor = $collectionPeriod->findOne(['name' => 'period']); if (count($cursor) <= 0 ) { $this->log->error('Period collection does not exist in Mongo DB.'); // Period collection created $collectionPeriod->insertOne(['name' => 'period']); $this->log->debug('Period collection created.'); } } catch (\MongoDB\Exception\RuntimeException $e) { $this->log->error($e->getMessage()); } } /** @inheritdoc */ public function getEarliestDate() { $collection = $this->client->selectCollection($this->config['database'], $this->table); try { $earliestDate = $collection->aggregate([ [ '$group' => [ '_id' => [], 'minDate' => ['$min' => '$statDate'], ] ] ])->toArray(); if(count($earliestDate) > 0) { return [ 'minDate' => $earliestDate[0]['minDate']->toDateTime()->format('U') ]; } } catch (\MongoDB\Exception\RuntimeException $e) { $this->log->error($e->getMessage()); } return []; } /** @inheritdoc */ public function getStats($filterBy = []) { // do we consider that the fromDt and toDt will always be provided? $fromDt = isset($filterBy['fromDt']) ? $filterBy['fromDt'] : null; $toDt = isset($filterBy['toDt']) ? $filterBy['toDt'] : null; $statDate = isset($filterBy['statDate']) ? $filterBy['statDate'] : null; // In the case of user switches from mysql to mongo - laststatId were saved as integer if (isset($filterBy['statId'])) { if (is_numeric($filterBy['statId'])) { throw new InvalidArgumentException(__('Invalid statId provided'), 'statId'); } else { $statId = $filterBy['statId']; } } else { $statId = null; } $type = isset($filterBy['type']) ? $filterBy['type'] : null; $displayIds = isset($filterBy['displayIds']) ? $filterBy['displayIds'] : []; $layoutIds = isset($filterBy['layoutIds']) ? $filterBy['layoutIds'] : []; $mediaIds = isset($filterBy['mediaIds']) ? $filterBy['mediaIds'] : []; $campaignId = isset($filterBy['campaignId']) ? $filterBy['campaignId'] : null; $eventTag = isset($filterBy['eventTag']) ? $filterBy['eventTag'] : null; // Limit $start = isset($filterBy['start']) ? $filterBy['start'] : null; $length = isset($filterBy['length']) ? $filterBy['length'] : null; // Match query $match = []; // fromDt/toDt Filter if (($fromDt != null) && ($toDt != null)) { $fromDt = new UTCDateTime($fromDt->format('U')*1000); $match['$match']['end'] = [ '$gt' => $fromDt]; $toDt = new UTCDateTime($toDt->format('U')*1000); $match['$match']['start'] = [ '$lte' => $toDt]; } else if (($fromDt != null) && ($toDt == null)) { $fromDt = new UTCDateTime($fromDt->format('U')*1000); $match['$match']['start'] = [ '$gte' => $fromDt]; } // statDate Filter // get the next stats from the given date if ($statDate != null) { $statDate = new UTCDateTime($statDate->format('U')*1000); $match['$match']['statDate'] = [ '$gte' => $statDate]; } if (!empty($statId)) { $match['$match']['_id'] = [ '$gt' => new ObjectId($statId)]; } // Displays Filter if (count($displayIds) != 0) { $match['$match']['displayId'] = [ '$in' => $displayIds ]; } // Campaign Filter // --------------- // Use the Layout Factory to get all Layouts linked to the provided CampaignId if ($campaignId != null) { $campaignIds = []; try { $layouts = $this->layoutFactory->getByCampaignId($campaignId, false); if (count($layouts) > 0) { foreach ($layouts as $layout) { $campaignIds[] = $layout->campaignId; } // Add to our match $match['$match']['campaignId'] = ['$in' => $campaignIds]; } } catch (NotFoundException $ignored) {} } // Type Filter if ($type != null) { $match['$match']['type'] = $type; } // Event Tag Filter if ($eventTag != null) { $match['$match']['eventName'] = $eventTag; } // Layout Filter if (count($layoutIds) != 0) { // Get campaignIds for selected layoutIds $campaignIds = []; foreach ($layoutIds as $layoutId) { try { $campaignIds[] = $this->layoutFactory->getCampaignIdFromLayoutHistory($layoutId); } catch (XiboException $ignored) { // TODO: this is quite inefficient and could be reworked to return an empty TimeSeriesResults $campaignIds[] = -1; } } $match['$match']['campaignId'] = [ '$in' => $campaignIds ]; } // Media Filter if (count($mediaIds) != 0) { $match['$match']['mediaId'] = [ '$in' => $mediaIds ]; } $collection = $this->client->selectCollection($this->config['database'], $this->table); $group = [ '$group' => [ '_id'=> null, 'count' => ['$sum' => 1], ] ]; if (count($match) > 0) { $totalQuery = [ $match, $group, ]; } else { $totalQuery = [ $group, ]; } // Get total try { $totalCursor = $collection->aggregate($totalQuery, ['allowDiskUse' => true]); $totalCount = $totalCursor->toArray(); $total = (count($totalCount) > 0) ? $totalCount[0]['count'] : 0; } catch (\MongoDB\Exception\RuntimeException $e) { $this->log->error('Error: Total Count. '. $e->getMessage()); throw new GeneralException(__('Sorry we encountered an error getting Proof of Play data, please consult your administrator')); } catch (\Exception $e) { $this->log->error('Error: Total Count. '. $e->getMessage()); throw new GeneralException(__('Sorry we encountered an error getting Proof of Play data, please consult your administrator')); } try { $project = [ '$project' => [ 'id'=> '$_id', 'type'=> 1, 'start'=> 1, 'end'=> 1, 'layout'=> '$layoutName', 'display'=> '$displayName', 'media'=> '$mediaName', 'tag'=> '$eventName', 'duration'=> '$duration', 'count'=> '$count', 'displayId'=> 1, 'layoutId'=> 1, 'widgetId'=> 1, 'mediaId'=> 1, 'campaignId'=> 1, 'statDate'=> 1, 'engagements'=> 1, 'tagFilter' => 1 ] ]; if (count($match) > 0) { $query = [ $match, $project, ]; } else { $query = [ $project, ]; } // Sort by id (statId) - we must sort before we do pagination as mongo stat has descending order indexing on start/end $query[]['$sort'] = ['id'=> 1]; if ($start !== null && $length !== null) { $query[]['$skip'] = $start; $query[]['$limit'] = $length; } $cursor = $collection->aggregate($query, ['allowDiskUse' => true]); $result = new TimeSeriesMongoDbResults($cursor); // Total $result->totalCount = $total; } catch (\MongoDB\Exception\RuntimeException $e) { $this->log->error('Error: Get total. '. $e->getMessage()); throw new GeneralException(__('Sorry we encountered an error getting Proof of Play data, please consult your administrator')); } catch (\Exception $e) { $this->log->error('Error: Get total. '. $e->getMessage()); throw new GeneralException(__('Sorry we encountered an error getting Proof of Play data, please consult your administrator')); } return $result; } /** @inheritdoc */ public function getExportStatsCount($filterBy = []) { // do we consider that the fromDt and toDt will always be provided? $fromDt = isset($filterBy['fromDt']) ? $filterBy['fromDt'] : null; $toDt = isset($filterBy['toDt']) ? $filterBy['toDt'] : null; $displayIds = isset($filterBy['displayIds']) ? $filterBy['displayIds'] : []; // Match query $match = []; // fromDt/toDt Filter if (($fromDt != null) && ($toDt != null)) { $fromDt = new UTCDateTime($fromDt->format('U')*1000); $match['$match']['end'] = [ '$gt' => $fromDt]; $toDt = new UTCDateTime($toDt->format('U')*1000); $match['$match']['start'] = [ '$lte' => $toDt]; } // Displays Filter if (count($displayIds) != 0) { $match['$match']['displayId'] = [ '$in' => $displayIds ]; } $collection = $this->client->selectCollection($this->config['database'], $this->table); // Get total try { $totalQuery = [ $match, [ '$group' => [ '_id'=> null, 'count' => ['$sum' => 1], ] ], ]; $totalCursor = $collection->aggregate($totalQuery, ['allowDiskUse' => true]); $totalCount = $totalCursor->toArray(); $total = (count($totalCount) > 0) ? $totalCount[0]['count'] : 0; } catch (\MongoDB\Exception\RuntimeException $e) { $this->log->error($e->getMessage()); throw new GeneralException(__('Sorry we encountered an error getting total number of Proof of Play data, please consult your administrator')); } catch (\Exception $e) { $this->log->error($e->getMessage()); throw new GeneralException(__('Sorry we encountered an error getting total number of Proof of Play data, please consult your administrator')); } return $total; } /** @inheritdoc */ public function deleteStats($maxage, $fromDt = null, $options = []) { // Set default options $options = array_merge([ 'maxAttempts' => 10, 'statsDeleteSleep' => 3, 'limit' => 1000, ], $options); // we dont use $options['limit'] anymore. // we delete all the records at once based on filter criteria (no-limit approach) $toDt = new UTCDateTime($maxage->format('U')*1000); $collection = $this->client->selectCollection($this->config['database'], $this->table); $rows = 1; $count = 0; if ($fromDt != null) { $start = new UTCDateTime($fromDt->format('U')*1000); $filter = [ 'start' => ['$lte' => $toDt], 'end' => ['$gt' => $start] ]; } else { $filter = [ 'start' => ['$lte' => $toDt] ]; } try { $deleteResult = $collection->deleteMany( $filter ); $rows = $deleteResult->getDeletedCount(); } catch (\MongoDB\Exception\RuntimeException $e) { $this->log->error($e->getMessage()); } $count += $rows; // Give MongoDB time to recover if ($rows > 0) { $this->log->debug('Stats delete effected ' . $rows . ' rows, sleeping.'); sleep($options['statsDeleteSleep']); } return $count; } /** @inheritdoc */ public function getEngine() { return 'mongodb'; } /** @inheritdoc */ public function executeQuery($options = []) { $this->log->debug('Execute MongoDB query.'); $options = array_merge([ 'allowDiskUse' => true ], $options); // Aggregate command options $aggregateConfig['allowDiskUse'] = $options['allowDiskUse']; if (!empty($options['maxTimeMS'])) { $aggregateConfig['maxTimeMS']= $options['maxTimeMS']; } $collection = $this->client->selectCollection($this->config['database'], $options['collection']); try { $cursor = $collection->aggregate($options['query'], $aggregateConfig); // log query $this->log->debug($cursor); $results = $cursor->toArray(); } catch (\MongoDB\Driver\Exception\RuntimeException $e) { $this->log->error($e->getMessage()); throw new GeneralException($e->getMessage()); } return $results; } }PK qY]݂ ApiSessionStorage.phpnu [ . */ namespace Xibo\Storage; use League\OAuth2\Server\Entity\AccessTokenEntity; use League\OAuth2\Server\Entity\AuthCodeEntity; use League\OAuth2\Server\Entity\ScopeEntity; use League\OAuth2\Server\Entity\SessionEntity; use League\OAuth2\Server\Storage\AbstractStorage; use League\OAuth2\Server\Storage\SessionInterface; class ApiSessionStorage extends AbstractStorage implements SessionInterface { /** * @var StorageServiceInterface */ private $store; /** * ApiAccessTokenStorage constructor. * @param StorageServiceInterface $store */ public function __construct($store) { if (!$store instanceof StorageServiceInterface) throw new \RuntimeException('Invalid $store'); $this->store = $store; } /** * Get Store * @return StorageServiceInterface */ protected function getStore() { return $this->store; } /** * {@inheritdoc} */ public function getByAccessToken(AccessTokenEntity $accessToken) { $result = $this->getStore()->select(' SELECT oauth_sessions.id, oauth_sessions.owner_type, oauth_sessions.owner_id, oauth_sessions.client_id, oauth_sessions.client_redirect_uri FROM oauth_sessions INNER JOIN oauth_access_tokens ON oauth_access_tokens.session_id =oauth_sessions.id WHERE oauth_access_tokens.access_token = :access_token ', [ 'access_token' => $accessToken->getId() ]); if (count($result) === 1) { $session = new SessionEntity($this->server); $session->setId($result[0]['id']); $session->setOwner($result[0]['owner_type'], $result[0]['owner_id']); return $session; } return; } /** * {@inheritdoc} */ public function getByAuthCode(AuthCodeEntity $authCode) { $result = $this->getStore()->select(' SELECT oauth_sessions.id, oauth_sessions.owner_type, oauth_sessions.owner_id, oauth_sessions.client_id, oauth_sessions.client_redirect_uri FROM oauth_sessions INNER JOIN oauth_auth_codes ON oauth_auth_codes.session_id = oauth_sessions.id WHERE oauth_auth_codes.auth_code = :auth_code ', [ 'auth_code' => $authCode->getId() ]); if (count($result) === 1) { $session = new SessionEntity($this->server); $session->setId($result[0]['id']); $session->setOwner($result[0]['owner_type'], $result[0]['owner_id']); return $session; } return; } /** * {@inheritdoc} */ public function getScopes(SessionEntity $session) { $result = $this->getStore()->select(' SELECT oauth_scopes.* FROM oauth_sessions INNER JOIN oauth_session_scopes ON oauth_sessions.id = oauth_session_scopes.session_id INNER JOIN oauth_scopes ON oauth_scopes.id = oauth_session_scopes.scope WHERE oauth_sessions.id = :id ', [ 'id' => $session->getId() ]); $scopes = []; foreach ($result as $scope) { $scopes[] = (new ScopeEntity($this->server))->hydrate([ 'id' => $scope['id'], 'description' => $scope['description'], ]); } return $scopes; } /** * {@inheritdoc} */ public function create($ownerType, $ownerId, $clientId, $clientRedirectUri = null) { $id = $this->getStore()->insert(' INSERT INTO oauth_sessions (owner_type, owner_id, client_id) VALUES (:owner_type, :owner_id, :client_id) ', [ 'owner_type' => $ownerType, 'owner_id' => $ownerId, 'client_id' => $clientId, ]); return $id; } /** * {@inheritdoc} */ public function associateScope(SessionEntity $session, ScopeEntity $scope) { $this->getStore()->insert(' INSERT INTO oauth_session_scopes (session_id, scope) VALUES (:session_id, :scope) ', [ 'session_id' => $session->getId(), 'scope' => $scope->getId(), ]); } }PK qYB} ApiAccessTokenStorage.phpnu [ . */ namespace Xibo\Storage; use League\OAuth2\Server\Entity\AccessTokenEntity; use League\OAuth2\Server\Entity\ScopeEntity; use League\OAuth2\Server\Storage\AbstractStorage; use League\OAuth2\Server\Storage\AccessTokenInterface; class ApiAccessTokenStorage extends AbstractStorage implements AccessTokenInterface { /** * @var StorageServiceInterface */ private $store; /** * ApiAccessTokenStorage constructor. * @param StorageServiceInterface $store */ public function __construct($store) { if (!$store instanceof StorageServiceInterface) throw new \RuntimeException('Invalid $store'); $this->store = $store; } /** * Get Store * @return StorageServiceInterface */ protected function getStore() { return $this->store; } /** * {@inheritdoc} */ public function get($token) { $result = $this->getStore()->select(' SELECT * FROM oauth_access_tokens WHERE access_token = :access_token ', [ 'access_token' => $token ]); if (count($result) === 1) { $token = (new AccessTokenEntity($this->server)) ->setId($result[0]['access_token']) ->setExpireTime($result[0]['expire_time']); return $token; } return; } /** * {@inheritdoc} */ public function getScopes(AccessTokenEntity $token) { $result = $this->getStore()->select(' SELECT oauth_scopes.id, oauth_scopes.description FROM oauth_access_token_scopes INNER JOIN oauth_scopes ON oauth_access_token_scopes.scope = oauth_scopes.id WHERE access_token = :access_token ', [ 'access_token' => $token ]); $response = []; if (count($result) > 0) { foreach ($result as $row) { $scope = (new ScopeEntity($this->server))->hydrate([ 'id' => $row['id'], 'description' => $row['description'], ]); $response[] = $scope; } } return $response; } /** * {@inheritdoc} */ public function create($token, $expireTime, $sessionId) { $this->getStore()->insert(' INSERT INTO oauth_access_tokens (access_token, session_id, expire_time) VALUES (:access_token, :session_id, :expire_time) ', [ 'access_token' => $token, 'session_id' => $sessionId, 'expire_time' => $expireTime, ]); } /** * {@inheritdoc} */ public function associateScope(AccessTokenEntity $token, ScopeEntity $scope) { $this->getStore()->insert(' INSERT INTO oauth_access_token_scopes (access_token, scope) VALUES (:access_token, :scope) ', [ 'access_token' => $token->getId(), 'scope' => $scope->getId(), ]); } /** * {@inheritdoc} */ public function delete(AccessTokenEntity $token) { $this->getStore()->update('DELETE FROM `oauth_access_token_scopes` WHERE access_token = :access_token', [ 'access_token' => $token->getId()]); $this->getStore()->update('DELETE FROM `oauth_access_tokens` WHERE access_token = :access_token', [ 'access_token' => $token->getId()]); } }PK qYR0[ StorageServiceInterface.phpnu [ . */ namespace Xibo\Storage; use League\OAuth2\Server\Entity\ClientEntity; use League\OAuth2\Server\Entity\SessionEntity; use League\OAuth2\Server\Storage\AbstractStorage; use League\OAuth2\Server\Storage\ClientInterface; class ApiClientStorage extends AbstractStorage implements ClientInterface { /** * @var StorageServiceInterface */ private $store; /** * ApiAccessTokenStorage constructor. * @param StorageServiceInterface $store */ public function __construct($store) { if (!$store instanceof StorageServiceInterface) throw new \RuntimeException('Invalid $store'); $this->store = $store; } /** * Get Store * @return StorageServiceInterface */ protected function getStore() { return $this->store; } /** * {@inheritdoc} */ public function get($clientId, $clientSecret = null, $redirectUri = null, $grantType = null) { $sql = ' SELECT oauth_clients.* FROM oauth_clients WHERE oauth_clients.id = :clientId '; $params = [ 'clientId' => $clientId ]; if ($redirectUri) { $sql = ' SELECT oauth_clients.* FROM oauth_clients INNER JOIN oauth_client_redirect_uris ON oauth_clients.id = oauth_client_redirect_uris.client_id WHERE oauth_clients.id = :clientId AND oauth_client_redirect_uris.redirect_uri = :redirect_uri '; $params['redirect_uri'] = $redirectUri; } if ($clientSecret !== null) { $sql .= ' AND oauth_clients.secret = :clientSecret '; $params['clientSecret'] = $clientSecret; } $result = $this->getStore()->select($sql, $params); if (count($result) === 1) { $client = new ClientEntity($this->server); $client->hydrate([ 'id' => $result[0]['id'], 'name' => $result[0]['name'], ]); // Check to see if this grant_type is allowed for this client switch ($grantType) { case 'authorization_code': if ($result[0]['authCode'] != 1) return false; break; case 'client_credentials': case 'mcaas': if ($result[0]['clientCredentials'] != 1) return false; break; default: return false; } return $client; } return false; } /** * {@inheritdoc} */ public function getBySession(SessionEntity $session) { $result = $this->getStore()->select(' SELECT oauth_clients.id, oauth_clients.name FROM oauth_clients INNER JOIN oauth_sessions ON oauth_clients.id = oauth_sessions.client_id WHERE oauth_sessions.id = :id ', [ 'id' => $session->getId() ]); if (count($result) === 1) { $client = new ClientEntity($this->server); $client->hydrate([ 'id' => $result[0]['id'], 'name' => $result[0]['name'], ]); return $client; } return false; } }PK qY(>2 2 PdoStorageService.phpnu [ . */ namespace Xibo\Storage; use Xibo\Exception\DeadlockException; use Xibo\Service\ConfigService; use Xibo\Service\LogService; /** * Class PDOConnect * Manages global connection state and the creation of connections * @package Xibo\Storage */ class PdoStorageService implements StorageServiceInterface { /** * @var \PDO[] The connection */ private $conn = []; /** @var array Statistics */ private static $stats = []; /** @var string */ private static $_version; /** * Logger * @var LogService */ private $log; /** * PDOConnect constructor. * @param LogService $logger */ public function __construct($logger = null) { $this->log = $logger; } /** @inheritdoc */ public function setConnection($name = 'default') { // Create a new connection $this->conn[$name] = PdoStorageService::newConnection(); return $this; } /** @inheritdoc */ public function close($name = null) { if ($name !== null && isset($this->conn[$name])) { $this->conn[$name] = null; unset($this->conn[$name]); } else { foreach ($this->conn as &$conn) { $conn = null; } $this->conn = []; } } /** * Create a DSN from the host/db name * @param string $host * @param string[Optional] $name * @return string */ private static function createDsn($host, $name = null) { if (strstr($host, ':')) { $hostParts = explode(':', $host); $dsn = 'mysql:host=' . $hostParts[0] . ';port=' . $hostParts[1] . ';'; } else { $dsn = 'mysql:host=' . $host . ';'; } if ($name != null) $dsn .= 'dbname=' . $name . ';'; return $dsn; } /** * Open a new connection using the stored details * @return \PDO */ public static function newConnection() { $dsn = PdoStorageService::createDsn(ConfigService::$dbConfig['host'], ConfigService::$dbConfig['name']); // Open the connection and set the error mode $conn = new \PDO($dsn, ConfigService::$dbConfig['user'], ConfigService::$dbConfig['password']); $conn->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); $conn->query("SET NAMES 'utf8'"); return $conn; } /** * Open a connection with the specified details * @param string $host * @param string $user * @param string $pass * @param string[Optional] $name * @return \PDO */ public function connect($host, $user, $pass, $name = null) { if (!isset($this->conn['default'])) $this->close('default'); $dsn = PdoStorageService::createDsn($host, $name); // Open the connection and set the error mode $this->conn['default'] = new \PDO($dsn, $user, $pass); $this->conn['default']->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); $this->conn['default']->query("SET NAMES 'utf8'"); return $this->conn['default']; } /** @inheritdoc */ public function getConnection($name = 'default') { if (!isset($this->conn[$name])) $this->conn[$name] = PdoStorageService::newConnection(); return $this->conn[$name]; } /** @inheritdoc */ public function exists($sql, $params, $connection = null, $reconnect = false) { if ($this->log != null) $this->log->sql($sql, $params); if ($connection === null) $connection = 'default'; try { $sth = $this->getConnection($connection)->prepare($sql); $sth->execute($params); $this->incrementStat($connection, 'exists'); if ($sth->fetch()) return true; else return false; } catch (\PDOException $PDOException) { // Throw if we're not expected to reconnect. if (!$reconnect) throw $PDOException; $errorCode = isset($PDOException->errorInfo[1]) ? $PDOException->errorInfo[1] : $PDOException->getCode(); if ($errorCode != 2006) { throw $PDOException; } else { $this->close($connection); return $this->exists($sql, $params, $connection, false); } } } /** @inheritdoc */ public function insert($sql, $params, $connection = null, $reconnect = false) { if ($this->log != null) $this->log->sql($sql, $params); if ($connection === null) $connection = 'default'; try { if (!$this->getConnection($connection)->inTransaction()) $this->getConnection($connection)->beginTransaction(); $sth = $this->getConnection($connection)->prepare($sql); $sth->execute($params); $this->incrementStat($connection, 'insert'); return intval($this->getConnection($connection)->lastInsertId()); } catch (\PDOException $PDOException) { // Throw if we're not expected to reconnect. if (!$reconnect) throw $PDOException; $errorCode = isset($PDOException->errorInfo[1]) ? $PDOException->errorInfo[1] : $PDOException->getCode(); if ($errorCode != 2006) { throw $PDOException; } else { $this->close($connection); return $this->insert($sql, $params, $connection, false); } } } /** @inheritdoc */ public function update($sql, $params, $connection = null, $reconnect = false) { if ($this->log != null) $this->log->sql($sql, $params); if ($connection === null) $connection = 'default'; try { if (!$this->getConnection($connection)->inTransaction()) $this->getConnection($connection)->beginTransaction(); $sth = $this->getConnection($connection)->prepare($sql); $sth->execute($params); $rows = $sth->rowCount(); $this->incrementStat($connection, 'update'); return $rows; } catch (\PDOException $PDOException) { // Throw if we're not expected to reconnect. if (!$reconnect) throw $PDOException; $errorCode = isset($PDOException->errorInfo[1]) ? $PDOException->errorInfo[1] : $PDOException->getCode(); if ($errorCode != 2006) { throw $PDOException; } else { $this->close($connection); return $this->update($sql, $params, $connection, false); } } } /** @inheritdoc */ public function select($sql, $params, $connection = null, $reconnect = false) { if ($this->log != null) $this->log->sql($sql, $params); if ($connection === null) $connection = 'default'; try { $sth = $this->getConnection($connection)->prepare($sql); $sth->execute($params); $this->incrementStat($connection, 'select'); return $sth->fetchAll(\PDO::FETCH_ASSOC); } catch (\PDOException $PDOException) { // Throw if we're not expected to reconnect. if (!$reconnect) throw $PDOException; $errorCode = isset($PDOException->errorInfo[1]) ? $PDOException->errorInfo[1] : $PDOException->getCode(); if ($errorCode != 2006) { throw $PDOException; } else { $this->close($connection); return $this->select($sql, $params, $connection, false); } } } /** @inheritdoc */ public function isolated($sql, $params, $connection = null, $reconnect = false) { // Should we log? if ($this->log != null) $this->log->sql($sql, $params); if ($connection === null) $connection = 'isolated'; try { $sth = $this->getConnection($connection)->prepare($sql); $sth->execute($params); $this->incrementStat('isolated', 'update'); } catch (\PDOException $PDOException) { // Throw if we're not expected to reconnect. if (!$reconnect) throw $PDOException; $errorCode = isset($PDOException->errorInfo[1]) ? $PDOException->errorInfo[1] : $PDOException->getCode(); if ($errorCode != 2006) { throw $PDOException; } else { $this->close($connection); return $this->isolated($sql, $params, $connection, false); } } } /** @inheritdoc */ public function updateWithDeadlockLoop($sql, $params, $connection = null) { $maxRetries = 2; // Should we log? if ($this->log != null) $this->log->sql($sql, $params); if ($connection === null) $connection = 'isolated'; // Prepare the statement $statement = $this->getConnection($connection)->prepare($sql); // Deadlock protect this statement $success = false; $retries = $maxRetries; do { try { $this->incrementStat($connection, 'update'); $statement->execute($params); // Successful $success = true; } catch (\PDOException $PDOException) { $errorCode = isset($PDOException->errorInfo[1]) ? $PDOException->errorInfo[1] : $PDOException->getCode(); if ($errorCode != 1213 && $errorCode != 1205) throw $PDOException; } if ($success) break; // Sleep a bit, give the DB time to breathe $queryHash = substr($sql, 0, 15) . '... [' . md5($sql . json_encode($params)) . ']'; $this->log->debug('Retrying query after a short nap, try: ' . (3 - $retries) . '. Query Hash: ' . $queryHash); usleep(10000); } while ($retries--); if (!$success) throw new DeadlockException(__('Failed to write to database after %d retries. Please try again later.', $maxRetries)); } /** @inheritdoc */ public function commitIfNecessary($name = 'default') { if ($this->getConnection($name)->inTransaction()) { $this->incrementStat($name, 'commit'); $this->getConnection($name)->commit(); } } /** * Set the TimeZone for this connection * @param string $timeZone e.g. -8:00 * @param string $connection */ public function setTimeZone($timeZone, $connection = 'default') { $this->getConnection($connection)->query('SET time_zone = \'' . $timeZone . '\';'); $this->incrementStat($connection, 'utility'); } /** * PDO stats * @return array */ public function stats() { return self::$stats; } /** @inheritdoc */ public function incrementStat($connection, $key) { $currentCount = (isset(self::$stats[$connection][$key])) ? self::$stats[$connection][$key] : 0; self::$stats[$connection][$key] = $currentCount + 1; } /** * Statically increment stats * @param $connection * @param $key */ public static function incrementStatStatic($connection, $key) { $currentCount = (isset(self::$stats[$connection][$key])) ? self::$stats[$connection][$key] : 0; self::$stats[$connection][$key] = $currentCount + 1; } /** * @inheritdoc */ public function getVersion() { if (self::$_version === null) { $results = $this->select('SELECT version() AS v', []); if (count($results) <= 0) return null; self::$_version = explode('-', $results[0]['v'])[0]; } return self::$_version; } }PK qY"G G MySqlTimeSeriesStore.phpnu [ . */ namespace Xibo\Storage; use Xibo\Exception\InvalidArgumentException; use Xibo\Exception\XiboException; use Xibo\Factory\CampaignFactory; use Xibo\Factory\LayoutFactory; use Xibo\Service\DateServiceInterface; use Xibo\Service\LogServiceInterface; /** * Class MySqlTimeSeriesStore * @package Xibo\Storage */ class MySqlTimeSeriesStore implements TimeSeriesStoreInterface { // Keep all stats in this array after processing private $stats = []; private $layoutCampaignIds = []; private $layoutIdsNotFound = []; /** @var StorageServiceInterface */ private $store; /** @var LogServiceInterface */ private $log; /** @var DateServiceInterface */ private $dateService; /** @var LayoutFactory */ protected $layoutFactory; /** @var CampaignFactory */ protected $campaignFactory; /** * @inheritdoc */ public function __construct($config = null) { } /** * @inheritdoc */ public function setDependencies($log, $date, $layoutFactory = null, $campaignFactory = null, $mediaFactory = null, $widgetFactory = null, $displayFactory = null) { $this->log = $log; $this->dateService = $date; $this->layoutFactory = $layoutFactory; $this->campaignFactory = $campaignFactory; return $this; } /** @inheritdoc */ public function addStat($statData) { // For a type "event" we have layoutid 0 so is campaignId // otherwise we should try and resolve the campaignId $campaignId = 0; if ($statData['type'] != 'event') { if (array_key_exists($statData['layoutId'], $this->layoutCampaignIds)) { $campaignId = $this->layoutCampaignIds[$statData['layoutId']]; } else { try { // Get the layout campaignId $campaignId = $this->layoutFactory->getCampaignIdFromLayoutHistory($statData['layoutId']); // Put layout campaignId to memory $this->layoutCampaignIds[$statData['layoutId']] = $campaignId; } catch (XiboException $error) { if (!in_array($statData['layoutId'], $this->layoutIdsNotFound)) { $this->layoutIdsNotFound[] = $statData['layoutId']; $this->log->error('Layout not found. Layout Id: '. $statData['layoutId']); } return; } } } // Set to Unix Timestamp $statData['statDate'] = $statData['statDate']->format('U'); $statData['fromDt'] = $statData['fromDt']->format('U'); $statData['toDt'] = $statData['toDt']->format('U'); $statData['campaignId'] = $campaignId; $statData['displayId'] = $statData['display']->displayId; $statData['engagements'] = json_encode($statData['engagements']); unset($statData['display']); $this->stats[] = $statData; } /** @inheritdoc */ public function addStatFinalize() { if (count($this->stats) > 0) { $sql = 'INSERT INTO `stat` (`type`, statDate, start, `end`, scheduleID, displayID, campaignID, layoutID, mediaID, Tag, `widgetId`, duration, `count`, `engagements`) VALUES '; $placeHolders = '(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'; $sql = $sql . implode(', ', array_fill(1, count($this->stats), $placeHolders)); // Flatten the array $data = []; foreach ($this->stats as $stat) { // Be explicit about the order of the keys $ordered = [ 'type' => $stat['type'], 'statDate' => $stat['statDate'], 'fromDt' => $stat['fromDt'], 'toDt' => $stat['toDt'], 'scheduleId' => $stat['scheduleId'], 'displayId' => $stat['displayId'], 'campaignId' => $stat['campaignId'], 'layoutId' => $stat['layoutId'], 'mediaId' => $stat['mediaId'], 'tag' => $stat['tag'], 'widgetId' => $stat['widgetId'], 'duration' => $stat['duration'], 'count' => $stat['count'], 'engagements' => $stat['engagements'] ]; // Add each value to another array in order foreach ($ordered as $field) { $data[] = $field; } } $this->store->isolated($sql, $data); } } /** @inheritdoc */ public function getEarliestDate() { $earliestDate = $this->store->select('SELECT MIN(statDate) AS minDate FROM `stat`', []); return [ 'minDate' => $earliestDate[0]['minDate'] ]; } /** @inheritdoc */ public function getStats($filterBy = []) { $fromDt = isset($filterBy['fromDt']) ? $filterBy['fromDt'] : null; $toDt = isset($filterBy['toDt']) ? $filterBy['toDt'] : null; $statDate = isset($filterBy['statDate']) ? $filterBy['statDate'] : null; // In the case of user switches from mongo to mysql - laststatId were saved as Mongo ObjectId string if (isset($filterBy['statId'])) { if (!is_numeric($filterBy['statId'])) { throw new InvalidArgumentException(__('Invalid statId provided'), 'statId'); } else { $statId = $filterBy['statId']; } } else { $statId = null; } $type = isset($filterBy['type']) ? $filterBy['type'] : null; $displayIds = isset($filterBy['displayIds']) ? $filterBy['displayIds'] : []; $layoutIds = isset($filterBy['layoutIds']) ? $filterBy['layoutIds'] : []; $mediaIds = isset($filterBy['mediaIds']) ? $filterBy['mediaIds'] : []; $campaignId = isset($filterBy['campaignId']) ? $filterBy['campaignId'] : null; $eventTag = isset($filterBy['eventTag']) ? $filterBy['eventTag'] : null; // Tag embedding $embedDisplayTags = isset($filterBy['displayTags']) ? $filterBy['displayTags'] : false; $embedLayoutTags = isset($filterBy['layoutTags']) ? $filterBy['layoutTags'] : false; $embedMediaTags = isset($filterBy['mediaTags']) ? $filterBy['mediaTags'] : false; // Limit $start = isset($filterBy['start']) ? $filterBy['start'] : null; $length = isset($filterBy['length']) ? $filterBy['length'] : null; $params = []; $select = 'SELECT stat.statId, stat.statDate, stat.type, stat.displayId, stat.widgetId, stat.layoutId, stat.mediaId, stat.campaignId, stat.start as start, stat.end as end, stat.tag, stat.duration, stat.count, stat.engagements, display.Display as display, layout.Layout as layout, media.Name AS media '; if ($embedDisplayTags) { $select .= ', ( SELECT GROUP_CONCAT(DISTINCT CONCAT(tag, \'|\', IFNULL(value, \'null\'))) FROM tag INNER JOIN lktagdisplaygroup ON lktagdisplaygroup.tagId = tag.tagId WHERE lktagdisplaygroup.displayGroupId = displaygroup.displayGroupID GROUP BY lktagdisplaygroup.displayGroupId ) AS displayTags '; } if ($embedMediaTags) { $select .= ', ( SELECT GROUP_CONCAT(DISTINCT CONCAT(tag, \'|\', IFNULL(value, \'null\'))) FROM tag INNER JOIN lktagmedia ON lktagmedia.tagId = tag.tagId WHERE lktagmedia.mediaId = media.mediaId GROUP BY lktagmedia.mediaId ) AS mediaTags '; } if ($embedLayoutTags) { $select .= ', ( SELECT GROUP_CONCAT(DISTINCT CONCAT(tag, \'|\', IFNULL(value, \'null\'))) FROM tag INNER JOIN lktaglayout ON lktaglayout.tagId = tag.tagId WHERE lktaglayout.layoutId = layout.layoutId GROUP BY lktaglayout.layoutId ) AS layoutTags '; } $body = ' FROM stat LEFT OUTER JOIN display ON stat.DisplayID = display.DisplayID LEFT OUTER JOIN `lkdisplaydg` ON lkdisplaydg.displayid = display.displayId LEFT OUTER JOIN `displaygroup` ON displaygroup.displaygroupid = lkdisplaydg.displaygroupid AND `displaygroup`.isDisplaySpecific = 1 LEFT OUTER JOIN layout ON layout.LayoutID = stat.LayoutID LEFT OUTER JOIN media ON media.mediaID = stat.mediaID LEFT OUTER JOIN widget ON widget.widgetId = stat.widgetId WHERE 1 = 1 '; // fromDt/toDt Filter if (($fromDt != null) && ($toDt != null)) { $body .= ' AND stat.end > '. $fromDt->format('U') . ' AND stat.start <= '. $toDt->format('U'); } else if (($fromDt != null) && empty($toDt)) { $body .= ' AND stat.start >= '. $fromDt->format('U'); } // statDate Filter // get the next stats from the given date if ($statDate != null) { $body .= ' AND stat.statDate >= ' . $statDate->format('U'); } if ($statId != null) { $body .= ' AND stat.statId > '. $statId; } if (count($displayIds) > 0) { $body .= ' AND stat.displayID IN (' . implode(',', $displayIds) . ')'; } // Type filter if ($type == 'layout') { $body .= ' AND `stat`.type = \'layout\' '; } else if ($type == 'media') { $body .= ' AND `stat`.type = \'media\' AND IFNULL(`media`.mediaId, 0) <> 0 '; } else if ($type == 'widget') { $body .= ' AND `stat`.type = \'widget\' AND IFNULL(`widget`.widgetId, 0) <> 0 '; } else if ($type == 'event') { $body .= ' AND `stat`.type = \'event\' '; } // Event Tag Filter if ($eventTag) { $body .= ' AND `stat`.tag = :eventTag'; $params['eventTag'] = $eventTag; } // Layout Filter if (count($layoutIds) != 0) { $layoutSql = ''; $i = 0; foreach ($layoutIds as $layoutId) { $i++; $layoutSql .= ':layoutId_' . $i . ','; $params['layoutId_' . $i] = $layoutId; } $body .= ' AND `stat`.campaignId IN (SELECT campaignId FROM `layouthistory` WHERE layoutId IN (' . trim($layoutSql, ',') . ')) '; } // Media Filter if (count($mediaIds) != 0) { $mediaSql = ''; $i = 0; foreach ($mediaIds as $mediaId) { $i++; $mediaSql .= ':mediaId_' . $i . ','; $params['mediaId_' . $i] = $mediaId; } $body .= ' AND `media`.mediaId IN (' . trim($mediaSql, ',') . ')'; } // Campaign // -------- // Filter on Layouts linked to a Campaign if ($campaignId != null) { $body .= ' AND stat.campaignId IN ( SELECT lkcampaignlayout.campaignId FROM `lkcampaignlayout` INNER JOIN `campaign` ON `lkcampaignlayout`.campaignId = `campaign`.campaignId AND `campaign`.isLayoutSpecific = 1 INNER JOIN `lkcampaignlayout` lkcl ON lkcl.layoutid = lkcampaignlayout.layoutId WHERE lkcl.campaignId = :campaignId ) '; $params['campaignId'] = $campaignId; } // Sorting $body .= ' ORDER BY stat.statId '; $limit = ''; if ($start !== null && $length !== null) { $limit = ' LIMIT ' . $start . ', ' . $length; } // Total count $resTotal = []; if ($start !== null && $length !== null) { $resTotal = $this->store->select(' SELECT COUNT(*) AS total FROM ( ' . $select . $body . ') total ', $params); } // Run our query using a connection object (to save memory) $connection = $this->store->getConnection(); $connection->setAttribute(\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false); // Execute sql statement $sql = $select . $body. $limit; $statement = $connection->prepare($sql); // Execute $statement->execute($params); $this->log->sql($sql, $params); $result = new TimeSeriesMySQLResults($statement); // Total $result->totalCount = isset($resTotal[0]['total']) ? $resTotal[0]['total'] : 0; return $result; } /** @inheritdoc */ public function getExportStatsCount($filterBy = []) { $fromDt = isset($filterBy['fromDt']) ? $filterBy['fromDt'] : null; $toDt = isset($filterBy['toDt']) ? $filterBy['toDt'] : null; $displayIds = isset($filterBy['displayIds']) ? $filterBy['displayIds'] : []; $params = []; $sql = ' SELECT COUNT(*) AS total FROM `stat` WHERE 1 = 1 '; // fromDt/toDt Filter if (($fromDt != null) && ($toDt != null)) { $sql .= ' AND stat.end > '. $fromDt->format('U') . ' AND stat.start <= '. $toDt->format('U'); } if (count($displayIds) > 0) { $sql .= ' AND stat.displayID IN (' . implode(',', $displayIds) . ')'; } // Total count $resTotal = $this->store->select($sql, $params); // Total $totalCount = isset($resTotal[0]['total']) ? $resTotal[0]['total'] : 0; return $totalCount; } /** @inheritdoc */ public function deleteStats($maxage, $fromDt = null, $options = []) { // Set some default options $options = array_merge([ 'maxAttempts' => 10, 'statsDeleteSleep' => 3, 'limit' => 10000, ], $options); // Convert to a simple type so that we can pass by reference to bindParam. $maxage = $maxage->format('U'); try { $i = 0; $rows = 1; if ($fromDt !== null) { // Convert to a simple type so that we can pass by reference to bindParam. $fromDt = $fromDt->format('U'); // Prepare a delete statement which we will use multiple times $delete = $this->store->getConnection() ->prepare('DELETE FROM `stat` WHERE stat.statDate >= :fromDt AND stat.statDate < :toDt ORDER BY statId LIMIT :limit'); $delete->bindParam(':fromDt', $fromDt, \PDO::PARAM_STR); $delete->bindParam(':toDt', $maxage, \PDO::PARAM_STR); $delete->bindParam(':limit', $options['limit'], \PDO::PARAM_INT); } else { $delete = $this->store->getConnection() ->prepare('DELETE FROM `stat` WHERE stat.statDate < :maxage LIMIT :limit'); $delete->bindParam(':maxage', $maxage, \PDO::PARAM_STR); $delete->bindParam(':limit', $options['limit'], \PDO::PARAM_INT); } $count = 0; while ($rows > 0) { $i++; // Run the delete $delete->execute(); // Find out how many rows we've deleted $rows = $delete->rowCount(); $count += $rows; // We shouldn't be in a transaction, but commit anyway just in case $this->store->commitIfNecessary(); // Give SQL time to recover if ($rows > 0) { $this->log->debug('Stats delete effected ' . $rows . ' rows, sleeping.'); sleep($options['statsDeleteSleep']); } // Break if we've exceeded the maximum attempts, assuming that has been provided if ($options['maxAttempts'] > -1 && $i >= $options['maxAttempts']) { break; } } $this->log->debug('Deleted Stats back to ' . $maxage . ' in ' . $i . ' attempts'); return $count; } catch (\PDOException $e) { $this->log->error($e->getMessage()); throw new \RuntimeException('Stats cannot be deleted.'); } } /** @inheritdoc */ public function executeQuery($options = []) { $this->log->debug('Execute MySQL query.'); $query = $options['query']; $params = $options['params']; $dbh = $this->store->getConnection(); $sth = $dbh->prepare($query); $sth->execute($params); // Get the results $results = $sth->fetchAll(); return $results; } /** * @param StorageServiceInterface $store * @return $this */ public function setStore($store) { $this->store = $store; return $this; } /** @inheritdoc */ public function getEngine() { return 'mysql'; } }PK qY* c c ApiRefreshTokenStorage.phpnu [ . */ namespace Xibo\Storage; use League\OAuth2\Server\Entity\RefreshTokenEntity; use League\OAuth2\Server\Storage\AbstractStorage; use League\OAuth2\Server\Storage\RefreshTokenInterface; class ApiRefreshTokenStorage extends AbstractStorage implements RefreshTokenInterface { /** * @var StorageServiceInterface */ private $store; /** * ApiAccessTokenStorage constructor. * @param StorageServiceInterface $store */ public function __construct($store) { if (!$store instanceof StorageServiceInterface) throw new \RuntimeException('Invalid $store'); $this->store = $store; } /** * Get Store * @return StorageServiceInterface */ protected function getStore() { return $this->store; } /** * {@inheritdoc} */ public function get($token) { $result = $this->getStore()->select(' SELECT * FROM oauth_refresh_tokens WHERE refresh_token = :refresh_token ', [ 'refresh_token' => $token ]); if (count($result) === 1) { $token = (new RefreshTokenEntity($this->server)) ->setId($result[0]['refresh_token']) ->setExpireTime($result[0]['expire_time']) ->setAccessTokenId($result[0]['access_token']); return $token; } return; } /** * {@inheritdoc} */ public function create($token, $expireTime, $accessToken) { $this->getStore()->insert(' INSERT INTO oauth_refresh_tokens (refresh_token, access_token, expire_time) VALUES (:refresh_token, :access_token, :expire_time) ', [ 'refresh_token' => $token, 'access_token' => $accessToken, 'expire_time' => $expireTime, ]); } /** * {@inheritdoc} */ public function delete(RefreshTokenEntity $token) { $this->getStore()->update('DELETE FROM oauth_refresh_tokens WHERE refresh_token = :refresh_token', ['refresh_token' => $token->getId()]); } }PK qY7.O ApiScopeStorage.phpnu [ . */ namespace Xibo\Storage; use League\OAuth2\Server\Entity\ScopeEntity; use League\OAuth2\Server\Storage\AbstractStorage; use League\OAuth2\Server\Storage\ScopeInterface; class ApiScopeStorage extends AbstractStorage implements ScopeInterface { /** * @var StorageServiceInterface */ private $store; /** * ApiAccessTokenStorage constructor. * @param StorageServiceInterface $store */ public function __construct($store) { if (!$store instanceof StorageServiceInterface) throw new \RuntimeException('Invalid $store'); $this->store = $store; } /** * Get Store * @return StorageServiceInterface */ protected function getStore() { return $this->store; } /** * {@inheritdoc} */ public function get($scope, $grantType = null, $clientId = null) { $result = $this->getStore()->select('SELECT * FROM oauth_scopes WHERE id = :id ', array('id' => $scope)); if (count($result) === 0) { return; } if ($clientId !== null) { // Check to see if this scope exists for this client $clientScope = $this->getStore()->select('SELECT * FROM `oauth_client_scopes` WHERE clientId = :clientId AND scopeId = :id', [ 'clientId' => $clientId, 'id' => $scope ]); if (count($clientScope) == 0) return; } return (new ScopeEntity($this->server))->hydrate([ 'id' => $result[0]['id'], 'description' => $result[0]['description'], ]); } }PK qYJ TimeSeriesStoreInterface.phpnu [ . */ namespace Xibo\Storage; use Xibo\Factory\CampaignFactory; use Xibo\Factory\DisplayFactory; use Xibo\Factory\LayoutFactory; use Xibo\Factory\MediaFactory; use Xibo\Factory\WidgetFactory; use Xibo\Service\DateServiceInterface; use Xibo\Service\LogServiceInterface; /** * Interface TimeSeriesStoreInterface * @package Xibo\Service */ interface TimeSeriesStoreInterface { /** * Time series constructor. * @param array $config */ public function __construct($config = null); /** * Set Time series Dependencies * @param LogServiceInterface $logger * @param DateServiceInterface $date * @param MediaFactory $mediaFactory * @param WidgetFactory $widgetFactory * @param LayoutFactory $layoutFactory * @param DisplayFactory $displayFactory * @param CampaignFactory $campaignFactory */ public function setDependencies($logger, $date, $layoutFactory = null, $campaignFactory = null, $mediaFactory = null, $widgetFactory = null, $displayFactory = null); /** * Process and add a single statdata to array * @param $statData array */ public function addStat($statData); /** * Write statistics to DB */ public function addStatFinalize(); /** * Get the earliest date * @return array */ public function getEarliestDate(); /** * Get statistics * @param $filterBy array[mixed]|null * @return TimeSeriesResultsInterface */ public function getStats($filterBy = []); /** * Get total count of export statistics * @param $filterBy array[mixed]|null * @return TimeSeriesResultsInterface */ public function getExportStatsCount($filterBy = []); /** * Delete statistics * @param $toDt \Jenssegers\Date\Date * @param $fromDt \Jenssegers\Date\Date|null * @param $options array * @return int number of deleted stat records * @throws \Exception */ public function deleteStats($toDt, $fromDt = null, $options = []); /** * Execute query * @param $options array|[] * @return array */ public function executeQuery($options = []); /** * Get the statistic store * @return string */ public function getEngine(); }PK qYvR\ \ TimeSeriesMySQLResults.phpnu [ . */ namespace Xibo\Storage; /** * Class TimeSeriesMySQLResults * @package Xibo\Storage */ class TimeSeriesMySQLResults implements TimeSeriesResultsInterface { /** * Statement * @var \PDOStatement */ private $object; /** * Total number of stats */ public $totalCount; /** * @inheritdoc */ public function __construct($stmtObject = null) { $this->object = $stmtObject; } /** * @inheritdoc */ public function getArray() { $rows = []; while ($row = $this->object->fetch(\PDO::FETCH_ASSOC)) { $entry = []; // Read the columns $entry['id'] = $row['statId']; $entry['type'] = $row['type']; $entry['start'] = $row['start']; $entry['end'] = $row['end']; $entry['layout'] = $row['layout']; $entry['display'] = $row['display']; $entry['media'] = $row['media']; $entry['tag'] = $row['tag']; $entry['duration'] = $row['duration']; $entry['count'] = $row['count']; $entry['displayId'] = $row['displayId']; $entry['layoutId'] = $row['layoutId']; $entry['widgetId'] = $row['widgetId']; $entry['mediaId'] = $row['mediaId']; $entry['campaignId'] = $row['campaignId']; $entry['statDate'] = $row['statDate']; $entry['engagements'] = isset($row['engagements']) ? json_decode($row['engagements']) : []; // Tags // Mimic the structure we have in Mongo. $entry['tagFilter'] = [ 'dg' => [], 'layout' => [], 'media' => [] ]; // Display Tags if (array_key_exists('displayTags', $row) && !empty($row['displayTags'])) { $tags = explode(',', $row['displayTags']); foreach ($tags as $tag) { $tag = explode('|', $tag); $value = $tag[1] ?? null; $entry['tagFilter']['dg'][] = [ 'tag' => $tag[0], 'value' => ($value === 'null') ? null : $value ]; } } // Layout Tags if (array_key_exists('layoutTags', $row) && !empty($row['layoutTags'])) { $tags = explode(',', $row['layoutTags']); foreach ($tags as $tag) { $tag = explode('|', $tag); $value = $tag[1] ?? null; $entry['tagFilter']['layout'][] = [ 'tag' => $tag[0], 'value' => ($value === 'null') ? null : $value ]; } } // Media Tags if (array_key_exists('mediaTags', $row) && !empty($row['mediaTags'])) { $tags = explode(',', $row['mediaTags']); foreach ($tags as $tag) { $tag = explode('|', $tag); $value = $tag[1] ?? null; $entry['tagFilter']['media'][] = [ 'tag' => $tag[0], 'value' => ($value === 'null') ? null : $value ]; } } $rows[] = $entry; } return ['statData'=> $rows]; } /** * @inheritdoc */ public function getNextRow() { return $this->object->fetch(\PDO::FETCH_ASSOC); } /** * @inheritdoc */ public function getTotalCount() { return $this->totalCount; } }PK qYuZq q ApiAuthCodeStorage.phpnu [ PK qYoP8 8 TimeSeriesMongoDbResults.phpnu [ PK qY7 7 ; TimeSeriesResultsInterface.phpnu [ PK qYz9h h $ MongoDbTimeSeriesStore.phpnu [ PK qY]݂ % ApiSessionStorage.phpnu [ PK qYB} m ApiAccessTokenStorage.phpnu [ PK qYR0[ StorageServiceInterface.phpnu [ PK qY2 p ApiClientStorage.phpnu [ PK qY(>2 2 4 PdoStorageService.phpnu [ PK qY"G G MySqlTimeSeriesStore.phpnu [ PK qY* c c L ApiRefreshTokenStorage.phpnu [ PK qY7.O }X ApiScopeStorage.phpnu [ PK qYJ Mb TimeSeriesStoreInterface.phpnu [ PK qYvR\ \ n TimeSeriesMySQLResults.phpnu [ PK &