lithium_anologue / branches / master / models / Anologue.php

history
<?php
/**
 * Anologue: anonymous, linear dialogue
 *
 * @copyright     Copyright 2010, Union of RAD (http://union-of-rad.org)
 * @license       http://opensource.org/licenses/bsd-license.php The BSD License
 */

namespace anologue\models;

use \lithium\util\Inflector;
use \lithium\data\Connections;
use \lithium\data\collection\DocumentSet;
use \lithium\data\entity\Document;
use \lithium\net\http\Service;
use \lithium\util\Validator;
use \anologue\models\Message;
use \anologue\extensions\helper\Oembed;


/**
 * The core model and messages container for Anologue.
 *
 * @see lithium\data\Model
 */
class Anologue extends \lithium\data\Model {

	public static $alias = 'Anologue';

	protected $_schema = array(
		'type' => 			array('default' => 'anologue'),
		'created' => 		array('default' => null),
		'title' => 			array('default' => null),
		'description' => 	array('default' => null),
		'webhook' => 		array('default' => null),
		'messages' => 		array('default' => array()),
		'viewers' => 		array('default' => array()),
	);

	/**
	 * Anologue meta
	 *
	 * @var array
	 * @see lithium\data\Model::$_meta
	 */
	protected $_meta = array(
		'connection' => 'anologue',
		'source' => 'anologue',
		'locked' => false,
	);

	/**
	 * List of restricted document ids
	 *
	 * @var array
	 */
	public static $restricted = array(
		'ping', 'changes', 'anologue', 'about', 'blog', 'news', 'download'
	);

	public static function __init(array $options = array()) {
		parent::__init($options);

		static::applyFilter('find', function ($self, $params, $chain) {
			$result = $chain->next($self, $params, $chain);

			if (empty($result)) {
				return $result;
			}

			if (!empty($result->messages)) {
				$messages = new DocumentSet(array(
					'data' => $result->messages->data(),
					'model' => 'anologue\models\Message'
				));
				$result->set(compact('messages'));
			}

			return $result;
		});

		static::applyFilter('save', function($self, $params, $chain) {
			$anologue = $params['entity'];

			if (empty($anologue->created)) {
				$created = time();
				$params['entity']->set(compact('created'));
			}

			if (
				empty($params['entity']->webhook)
				|| !Validator::rule('url', $params['entity']->webhook)
			) {
				unset($params['entity']->webhook);
			}

			if (!empty($anologue->description)) {
				$params['entity']->description = Oembed::classify(
					$params['entity']->description, array('markdown' => true)
				);
			}

			if (empty($anologue->id) && !empty($anologue->title)) {
				$id = $slug = Inflector::slug($anologue->title);

				if (strlen($id) < 5) {
					$id = $slug = $id . '-' . substr(md5($slug . time()), 0, 5);
				}

				while (in_array($id, $self::$restricted) || $existing = Anologue::first($id)) {
					$id = "$slug-" .  substr(md5($slug . time()), 0, 4);
				}

				$params['entity']->set(compact('id'));
			}

			$result = $chain->next($self, $params, $chain);

			if ($result && $anologue->webhook) {
				$self::webhook($anologue->webhook, $anologue->data());
			}

			return $result;
		});

		$self = static::_object();
		$self->_setupFinders();
	}

	protected function _setupFinders() {
		$self = static::_object();
		$self->_finders['changes'] = function($self, $params, $chain) {
			$query = (array) $params['options']['conditions'] + array(
				'filter' => 'changes/id'
			);
			$connection = Connections::get($self::meta('connection'));
			$result = $connection->get(
				$connection->_config['database'] . '/_changes/',
				$query, array('type' => null)
			);
			if (empty($result) || empty($result->results)) {
				return 0;
			}
			return new Document(array(
				'data' => $result->results[0],
				'model' => __CLASS__
			));
		};
	}

	public static function ping($id, $data = array()) {
		$default = array(
			'key' => null,
			'user' => array(
				'name' => null,
				'email' => null,
				'url' => null
			)
		);
		$data += $default;

		if (!empty($data['user']['email'])) {
			$data['user']['email'] = md5($data['user']['email']);
		}

		if (empty($data['user']['name'])) {
			$data['user']['name'] = 'anonymous';
		}

		$record = static::first($id);
		$anologue = $record->data();

		$cutoff = strtotime('-2 minutes');

		$viewers = array();
		if (!empty($anologue['viewers'])) {
			foreach ($anologue['viewers'] as $viewer => $value) {
				if ($value->timestamp >= $cutoff) {
					$viewers[$viewer] = $anologue['viewers'][$viewer];
				}
			}
		}

		$viewers[$data['key']] = array_filter(array_intersect_key($data['user'], $default['user']))
			+ array('timestamp' => time());

		return $record->save(compact('viewers'));
	}

	/**
	 * Append a message to an existing anologue. For user privacy, hashes the email before saving.
	 *
	 * @param integer $id
	 * @param mixed $message array or instance of `anologue\models\Message`
	 * @see anologue\models\Message
	 * @see lithium\data\Model::save()
	 */
	public static function addMessage($id, $message = array()) {

		$anologue = static::first($id);

		if (empty($anologue)) {
			return false;
		}

		$message = Message::create($message);

		if (!$message->save()) {
			return false;
		}

		$messages = !empty($anologue->messages)
			? $anologue->messages->data()
			: array();

		$messages[] = $message;

		$messages = new DocumentSet(array(
			'data' => $messages,
			'model' => 'anologue\models\Message'
		));

		$anologue->messages = $messages;

		return $anologue->save();
	}

	/**
	 * Query changes for a particular document
	 *
	 * @param string|array $id if a string assumed id, otherwise, assumed array of conditions
	 * @param array $conditions (optional) array of conditions
	 * @see http://wiki.apache.org/couchdb/HTTP_database_API#Changes
	 */
	public static function changes($id = null, $conditions = array()) {
		$defaults = array(
			'id' => null,
			'feed' => 'longpoll',
			'timeout' => 55000,
			'since' => 0,
			'style' => 'all_docs'
		);

		$conditions['id'] = $id;
		if (is_array($id)) {
			$conditions = $id;
		}

		$conditions += $defaults;

		return static::find('changes', compact('conditions'));
	}

	/**
	 * Post data in json format to a url
	 *
	 * @param string $uri
	 * @param array $data
	 * @return void
	 */
	public static function webhook($uri, $data) {
		extract($url = parse_url($uri));
		$path = str_replace($scheme . '://' . $host, '', $uri);
		$http = new Service(compact('scheme','host'));
		return $http->post($path, $data, array('type' => 'json'));
	}
}

?>