Created
September 6, 2011 12:16
-
-
Save xwero/1197391 to your computer and use it in GitHub Desktop.
Ugling: static site generator with phing (start of the template engine)
This file contains 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
<?xml version="1.0" encoding="utf-8"?> | |
<project name="Ugling" basedir="." default="loop"> | |
<!-- set variables to use in the tasks --> | |
<property name="dir.raw.content" value="content" /> | |
<property name="dir.www.content" value="online/" /> | |
<property name="dir.templates" value="templates" /> | |
<taskdef name="markdown" classname="phing.tasks.ext.MarkdownTask" /> | |
<taskdef name="template" classname="phing.tasks.ext.TemplateTask" /> | |
<target name="loop"> | |
<markdown destination="${dir.www.content}" removefilesetdir="${dir.raw.content}"> | |
<fileset dir="${dir.raw.content}"> | |
<include name="*.md" /> | |
<include name="**/*.md" /> | |
</fileset> | |
</markdown> | |
<template templatesdir="${dir.templates}"> | |
<fileset dir="${dir.www.content}"> | |
<include name="*.html" /> | |
<include name="**/*.html" /> | |
</fileset> | |
</template> | |
</target> | |
</project> |
This file contains 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 | |
require_once 'phing/Task.php'; | |
require_once 'phing/util/FileUtils.php'; | |
require_once 'System.php'; | |
require_once "phing/tasks/ext/markdown/markdown.php"; | |
class MarkdownTask extends Task { | |
/** | |
* @var string Taskname for logger | |
*/ | |
protected $taskName = 'Markdown'; | |
/** | |
* Result format, defaults to "html". | |
* @see $supportedFormats for all possible options | |
* | |
* @var string | |
*/ | |
protected $format = 'html'; | |
/** | |
* Input file in markdown format. | |
* Required | |
* | |
* @var string | |
*/ | |
protected $file = null; | |
/** | |
* Output file or directory. May be omitted. | |
* When it ends with a slash, it is considered to be a directory | |
* | |
* @var string | |
*/ | |
protected $destination = null; | |
protected $removefilesetdir = null; | |
protected $filesets = array(); // all fileset objects assigned to this task | |
protected $mapperElement = null; | |
/** | |
* all filterchains objects assigned to this task | |
* | |
* @var array | |
*/ | |
protected $filterChains = array(); | |
/** | |
* mode to create directories with | |
* | |
* @var integer | |
*/ | |
protected $mode = 0755; | |
/** | |
* Only render files whole source files are newer than the | |
* target files | |
* | |
* @var boolean | |
*/ | |
protected $uptodate = false; | |
/** | |
* The main entry point method. | |
* | |
* @return void | |
*/ | |
public function main() | |
{ | |
if (count($this->filterChains)) { | |
$this->fileUtils = new FileUtils(); | |
} | |
if ($this->file != '') { | |
$file = $this->file; | |
$targetFile = $this->getTargetFile($file, $this->destination); | |
$this->render($file, $targetFile); | |
return; | |
} | |
if (!count($this->filesets)) { | |
throw new BuildException( | |
'"file" attribute or "fileset" subtag required' | |
); | |
} | |
// process filesets | |
$mapper = null; | |
if ($this->mapperElement !== null) { | |
$mapper = $this->mapperElement->getImplementation(); | |
} | |
$project = $this->getProject(); | |
foreach ($this->filesets as $fs) { | |
$ds = $fs->getDirectoryScanner($project); | |
$fromDir = $fs->getDir($project); | |
$srcFiles = $ds->getIncludedFiles(); | |
foreach ($srcFiles as $src) { | |
$file = new PhingFile($fromDir, $src); | |
if ($mapper !== null) { | |
$results = $mapper->main($file); | |
if ($results === null) { | |
throw new BuildException( | |
sprintf( | |
'No filename mapper found for "%s"', | |
$file | |
) | |
); | |
} | |
$targetFile = reset($results); | |
} else { | |
$targetFile = $this->getTargetFile($file, $this->destination); | |
} | |
$this->render($file, $targetFile); | |
} | |
} | |
} | |
/** | |
* Renders a single file and applies filters on it | |
* | |
* @param string $tool conversion tool to use | |
* @param string $source markdown source file | |
* @param string $targetFile target file name | |
* | |
* @return void | |
*/ | |
protected function render($source, $targetFile) | |
{ | |
if (count($this->filterChains) == 0) { | |
return $this->renderFile($source, $targetFile); | |
} | |
$tmpTarget = tempnam(sys_get_temp_dir(), 'tmp-'); | |
$this->renderFile($source, $tmpTarget); | |
$this->fileUtils->copyFile( | |
new PhingFile($tmpTarget), | |
new PhingFile($targetFile), | |
true, false, $this->filterChains, | |
$this->getProject(), $this->mode | |
); | |
unlink($tmpTarget); | |
} | |
/** | |
* Renders a single file with the markdown tool. | |
* | |
* @param string $tool conversion tool to use | |
* @param string $source markdown source file | |
* @param string $targetFile target file name | |
* | |
* @return void | |
* | |
* @throws BuildException When the conversion fails | |
*/ | |
protected function renderFile($source, $targetFile) | |
{ | |
if ($this->uptodate && file_exists($targetFile) | |
&& filemtime($source) <= filemtime($targetFile) | |
) { | |
//target is up to date | |
return; | |
} | |
//work around a bug in php by replacing /./ with / | |
$targetDir = str_replace('/./', '/', dirname($targetFile)); | |
if (!is_dir($targetDir)) { | |
mkdir($targetDir, $this->mode, true); | |
} | |
$arOutput = Markdown(file_get_contents($source)); | |
$retval = file_put_contents($targetFile,$arOutput); | |
if ( ! $retval) { | |
$this->log('File not rendered.', Project::MSG_INFO); | |
throw new BuildException('Rendering markdown failed'); | |
} | |
$this->log('File rendered.', Project::MSG_DEBUG); | |
} | |
/** | |
* Determines and returns the target file name from the | |
* input file and the configured destination name. | |
* | |
* @param string $file Input file | |
* @param string $destination Destination file or directory name, | |
* may be null | |
* | |
* @return string Target file name | |
* | |
* @uses $format | |
* @uses $targetExt | |
*/ | |
public function getTargetFile($file, $destination = null) | |
{ | |
if ($destination != '' | |
&& substr($destination, -1) !== '/' | |
&& substr($destination, -1) !== '\\' | |
) { | |
return $destination; | |
} | |
if (strtolower(substr($file, -3)) == '.md') { | |
$file = substr($file, 0, -3); | |
} | |
if($this->removefilesetdir) | |
$file = str_replace($this->removefilesetdir.DIRECTORY_SEPARATOR,'',$file); | |
return $destination . $file . '.' . $this->format; | |
} | |
/** | |
* The setter for the attribute "file" | |
* | |
* @param string $file Path of file to render | |
* | |
* @return void | |
*/ | |
public function setFile($file) | |
{ | |
$this->file = $file; | |
} | |
/** | |
* The setter for the attribute "destination" | |
* | |
* @param string $destination Output file or directory. When it ends | |
* with a slash, it is taken as directory. | |
* | |
* @return void | |
*/ | |
public function setDestination($destination) | |
{ | |
$this->destination = $destination; | |
} | |
/** | |
* The setter for the attribute "destination" | |
* | |
* @param string $destination Output file or directory. When it ends | |
* with a slash, it is taken as directory. | |
* | |
* @return void | |
*/ | |
public function setRemovefilesetdir($removefilesetdir) | |
{ | |
$this->removefilesetdir = $removefilesetdir; | |
} | |
/** | |
* The setter for the attribute "uptodate" | |
* | |
* @param string $uptodate True/false | |
* | |
* @return void | |
*/ | |
public function setUptodate($uptodate) | |
{ | |
$this->uptodate = (boolean)$uptodate; | |
} | |
/** | |
* Nested creator, creates a FileSet for this task | |
* | |
* @return object The created fileset object | |
*/ | |
public function createFileSet() | |
{ | |
$num = array_push($this->filesets, new FileSet()); | |
return $this->filesets[$num-1]; | |
} | |
/** | |
* Nested creator, creates one Mapper for this task | |
* | |
* @return Mapper The created Mapper type object | |
* | |
* @throws BuildException | |
*/ | |
public function createMapper() | |
{ | |
if ($this->mapperElement !== null) { | |
throw new BuildException( | |
'Cannot define more than one mapper', $this->location | |
); | |
} | |
$this->mapperElement = new Mapper($this->project); | |
return $this->mapperElement; | |
} | |
/** | |
* Creates a filterchain, stores and returns it | |
* | |
* @return FilterChain The created filterchain object | |
*/ | |
public function createFilterChain() | |
{ | |
$num = array_push($this->filterChains, new FilterChain($this->project)); | |
return $this->filterChains[$num-1]; | |
} | |
} |
This file contains 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 | |
require_once 'phing/Task.php'; | |
require_once 'phing/util/FileUtils.php'; | |
require_once 'System.php'; | |
require_once "phing/tasks/ext/markdown/markdown.php"; | |
class TemplateTask extends Task { | |
/** | |
* @var string Taskname for logger | |
*/ | |
protected $taskName = 'Template'; | |
/** | |
* Input file in markdown format. | |
* Required | |
* | |
* @var string | |
*/ | |
protected $file = null; | |
/** | |
* Output file or directory. May be omitted. | |
* When it ends with a slash, it is considered to be a directory | |
* | |
* @var string | |
*/ | |
protected $destination = null; | |
/** | |
* templatefiles directory. required. | |
* | |
* @var string | |
*/ | |
protected $templatesDir; | |
/** | |
* default template content. required. | |
* | |
* @var string | |
*/ | |
protected $template_content; | |
/** | |
* default template placeholders. at least one required. | |
* | |
* @var array | |
*/ | |
protected $template_placeholders = array(); | |
protected $filesets = array(); // all fileset objects assigned to this task | |
protected $mapperElement = null; | |
/** | |
* all filterchains objects assigned to this task | |
* | |
* @var array | |
*/ | |
protected $filterChains = array(); | |
/** | |
* mode to create directories with | |
* | |
* @var integer | |
*/ | |
protected $mode = 0755; | |
/** | |
* Only render files whole source files are newer than the | |
* target files | |
* | |
* @var boolean | |
*/ | |
protected $uptodate = false; | |
/** | |
* The main entry point method. | |
* | |
* @return void | |
*/ | |
public function main() | |
{ | |
if( ! $this->templatesDir){ | |
throw new BuildException('The templates directory is required'); | |
} | |
$this->setTemplatePlaceholders('default'); | |
if (count($this->filterChains)) { | |
$this->fileUtils = new FileUtils(); | |
} | |
if ($this->file != '') { | |
$file = $this->file; | |
$targetFile = $this->getTargetFile($file, $this->destination); | |
$this->render($file, $targetFile); | |
return; | |
} | |
if (!count($this->filesets)) { | |
throw new BuildException( | |
'"file" attribute or "fileset" subtag required' | |
); | |
} | |
// process filesets | |
$mapper = null; | |
if ($this->mapperElement !== null) { | |
$mapper = $this->mapperElement->getImplementation(); | |
} | |
$project = $this->getProject(); | |
foreach ($this->filesets as $fs) { | |
$ds = $fs->getDirectoryScanner($project); | |
$fromDir = $fs->getDir($project); | |
$srcFiles = $ds->getIncludedFiles(); | |
foreach ($srcFiles as $src) { | |
$file = new PhingFile($fromDir, $src); | |
if ($mapper !== null) { | |
$results = $mapper->main($file); | |
if ($results === null) { | |
throw new BuildException( | |
sprintf( | |
'No filename mapper found for "%s"', | |
$file | |
) | |
); | |
} | |
$targetFile = reset($results); | |
} else { | |
$targetFile = $this->getTargetFile($file, $this->destination); | |
} | |
$this->render($file, $targetFile); | |
} | |
} | |
} | |
/* | |
* | |
*/ | |
protected function setTemplatePlaceholders($templateDir) | |
{ | |
if($templateDir == 'default'){ | |
if( ! file_exists($this->templatesDir.DIRECTORY_SEPARATOR.'template.html')){ | |
throw new BuildException('A template.html file must in the templates directory'); | |
} | |
$this->template_content = file_get_contents($this->templatesDir.DIRECTORY_SEPARATOR.'template.html'); | |
preg_match_all('/\[[A-Z]+\]/',$this->template_content,$placeholders); | |
if( ! $placeholders){ | |
throw new BuildException('At least a [CONTENT] placeholder is required'); | |
} | |
$this->template_placeholders = $placeholders[0]; | |
return true; | |
}else{ | |
} | |
} | |
/** | |
* Renders a single file and applies filters on it | |
* | |
* @param string $tool conversion tool to use | |
* @param string $source markdown source file | |
* @param string $targetFile target file name | |
* | |
* @return void | |
*/ | |
protected function render($source, $targetFile) | |
{ | |
if (count($this->filterChains) == 0) { | |
return $this->renderFile($source, $targetFile); | |
} | |
$tmpTarget = tempnam(sys_get_temp_dir(), 'tmp-'); | |
$this->renderFile($source, $tmpTarget); | |
$this->fileUtils->copyFile( | |
new PhingFile($tmpTarget), | |
new PhingFile($targetFile), | |
true, false, $this->filterChains, | |
$this->getProject(), $this->mode | |
); | |
unlink($tmpTarget); | |
} | |
/** | |
* Renders a single file with the markdown tool. | |
* | |
* @param string $tool conversion tool to use | |
* @param string $source markdown source file | |
* @param string $targetFile target file name | |
* | |
* @return void | |
* | |
* @throws BuildException When the conversion fails | |
*/ | |
protected function renderFile($source, $targetFile) | |
{ | |
if ($this->uptodate && file_exists($targetFile) | |
&& filemtime($source) <= filemtime($targetFile) | |
) { | |
//target is up to date | |
return; | |
} | |
//work around a bug in php by replacing /./ with / | |
$targetDir = str_replace('/./', '/', dirname($targetFile)); | |
if (!is_dir($targetDir)) { | |
mkdir($targetDir, $this->mode, true); | |
} | |
$arOutput = $this->wrapTemplate($source); | |
$retval = file_put_contents($targetFile,$arOutput); | |
if ( ! $retval) { | |
$this->log('File not rendered.', Project::MSG_INFO); | |
throw new BuildException('Rendering template failed'); | |
} | |
$this->log('File rendered.', Project::MSG_DEBUG); | |
} | |
protected function wrapTemplate($sourcefile) | |
{ | |
$template_content = $this->template_content; | |
$template_placeholders = $this->template_placeholders; | |
if(strpos($sourcefile,DIRECTORY_SEPARATOR) !== false){ | |
} | |
$placeholders = array_fill_keys($template_placeholders,''); | |
$placeholders['[CONTENT]'] = file_get_contents($sourcefile); | |
return strtr($template_content,$placeholders); | |
} | |
/** | |
* Determines and returns the target file name from the | |
* input file and the configured destination name. | |
* | |
* @param string $file Input file | |
* @param string $destination Destination file or directory name, | |
* may be null | |
* | |
* @return string Target file name | |
* | |
* @uses $format | |
* @uses $targetExt | |
*/ | |
public function getTargetFile($file, $destination = null) | |
{ | |
if ($destination != '' | |
&& substr($destination, -1) !== '/' | |
&& substr($destination, -1) !== '\\' | |
) { | |
return $destination; | |
} | |
return $destination . $file ; | |
} | |
/** | |
* The setter for the attribute "templatesdir" | |
* | |
* @param string $templatesDir directory of the templatefiles | |
* | |
* @return void | |
*/ | |
public function setTemplatesDir($templatesDir) | |
{ | |
$this->templatesDir = $templatesDir; | |
$this->template = $templatesDir.'/template.html'; | |
} | |
/** | |
* The setter for the attribute "uptodate" | |
* | |
* @param string $uptodate True/false | |
* | |
* @return void | |
*/ | |
public function setUptodate($uptodate) | |
{ | |
$this->uptodate = (boolean)$uptodate; | |
} | |
/** | |
* Nested creator, creates a FileSet for this task | |
* | |
* @return object The created fileset object | |
*/ | |
public function createFileSet() | |
{ | |
$num = array_push($this->filesets, new FileSet()); | |
return $this->filesets[$num-1]; | |
} | |
/** | |
* Nested creator, creates one Mapper for this task | |
* | |
* @return Mapper The created Mapper type object | |
* | |
* @throws BuildException | |
*/ | |
public function createMapper() | |
{ | |
if ($this->mapperElement !== null) { | |
throw new BuildException( | |
'Cannot define more than one mapper', $this->location | |
); | |
} | |
$this->mapperElement = new Mapper($this->project); | |
return $this->mapperElement; | |
} | |
/** | |
* Creates a filterchain, stores and returns it | |
* | |
* @return FilterChain The created filterchain object | |
*/ | |
public function createFilterChain() | |
{ | |
$num = array_push($this->filterChains, new FilterChain($this->project)); | |
return $this->filterChains[$num-1]; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment