Skip to content

Instantly share code, notes, and snippets.

@sunny
Created June 16, 2009 17:39
Show Gist options
  • Save sunny/130786 to your computer and use it in GitHub Desktop.
Save sunny/130786 to your computer and use it in GitHub Desktop.
//<?php
/*
* Patcher
* MODx plugin that adds check boxes in the administration to apply .patch
* files to MODx core.
*
* Requirements
* ============
* - MODx 0.9.6
* - UNIX system with `mv` and `patch`
* - ability to `exec()` in PHP
* - patch files in the unified diff format
*
* Installation
* ============
* - Create a new plugin with this file. And check the following event hooks:
* - OnMiscSettingsRender
* - OnManagerPageInit
* - Create the `assets/plugins/patcher` folder.
* - Place .patch files in unified diff format in this folder.
* - Under Misc Settings in MODx's configuration there will be checkboxes
* for every patch file you added.
* Once checked, they are applied and renamed `.patch.applied`.
*
*
* Licence
* ======
* Copyright Sunny Ripert <http://sunfox.org>
* Cosmic Communication <http://agencecosmic.com>
* Under the WTFPL <http://sam.zoy.org/wtfpl/>
*
*/
if (!defined('PATCHER_DIRECTORY')) {
define('PATCHER_RELATIVE_DIRECTORY', 'assets/plugins/patcher'); // no trailing slash
define('PATCHER_DIRECTORY', '../'.PATCHER_RELATIVE_DIRECTORY); // no trailing slash
define('PATCHER_LOG_FILE', '/logs/patcher.log');
// Returns an array of patches with the patch id as key
// and an array of the before/after patch states as values:
// 0: previous state from the filename (if the file ends with .applied)
// 1: current state from the database
//
// Example:
// patcher_get_patches() # => array('continue-save'=>array(0,0), 'edit-mode'=>array(1,1));
function patcher_get_patches() {
global $modx;
$patches = array();
$files = array_merge(glob(PATCHER_DIRECTORY . '/*.patch.applied'),
glob(PATCHER_DIRECTORY . '/*.patch'));
foreach ($files as $file) {
$id = preg_replace('/\.patch(\.applied)?$/', '', basename($file));
// previous state from filename
$previous = preg_match('/\.applied$/', $file) ? 1 : 0;
// current state from database
$current = isset($modx->config["patcher_$id"]) ? $modx->config["patcher_$id"] : 0;
$patches[$id] = array((int) $previous, (int) $current);
}
return $patches;
}
function patcher_apply_patch($id, $reverse = false) {
$patch = PATCHER_RELATIVE_DIRECTORY.'/'.$id.'.patch';
if (patcher_exec("patch -d .. -p1 -i '$patch' 2>&1"))
patcher_exec("mv '../$patch' '../$patch.applied' 2>&1");
}
function patcher_reverse_patch($id) {
$patch = PATCHER_RELATIVE_DIRECTORY.'/'.$id.'.patch';
if (patcher_exec("patch -R -d .. -p1 -i '$patch.applied'"))
patcher_exec("mv '../$patch.applied' '../$patch'");
}
function patcher_exec($cmd) {
patcher_log("$ ".$cmd);
$return = exec($cmd, $out, $err_code);
if ($return)
patcher_log($return);
return $err_code == 0;
}
function patcher_log($txt) {
$fp = fopen(PATCHER_LOG_FILE, 'a');
fwrite($fp, $txt."\n");
fclose($fp);
return $txt;
global $modx;
$modx->l($txt."\n", 'patcher.log');
return $txt;
}
}
// Handle event
$e = &$modx->Event;
switch ($e->name) {
// Settings page render
case 'OnMiscSettingsRender':
// Print html of the settings
$html = '
<table cellspacing="0" cellpadding="3" border="0" style="width:100%" class="cosmic_settings">
<tr class="row1">
<td class="warning" style="width:25%">Apply patches:</td>
<td>
<ul>';
foreach (patcher_get_patches() as $id => $values) {
$no = $values[0] == 0 ? ' checked="checked"' : '';
$yes = $values[0] == 1 ? ' checked="checked"' : '';
$html .= '
<li style="list-style-type:none;list-style-image:none;">
<label style="font-weight:bold">
<input type="radio" value="0"'.$no.'
name="patcher_'.htmlspecialchars($id).'">
no
</label>
<label style="font-weight:bold">
<input type="radio" value="1"'.$yes.'
name="patcher_'.htmlspecialchars($id).'">
yes
</label>
'.htmlspecialchars($id).'
</li>';
}
$html .= '
</ul>
</td>
</tr>
<tr>
<td colspan="2">
<div class="split"/>
</td>
</tr>
</table>';
$e->output($html);
break;
// Apply or remove the patches with "pending" state
case 'OnManagerPageInit':
foreach (patcher_get_patches() as $id => $values) {
list($previous, $current) = $values;
if ($current == 1 and $previous == 0)
patcher_apply_patch($id);
elseif ($current == 0 and $previous == 1)
patcher_reverse_patch($id);
}
default :
return; // stop here - this is very important.
break;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment