вторник, 17 августа 2010 г.

Flash-сайт на MODx -проблемка

В предвкушении победы особенно обидно было увидеть сообщение
Error opening URL 'http://test_adamant.ru/main.xml' в Debug-консоли Flash. Видимо дело в том, что Flash не умеет работать с URL-ами, он умеет загружать XML-файлы... Как следствие придется написать модуль, который будет преобразовывать документы MODx (уже готовый XML - ах, как обидно) в файл XML...

Flash-сайт на MODx

Выдалась свободная минутка, и я пишу, дабы в будущем не забыть. У меня получилось)). Я знаю (пока теоретически) как использовать MODx, как Flash CMS. Фишка вот в чем. Флэш не умеет общаться с БД (MySQL в частности) напрямую. Нужен посредник. Обычно в качестве оного выступает скрипт PHP, который выполняет запрос к БД, результат его получает в виде XML (точнее генерирует XML на основе полученного результата). Но. В моем случае эту функцию, как и функцию редактора и контрольной панели выполняет система MODx.

Итак, как это сделать:
1. Чтобы HTML-тэги не интерпретировались, как XML-тэги, создадим сниппет, который соберет содержимое документа "до кучи")), назовем его HTML2XML_Converter:


<?php
include_once('manager/includes/config.inc.php');
include_once(MODX_MANAGER_PATH.'/includes/document.parser.class.inc.php');
include_once('classes/docAPI.php');
$modx = new DocumentParser;

$doc=new Document($docId);
$content=htmlentities($doc->get('content'));
//Если содержимое документа является сниппетом

echo $content;
?>



2. Создаем шаблон, назовем его XML_Template

<?xml version="1.0" encoding="UTF-8" ?>
<root>
<password>[*password*]</password>
<pagetitle>[*pagetitle*]</pagetitle>
<content>[!HTML2XML_Converter? &docId=`[*id*]`!]</content>
</root>


3. Заходим в настройки страницы, и меняем тип содержимого на text/xml

Отдельный момент. Если в содержимом документа есть вызовы сниппета, их необходимо вывести в шаблон. Простой пример - галерея. Вызов: [[Ditto? &tpl=`ditto_xml`]]. Но при выборке его из [*content*] будет возвращен вызов сниппета, а не результат его работы. Чтобы этого не случилось, необходимо перенести его вызов в шаблон:


<?xml version="1.0" encoding="UTF-8" ?>
<root>
<password>[*password*]</password>
<pagetitle>[*pagetitle*]</pagetitle>
<content>[[Ditto? &tpl=`ditto_xml`]]</content>
</root>


P.S.: Содержимое файла docApi:


<?
/***************************************************************
Класс для Создания/Редактирования/Удаления документов MODx
Версия 0.5.1a
Автор: ur001
Контакты: ur001@mail.ru
Использование:
Пример 1 (создание):
require_once('assets/libs/docapi/document.class.inc.php');
$doc = new Document();
$doc->Set('parent',$folder);
$doc->Set('alias','post'.time());
$doc->Set('content','Текст документа');
$doc->Set('template','GuestBookComments');
$doc->Set('tvComment','Комментарий к посту');
$doc->Save();

Пример 2 (редактирование):
require_once('assets/libs/docapi/document.class.inc.php');
$doc = new Document($id,'content');
$content=$doc->Get('content');
$doc->Set('content', '

'.$content.'

');
$doc->Save();

Область применения:
Написание гостевых книг, блогов, форумов, модулей для
администрирования из frontedn-а (новоси, галлереи)

TODO:
* Необходимо тестирование
* document_groups

Важно:
1) Это не врапер для MODx API. Это дополнение функционала
для создания и редактирования документов
2) Не следует создавать этот класс только для получения
значений TV или удаления документов в цикле. Для этого
есть соответствующие функции, такие как
$modx->getTemplateVars(); и $modx->db->delete();

***************************************************************/
class Document{
var $fields; // Массив полей документа
var $tvs; // Массив TV

var $tvNames; // Массив возможных имен TV
var $oldTVs; // Массив уже определенных в документе значений TV
var $isNew; // true - новый документ, false - существующий

/***********************************************
Инициализация класса
$id - идентификатор существующего документа
или 0 для создание нового
$fields - список полей разделенных запятой
************************************************/
function Document($id=0,$fields="*"){
global $modx;
$this->isNew = $id==0;
if(!$this->isNew){
$this->fields = $modx->getPageInfo($id,0,$fields);
$this->fields['id']=$id;
}
else
$this->fields = array(
'pagetitle' => 'Новый документ',
'alias' => '',
'parent' => 0,
'createdon' => time(),
'createdby' => '0',
'editedon' => '0',
'editedby' => '0',
'published' => '1',
'deleted' => '0',
'hidemenu' => '0',
'template' => '0',
'content' => ''
);
}

/***********************************************
Сохранение/Обновление документа
************************************************/
function Save(){
global $modx;
$tablename=$modx->getFullTableName('site_content');
if($this->isNew){
$this->fields['id']=$modx->db->insert(&$this->fields, $tablename);
$this->isNew = false;
} else {
$id=$this->fields['id'];
$modx->db->update(&$this->fields, $tablename, "id=$id");
}
if(is_array($this->tvs)) $this->saveTVs();
}


/***********************************************
Получение значения поля документа или TV
$field - Имя поля документа или имя TV
с префиксом 'tv'
Результат: значение поля, TV или null
************************************************/
function Get($field){
switch(1){
case substr($field,0,2)=='tv': return $this->GetTV(substr($field,2));
default: return isset($this->fields[$field]) ? $this->fields[$field] : null;
}
}

/***********************************************
Установка значения поля документа (включая TV)
$field - Имя поля документа. Для установки TV
следует добавить префикс 'tv'
$value - Значение
Результат: true, или false при неудаче
************************************************/
function Set($field, $value){
switch(1){
case substr($field,0,2)=='tv': return $this->SetTV(substr($field,2), $value);
case $field=='template': return $this->SetTemplate($value);
default: $this->fields[$field]=$value; return true;
}
}


/***********************************************
Получение значения TV.
$name - имя TV
************************************************/
function GetTV($tv){
if(!is_array($this->tvs)){
if($this->isNew) return null;
$this->tvs=array();
}
// Поиск в значениях установленных функцией Set()
if(isset($this->tvs[$tv])) return $this->tvs[$tv];
// Поиск в TV уже определенных для документа
// Если они еще не получены вызываем fillOldTVValues()
if(!is_array($this->oldTVs)){
if($this->isNew) return null;
$this->fillOldTVValues();
}
if(isset($this->oldTVs[$tv])) return $this->oldTVs[$tv];
return null;
}

/***********************************************
Установка TV
************************************************/
function SetTV($tv,$value){
if(!is_array($this->tvs)) $this->tvs=array();
$this->tvs[$tv]=$value;
}

/***********************************************
Установка шаблона документа
$tpl - Имя или идентификатор шаблона
************************************************/
function SetTemplate($tpl){
global $modx;
// Если указано имч шаблона, получаем id
if(!is_numeric($tpl)) {
$tablename=$modx->getFullTableName('site_templates');
$tpl = $modx->db->getValue("SELECT id FROM $tablename WHERE templatename='$tpl' LIMIT 1");
if(empty($tpl)) return false;
}

$this->fields['template']=$tpl;
return true;
}

/************************************************************
Удаление доккумента и связанных с ним TV
*************************************************************/
function Delete(){
if($this->isNew) return;
global $modx;
$id=$this->fields['id'];
$modx->db->delete($modx->getFullTableName('site_content'),"id=$id");
$modx->db->delete($modx->getFullTableName('site_tmplvar_contentvalues'),"contentid=$id");
$this->isNew=true;
}

/************************************************************
Сохранение значений TV, служебная функция. Сохраняются
тлько значения поределенные в $tvNames, если Такое имя TV
уже есть в oldTVs, происходит update, иначе insert
*************************************************************/
function saveTVs(){
global $modx;
if(!is_array($this->tvNames))$this->fillTVNames();
if(!is_array($this->oldTVs) && !$this->isNew)
$this->fillOldTVValues();
else
$this->oldTVs = array();

if(!is_array($this->tvNames))$this->fillTVNames();
$tvc = $modx->getFullTableName('site_tmplvar_contentvalues');
$id=$this->fields['id'];
foreach($this->tvs as $tv=>$value)
if(isset($this->tvNames[$tv])){
$tmplvarid=$this->tvNames[$tv];
if(isset($this->oldTVs[$tv])){
if($this->oldTVs[$tv]==$this->tvNames[$tv]) continue;
$sql="UPDATE $tvc SET value='$value' WHERE tmplvarid=$tmplvarid AND contentid=$id";
}
else
$sql="INSERT INTO $tvc (tmplvarid,value,contentid) VALUES ($tmplvarid,'$value',$id)";
$modx->db->query($sql);
}
}

/************************************************************
Заполнение массива значений TV ($oldTVs), служебная функция.
В отличие от $modx->getTemplateVars, берет только
фактические значения игнорируя значения "по-умолчанию"
*************************************************************/
function fillOldTVValues(){
global $modx;
$tvc = $modx->getFullTableName('site_tmplvar_contentvalues');
$tvs = $modx->getFullTableName('site_tmplvars');
$sql = 'SELECT tvs.name as name, tvc.value as value '.
"FROM $tvc tvc INNER JOIN $tvs tvs ".
'ON tvs.id=tvc.tmplvarid WHERE tvc.contentid ='.$this->fields['id'];
$result = $modx->db->query($sql);
$this->oldTVs = array();
while ($row = mysql_fetch_assoc($result)) $this->oldTVs[$row['name']] = $row['value'];
}

/************************************************************
Заполнение массива имен TV ($tvNames)), служебная функция.
*************************************************************/
function fillTVNames(){
global $modx;
$this->tvNames = array();
$tvs = $modx->getFullTableName('site_tmplvars');
$result = $modx->db->select('id, name', $modx->getFullTableName('site_tmplvars'));
while ($row = mysql_fetch_assoc($result)) $this->tvNames[$row['name']] = $row['id'];
}
}
?>