dir = BASEDIR.'data/'.$dirname.'/';
$this->projname = $projname;
$matches = [];
// Identify resources in folder
$ls = scandir($this->dir);
$found_real_lastupdate = false;
foreach($ls as $file) {
if ($file[0] == '.') {
continue;
}
if ($file == 'README.txt') {
$this->longdesc = file_get_contents($this->dir.'README.txt');
$this->longdesc = str_replace("\r", "", $this->longdesc); // filter windows CR
$this->longdesc = preg_replace("~[\s\t]*$~s", "", $this->longdesc); // filter trailing spaces at line endings
// Guess 'last update' time
if (preg_match('~\n(\d\d\d\d-\d\d-\d\d)~', $this->longdesc, $matches)) {
// Use first date entry (assumed to be a CHANGELOG)
$this->lastupdate = strtotime($matches[1]);
$found_real_lastupdate = true;
}
// Find number of releases
preg_match_all('~\n(\d\d\d\d-\d\d-\d\d)~', $this->longdesc, $matches, PREG_SET_ORDER);
$this->numreleases = count($matches);
// Find support lifespan (newest minus youngest)
$eldest = time();
$newest = 0;
foreach($matches as $match) {
$stamp = strtotime($match[1]);
$eldest = min($stamp, $eldest);
$newest = max($stamp, $newest);
}
$this->lifespan = floor(max(0, $newest - $eldest) / 3600); // could divide by 86400 but it doesn't matter
// Find 'written in'
if (preg_match('~Written in ([^\\n]+)~', $this->longdesc, $matches)) {
$this->subtag = rtrim($matches[1], ' .');
}
// Find `go-get` tags
$this->longdesc = preg_replace_callback('~\[go-get\](.+)\[/go-get\]~', function($matches) {
$this->go_get_target = $matches[1];
return '';
}, $this->longdesc);
// Find tags
if (preg_match('~Tags: ([^\\n]+)~', $this->longdesc, $matches)) {
$this->tags = array_map('trim', explode(',', $matches[1]));
}
// Find `entry` tags
$this->longdesc = preg_replace(
'~\\[entry=([^\\]]+?)\\](.+?)\\[/entry\\]~m',
'[url='.BASEURL.'\\1/]\\2[/url]', // hesc still hasn't happened, transform bbcode->bbcode
$this->longdesc
);
// Extract short description
$parts = explode("\n", $this->longdesc);
$this->shortdesc = array_shift($parts);
$this->shortdesc[0] = strtolower($this->shortdesc[0]); // cosmetic lowercase
// Filters for longdesc
$this->prefix_html = '';
$this->longdesc = preg_replace_callback('~\nWritten in ([^\\n]+)~ms', function($matches) {
$this->prefix_html .= (
(SHIELDS_PREFIX ? mkshield('build', 'success', 'brightgreen').' ' : '').
mkshield('written in', rtrim($matches[1], '.'), 'blue')
);
return '';
}, $this->longdesc);
// Find 'git-repository' tags
$this->longdesc = preg_replace_callback('~\[git\](.+)\[/git\]~', function($matches) {
$this->git_repo = $matches[1];
if (strlen($this->prefix_html) > 0) {
$this->prefix_html .= ' ';
}
$this->prefix_html .= ''.mkshield('vcs', 'git', 'yellowgreen', ['logo' => 'git']).'';
return '';
}, $this->longdesc);
while(strpos($this->longdesc, "\n\n\n") !== false) {
$this->longdesc = str_replace("\n\n\n", "\n\n", $this->longdesc);
}
$this->longdesc = rtrim($this->longdesc, "\n")."\n";
continue;
}
if (! $found_real_lastupdate) {
$this->lastupdate = max(
$this->lastupdate,
// filectime($this->dir.$file),
($file == 'README.txt' ? filectime($this->dir.$file) : filemtime($this->dir.$file)) // Don't count README updates
);
}
if (is_image($file)) {
$this->images[] = $file;
} else {
$this->downloads[] = $file;
}
}
natcasesort($this->downloads);
$this->downloads = array_reverse($this->downloads);
for($i = 0, $e = count($this->downloads); $i !== $e; ++$i) {
$this->downloads_hashes[] = (
sha1_file($this->dir.$this->downloads[$i])
);
}
}
public function genHomeImage() {
if (count($this->images)) {
$this->homeimage = mkthumbnail(
$this->dir.$this->images[0],
null, // raw handle
INDEX_THUMB_W, INDEX_THUMB_H
);
}
}
protected function filterLongDescArea() {
// If *all* downloads can be assigned to a changelog release entry,
// then move the files
// A release entry is marked by any string following the date field.
// Add one more NL than we really want, for regex reasons
$this->longdesc .= "\n";
preg_match_all('~^(\d\d\d\d-\d\d-\d\d)\s?:? (.+?)\n\n~ms', $this->longdesc, $matches, PREG_SET_ORDER);
do {
// Ensure changelog exists
if (! count($matches)) {
break;
}
// Find all tags
$known_tags = [];
foreach($matches as $i => $match) {
$tag = trim(explode("\n", $match[2])[0]);
$known_tags[$tag] = $i;
}
// Ensure all release entries have tags
if (count($matches) != count($known_tags)) {
error_log("[".$this->projname."] not all release entries have tags\n");
break;
}
// Ensure all downloads can be assigned to tags.
// In the event of a download matching multiple tags, it'll
// be assigned to the newest (topmost) entry
$found_idx = [];
$render_per_tag = [];
foreach(array_keys($known_tags) as $tagname) {
$render_per_tag[$tagname] = [];
}
foreach($this->downloads as $idx => $filename) {
foreach(array_keys($known_tags) as $tagname) {
if (stripos($filename, (string)$tagname) !== false) {
$found_idx[$idx] = $tagname;
$render_per_tag[$tagname][$idx] = $filename;
break; // next file
}
}
}
if (count($found_idx) != count($this->downloads)) {
error_log("[".$this->projname."] not all downloads have matching tags");
break;
}
// Make HTML modifications
foreach($known_tags as $tag_name => $tag_idx) {
$find = rtrim($matches[$tag_idx][0]);
$this->longdesc = str_replace(
$find,
$find.'${{TAG_'.$tag_idx.'}}',
$this->longdesc
);
}
$this->longdesc = substr($this->longdesc, 0, strlen($this->longdesc)-1); // Strip the extra NL we added
$this->longdesc = text2html($this->longdesc);
foreach($known_tags as $tag_name => $tag_idx) {
$this->longdesc = str_replace(
'${{TAG_'.$tag_idx.'}}',
$this->renderDownloadsBlock($render_per_tag[$tag_name], false),
$this->longdesc
);
}
$this->longdesc = str_replace("\n
", "", $this->longdesc);
// Skip displaying the global downloads area
// This flag also indicates that the content has been pre-HTMLified
$this->downloads_section_was_replaced = true;
// Successful upgrade
} while(false);
if (! $this->downloads_section_was_replaced) {
$this->longdesc = substr($this->longdesc, 0, strlen($this->longdesc)-1); // Strip the extra NL we added
}
}
public function numDownloads() {
return count($this->downloads);
}
public function write() {
// Generate image thumbnails
foreach($this->images as $idx => $image) {
$outfile = BASEDIR.'wwwroot/img/'.$this->projname.'_'.$idx;
copy($this->dir.$image, $outfile.'.'.str_ext($image));
mkthumbnail($outfile.'.'.str_ext($image), $outfile.'_thumb.jpg', PAGE_THUMB_W, PAGE_THUMB_H);
}
// Copy downloads to wwwroot
foreach($this->downloads as $idx => $filename) {
$cmkdir = @mkdir( BASEDIR.'wwwroot/srv/'.$this->downloads_hashes[$idx] );
if (! $cmkdir) {
fputs(
STDOUT,
"WARNING: Couldn't create directory ".$this->downloads_hashes[$idx].
" for file '${filename}'".
" in project '".$this->projname."'!\n"
);
}
copy(
$this->dir.$filename,
BASEDIR.'wwwroot/srv/'.$this->downloads_hashes[$idx].'/'.$filename
);
}
// Generate index page
ob_start();
$this->index();
$extra_head_items = [];
$extra_head_items[] = ''; // TODO include golang `go get` meta if necessary
if (strlen($this->go_get_target) > 0) {
$extra_head_items[] = ''; // TODO include golang `go get` meta if necessary
}
$idxfile = template($this->projname.' | '.SITE_TITLE, ob_get_clean(), implode("\n", $extra_head_items));
mkdir(BASEDIR.'wwwroot/'.$this->projname);
file_put_contents(BASEDIR.'wwwroot/'.$this->projname.'/index.html', $idxfile); // new URL format
file_put_contents(BASEDIR.'wwwroot/'.$this->projname.'.html', redirecthtml(BASEURL.$this->projname)); // old URL format
}
public function getClassAttr() {
if (count($this->tags)) {
return 'taggedWith-'.implode(' taggedWith-', $this->tags);
} else {
return '';
}
}
public function renderDownloadsBlock($render_downloads, $include_header=false) {
if (! count($render_downloads)) {
return;
}
ob_start();
?>
DOWNLOAD