예제 #1
0
파일: Log.php 프로젝트: fignew/xibo-cms
 /**
  * Audit Log
  * @param string $entity
  * @param int $entityId
  * @param string $message
  * @param string|object|array $object
  */
 public static function audit($entity, $entityId, $message, $object)
 {
     \Debug::Audit(sprintf('Audit Trail message recorded for %s with id %d. Message: %s', $entity, $entityId, $message));
     if (self::$_auditLogStatement == null) {
         $dbh = \PDOConnect::newConnection();
         self::$_auditLogStatement = $dbh->prepare('
             INSERT INTO `auditlog` (logDate, userId, entity, message, entityId, objectAfter)
               VALUES (:logDate, :userId, :entity, :message, :entityId, :objectAfter)
         ');
     }
     // If we aren't a string then encode
     if (!is_string($object)) {
         $object = json_encode($object);
     }
     self::$_auditLogStatement->execute(array('logDate' => time(), 'userId' => \Kit::GetParam('userid', _SESSION, _INT, 0), 'entity' => $entity, 'message' => $message, 'entityId' => $entityId, 'objectAfter' => $object));
 }
예제 #2
0
 /**
  * Does the cache have the specified key
  * @param  string $key The Key
  * @return boolean True or False
  */
 public static function has($key)
 {
     // Load the key
     self::load($key);
     if (isset(self::$_data[$key]) && self::$_data[$key] != null) {
         // If the key has expired remove it
         if (self::$_data[$key]['expires'] < time()) {
             Debug::Audit($key . ' Expired: ' . self::$_data[$key]['expires']);
             // Remove it
             self::remove($key);
             return false;
         }
         Debug::Audit($key . ' present and in date');
         return true;
     }
     Debug::Audit($key . ' not present');
     return false;
 }
예제 #3
0
 private function request($latitude, $longitude, $time = null, $options = array())
 {
     $request_url = self::API_ENDPOINT . '[APIKEY]' . '/' . $latitude . ',' . $longitude . (is_null($time) ? '' : ',' . $time);
     if (!empty($options)) {
         $request_url .= '?' . http_build_query($options);
     }
     \Debug::Audit('Calling API with: ' . $request_url);
     $request_url = str_replace('[APIKEY]', $this->api_key, $request_url);
     $httpOptions = array(CURLOPT_TIMEOUT => 20, CURLOPT_SSL_VERIFYPEER => true, CURLOPT_USERAGENT => 'Xibo Digital Signage', CURLOPT_HEADER => false, CURLINFO_HEADER_OUT => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_URL => $request_url);
     // Proxy support
     if (\Config::GetSetting('PROXY_HOST') != '' && !\Config::isProxyException($request_url)) {
         $httpOptions[CURLOPT_PROXY] = \Config::GetSetting('PROXY_HOST');
         $httpOptions[CURLOPT_PROXYPORT] = \Config::GetSetting('PROXY_PORT');
         if (\Config::GetSetting('PROXY_AUTH') != '') {
             $httpOptions[CURLOPT_PROXYUSERPWD] = \Config::GetSetting('PROXY_AUTH');
         }
     }
     $curl = curl_init();
     curl_setopt_array($curl, $httpOptions);
     $result = curl_exec($curl);
     // Get the response headers
     $outHeaders = curl_getinfo($curl);
     if ($outHeaders['http_code'] == 0) {
         // Unable to connect
         \Debug::Error('Unable to reach Forecast API. No Host Found (HTTP Code 0). Curl Error = ' . curl_error($curl));
         return false;
     } else {
         if ($outHeaders['http_code'] != 200) {
             \Debug::Error('ForecastIO API returned ' . $outHeaders['http_code'] . ' status. Unable to proceed. Headers = ' . var_export($outHeaders, true));
             // See if we can parse the error.
             $body = json_decode($result);
             \Debug::Error('ForecastIO Error: ' . (isset($body->errors[0]) ? $body->errors[0]->message : 'Unknown Error'));
             return false;
         }
     }
     // Parse out header and body
     $body = json_decode($result);
     return $body;
 }
예제 #4
0
 /**
  * PHONE_HOME if required
  */
 private function PhoneHome()
 {
     if (Config::GetSetting('PHONE_HOME') == 'On') {
         // Find out when we last PHONED_HOME :D
         // If it's been > 28 days since last PHONE_HOME then
         if (Config::GetSetting('PHONE_HOME_DATE') < time() - 60 * 60 * 24 * 28) {
             try {
                 $dbh = PDOConnect::init();
                 // Retrieve number of displays
                 $sth = $dbh->prepare('SELECT COUNT(*) AS Cnt FROM `display` WHERE `licensed` = 1');
                 $sth->execute();
                 $PHONE_HOME_CLIENTS = $sth->fetchColumn();
                 // Retrieve version number
                 $PHONE_HOME_VERSION = Config::Version('app_ver');
                 $PHONE_HOME_URL = Config::GetSetting('PHONE_HOME_URL') . "?id=" . urlencode(Config::GetSetting('PHONE_HOME_KEY')) . "&version=" . urlencode($PHONE_HOME_VERSION) . "&numClients=" . urlencode($PHONE_HOME_CLIENTS);
                 if ($this->isAuditing == 1) {
                     Debug::LogEntry("audit", "PHONE_HOME_URL " . $PHONE_HOME_URL, "xmds", "RequiredFiles");
                 }
                 // Set PHONE_HOME_TIME to NOW.
                 $sth = $dbh->prepare('UPDATE `setting` SET `value` = :time WHERE `setting`.`setting` = :setting LIMIT 1');
                 $sth->execute(array('time' => time(), 'setting' => 'PHONE_HOME_DATE'));
                 @file_get_contents($PHONE_HOME_URL);
                 if ($this->isAuditing == 1) {
                     Debug::Audit("PHONE_HOME [OUT]", $this->displayId);
                 }
             } catch (Exception $e) {
                 Debug::Error($e->getMessage(), $this->displayId);
                 return false;
             }
         }
     }
 }
예제 #5
0
 /**
  * Logs in a specific user
  * @param int $userId
  * @return bool
  */
 function LoginServices($userId)
 {
     try {
         $dbh = PDOConnect::init();
         $sth = $dbh->prepare('SELECT UserName, usertypeid, homepage FROM user WHERE userID = :userId AND Retired = 0');
         $sth->execute(array('userId' => $userId));
         if (!($results = $sth->fetch())) {
             return false;
         }
         $this->userid = $userId;
         $this->userName = Kit::ValidateParam($results['UserName'], _USERNAME);
         $this->usertypeid = Kit::ValidateParam($results['usertypeid'], _INT);
         $this->homePage = Kit::ValidateParam($results['homepage'], _WORD);
         return true;
     } catch (Exception $e) {
         Debug::Audit($e->getMessage());
         return false;
     }
 }
예제 #6
0
 public function LoadDefault()
 {
     Debug::Audit('Load Default ' . $this->type);
     try {
         $dbh = PDOConnect::init();
         // Load the config from disk
         $this->loadFromFile();
         // See if we have a default for this player type
         $sth = $dbh->prepare('SELECT * FROM `displayprofile` WHERE `type` = :type AND isdefault = 1');
         $sth->execute(array('type' => $this->type));
         if (!($row = $sth->fetch())) {
             // We don't so we should stick with the global default
             Debug::Audit('Fall back to global default');
         } else {
             // We do, so we should overwrite the global default with our stored preferences
             $this->name = Kit::ValidateParam($row['name'], _STRING);
             $this->type = Kit::ValidateParam($row['type'], _STRING);
             $this->isDefault = Kit::ValidateParam($row['isdefault'], _INT);
             $this->userId = Kit::ValidateParam($row['userid'], _INT);
             // Load the client settings into an array
             $config = Kit::ValidateParam($row['config'], _HTMLSTRING);
             $config = $config == '' ? array() : json_decode($config, true);
             // We have an array of settings that we must use to overwrite the values in our global config
             for ($i = 0; $i < count($this->config); $i++) {
                 // Does this setting exist in our store?
                 for ($j = 0; $j < count($config); $j++) {
                     if ($config[$j]['name'] == $this->config[$i]['name']) {
                         $this->config[$i]['value'] = $config[$j]['value'];
                         break;
                     }
                 }
             }
             $this->isNew = false;
         }
         return true;
     } catch (Exception $e) {
         Debug::LogEntry('error', $e->getMessage());
         if (!$this->IsError()) {
             $this->SetError(1, __('Unknown Error'));
         }
         return false;
     }
 }
예제 #7
0
 /**
  * Data Set Results
  * @param <type> $dataSetId
  * @param <type> $columnIds
  * @param <type> $filter
  * @param <type> $ordering
  * @param <type> $lowerLimit
  * @param <type> $upperLimit
  * @return <type>
  */
 public function DataSetResults($dataSetId, $columnIds, $filter = '', $ordering = '', $lowerLimit = 0, $upperLimit = 0, $displayId = 0)
 {
     $blackList = array(';', 'INSERT', 'UPDATE', 'SELECT', 'DELETE', 'TRUNCATE', 'TABLE', 'FROM', 'WHERE');
     try {
         $dbh = PDOConnect::init();
         $params = array('dataSetId' => $dataSetId);
         $selectSQL = '';
         $outserSelect = '';
         $finalSelect = '';
         $results = array();
         $headings = array();
         $allowedOrderCols = array();
         $filter = str_replace($blackList, '', $filter);
         $filter = str_replace('[DisplayId]', $displayId, $filter);
         $columns = explode(',', $columnIds);
         // Get the Latitude and Longitude ( might be used in a formula )
         if ($displayId == 0) {
             $defaultLat = Config::GetSetting('DEFAULT_LAT');
             $defaultLong = Config::GetSetting('DEFAULT_LONG');
             $displayGeoLocation = "GEOMFROMTEXT('POINT(" . $defaultLat . " " . $defaultLong . ")')";
         } else {
             $displayGeoLocation = sprintf("(SELECT GeoLocation FROM `display` WHERE DisplayID = %d)", $displayId);
         }
         // Get all columns for the cross tab
         $sth = $dbh->prepare('SELECT DataSetColumnID, Heading, DataSetColumnTypeID, Formula, DataTypeID FROM datasetcolumn WHERE DataSetID = :dataSetId');
         $sth->execute(array('dataSetId' => $dataSetId));
         $allColumns = $sth->fetchAll();
         foreach ($allColumns as $col) {
             $heading = $col;
             $heading['Text'] = $heading['Heading'];
             $allowedOrderCols[] = $heading['Heading'];
             $formula = str_replace($blackList, '', htmlspecialchars_decode($col['Formula'], ENT_QUOTES));
             // Is this column a formula column or a value column?
             if ($col['DataSetColumnTypeID'] == 2) {
                 // Formula
                 $formula = str_replace('[DisplayGeoLocation]', $displayGeoLocation, $formula);
                 $formula = str_replace('[DisplayId]', $displayId, $formula);
                 $heading['Heading'] = $formula . ' AS \'' . $heading['Heading'] . '\'';
             } else {
                 // Value
                 $selectSQL .= sprintf("MAX(CASE WHEN DataSetColumnID = %d THEN `Value` ELSE null END) AS '%s', ", $col['DataSetColumnID'], $heading['Heading']);
             }
             $headings[] = $heading;
         }
         // Build our select statement including formulas
         foreach ($headings as $heading) {
             if ($heading['DataSetColumnTypeID'] == 2) {
                 // This is a formula, so the heading has been morphed into some SQL to run
                 $outserSelect .= ' ' . $heading['Heading'] . ',';
             } else {
                 $outserSelect .= sprintf(' `%s`,', $heading['Heading']);
             }
         }
         $outserSelect = rtrim($outserSelect, ',');
         // For each heading, put it in the correct order (according to $columns)
         foreach ($columns as $visibleColumn) {
             foreach ($headings as $heading) {
                 if ($heading['DataSetColumnID'] == $visibleColumn) {
                     $finalSelect .= sprintf(' `%s`,', $heading['Text']);
                     $results['Columns'][] = $heading;
                 }
             }
         }
         $finalSelect = rtrim($finalSelect, ',');
         // We are ready to build the select and from part of the SQL
         $SQL = "SELECT {$finalSelect} ";
         $SQL .= "  FROM ( ";
         $SQL .= "   SELECT {$outserSelect} ,";
         $SQL .= "           RowNumber ";
         $SQL .= "     FROM ( ";
         $SQL .= "      SELECT {$selectSQL} ";
         $SQL .= "          RowNumber ";
         $SQL .= "        FROM (";
         $SQL .= "          SELECT datasetcolumn.DataSetColumnID, datasetdata.RowNumber, datasetdata.`Value` ";
         $SQL .= "            FROM datasetdata ";
         $SQL .= "              INNER JOIN datasetcolumn ";
         $SQL .= "              ON datasetcolumn.DataSetColumnID = datasetdata.DataSetColumnID ";
         $SQL .= "            WHERE datasetcolumn.DataSetID = :dataSetId ";
         $SQL .= "          ) datasetdatainner ";
         $SQL .= "      GROUP BY RowNumber ";
         $SQL .= "    ) datasetdata ";
         if ($filter != '') {
             $SQL .= ' WHERE ' . $filter;
         }
         $SQL .= ' ) finalselect ';
         if ($ordering != '') {
             $order = ' ORDER BY ';
             $ordering = explode(',', $ordering);
             foreach ($ordering as $orderPair) {
                 // Sanitize the clause
                 $sanitized = str_replace(' ASC', '', str_replace(' DESC', '', $orderPair));
                 // Check allowable
                 if (!in_array($sanitized, $allowedOrderCols)) {
                     Debug::Info('Disallowed column: ' . $sanitized);
                     continue;
                 }
                 // Substitute
                 if (strripos($orderPair, ' DESC')) {
                     $order .= sprintf(' `%s`  DESC,', $sanitized);
                 } else {
                     if (strripos($orderPair, ' ASC')) {
                         $order .= sprintf(' `%s`  ASC,', $sanitized);
                     } else {
                         $order .= sprintf(' `%s`,', $sanitized);
                     }
                 }
             }
             $SQL .= trim($order, ',');
         } else {
             $SQL .= " ORDER BY RowNumber ";
         }
         if ($lowerLimit != 0 || $upperLimit != 0) {
             // Lower limit should be 0 based
             if ($lowerLimit != 0) {
                 $lowerLimit = $lowerLimit - 1;
             }
             // Upper limit should be the distance between upper and lower
             $upperLimit = $upperLimit - $lowerLimit;
             // Substitute in
             $SQL .= sprintf(' LIMIT %d, %d ', $lowerLimit, $upperLimit);
         }
         Debug::Audit($SQL . ' ' . var_export($params, true));
         $sth = $dbh->prepare($SQL);
         //$sth->debugDumpParams();
         $sth->execute($params);
         $results['Rows'] = $sth->fetchAll();
         return $results;
     } catch (Exception $e) {
         Debug::Error($e->getMessage());
         if (!$this->IsError()) {
             $this->SetError(1, __('Unknown Error'));
         }
         return false;
     }
 }
예제 #8
0
 /**
  * Delete unused media for user
  * @param int $userId
  * @return bool
  */
 public function deleteUnusedForUser($userId)
 {
     foreach (Media::entriesUnusedForUser($userId) as $item) {
         Debug::Audit('Deleting unused media: ' . $item['mediaId']);
         if (!$this->Delete($item['mediaId'])) {
             return false;
         }
     }
     return true;
 }
예제 #9
0
 /**
  * Replace media in all layouts.
  * @param <type> $oldMediaId
  * @param <type> $newMediaId
  */
 private function ReplaceMediaInAllLayouts($replaceInLayouts, $replaceBackgroundImages, $oldMediaId, $newMediaId)
 {
     $count = 0;
     Debug::LogEntry('audit', sprintf('Replacing mediaid %s with mediaid %s in all layouts', $oldMediaId, $newMediaId), 'module', 'ReplaceMediaInAllLayouts');
     try {
         $dbh = PDOConnect::init();
         // Some update statements to use
         $sth = $dbh->prepare('SELECT lklayoutmediaid, regionid FROM lklayoutmedia WHERE mediaid = :media_id AND layoutid = :layout_id');
         $sth_update = $dbh->prepare('UPDATE lklayoutmedia SET mediaid = :media_id WHERE lklayoutmediaid = :lklayoutmediaid');
         // Loop through a list of layouts this user has access to
         foreach ($this->user->LayoutList() as $layout) {
             $layoutId = $layout['layoutid'];
             // Does this layout use the old media id?
             $sth->execute(array('media_id' => $oldMediaId, 'layout_id' => $layoutId));
             $results = $sth->fetchAll();
             if (count($results) <= 0) {
                 continue;
             }
             Debug::LogEntry('audit', sprintf('%d linked media items for layoutid %d', count($results), $layoutId), 'module', 'ReplaceMediaInAllLayouts');
             // Create a region object for later use (new one each time)
             $layout = new Layout();
             $region = new region($this->db);
             // Loop through each media link for this layout
             foreach ($results as $row) {
                 // Get the LKID of the link between this layout and this media.. could be more than one?
                 $lkId = $row['lklayoutmediaid'];
                 $regionId = $row['regionid'];
                 if ($regionId == 'background') {
                     Debug::Audit('Replacing background image');
                     if (!$replaceBackgroundImages) {
                         continue;
                     }
                     // Straight swap this background image node.
                     if (!$layout->EditBackgroundImage($layoutId, $newMediaId)) {
                         return false;
                     }
                 } else {
                     if (!$replaceInLayouts) {
                         continue;
                     }
                     // Get the Type of this media
                     if (!($type = $region->GetMediaNodeType($layoutId, '', '', $lkId))) {
                         continue;
                     }
                     // Create a new media node use it to swap the nodes over
                     Debug::LogEntry('audit', 'Creating new module with MediaID: ' . $newMediaId . ' LayoutID: ' . $layoutId . ' and RegionID: ' . $regionId, 'region', 'ReplaceMediaInAllLayouts');
                     try {
                         $module = ModuleFactory::createForMedia($type, $newMediaId, $this->db, $this->user);
                     } catch (Exception $e) {
                         Debug::Error($e->getMessage());
                         return false;
                     }
                     // Sets the URI field
                     if (!$module->SetRegionInformation($layoutId, $regionId)) {
                         return false;
                     }
                     // Get the media xml string to use in the swap.
                     $mediaXmlString = $module->AsXml();
                     // Swap the nodes
                     if (!$region->SwapMedia($layoutId, $regionId, $lkId, $oldMediaId, $newMediaId, $mediaXmlString)) {
                         return false;
                     }
                 }
                 // Update the LKID with the new media id
                 $sth_update->execute(array('media_id' => $newMediaId, 'lklayoutmediaid' => $row['lklayoutmediaid']));
                 $count++;
             }
         }
     } catch (Exception $e) {
         Debug::LogEntry('error', $e->getMessage());
         if (!$this->IsError()) {
             $this->SetError(1, __('Unknown Error'));
         }
         return false;
     }
     Debug::LogEntry('audit', sprintf('Replaced media in %d layouts', $count), 'module', 'ReplaceMediaInAllLayouts');
 }
예제 #10
0
 /**
  * Edits an event
  * @return 
  */
 public function EditEvent()
 {
     // Check the token
     if (!Kit::CheckToken(Kit::GetParam('token_id', _POST, _STRING))) {
         trigger_error(__('Sorry the form has expired. Please refresh.'), E_USER_ERROR);
     }
     $db =& $this->db;
     $user =& $this->user;
     $response = new ResponseManager();
     $eventId = Kit::GetParam('EventID', _POST, _INT, 0);
     $campaignId = Kit::GetParam('CampaignID', _POST, _INT, 0);
     $fromDT = Kit::GetParam('starttime', _POST, _STRING);
     $toDT = Kit::GetParam('endtime', _POST, _STRING);
     $displayGroupIDs = Kit::GetParam('DisplayGroupIDs', _POST, _ARRAY);
     $isPriority = Kit::GetParam('is_priority', _POST, _CHECKBOX);
     $repeatType = Kit::GetParam('rec_type', _POST, _STRING);
     $repeatInterval = Kit::GetParam('rec_detail', _POST, _INT);
     $repeatToDt = Kit::GetParam('rec_range', _POST, _STRING);
     $displayOrder = Kit::GetParam('DisplayOrder', _POST, _INT);
     $isNextButton = Kit::GetParam('next', _GET, _BOOL, false);
     // Convert our ISO strings
     $fromDT = DateManager::getTimestampFromString($fromDT);
     $toDT = DateManager::getTimestampFromString($toDT);
     if ($repeatToDt != '') {
         $repeatToDt = DateManager::getTimestampFromString($repeatToDt);
     }
     Debug::Audit('Times received are: FromDt=' . $fromDT . '. ToDt=' . $toDT . '. RepeatToDt=' . $repeatToDt);
     // Validate layout
     if ($campaignId == 0) {
         trigger_error(__("No layout selected"), E_USER_ERROR);
     }
     // check that at least one display has been selected
     if ($displayGroupIDs == '') {
         trigger_error(__("No displays selected"), E_USER_ERROR);
     }
     // validate the dates
     if ($toDT < $fromDT) {
         trigger_error(__('Can not have an end time earlier than your start time'), E_USER_ERROR);
     }
     // Check recurrence dT is in the future or empty
     if ($repeatToDt != '' && $repeatToDt < time() - 86400) {
         trigger_error(__("Your repeat until date is in the past. Cannot schedule events to repeat in to the past"), E_USER_ERROR);
     }
     // Ready to do the edit
     $scheduleObject = new Schedule($db);
     if (!$scheduleObject->Edit($eventId, $displayGroupIDs, $fromDT, $toDT, $campaignId, $repeatType, $repeatInterval, $repeatToDt, $isPriority, $this->user->userid, $displayOrder)) {
         trigger_error($scheduleObject->GetErrorMessage(), E_USER_ERROR);
     }
     $response->SetFormSubmitResponse(__("The Event has been Modified."));
     $response->callBack = 'CallGenerateCalendar';
     $response->Respond();
 }
예제 #11
0
 protected function getTwitterFeed($displayId = 0, $isPreview = true)
 {
     if (!extension_loaded('curl')) {
         trigger_error(__('cURL extension is required for Twitter'));
         return false;
     }
     // Do we need to add a geoCode?
     $geoCode = '';
     $distance = $this->GetOption('tweetDistance');
     if ($distance != 0) {
         // Use the display ID or the default.
         if ($displayId != 0) {
             // Look up the lat/long
             $display = new Display();
             $display->displayId = $displayId;
             $display->Load();
             $defaultLat = $display->latitude;
             $defaultLong = $display->longitude;
         } else {
             $defaultLat = Config::GetSetting('DEFAULT_LAT');
             $defaultLong = Config::GetSetting('DEFAULT_LONG');
         }
         // Built the geoCode string.
         $geoCode = implode(',', array($defaultLat, $defaultLong, $distance)) . 'mi';
     }
     // Connect to twitter and get the twitter feed.
     $key = md5($this->GetOption('searchTerm') . $this->GetOption('resultType') . $this->GetOption('tweetCount', 15) . $geoCode);
     if (!Cache::has($key) || Cache::get($key) == '') {
         Debug::Audit('Querying API for ' . $this->GetOption('searchTerm'));
         // We need to search for it
         if (!($token = $this->getToken())) {
             return false;
         }
         // We have the token, make a tweet
         if (!($data = $this->searchApi($token, $this->GetOption('searchTerm'), $this->GetOption('resultType'), $geoCode, $this->GetOption('tweetCount', 15)))) {
             return false;
         }
         // Cache it
         Cache::put($key, $data, $this->GetSetting('cachePeriod'));
     } else {
         Debug::Audit('Served from Cache');
         $data = Cache::get($key);
     }
     Debug::Audit(var_export(json_encode($data), true));
     // Get the template
     $template = $this->GetRawNode('template');
     // Parse the text template
     $matches = '';
     preg_match_all('/\\[.*?\\]/', $template, $matches);
     // Build an array to return
     $return = array();
     // Media Object to get profile images
     $media = new Media();
     $layout = new Layout();
     // Expiry time for any media that is downloaded
     $expires = time() + $this->GetSetting('cachePeriodImages') * 60 * 60;
     // Remove URL setting
     $removeUrls = $this->GetOption('removeUrls', 1);
     // If we have nothing to show, display a no tweets message.
     if (count($data->statuses) <= 0) {
         // Create ourselves an empty tweet so that the rest of the code can continue as normal
         $user = new stdClass();
         $user->name = '';
         $user->screen_name = '';
         $user->profile_image_url = '';
         $tweet = new stdClass();
         $tweet->text = $this->GetOption('noTweetsMessage', __('There are no tweets to display'));
         $tweet->created_at = date("Y-m-d H:i:s");
         $tweet->user = $user;
         // Append to our statuses
         $data->statuses[] = $tweet;
     }
     // This should return the formatted items.
     foreach ($data->statuses as $tweet) {
         // Substitute for all matches in the template
         $rowString = $template;
         foreach ($matches[0] as $sub) {
             // Always clear the stored template replacement
             $replace = '';
             // Maybe make this more generic?
             switch ($sub) {
                 case '[Tweet]':
                     // Get the tweet text to operate on
                     $tweetText = $tweet->text;
                     // Replace URLs with their display_url before removal
                     if (isset($tweet->entities->urls)) {
                         foreach ($tweet->entities->urls as $url) {
                             $tweetText = str_replace($url->url, $url->display_url, $tweetText);
                         }
                     }
                     // Handle URL removal if requested
                     if ($removeUrls == 1) {
                         $tweetText = preg_replace("((https?|ftp|gopher|telnet|file|notes|ms-help):((\\/\\/)|(\\\\))+[\\w\\d:#\\@%\\/;\$()~_?\\+-=\\\\.&]*)", '', $tweetText);
                     }
                     $replace = emoji_unified_to_html($tweetText);
                     break;
                 case '[User]':
                     $replace = $tweet->user->name;
                     break;
                 case '[ScreenName]':
                     $replace = $tweet->user->screen_name;
                     break;
                 case '[Date]':
                     $replace = date($this->GetOption('dateFormat', Config::GetSetting('DATE_FORMAT')), DateManager::getDateFromGregorianString($tweet->created_at));
                     break;
                 case '[ProfileImage]':
                     // Grab the profile image
                     if ($tweet->user->profile_image_url != '') {
                         $file = $media->addModuleFileFromUrl($tweet->user->profile_image_url, 'twitter_' . $tweet->user->id, $expires);
                         // Tag this layout with this file
                         $layout->AddLk($this->layoutid, 'module', $file['mediaId']);
                         $replace = $isPreview ? '<img src="index.php?p=module&mod=image&q=Exec&method=GetResource&mediaid=' . $file['mediaId'] . '" />' : '<img src="' . $file['storedAs'] . '" />';
                     }
                     break;
                 case '[Photo]':
                     // See if there are any photos associated with this tweet.
                     if (isset($tweet->entities->media) && count($tweet->entities->media) > 0) {
                         // Only take the first one
                         $photoUrl = $tweet->entities->media[0]->media_url;
                         if ($photoUrl != '') {
                             $file = $media->addModuleFileFromUrl($photoUrl, 'twitter_photo_' . $tweet->user->id . '_' . $tweet->entities->media[0]->id_str, $expires);
                             $replace = $isPreview ? '<img src="index.php?p=module&mod=image&q=Exec&method=GetResource&mediaid=' . $file['mediaId'] . '" />' : '<img src="' . $file['storedAs'] . '" />';
                             // Tag this layout with this file
                             $layout->AddLk($this->layoutid, 'module', $file['mediaId']);
                         }
                     }
                     break;
                 default:
                     $replace = '';
             }
             $rowString = str_replace($sub, $replace, $rowString);
         }
         // Substitute the replacement we have found (it might be '')
         $return[] = $rowString;
     }
     // Return the data array
     return $return;
 }
예제 #12
0
 public function TidyLibrary($tidyOldRevisions, $cleanUnusedFiles)
 {
     // Also run a script to tidy up orphaned media in the library
     $library = Config::GetSetting('LIBRARY_LOCATION');
     $library = rtrim($library, '/') . '/';
     $mediaObject = new Media();
     Debug::Audit('Library Location: ' . $library);
     // Dump the files in the temp folder
     foreach (scandir($library . 'temp') as $item) {
         if ($item == '.' || $item == '..') {
             continue;
         }
         Debug::Audit('Deleting temp file: ' . $item);
         unlink($library . 'temp' . DIRECTORY_SEPARATOR . $item);
     }
     $media = array();
     $unusedMedia = array();
     $unusedRevisions = array();
     // Run a query to get an array containing all of the media in the library
     try {
         $dbh = PDOConnect::init();
         $sth = $dbh->prepare('
             SELECT media.mediaid, media.storedAs, media.type, media.isedited,
                 SUM(CASE WHEN IFNULL(lklayoutmedia.lklayoutmediaid, 0) = 0 THEN 0 ELSE 1 END) AS UsedInLayoutCount,
                 SUM(CASE WHEN IFNULL(lkmediadisplaygroup.id, 0) = 0 THEN 0 ELSE 1 END) AS UsedInDisplayCount
               FROM `media`
                 LEFT OUTER JOIN `lklayoutmedia`
                 ON lklayoutmedia.mediaid = media.mediaid
                 LEFT OUTER JOIN `lkmediadisplaygroup`
                 ON lkmediadisplaygroup.mediaid = media.mediaid
             GROUP BY media.mediaid, media.storedAs, media.type, media.isedited ');
         $sth->execute(array());
         foreach ($sth->fetchAll() as $row) {
             $media[$row['storedAs']] = $row;
             // Ignore any module files or fonts
             if ($row['type'] == 'module' || $row['type'] == 'font') {
                 continue;
             }
             // Collect media revisions that aren't used
             if ($tidyOldRevisions && $row['UsedInLayoutCount'] <= 0 && $row['UsedInDisplayCount'] <= 0 && $row['isedited'] > 0) {
                 $unusedRevisions[$row['storedAs']] = $row;
             } else {
                 if ($cleanUnusedFiles && $row['UsedInLayoutCount'] <= 0 && $row['UsedInDisplayCount'] <= 0) {
                     $unusedMedia[$row['storedAs']] = $row;
                 }
             }
         }
     } catch (Exception $e) {
         Debug::LogEntry('error', $e->getMessage());
         if (!$this->IsError()) {
             $this->SetError(1, __('Unknown Error'));
         }
         return false;
     }
     //Debug::Audit(var_export($media, true));
     //Debug::Audit(var_export($unusedMedia, true));
     // Get a list of all media files
     foreach (scandir($library) as $file) {
         if ($file == '.' || $file == '..') {
             continue;
         }
         if (is_dir($library . $file)) {
             continue;
         }
         // Ignore thumbnails
         if (strstr($file, 'tn_') || strstr($file, 'bg_')) {
             continue;
         }
         // Is this file in the system anywhere?
         if (!array_key_exists($file, $media)) {
             // Totally missing
             Debug::Audit('Deleting file: ' . $file);
             // If not, delete it
             $mediaObject->DeleteMediaFile($file);
         } else {
             if (array_key_exists($file, $unusedRevisions)) {
                 // It exists but isn't being used any more
                 Debug::Audit('Deleting unused revision media: ' . $media[$file]['mediaid']);
                 $mediaObject->Delete($media[$file]['mediaid']);
             } else {
                 if (array_key_exists($file, $unusedMedia)) {
                     // It exists but isn't being used any more
                     Debug::Audit('Deleting unused media: ' . $media[$file]['mediaid']);
                     $mediaObject->Delete($media[$file]['mediaid']);
                 }
             }
         }
     }
     return true;
 }
예제 #13
0
 /**
  * Should the host be considered a proxy exception
  * @param $host
  * @return bool
  */
 public static function isProxyException($host)
 {
     $proxyException = Config::GetSetting('PROXY_EXCEPTIONS');
     Debug::Audit($host . ' in ' . $proxyException . '. Pos = ' . stripos($host, $proxyException));
     return $proxyException != '' && stripos($host, $proxyException) > -1;
 }
예제 #14
0
 /**
  * Exchange request token and secret for an access token and
  * secret, to sign API calls.
  *
  * @returns array("oauth_token" => "the-access-token",
  *                "oauth_token_secret" => "the-access-secret",
  *                "user_id" => "9436992",
  *                "screen_name" => "abraham")
  */
 function getAccessToken($Twitteroauth_verifier)
 {
     $parameters = array();
     $parameters['oauth_verifier'] = $Twitteroauth_verifier;
     $request = $this->TwitteroAuthRequest($this->accessTokenURL(), 'GET', $parameters);
     $token = TwitterOAuthUtil::parse_parameters($request);
     Debug::Audit(var_export($token, true));
     $this->token = new TwitterOAuthConsumer($token['oauth_token'], $token['oauth_token_secret']);
     return $token;
 }
예제 #15
0
 /**
  * Assess each Display to correctly set the logged in flag based on last accessed time
  * @return
  */
 public static function ValidateDisplays()
 {
     // Maintain an array of timed out displays
     $timedOutDisplays = array();
     try {
         $dbh = PDOConnect::init();
         $statObject = new Stat();
         // Get a list of all displays and there last accessed / alert time out value
         $sth = $dbh->prepare('SELECT displayid, display, lastaccessed, alert_timeout, client_type, displayprofileid, email_alert, loggedin FROM display');
         $sthUpdate = $dbh->prepare('UPDATE display SET loggedin = 0 WHERE displayid = :displayid');
         $sth->execute(array());
         // Get the global time out (overrides the alert time out on the display if 0)
         $globalTimeout = Config::GetSetting('MAINTENANCE_ALERT_TOUT') * 60;
         $displays = $sth->fetchAll();
         foreach ($displays as $row) {
             $displayid = Kit::ValidateParam($row['displayid'], _INT);
             $lastAccessed = Kit::ValidateParam($row['lastaccessed'], _INT);
             $alertTimeout = Kit::ValidateParam($row['alert_timeout'], _INT);
             $clientType = Kit::ValidateParam($row['client_type'], _WORD);
             $loggedIn = Kit::ValidateParam($row['loggedin'], _INT);
             // Get the config object
             if ($alertTimeout == 0 && $clientType != '') {
                 $displayProfileId = empty($row['displayprofileid']) ? 0 : Kit::ValidateParam($row['displayprofileid'], _INT);
                 $display = new Display();
                 $display->displayId = $displayid;
                 $display->displayProfileId = $displayProfileId;
                 $display->clientType = $clientType;
                 $timeoutToTestAgainst = $display->GetSetting('collectInterval', $globalTimeout);
             } else {
                 $timeoutToTestAgainst = $globalTimeout;
             }
             // Store the time out to test against
             $row['timeout'] = $timeoutToTestAgainst;
             $timeOut = $lastAccessed + $timeoutToTestAgainst;
             // If the last time we accessed is less than now minus the time out
             if ($timeOut < time()) {
                 Debug::Audit('Timed out display. Last Accessed: ' . date('Y-m-d h:i:s', $lastAccessed) . '. Time out: ' . date('Y-m-d h:i:s', $timeOut));
                 // If this is the first switch (i.e. the row was logged in before)
                 if ($loggedIn == 1) {
                     // Update the display and set it as logged out
                     $sthUpdate->execute(array('displayid' => $displayid));
                     // Log the down event
                     $statObject->displayDown($displayid, $lastAccessed);
                 }
                 // Store this row
                 $timedOutDisplays[] = $row;
             }
         }
         return $timedOutDisplays;
     } catch (Exception $e) {
         Debug::LogEntry('error', $e->getMessage(), get_class(), __FUNCTION__);
         return false;
     }
 }
예제 #16
0
 /**
  * Store the media inventory for a client
  * @param string $version
  * @param string $serverKey
  * @param string $hardwareKey
  * @param string $inventory
  * @return bool
  * @throws SoapFault
  */
 public function MediaInventory($version, $serverKey, $hardwareKey, $inventory)
 {
     // Sanitize
     $serverKey = Kit::ValidateParam($serverKey, _STRING);
     $hardwareKey = Kit::ValidateParam($hardwareKey, _STRING);
     $inventory = Kit::ValidateParam($inventory, _HTMLSTRING);
     // Check the serverKey matches
     if ($serverKey != Config::GetSetting('SERVER_KEY')) {
         throw new SoapFault('Sender', 'The Server key you entered does not match with the server key at this address');
     }
     // Make sure we are sticking to our bandwidth limit
     if (!$this->CheckBandwidth()) {
         throw new SoapFault('Receiver', "Bandwidth Limit exceeded");
     }
     // Auth this request...
     if (!$this->AuthDisplay($hardwareKey)) {
         throw new SoapFault('Receiver', 'This display client is not licensed');
     }
     if ($this->isAuditing == 1) {
         Debug::Audit($inventory, 'xmds', $this->displayId);
     }
     // Check that the $inventory contains something
     if ($inventory == '') {
         throw new SoapFault('Receiver', 'Inventory Cannot be Empty');
     }
     // Load the XML into a DOMDocument
     $document = new DOMDocument("1.0");
     $document->loadXML($inventory);
     // Assume we are complete (but we are getting some)
     $mediaInventoryComplete = 1;
     $xpath = new DOMXPath($document);
     $fileNodes = $xpath->query("//file");
     foreach ($fileNodes as $node) {
         $mediaId = $node->getAttribute('id');
         $complete = $node->getAttribute('complete');
         $md5 = $node->getAttribute('md5');
         $lastChecked = $node->getAttribute('lastChecked');
         // TODO: Check the MD5?
         // If this item is a 0 then set not complete
         if ($complete == 0) {
             $mediaInventoryComplete = 2;
         }
     }
     // Touch the display record
     $displayObject = new Display();
     $displayObject->Touch($this->displayId, array('mediaInventoryStatus' => $mediaInventoryComplete, 'mediaInventoryXml' => $inventory));
     $this->LogBandwidth($this->displayId, Bandwidth::$MEDIAINVENTORY, strlen($inventory));
     return true;
 }
예제 #17
0
 /**
  * GetResource
  *     Return the rendered resource to be used by the client (or a preview)
  *     for displaying this content.
  * @param integer $displayId If this comes from a real client, this will be the display id.
  * @return string
  */
 public function GetResource($displayId = 0)
 {
     // Load in the template
     $template = file_get_contents('modules/preview/HtmlTemplate.html');
     $isPreview = Kit::GetParam('preview', _REQUEST, _WORD, 'false') == 'true';
     // Replace the View Port Width?
     if ($isPreview) {
         $template = str_replace('[[ViewPortWidth]]', $this->width, $template);
     }
     // Information from the Module
     $duration = $this->duration;
     // Generate a JSON string of items.
     if (!($items = $this->getYql($displayId, $isPreview))) {
         return '';
     }
     // Run through each item and substitute with the template
     $itemTemplate = $this->GetRawNode('template');
     $renderedItems = array();
     foreach ($items as $item) {
         $renderedItems[] = $this->makeSubstitutions($item, $itemTemplate);
     }
     Debug::Audit('Items: ' . var_export($items, true));
     Debug::Audit('Rendered items: ' . var_export($renderedItems, true));
     $options = array('type' => $this->type, 'fx' => $this->GetOption('effect', 'none'), 'speed' => $this->GetOption('speed', 500), 'duration' => $duration, 'durationIsPerItem' => $this->GetOption('durationIsPerItem', 0) == 1, 'numItems' => count($renderedItems), 'itemsPerPage' => 1, 'originalWidth' => $this->width, 'originalHeight' => $this->height, 'previewWidth' => Kit::GetParam('width', _GET, _DOUBLE, 0), 'previewHeight' => Kit::GetParam('height', _GET, _DOUBLE, 0), 'scaleOverride' => Kit::GetParam('scale_override', _GET, _DOUBLE, 0));
     // Replace the control meta with our data from twitter
     $controlMeta = '<!-- NUMITEMS=' . count($items) . ' -->' . PHP_EOL . '<!-- DURATION=' . ($this->GetOption('durationIsPerItem', 0) == 0 ? $duration : $duration * count($items)) . ' -->';
     $template = str_replace('<!--[[[CONTROLMETA]]]-->', $controlMeta, $template);
     // Replace the head content
     $headContent = '';
     // Add the CSS if it isn't empty
     $css = $this->GetRawNode('styleSheet');
     if ($css != '') {
         $headContent .= '<style type="text/css">' . $css . '</style>';
     }
     $backgroundColor = $this->GetOption('backgroundColor');
     if ($backgroundColor != '') {
         $headContent .= '<style type="text/css">body, .page, .item { background-color: ' . $backgroundColor . ' }</style>';
     }
     // Add our fonts.css file
     $headContent .= '<link href="' . ($isPreview ? 'modules/preview/' : '') . 'fonts.css" rel="stylesheet" media="screen">';
     $headContent .= '<link href="' . ($isPreview ? 'modules/theme/twitter/' : '') . 'emoji.css" rel="stylesheet" media="screen">';
     $headContent .= '<style type="text/css">' . file_get_contents(Theme::ItemPath('css/client.css')) . '</style>';
     // Replace the Head Content with our generated javascript
     $template = str_replace('<!--[[[HEADCONTENT]]]-->', $headContent, $template);
     // Add some scripts to the JavaScript Content
     $javaScriptContent = '<script type="text/javascript" src="' . ($isPreview ? 'modules/preview/vendor/' : '') . 'jquery-1.11.1.min.js"></script>';
     // Need the cycle plugin?
     if ($this->GetSetting('effect') != 'none') {
         $javaScriptContent .= '<script type="text/javascript" src="' . ($isPreview ? 'modules/preview/vendor/' : '') . 'jquery-cycle-2.1.6.min.js"></script>';
     }
     // Need the marquee plugin?
     if (stripos($this->GetSetting('effect'), 'marquee') !== false) {
         $javaScriptContent .= '<script type="text/javascript" src="' . ($isPreview ? 'modules/preview/vendor/' : '') . 'jquery.marquee.min.js"></script>';
     }
     $javaScriptContent .= '<script type="text/javascript" src="' . ($isPreview ? 'modules/preview/' : '') . 'xibo-layout-scaler.js"></script>';
     $javaScriptContent .= '<script type="text/javascript" src="' . ($isPreview ? 'modules/preview/' : '') . 'xibo-text-render.js"></script>';
     $javaScriptContent .= '<script type="text/javascript">';
     $javaScriptContent .= '   var options = ' . json_encode($options) . ';';
     $javaScriptContent .= '   var items = ' . Kit::jsonEncode($renderedItems) . ';';
     $javaScriptContent .= '   $(document).ready(function() { ';
     $javaScriptContent .= '       $("body").xiboLayoutScaler(options); $("#content").xiboTextRender(options, items); ';
     $javaScriptContent .= '   }); ';
     $javaScriptContent .= '</script>';
     // Replace the Head Content with our generated javascript
     $template = str_replace('<!--[[[JAVASCRIPTCONTENT]]]-->', $javaScriptContent, $template);
     // Replace the Body Content with our generated text
     $template = str_replace('<!--[[[BODYCONTENT]]]-->', '', $template);
     return $template;
 }
예제 #18
0
 /**
  * Shows the TimeLine
  */
 public function TimelineList()
 {
     $db =& $this->db;
     $user =& $this->user;
     $user->SetPref('timeLineView', 'list');
     $response = new ResponseManager();
     $response->html = '';
     $layoutId = Kit::GetParam('layoutid', _GET, _INT);
     $regionId = Kit::GetParam('regionid', _REQUEST, _STRING);
     // Make sure we have permission to edit this region
     Kit::ClassLoader('region');
     $region = new region($db);
     $ownerId = $region->GetOwnerId($layoutId, $regionId);
     $regionAuth = $this->user->RegionAssignmentAuth($ownerId, $layoutId, $regionId, true);
     if (!$regionAuth->edit) {
         trigger_error(__('You do not have permissions to edit this region'), E_USER_ERROR);
     }
     $response->html .= '<div class="container-fluid">';
     $response->html .= '<div class="row">';
     // Set the theme module buttons
     $this->ThemeSetModuleButtons($layoutId, $regionId);
     $response->html .= Theme::RenderReturn('layout_designer_form_timeline');
     // Load the XML for this layout and region, we need to get the media nodes.
     // These form the time line and go in the right column
     // Generate an ID for the list (this is passed into the reorder function)
     $timeListMediaListId = uniqid('timelineMediaList_');
     $response->html .= '<div class="col-md-10">';
     $response->html .= '<div id="timelineControl" class="timelineColumn" layoutid="' . $layoutId . '" regionid="' . $regionId . '">';
     $response->html .= '    <div class="timelineMediaVerticalList">';
     $response->html .= '        <ul id="' . $timeListMediaListId . '" class="timelineSortableListOfMedia">';
     // How are we going to colour the bars, my media type or my permissions
     $timeBarColouring = Config::GetSetting('REGION_OPTIONS_COLOURING');
     // Create a layout object
     $region = new Region($db);
     foreach ($region->GetMediaNodeList($layoutId, $regionId) as $mediaNode) {
         // Put this node vertically in the region time line
         $mediaId = $mediaNode->getAttribute('id');
         $lkId = $mediaNode->getAttribute('lkid');
         $mediaType = $mediaNode->getAttribute('type');
         $mediaDuration = $mediaNode->getAttribute('duration');
         $ownerId = $mediaNode->getAttribute('userId');
         // Permissions for this assignment
         $auth = $user->MediaAssignmentAuth($ownerId, $layoutId, $regionId, $mediaId, true);
         // Skip over media assignments that we do not have permission to see
         if (!$auth->view) {
             continue;
         }
         Debug::LogEntry('audit', sprintf('Permission Granted to View MediaID: %s', $mediaId), 'layout', 'TimeLine');
         // Create a media module to handle all the complex stuff
         try {
             $tmpModule = ModuleFactory::load($mediaType, $layoutId, $regionId, $mediaId, $lkId, $this->db, $this->user);
         } catch (Exception $e) {
             Debug::Audit('Caught exception from Module Create');
             trigger_error($e->getMessage(), E_USER_ERROR);
         }
         $mediaName = $tmpModule->GetName();
         $transitionIn = $tmpModule->GetTransition('in');
         $transitionOut = $tmpModule->GetTransition('out');
         // Colouring for the media block
         if ($timeBarColouring == 'Permissions') {
             $mediaBlockColouringClass = 'timelineMediaItemColouring_' . ($auth->edit ? 'enabled' : 'disabled');
         } else {
             $mediaBlockColouringClass = 'timelineMediaItemColouringDefault timelineMediaItemColouring_' . $mediaType;
         }
         // Create the list item
         $response->html .= '<li class="timelineMediaListItem" mediaid="' . $mediaId . '" lkid="' . $lkId . '">';
         // In transition
         $response->html .= '    <div class="timelineMediaInTransition">';
         if ($transitionIn != 'None') {
             $response->html .= '<span>' . $transitionIn . '</span>';
         }
         $response->html .= '    </div>';
         // Media Bar
         $response->html .= '    <div class="timelineMediaItem">';
         $response->html .= '        <ul class="timelineMediaItemLinks">';
         // Create some links
         if ($auth->edit) {
             $response->html .= '<li><a class="XiboFormButton timelineMediaBarLink" href="index.php?p=module&mod=' . $mediaType . '&q=Exec&method=EditForm&layoutid=' . $layoutId . '&regionid=' . $regionId . '&mediaid=' . $mediaId . '&lkid=' . $lkId . '" title="' . __('Click to edit this media') . '">' . __('Edit') . '</a></li>';
         }
         if ($auth->del) {
             $response->html .= '<li><a class="XiboFormButton timelineMediaBarLink" href="index.php?p=module&mod=' . $mediaType . '&q=Exec&method=DeleteForm&layoutid=' . $layoutId . '&regionid=' . $regionId . '&mediaid=' . $mediaId . '&lkid=' . $lkId . '" title="' . __('Click to delete this media') . '">' . __('Delete') . '</a></li>';
         }
         if ($auth->modifyPermissions) {
             $response->html .= '<li><a class="XiboFormButton timelineMediaBarLink" href="index.php?p=module&mod=' . $mediaType . '&q=Exec&method=PermissionsForm&layoutid=' . $layoutId . '&regionid=' . $regionId . '&mediaid=' . $mediaId . '&lkid=' . $lkId . '" title="Click to change permissions for this media">' . __('Permissions') . '</a></li>';
         }
         if (count($this->user->TransitionAuth('in')) > 0) {
             $response->html .= '<li><a class="XiboFormButton timelineMediaBarLink" href="index.php?p=module&mod=' . $mediaType . '&q=Exec&method=TransitionEditForm&type=in&layoutid=' . $layoutId . '&regionid=' . $regionId . '&mediaid=' . $mediaId . '&lkid=' . $lkId . '" title="' . __('Click to edit this transition') . '">' . __('In Transition') . '</a></li>';
         }
         if (count($this->user->TransitionAuth('out')) > 0) {
             $response->html .= '<li><a class="XiboFormButton timelineMediaBarLink" href="index.php?p=module&mod=' . $mediaType . '&q=Exec&method=TransitionEditForm&type=out&layoutid=' . $layoutId . '&regionid=' . $regionId . '&mediaid=' . $mediaId . '&lkid=' . $lkId . '" title="' . __('Click to edit this transition') . '">' . __('Out Transition') . '</a></li>';
         }
         $response->html .= '        </ul>';
         // Put the media name in
         $response->html .= '        <div class="timelineMediaDetails ' . $mediaBlockColouringClass . '">';
         $response->html .= '            <h3>' . sprintf('%s (%d seconds)', $mediaName == '' ? __($tmpModule->displayType) : $mediaName, $mediaDuration) . '</h3>';
         $response->html .= '        </div>';
         // Put the media hover preview in
         $mediaHoverPreview = $tmpModule->HoverPreview();
         $response->html .= '        <div class="timelineMediaPreview">' . $mediaHoverPreview . '</div>';
         // End the time line media item
         $response->html .= '    </div>';
         // Out transition
         $response->html .= '    <div class="timelineMediaOutTransition">';
         if ($transitionOut != 'None') {
             $response->html .= '<span>' . $transitionOut . '</span>';
         }
         $response->html .= '    </div>';
         // End of this media item
         $response->html .= '</li>';
     }
     $response->html .= '        </ul>';
     $response->html .= '    </div>';
     // Output a div to contain the preview for this media item
     $response->html .= '    <div id="timelinePreview"></div>';
     $response->html .= '    </div>';
     $response->html .= '</div>';
     $response->html .= '</div>';
     $response->html .= '</div>';
     // Finish constructing the response
     $response->callBack = 'LoadTimeLineCallback';
     $response->dialogClass = 'modal-big';
     $response->dialogTitle = __('Region Timeline');
     $response->dialogSize = true;
     $response->dialogWidth = '1000px';
     $response->dialogHeight = '550px';
     $response->focusInFirstInput = false;
     // Add some buttons
     $response->AddButton(__('Save Order'), 'XiboTimelineSaveOrder("' . $timeListMediaListId . '","' . $layoutId . '","' . $regionId . '")');
     $response->AddButton(__('Switch to Grid'), 'XiboSwapDialog("index.php?p=timeline&q=TimelineGrid&layoutid=' . $layoutId . '&regionid=' . $regionId . '")');
     $response->AddButton(__('Help'), 'XiboHelpRender("' . HelpManager::Link('Layout', 'RegionOptions') . '")');
     $response->AddButton(__('Close'), 'XiboDialogClose()');
     $response->Respond();
 }
예제 #19
0
 /** 
  * Loads templates for this module
  */
 public function loadTemplates()
 {
     // Scan the folder for template files
     foreach (glob('modules/theme/forecastio/*.template.json') as $template) {
         // Read the contents, json_decode and add to the array
         $this->settings['templates'][] = json_decode(file_get_contents($template), true);
     }
     Debug::Audit(count($this->settings['templates']));
 }
예제 #20
0
 /**
  * Shows the stats grid
  */
 public function StatsGrid()
 {
     $db =& $this->db;
     $user =& $this->user;
     $response = new ResponseManager();
     $fromDt = DateManager::getIsoDateFromString(Kit::GetParam('fromdt', _POST, _STRING));
     $toDt = DateManager::getIsoDateFromString(Kit::GetParam('todt', _POST, _STRING));
     $displayId = Kit::GetParam('displayid', _POST, _INT);
     $mediaId = Kit::GetParam('mediaid', _POST, _INT);
     // What if the fromdt and todt are exactly the same?
     // in this case assume an entire day from midnight on the fromdt to midnight on the todt (i.e. add a day to the todt)
     if ($fromDt == $toDt) {
         $toDt = date("Y-m-d", strtotime($toDt) + 86399);
     }
     Debug::Audit('Converted Times received are: FromDt=' . $fromDt . '. ToDt=' . $toDt);
     // Get an array of display id this user has access to.
     $displays = $this->user->DisplayList();
     $display_ids = array();
     foreach ($displays as $display) {
         $display_ids[] = $display['displayid'];
     }
     if (count($display_ids) <= 0) {
         trigger_error(__('No displays with View permissions'), E_USER_ERROR);
     }
     // 3 grids showing different stats.
     // Layouts Ran
     $SQL = 'SELECT display.Display, layout.Layout, COUNT(StatID) AS NumberPlays, SUM(TIME_TO_SEC(TIMEDIFF(end, start))) AS Duration, MIN(start) AS MinStart, MAX(end) AS MaxEnd ';
     $SQL .= '  FROM stat ';
     $SQL .= '  INNER JOIN layout ON layout.LayoutID = stat.LayoutID ';
     $SQL .= '  INNER JOIN display ON stat.DisplayID = display.DisplayID ';
     $SQL .= " WHERE stat.type = 'layout' ";
     $SQL .= sprintf("  AND stat.end > '%s' ", $fromDt);
     $SQL .= sprintf("  AND stat.start <= '%s' ", $toDt);
     $SQL .= ' AND stat.displayID IN (' . implode(',', $display_ids) . ') ';
     if ($displayId != 0) {
         $SQL .= sprintf("  AND stat.displayID = %d ", $displayId);
     }
     $SQL .= 'GROUP BY display.Display, layout.Layout ';
     $SQL .= 'ORDER BY display.Display, layout.Layout';
     // Log
     Debug::sql($SQL);
     if (!($results = $this->db->query($SQL))) {
         trigger_error($db->error());
         trigger_error(__('Unable to get Layouts Shown'), E_USER_ERROR);
     }
     $cols = array(array('name' => 'Display', 'title' => __('Display')), array('name' => 'Layout', 'title' => __('Layout')), array('name' => 'NumberPlays', 'title' => __('Number of Plays')), array('name' => 'DurationSec', 'title' => __('Total Duration (s)')), array('name' => 'Duration', 'title' => __('Total Duration')), array('name' => 'MinStart', 'title' => __('First Shown')), array('name' => 'MaxEnd', 'title' => __('Last Shown')));
     Theme::Set('table_cols', $cols);
     $rows = array();
     while ($row = $db->get_assoc_row($results)) {
         $row['Display'] = Kit::ValidateParam($row['Display'], _STRING);
         $row['Layout'] = Kit::ValidateParam($row['Layout'], _STRING);
         $row['NumberPlays'] = Kit::ValidateParam($row['NumberPlays'], _INT);
         $row['DurationSec'] = Kit::ValidateParam($row['Duration'], _INT);
         $row['Duration'] = sec2hms(Kit::ValidateParam($row['Duration'], _INT));
         $row['MinStart'] = DateManager::getLocalDate(strtotime(Kit::ValidateParam($row['MinStart'], _STRING)));
         $row['MaxEnd'] = DateManager::getLocalDate(strtotime(Kit::ValidateParam($row['MaxEnd'], _STRING)));
         $rows[] = $row;
     }
     Theme::Set('table_rows', $rows);
     Theme::Set('table_layouts_shown', Theme::RenderReturn('table_render'));
     // Media Ran
     $SQL = 'SELECT display.Display, media.Name, COUNT(StatID) AS NumberPlays, SUM(TIME_TO_SEC(TIMEDIFF(end, start))) AS Duration, MIN(start) AS MinStart, MAX(end) AS MaxEnd ';
     $SQL .= '  FROM stat ';
     $SQL .= '  INNER JOIN display ON stat.DisplayID = display.DisplayID ';
     $SQL .= '  INNER JOIN  media ON media.MediaID = stat.MediaID ';
     $SQL .= " WHERE stat.type = 'media' ";
     $SQL .= sprintf("  AND stat.end > '%s' ", $fromDt);
     $SQL .= sprintf("  AND stat.start <= '%s' ", $toDt);
     $SQL .= ' AND stat.displayID IN (' . implode(',', $display_ids) . ') ';
     if ($mediaId != 0) {
         $SQL .= sprintf("  AND media.MediaID = %d ", $mediaId);
     }
     if ($displayId != 0) {
         $SQL .= sprintf("  AND stat.displayID = %d ", $displayId);
     }
     $SQL .= 'GROUP BY display.Display, media.Name ';
     $SQL .= 'ORDER BY display.Display, media.Name';
     if (!($results = $this->db->query($SQL))) {
         trigger_error($db->error());
         trigger_error(__('Unable to get Library Media Ran'), E_USER_ERROR);
     }
     $cols = array(array('name' => 'Display', 'title' => __('Display')), array('name' => 'Media', 'title' => __('Media')), array('name' => 'NumberPlays', 'title' => __('Number of Plays')), array('name' => 'DurationSec', 'title' => __('Total Duration (s)')), array('name' => 'Duration', 'title' => __('Total Duration')), array('name' => 'MinStart', 'title' => __('First Shown')), array('name' => 'MaxEnd', 'title' => __('Last Shown')));
     Theme::Set('table_cols', $cols);
     $rows = array();
     while ($row = $db->get_assoc_row($results)) {
         $row['Display'] = Kit::ValidateParam($row['Display'], _STRING);
         $row['Media'] = Kit::ValidateParam($row['Name'], _STRING);
         $row['NumberPlays'] = Kit::ValidateParam($row['NumberPlays'], _INT);
         $row['DurationSec'] = Kit::ValidateParam($row['Duration'], _INT);
         $row['Duration'] = sec2hms(Kit::ValidateParam($row['Duration'], _INT));
         $row['MinStart'] = DateManager::getLocalDate(strtotime(Kit::ValidateParam($row['MinStart'], _STRING)));
         $row['MaxEnd'] = DateManager::getLocalDate(strtotime(Kit::ValidateParam($row['MaxEnd'], _STRING)));
         $rows[] = $row;
     }
     Theme::Set('table_rows', $rows);
     Theme::Set('table_media_shown', Theme::RenderReturn('table_render'));
     // Media on Layouts Ran
     $SQL = "SELECT display.Display, layout.Layout, IFNULL(media.Name, 'Text/Rss/Webpage') AS Name, COUNT(StatID) AS NumberPlays, SUM(TIME_TO_SEC(TIMEDIFF(end, start))) AS Duration, MIN(start) AS MinStart, MAX(end) AS MaxEnd ";
     $SQL .= '  FROM stat ';
     $SQL .= '  INNER JOIN display ON stat.DisplayID = display.DisplayID ';
     $SQL .= '  INNER JOIN layout ON layout.LayoutID = stat.LayoutID ';
     $SQL .= '  LEFT OUTER JOIN media ON media.MediaID = stat.MediaID ';
     $SQL .= " WHERE stat.type = 'media' ";
     $SQL .= sprintf("  AND stat.end > '%s' ", $fromDt);
     $SQL .= sprintf("  AND stat.start <= '%s' ", $toDt);
     $SQL .= ' AND stat.displayID IN (' . implode(',', $display_ids) . ') ';
     if ($mediaId != 0) {
         $SQL .= sprintf("  AND media.MediaID = %d ", $mediaId);
     }
     if ($displayId != 0) {
         $SQL .= sprintf("  AND stat.displayID = %d ", $displayId);
     }
     $SQL .= "GROUP BY display.Display, layout.Layout, IFNULL(media.Name, 'Text/Rss/Webpage') ";
     $SQL .= "ORDER BY display.Display, layout.Layout, IFNULL(media.Name, 'Text/Rss/Webpage')";
     if (!($results = $this->db->query($SQL))) {
         trigger_error($db->error());
         trigger_error(__('Unable to get Library Media Ran'), E_USER_ERROR);
     }
     $cols = array(array('name' => 'Display', 'title' => __('Display')), array('name' => 'Layout', 'title' => __('Layout')), array('name' => 'Media', 'title' => __('Media')), array('name' => 'NumberPlays', 'title' => __('Number of Plays')), array('name' => 'DurationSec', 'title' => __('Total Duration (s)')), array('name' => 'Duration', 'title' => __('Total Duration')), array('name' => 'MinStart', 'title' => __('First Shown')), array('name' => 'MaxEnd', 'title' => __('Last Shown')));
     Theme::Set('table_cols', $cols);
     $rows = array();
     while ($row = $db->get_assoc_row($results)) {
         $row['Display'] = Kit::ValidateParam($row['Display'], _STRING);
         $row['Layout'] = Kit::ValidateParam($row['Layout'], _STRING);
         $row['Media'] = Kit::ValidateParam($row['Name'], _STRING);
         $row['NumberPlays'] = Kit::ValidateParam($row['NumberPlays'], _INT);
         $row['DurationSec'] = Kit::ValidateParam($row['Duration'], _INT);
         $row['Duration'] = sec2hms(Kit::ValidateParam($row['Duration'], _INT));
         $row['MinStart'] = DateManager::getLocalDate(strtotime(Kit::ValidateParam($row['MinStart'], _STRING)));
         $row['MaxEnd'] = DateManager::getLocalDate(strtotime(Kit::ValidateParam($row['MaxEnd'], _STRING)));
         $rows[] = $row;
     }
     Theme::Set('table_rows', $rows);
     Theme::Set('table_media_on_layouts_shown', Theme::RenderReturn('table_render'));
     $output = Theme::RenderReturn('stats_page_grid');
     $response->SetGridResponse($output);
     $response->paging = false;
     $response->Respond();
 }
예제 #21
0
 /**
  * Json Encode, handling and logging errors
  * http://stackoverflow.com/questions/10199017/how-to-solve-json-error-utf8-error-in-php-json-decode
  * @param  mixed $mixed The item to encode
  * @return mixed The Encoded Item
  */
 public static function jsonEncode($mixed)
 {
     if (version_compare(PHP_VERSION, '5.4.0') >= 0) {
         $encoded = json_encode($mixed, JSON_PRETTY_PRINT);
     } else {
         $encoded = json_encode($mixed);
     }
     switch (json_last_error()) {
         case JSON_ERROR_NONE:
             return $encoded;
         case JSON_ERROR_DEPTH:
             Debug::Audit('Maximum stack depth exceeded');
             return false;
         case JSON_ERROR_STATE_MISMATCH:
             Debug::Audit('Underflow or the modes mismatch');
             return false;
         case JSON_ERROR_CTRL_CHAR:
             Debug::Audit('Unexpected control character found');
             return false;
         case JSON_ERROR_SYNTAX:
             Debug::Audit('Syntax error, malformed JSON');
             return false;
         case JSON_ERROR_UTF8:
             $clean = Kit::utf8ize($mixed);
             return Kit::jsonEncode($clean);
         default:
             Debug::Audit('Unknown error');
             return false;
     }
 }
예제 #22
0
         header('HTTP/1.0 404 Not Found');
     } else {
         // Issue magic packet
         // Send via Apache X-Sendfile header?
         if ($sendFileMode == 'Apache') {
             Debug::LogEntry('audit', 'HTTP GetFile request redirecting to ' . Config::GetSetting('LIBRARY_LOCATION') . $file['storedAs'], 'services');
             header('X-Sendfile: ' . Config::GetSetting('LIBRARY_LOCATION') . $file['storedAs']);
         } else {
             if ($sendFileMode == 'Nginx') {
                 header('X-Accel-Redirect: /download/' . $file['storedAs']);
             } else {
                 header('HTTP/1.0 404 Not Found');
             }
         }
         // Debug
         Debug::Audit('File request via magic packet. ' . $file['storedAs'], $file['displayId']);
         // Log bandwidth
         $bandwidth = new Bandwidth();
         $bandwidth->Log($file['displayId'], 4, $file['size']);
     }
     exit;
 }
 try {
     $wsdl = 'lib/service/service_v' . $version . '.wsdl';
     if (!file_exists($wsdl)) {
         $serviceResponse->ErrorServerError('Your client is not the correct version to communicate with this CMS.');
     }
     $soap = new SoapServer($wsdl);
     //$soap = new SoapServer($wsdl, array('cache_wsdl' => WSDL_CACHE_NONE));
     $soap->setClass('XMDSSoap' . $version);
     $soap->handle();
예제 #23
0
 /**
  * Import a Folder of ZIP files
  * @param  [string] $folder The folder to import
  */
 public function importFolder($folder)
 {
     Debug::Audit('Importing folder: ' . $folder);
     if (is_dir($folder)) {
         // Get a list of files
         foreach (array_diff(scandir($folder), array('..', '.')) as $file) {
             Debug::Audit('Found file: ' . $file);
             if (stripos($file, '.zip')) {
                 $this->Import($folder . DIRECTORY_SEPARATOR . $file, NULL, 1, false, false, true, false);
             }
         }
     }
 }
예제 #24
0
 /**
  * Deletes an Xibo User Group
  * @param int $userGroupId
  * @return bool
  */
 public function Delete($userGroupId)
 {
     Debug::Audit('IN: ' . $userGroupId);
     try {
         $dbh = PDOConnect::init();
         $params = array('groupid' => $userGroupId);
         // Delete all permissions
         $sth = $dbh->prepare('DELETE FROM `lkcampaigngroup` WHERE GroupID = :groupid');
         $sth->execute($params);
         $sth = $dbh->prepare('DELETE FROM `lkdatasetgroup` WHERE GroupID = :groupid');
         $sth->execute($params);
         $sth = $dbh->prepare('DELETE FROM `lkdisplaygroupgroup` WHERE GroupID = :groupid');
         $sth->execute($params);
         $sth = $dbh->prepare('DELETE FROM `lklayoutmediagroup` WHERE GroupID = :groupid');
         $sth->execute($params);
         $sth = $dbh->prepare('DELETE FROM `lklayoutregiongroup` WHERE GroupID = :groupid');
         $sth->execute($params);
         $sth = $dbh->prepare('DELETE FROM `lkmediagroup` WHERE GroupID = :groupid');
         $sth->execute($params);
         // Remove linked users
         $sth = $dbh->prepare('DELETE FROM `lkusergroup` WHERE GroupID = :groupid');
         $sth->execute($params);
         // Delete all menu links
         $sth = $dbh->prepare('DELETE FROM `lkmenuitemgroup` WHERE GroupID = :groupid');
         $sth->execute($params);
         // Delete all page links
         $sth = $dbh->prepare('DELETE FROM `lkpagegroup` WHERE GroupID = :groupid');
         $sth->execute($params);
         // Delete the user group
         $sth = $dbh->prepare('DELETE FROM `group` WHERE GroupID = :groupid');
         $sth->execute($params);
         return true;
     } catch (Exception $e) {
         Debug::LogEntry('error', $e->getMessage());
         if (!$this->IsError()) {
             return $this->SetError(25015, __('Unable to delete User Group.'));
         }
         return false;
     }
 }
예제 #25
0
 /**
  * Get Resource
  */
 public function GetResource($displayId = 0)
 {
     $proportional = Kit::GetParam('proportional', _GET, _BOOL, true);
     $thumb = Kit::GetParam('thumb', _GET, _BOOL, false);
     $dynamic = isset($_REQUEST['dynamic']);
     $file = $this->storedAs;
     $width = intval(Kit::GetParam('width', _REQUEST, _DOUBLE, 80));
     $height = intval(Kit::GetParam('height', _REQUEST, _DOUBLE, 80));
     // File upload directory.. get this from the settings object
     $library = Config::GetSetting("LIBRARY_LOCATION");
     $fileName = $library . $file;
     Debug::Audit(sprintf('Image Request %dx%d %s. Thumb: %s', $width, $height, $fileName, $thumb));
     // If we are a thumb request then output the cached thumbnail
     if ($thumb) {
         $fileName = $library . sprintf('tn_%dx%d_%s', $width, $height, $file);
         // If the thumbnail doesn't exist then create one
         if (!file_exists($fileName)) {
             Debug::LogEntry('audit', 'File doesnt exist, creating a thumbnail for ' . $fileName);
             if (!($info = getimagesize($library . $file))) {
                 die($library . $file . ' is not an image');
             }
             ResizeImage($library . $file, $fileName, $width, $height, $proportional, 'file');
         }
     }
     // Get the info for this new temporary file
     if (!($info = getimagesize($fileName))) {
         $fileName = 'theme/default/img/forms/filenotfound.png';
         $this->ReturnFile($fileName);
         exit;
     }
     if ($dynamic && !$thumb && $info[2]) {
         $width = intval(Kit::GetParam('width', _REQUEST, _DOUBLE, 80));
         $height = intval(Kit::GetParam('height', _REQUEST, _DOUBLE, 80));
         // dynamically create an image of the correct size - used for previews
         ResizeImage($fileName, '', $width, $height, $proportional, 'browser');
         exit;
     }
     if (!file_exists($fileName)) {
         //not sure
         Debug::LogEntry('audit', "Cant find: {$uid}", 'module', 'GetResource');
         $fileName = 'theme/default/img/forms/filenotfound.png';
     }
     $this->ReturnFile($fileName);
     exit;
 }
예제 #26
0
 private function GetDataSetItems($displayId, $isPreview, $text)
 {
     $db =& $this->db;
     // Extra fields for data sets
     $dataSetId = $this->GetOption('datasetid');
     $upperLimit = $this->GetOption('upperLimit');
     $lowerLimit = $this->GetOption('lowerLimit');
     $filter = $this->GetOption('filter');
     $ordering = $this->GetOption('ordering');
     Debug::LogEntry('audit', 'Then template for each row is: ' . $text);
     // Set an expiry time for the media
     $media = new Media();
     $layout = new Layout();
     $expires = time() + $this->GetOption('updateInterval', 3600) * 60;
     // Combine the column id's with the dataset data
     $matches = '';
     preg_match_all('/\\[(.*?)\\]/', $text, $matches);
     $columnIds = array();
     foreach ($matches[1] as $match) {
         // Get the column id's we are interested in
         Debug::LogEntry('audit', 'Matched column: ' . $match);
         $col = explode('|', $match);
         $columnIds[] = $col[1];
     }
     // Get the dataset results
     $dataSet = new DataSet($db);
     if (!($dataSetResults = $dataSet->DataSetResults($dataSetId, implode(',', $columnIds), $filter, $ordering, $lowerLimit, $upperLimit, $displayId))) {
         return;
     }
     // Create an array of header|datatypeid pairs
     $columnMap = array();
     foreach ($dataSetResults['Columns'] as $col) {
         $columnMap[$col['Text']] = $col;
     }
     Debug::Audit(var_export($columnMap, true));
     $items = array();
     foreach ($dataSetResults['Rows'] as $row) {
         // For each row, substitute into our template
         $rowString = $text;
         foreach ($matches[1] as $sub) {
             // Pick the appropriate column out
             $subs = explode('|', $sub);
             // The column header
             $header = $subs[0];
             $replace = $row[$header];
             // Check in the columns array to see if this is a special one
             if ($columnMap[$header]['DataTypeID'] == 4) {
                 // Download the image, alter the replace to wrap in an image tag
                 $file = $media->addModuleFileFromUrl(str_replace(' ', '%20', htmlspecialchars_decode($replace)), 'ticker_dataset_' . md5($dataSetId . $columnMap[$header]['DataSetColumnID'] . $replace), $expires);
                 // Tag this layout with this file
                 $layout->AddLk($this->layoutid, 'module', $file['mediaId']);
                 $replace = $isPreview ? '<img src="index.php?p=module&mod=image&q=Exec&method=GetResource&mediaid=' . $file['mediaId'] . '" />' : '<img src="' . $file['storedAs'] . '" />';
             }
             $rowString = str_replace('[' . $sub . ']', $replace, $rowString);
         }
         $items[] = $rowString;
     }
     return $items;
 }
예제 #27
0
 public function displayUp($displayId)
 {
     try {
         $dbh = PDOConnect::init();
         Debug::Audit('Display Up: ' . $displayId);
         $sth = $dbh->prepare('UPDATE `stat` SET end = :toDt WHERE displayId = :displayId AND end IS NULL AND type = :type');
         $sth->execute(array('toDt' => date('Y-m-d H:i:s'), 'type' => 'displaydown', 'displayId' => $displayId));
         return true;
     } catch (Exception $e) {
         Debug::LogEntry('error', $e->getMessage(), get_class(), __FUNCTION__);
         if (!$this->IsError()) {
             $this->SetError(1, __('Unknown Error'));
         }
         return false;
     }
 }
예제 #28
0
 /**
  * Sets the Members of a group
  */
 public function SetMembers()
 {
     $db =& $this->db;
     $response = new ResponseManager();
     $groupObject = new UserGroup($db);
     $groupId = Kit::GetParam('GroupID', _REQUEST, _INT);
     $users = Kit::GetParam('UserID', _POST, _ARRAY, array());
     // We will receive a list of users from the UI which are in the "assign column" at the time the form is
     // submitted.
     // We want to go through and unlink any users that are NOT in that list, but that the current user has access
     // to edit.
     // We want to add any users that are in that list (but aren't already assigned)
     // All users that this session has access to
     if (!($allUsers = $this->user->userList())) {
         trigger_error(__('Error getting all users'), E_USER_ERROR);
     }
     // Convert to an array of ID's for convenience
     $allUserIds = array_map(function ($array) {
         return $array['userid'];
     }, $allUsers);
     // Users in group
     $usersAssigned = UserData::entries(null, array('groupIds' => array($groupId)));
     Debug::Audit('All userIds we want to assign: ' . var_export($users, true));
     Debug::Audit('All userIds we have access to: ' . var_export($allUserIds, true));
     foreach ($usersAssigned as $user) {
         /* @var Userdata $user */
         // Did this session have permission to do anything to this user?
         // If not, move on
         if (!in_array($user->userId, $allUserIds)) {
             continue;
         }
         Debug::Audit('Logged in user has permission to make changes to this assigned user ' . $user->userId);
         // Is this user in the provided list of users?
         if (in_array($user->userId, $users)) {
             // This user is already assigned, so we remove it from the $users array
             Debug::Audit('This user is already assigned ' . $user->userId);
             if (($key = array_search($user->userId, $users)) !== false) {
                 unset($users[$key]);
             }
         } else {
             Debug::Audit('This user is assigned, but not in the list of assignments ' . $user->userId);
             // It isn't therefore needs to be removed
             if (!$groupObject->Unlink($groupId, $user->userId)) {
                 trigger_error($groupObject->GetErrorMessage(), E_USER_ERROR);
             }
         }
     }
     Debug::Audit('All userIds we want to assign after sorting: ' . var_export($users, true));
     // Add any users that are still missing after tha assignment process
     foreach ($users as $userId) {
         Debug::Audit('User was missing, linking them: ' . $userId);
         // Add any that are missing
         if (!$groupObject->Link($groupId, $userId)) {
             trigger_error($groupObject->GetErrorMessage(), E_USER_ERROR);
         }
     }
     $response->SetFormSubmitResponse(__('Group membership set'), false);
     $response->Respond();
 }