I know there are a few other approaches i have seen to being able to edit ModX content from the file system rather then from the manager, and i am going to humbly submit my attempt to the group. I have taken a look at modxfs-php but it seemed needlessly complex as well as requiring your system to support Fuse, the other method i saw was the use of just creating simple snippets in the manager that include your external resources verbatim but this seemed more labour intensive then i would like. And neither of these methods seemed to have a very clear cut way of handling template variables.
Attached is the plugin code for my method of keeping things in sync, the only thing that you need to keep in mind when adding it is that you must hook it up to
OnHandleRequest event. Once you do that your good to go.
This plugin assumes a couple things:
1) All of your components are located in $modx->config[’base_path’] . ’core/components’
2) Your component library consists of 3 folders, "chunks", "snippets", "templates" and a single file called .dsyncable (this ensures that only the components you wish to be synced will be)
3) Snippet files have a file extension ".php", chunks/templates have a file extension ".html" and template variable manifests have a file extension ".txml"
The name of the items created will match the name of the filename that it is created under (ie. templates/Home_Page.html will create a template named "Home_Page" and it will parse templates/Home_Page.txml" for its template variables)
Here is an example of a template manifest file:
<vars>
<modTemplateVar type="dropdown" elements="Yes||No" display="string" default_text="No" display_params="%26format=">casestudy-hide-from-homepage</modTemplateVar>
<modTemplateVar type="image" elements="" display="default" default_text="" display_params="">casestudy-square-image</modTemplateVar>
<modTemplateVar type="text">casestudy-main-tag</modTemplateVar>
<modTemplateVar type="textarea">casestudy-awards</modTemplateVar>
<modTemplateVar type="image" elements="" display="default" default_text="" display_params="">casestudy-main-image</modTemplateVar>
<modTemplateVar type="image" elements="" display="default" default_text="" display_params="">casestudy-content-image</modTemplateVar>
<modTemplateVar type="image" elements="" display="default" default_text="" display_params="">casestudy-quote-image</modTemplateVar>
<modTemplateVar type="text" elements="" display="default" default_text="" display_params="">casestudy-quote-text</modTemplateVar>
<modTemplateVar type="text" elements="" display="default" default_text="" display_params="">casestudy-quote-name</modTemplateVar>
<modTemplateVar type="text" elements="" display="default" default_text="" display_params="">casestudy-quote-title</modTemplateVar>
<modTemplateVar type="text">casestudy-url</modTemplateVar>
</vars>
The main advantage i see to this method is for templates themselves. I work in a team environment where we all share a SVN code base and i wanted to allow the designer to work on the HTML/CSS statically without having to even think about ModX. As the template html files will render properly with all of their resources on their own without the need for a ModX installation. As well as being able to deploy a package to multiple environments from the command line.
So yeah community let me know your thoughts, this hasnt been totally polished yet and i can think of a few bugs that i have not resolved, and i havnt added support for plugins yet either but i use this for all of my ModX projects at the moment. Enjoy! Its only 225 lines of plugin code, so i will just post it here. All of the "echos" in there are because previously i just ran it from the command line rather then integrating it as a plugin.
<?php
// predefined folders
// /snippets
// /chunks
// /templates / template variables
$components = scandir($modx->config['base_path'] . 'core/components');
function dirSize($directory) {
$size = 0;
foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($directory)) as $file){
$size+=$file->getSize();
}
return $size;
}
foreach ($components as $component)
{
// we dont want to fuck with code that does not usre this method
if (!file_exists($modx->config['base_path'] . 'core/components/' . $component . '/.dsyncable'))
continue;
$basepath = $modx->base_path . 'core/components/' . $component;
$to_sync = array(array('type' => 'modSnippet', 'folder' => '/snippets', 'ext' => '.php', 'identifier' => 'name'),
array('type' => 'modTemplate', 'folder' => '/templates', 'ext' => '.html', 'identifier' => 'templatename'),
array('type' => 'modChunk', 'folder' => '/chunks', 'ext' => '.html', 'identifier' => 'name'));
foreach ($to_sync as $sync_item)
{
$items = scandir($basepath . $sync_item['folder']);
foreach ($items as $key => $item)
{
// we only want to sync html files to the database, everything else is just static content
if (strpos($item, $sync_item['ext']) === false)
unset($items[$key]);
}
////echo "Found " . (count($items)) . " " . $sync_item['type'] . "\n";
foreach ($items as $item)
{
// get the template name and content
$item_name = str_replace($sync_item['ext'], "", $item);
$item_content = file_get_contents($basepath . $sync_item['folder'] . '/' . $item);
// check for existance, if not create it, otherwise update it with the FS data
if (!$obj = $modx->getObject($sync_item['type'], array($sync_item['identifier'] => $item_name)))
{
//echo "Creating New " . $sync_item['type'] . " - " . $item_name . "\n";
$obj = $modx->newObject($sync_item['type'], array($sync_item['identifier'] => $item_name));
}
else
{
//echo "Updating Existing " . $sync_item['type'] . " - " . $item_name . "\n";
}
// update the content and save
$obj->setContent($item_content);
$obj->save();
}
}
// template variables
$items = scandir($basepath . '/templates');
foreach ($items as $key => $item)
{
// we only want to sync html files to the database, everything else is just static content
if (strpos($item, '.txml') === false)
unset($items[$key]);
}
foreach ($items as $item)
{
// get the template name and content
$template_name = str_replace('.txml', "", $item);
$tv_content = file_get_contents($basepath . '/templates' . '/' . $item);
if (!$tv_xml = simplexml_load_string($tv_content))
{
//echo "Failed to Load TV XML";
die;
}
$template = $modx->getObject('modTemplate', array('templatename' => $template_name));
//echo "Creating " . count($tv_xml->modTemplateVar) . " modTemplateVar/modTemplateVarTemplates\n";
foreach ($tv_xml->modTemplateVar as $rank => $template_variable_name)
{
if (!$tv = $modx->getObject('modTemplateVar', array('name' => (string)$template_variable_name)))
{
//echo "Creating New modTemplateVar - " . $template_variable_name . "\n";
$tv = $modx->newObject('modTemplateVar', array('name' => (string)$template_variable_name));
$tv->set('type', $template_variable_name['type']);
$tv->set('caption', (string)$template_variable_name);
$tv->set('description', $template_variable_name['description']);
$tv->set('editor_type', $template_variable_name['editor_type']);
$tv->set('category', $template_variable_name['category']);
$tv->set('locked', $template_variable_name['locked']);
$tv->set('elements', $template_variable_name['elements']);
$tv->set('display', $template_variable_name['display']);
$tv->set('display', $template_variable_name['display']);
$tv->set('display_params', $template_variable_name['display_params']);
$tv->set('default_text', $template_variable_name['default_text']);
$tv->set('properties', $template_variable_name['properties']);
$tv->save();
$tv_assoc = $modx->newObject('modTemplateVarTemplate');
$tv_assoc->set('tmplvarid', $tv->get('id'));
$tv_assoc->set('templateid', $template->get('id'));
$tv_assoc->set('rank', $rank);
$tv_assoc->save();
}
else
{
//echo "Updating Existing modTemplateVar - " . $template_variable_name . " ";
$tv->set('type', $template_variable_name['type']);
$tv->set('caption', (string)$template_variable_name);
$tv->set('description', $template_variable_name['description']);
$tv->set('editor_type', $template_variable_name['editor_type']);
$tv->set('category', $template_variable_name['category']);
$tv->set('locked', $template_variable_name['locked']);
$tv->set('elements', $template_variable_name['elements']);
$tv->set('display', $template_variable_name['display']);
$tv->set('display', $template_variable_name['display']);
$tv->set('display_params', urldecode($template_variable_name['display_params']));
$tv->set('default_text', $template_variable_name['default_text']);
$tv->set('properties', $template_variable_name['properties']);
$tv->save();
if (!$tv_assoc = $modx->getObject('modTemplateVarTemplate', array('templateid' => $template->get('id'), 'tmplvarid' => $tv->get('id'))))
{
//echo " Creating Association to Template\n";
$tv_assoc = $modx->newObject('modTemplateVarTemplate');
$tv_assoc->set('tmplvarid', $tv->get('id'));
$tv_assoc->set('templateid', $template->get('id'));
$tv_assoc->set('rank', $rank);
$tv_assoc->save();
}
else
{
//echo " Already Associated to Template\n";
}
}
}
}
// static assets (javascript, css ect...)
// template variables
$items = array();
$items = scandir($basepath . '/templates');
foreach ($items as $key => $item)
{
// we only want to sync html files to the database, everything else is just static content
if (strpos($item, '.txml') !== false || strpos($item, '.html') !== false || $item == '.' || $item == '..' || $item == '.svn')
unset($items[$key]);
}
//echo "Found " . (count($items)) . " Static Content Items\n";
print_r($items);
foreach ($items as $item)
{
if (is_dir($basepath . '/templates/' . $item))
{
// lets check to make sure we are not syncing static resourses everytime (which can be big)
if (dirSize($basepath . '/templates/' . $item) == dirSize($modx->config['base_path'] . $item))
{
//echo "Folder Exists - " . $item . " Skipping\n";
continue;
}
else
{
//echo "Copying Folder - " . $item . "\n";
}
}
else
{
// lets check to make sure we are not syncing static resourses everytime (which can be big)
if (filesize($basepath . '/templates/' . $item) == filesize($modx->config['base_path'] . $item))
{
//echo "File Exists - " . $item . " Skipping\n";
coninue;
}
else
{
//echo "Copying File - " . $item . "\n";
}
}
$cmd = 'cp -r ' . $basepath . '/templates/' . $item . ' ' . $modx->config['base_path'] . '.';
exec($cmd);
}
// clear the modx cache
}
$cm = $modx->getCacheManager();
$cm->clearCache();
?>