Show:

File: platform/plugins/Users/classes/Users/Device.php

<?php
/**
 * @module Users
 */

/**
 * Class representing 'Device' rows in the 'Users' database
 * You can create an object of this class either to
 * access its non-static methods, or to actually
 * represent a device row in the Users database.
 *
 * @class Users_Device
 * @extends Base_Users_Device
 */
class Users_Device extends Base_Users_Device
{
	/**
	 * The setUp() method is called the first time
	 * an object of this class is constructed.
	 * @method setUp
	 */
	function setUp()
	{
		parent::setUp();
	}
	
	/**
	 * Adds a device to the system, after sending a test notification to it
	 * @param {array} $device
	 * @param {string} $device.userId the id of a user in the system
	 * @param {string} $device.deviceId the id of the device on the external platform
	 * @param {string} $device.platform an external platform, such as "ios" or "facebook"
	 * @param {string} $device.appId external app id registered with the platform
	 * @param {string} $device.formFactor the form factor of the device
	 * @param {string} $device.version the version of the platform
	 * @param {string} [$device.sessionId=Q_Session::id()] the session id to associate to the device.
	 * @param {boolean} [$skipNotification=false] if true, skips sending notification
	 * @return {Users_Device}
	 */
	static function add($device, $skipNotification=false)
	{
		if (($device['platform'] === 'chrome') || ($device['platform'] === 'firefox')) {
			$fields = array('userId', 'deviceId', 'platform', 'appId', 'formFactor', 'version', 'auth', 'p256dh');
		} else {
			$fields = array('userId', 'deviceId', 'platform', 'appId', 'formFactor', 'version');
		}
		Q_Valid::requireFields($fields, $device, true);
		$userId = $device['userId'];
		$deviceId = $device['deviceId'];
		$platform = $device['platform'];
		$platformAppId = $device['appId'];
		$auth = !empty($device['auth']) ? $device['auth'] : null;
		$p256dh = !empty($device['p256dh']) ? $device['p256dh'] : null;
		$apps = Q_Config::expect('Users', 'apps', $platform);
		list($appId, $info) = Users::appInfo($platform, $platformAppId);
		if (!$info) {
			throw new Q_Exception_MissingConfig(array(
				'fieldpath' => "Users/apps/$platform/.../appId=$platformAppId"
			));
		}
		$sessionId = isset($device['sessionId']) ? $device['sessionId'] : Q_Session::id();
		$user = Users::loggedInUser();
		$info = array_merge(Q_Request::userAgentInfo(), array(
			'sessionId' => $sessionId,
			'userId' => $userId,
			'deviceId' => $deviceId,
			'appId' => $platformAppId,
			'auth' => $auth,
			'p256dh' => $p256dh
		));
		$deviceArray = Q::take($device, $info);
		$className = "Users_Device_" . ucfirst($platform);
		$deviceRow = new $className($deviceArray);
		if (!$skipNotification) {
			// The following call may throw an exception if deviceId is invalid.
			// This may cancel Users::register() registration and remove user.
			$alert = Q_Config::get(
				"Users", "apps", $platform, $appId, "device", "added",
				"Notifications have been enabled"
			);
			$payload = compact('userId');
			$deviceRow->pushNotification(compact('alert', 'payload'));
		}
		$deviceRow->save(true);
		$deviceArray['deviceId'] = $deviceRow->deviceId;
		if ($sessionId) {
			$fields = array('deviceId', 'appId', 'platform', 'version', 'formFactor');
			Users_Session::update()
				->set(Q::take($deviceArray, $fields))
				->where(array('id' => $sessionId))
				->execute();
		}
		$deviceArray['Q/method'] = 'Users/device';
		Q_Utils::sendToNode($deviceArray);
		return $deviceRow;
	}
	
	/**
	 * Given a userId and optional platform and appId,
	 * retrieve an array of the latest devices, ordered by time inserted.
	 * @method byApp
	 * @static
	 * @param {string} [$userId=Users::loggedInUser()] The id of the user
	 * @param {string} [$platform=Q_Request::platform()] The external platform
	 * @param {string} [$appId=Q::app()] External or internal platform app id
	 * @return {array}
	 */
	static function byApp($userId = null, $platform = null, $appId = null)
	{
		if (!isset($userId)) {
			$user = Users::loggedInUser();
			if (!$user) {
				return array();
			}
			$userId = $user->id;
		}
		if (!isset($platform)) {
			$platform = Q_Request::platform();
		}
		if (!isset($appId)) {
			$appId = Q::app();
		} else {
			list($appId, $appInfo) = Users::appInfo($appId);
			$appId = $appInfo['appId'];
		}
		return Users_Device::select()
			->where(compact('userId', 'platform', 'appId'))
			->orderBy('insertedTime', false)
			->fetchDbRows();
	}
	
	/**
	 * You can use this method to send push notifications.
	 * It is far better, however, to use the Qbix Platform's offline notification
	 * mechanisms using Node.js instead of PHP. That way, you can be sure of re-using
	 * the same persistent connection.
	 * @method pushNotification
	 * @param {array} $notification
	 * @param {string|array} [$notification.alert] Either the text of an alert to show,
	 *  or an object with the following fields:
	 * @param {string} [$notification.alert.title] The title of the notification
	 * @param {string} [$notification.alert.body] The body of the notification
	 * @param {string} [$notification.alert.titleLocKey] Apple-only
	 * @param {string} [$notification.alert.titleLocArgs] Apple-only
	 * @param {string} [$notification.alert.actionLocKey] Apple-only
	 * @param {string} [$notification.alert.locKey] Apple-only
	 * @param {string} [$notification.alert.locArgs] Apple-only
	 * @param {string} [$notification.alert.launchImage] Apple-only
	 * @param {string} [$notification.badge] The badge
	 * @param {string} [$notification.sound] The name of the sound file in the app bundle or Library/Sounds folder
	 * @param {string} [$notification.icon] Url of icon, can be png any square size
	 * @param {string} [$notification.url] Url to which the notifiation will be linked
	 * @param {array} [$notification.actions] Array of up to two arrays with keys 'action' and 'title'.
	 * @param {string} [$notification.category] Apple-only. The name of the category for actions registered on the client side.
	 * @param {array} [$notification.payload] Put all your custom notification fields here
	 * @param {integer} [$notification.expiry=null] Number of seconds until notification expires
	 *    and does not need to be stored anymore on the device.
	 *    Pass -1 to ask the device not to store it at all.
	 * @param {string} [$notification.priority="high"] Can be set to "normal" to make it lower priority
	 * @param {string} [$notification.collapseId] A string under 64 bytes for collapsing notifications
	 * @param {string} [$notification.id] You can provide your own uuid for the notification
	 * @param {array} [$options]
	 * @param {boolean} [$options.scheduled=false] if true, doesn't send immediately.
	 *  You should call Users_Device_{YourPlatform}::sendPushNotifications()
	 *  to send all scheduled notifications in a batch.
	 * @param {boolean} [$options.silent=false] Deliver a silent notification, may throw an exception
	 */
	function pushNotification($notification, $options = array())
	{
		$this->handlePushNotification($notification, $options);
	}
	
	/**
	 * Schedules a push notification.
	 * This default implementation, just throws an error.
	 * @method handlePushNotification
	 */
	protected function handlePushNotification($notifications, $options = array())
	{
		throw new Q_Exception_MethodNotSupported(array(
			'method' => 'schedulePushNotification'
		));
	}
	
	/**
	 * Sends all scheduled push notifications
	 * @method sendPushNotifications
	 * Default implementation
	 */
	static function sendPushNotifications()
	{
		throw new Q_Exception_MethodNotSupported(array(
			'method' => 'sendPushNotifications'
		));
	}

	/**
	 * Called by various Db methods to get a custom row object
	 * @param {array} $fields Any fields to set in the row
	 * @param {string} [$stripPrefix=null] Any prefix to strip from the fields
	 * @return Users_Device
	 */
	static function newRow($fields, $stripPrefix = null)
	{
		Q_Valid::requireFields(array('platform'), $fields, true);
		$platform = ucfirst(strtolower($fields['platform']));
		$className = "Users_Device_$platform";
		$row = new $className();
		return $row->copyFrom($fields, $stripPrefix, false, false);
	}

	/**
	 * Implements the __set_state method, so it can work with
	 * with var_export and be re-imported successfully.
	 * @method __set_state
	 * @param {array} $array
	 * @return {Users_Device} Class instance
	 */
	static function __set_state(array $array) {
		$result = new Users_Device();
		foreach($array as $k => $v)
			$result->$k = $v;
		return $result;
	}
};