Created
June 16, 2009 17:39
-
-
Save sunny/130786 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//<?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