Files
Rusty Shackleford af24bd579f Initial commit
2014-05-24 05:27:14 +04:00

500 lines
20 KiB
PHP

<?php # KT_GENERATE # Structure generator
use Katana\Seal as Seal;
use Katana\Spell as Spell;
import(Module::regex);
final class Generate extends Protector {
static function prepare($literal, $tid, $post, $files, $omitted = true){ return #
"<article id='P{$post['id']}' ".(($tid == $post['id']) ? "class='post post_op' data-node='{$literal}'" : "class='post'")." data-wire='{$tid}' data-post='{$post['id']}'>"
.(($tid == $post['id']) ? "" : "<div class='post__reply'>")
."<header class='post__header'>"
."<a href='/{$literal}' class='post__board highlight' hidden>{$literal}</a>"
.(empty($post['subj'])?"":"<a href='/{$literal}/post/{$post['id']}' class='post__title highlight'>{$post['subj']}</a>")
.(empty($post['name'])?"":"<span class='post__name'>{$post['name']}"
.(empty($post['trip'])?"":"<sup>{$post['trip']}</sup>")
."</span>")
."<a name='#P{$post['id']}' href='/{$literal}/post/{$post['id']}' class='post__reflink'>#</a>"
."<a name='#A{$post['id']}' href='/{$literal}/thread/{$tid}#P{$post['id']}' class='post__reflink'>{$post['id']}</a>"
."<time class='post__date thread__date' pubdate>".static::prepare_timestamp($post['unix'])."</time>"
.(($tid == $post['id']) && $post['spec'] ? static::prepare_spells($post['spec']) : "")
."<label hidden data-menu class='post__icon label icon icon-more_horiz menu__icon post__icon' title='Actions with &gt;&gt;{$post['id']}'></label>"
."<label data-answer class='post__icon label icon icon-reply' title='Answer to &gt;&gt;{$post['id']}'></label>"
."</header><main class='post__message'>"
.static::prepare_files($files)
.$post['text']
."</main>"
."<footer class='".(($tid == $post['id']) ? "thread__footer" : "post__footer")."'>"
.(($tid == $post['id']) && $omitted
? "<a href='/{$literal}/thread/{$tid}' class='thread__open drown'>Open</a>&nbsp;<span class='thread__omitted drown'>(".static::prepare_omitted($tid).")</span>"
: ""
)
."</footer>"
.(($tid == $post['id']) ? "" : "</div>")
."</article>";
}
static function prepare_spells($mode){
$mode = abs(intval($mode));
$cast = "";
if($mode < 0 || $mode > 0xFF){
return $cast;
}
$spell = function($icon){ return "<i class='icon-spell-{$icon}' title='{$icon}'></i>"; };
if($mode&Spell::LOCKED) $cast .= $spell("locked");
if($mode&Spell::PINNED) $cast .= $spell("pinned");
if($mode&Spell::CURSED) $cast .= $spell("cursed");
if($mode&Spell::HIDDEN) $cast .= $spell("hidden");
if($mode&Spell::SHARDS) $cast .= $spell("shards");
if($mode&Spell::SPIRIT) $cast .= $spell("spirit");
if($mode&Spell::UNDEAD) $cast .= $spell("undead");
if($mode&Spell::CUSTOM) $cast .= $spell("custom");
if($mode&Spell::ASTRAL) $cast .= $spell("astral");
return $cast;
}
static function prepare_refmap($literal, $tid, $pid){
$map = decode("static/node/{$literal}/map/{$tid}");
$ref = "";
if(isset($map[$pid])){
foreach($map[$pid] as $reflink){
$ref.= "<a class='refmap__link' href='/{$reflink[0]}/post/{$reflink[1]}'>&gt;&gt;{$reflink[1]}</a>";
}
}
return $ref;
}
static function prepare_omitted($tid){
$o = intval(X::$cata[$tid][1]) - intval(X::$conf['kt']['maxposts']) + 1;
return ($o > 0 ? $o : "nothing")." omitted";
}
static function prepare_files($files){
if(empty($files)){ return ""; }
$html = "";
foreach($files as $file){
$html .= static::prepare_file($file['name'], $file['orig'], $file['info'], $file['size']);
}
return $html;
}
static function prepare_file($unique_name, $original_name, $finfo, $size){
if(empty($unique_name)){
return "";
}
if(Regex::has_preview($unique_name)){
$real_file = "static/node/".X::$node."/pre/".Regex::get_file_name($unique_name);
if (file_exists(ROOT.$real_file.".jpg")){
$real_file.= ".jpg";
}
if (file_exists(ROOT.$real_file.".jpeg")){
$real_file.= ".jpeg";
}
if (file_exists(ROOT.$real_file.".png")){
$real_file.= ".png";
}
if (file_exists(ROOT.$real_file.".gif")){
$real_file.= ".gif";
}
if (file_exists(ROOT.$real_file.".webp")){
$real_file.= ".webp";
}
return "<figure class='file'>"
."<figcaption>"
."<a href='/static/node/".X::$node."/src/{$unique_name}' class='file__name' download='{$original_name}' title='Download as {$original_name}'><i class='icon icon-download'></i>&nbsp;{$original_name}</a>"
."</figcaption>"
."<div class='file__preview'>"
."<a target='_blank' href='/static/node/".X::$node."/src/{$unique_name}'>"
."<img class='post__img' src='/{$real_file}' alt='nani?!'>"
."<figcaption class='file__size overlay' hidden>"
."<span>{$finfo}</span>"
."<span>".Chrona::bytes($size)."</span>"
."</figcaption>"
."</a>"
."<a target='_blank' href='http://iqdb.org/?url=".FQDN."/static/node/".X::$node."/src/{$unique_name}' class='file__search icon icon-search icon_hover_light icon_size_b' title='Search on IQDB' hidden></a>"
."</div>"
."</figure>";
} else { # not image/video
$icon = "icon-file-doc";
if(preg_match(Regex::TYPE_AUDIO, $unique_name)){
$icon = "icon-file-aud";
}
elseif(preg_match(Regex::TYPE_FLASH, $unique_name)){
/*$prev = "/static/node/".X::$node."/pre/".Regex::get_file_name($unique_name).".jpg";
if(file_exists(PATH.$prev)){
}*/
$icon = "icon-file-fla";
}
elseif(preg_match(Regex::TYPE_PASTE, $unique_name)){
$icon = "icon-file-txt";
}
elseif(preg_match(Regex::TYPE_ARCHIVE, $unique_name)){
$icon = "icon-file-arc";
}
elseif(preg_match(Regex::TYPE_BITTORRENT, $unique_name)){
$icon = "icon-file-p2p";
}
elseif(preg_match(Regex::TYPE_CIPHER, $unique_name)){
$icon = "icon-file-key";
}
elseif(preg_match(Regex::TYPE_CONTAINER, $unique_name)){
$icon = "icon-file-vlt";
}
else $icon = "icon-file-doc";
return "<figure class='file'>"
."<figcaption>"
."<a href='/static/node/".X::$node."/src/{$unique_name}' class='file__name' download='{$original_name}' title='Download as {$original_name}'><i class='icon icon-download'></i>&nbsp;{$original_name}</a>"
."</figcaption>"
."<div class='file__preview file__preview_has_icon'>"
."<a href='/static/node/".X::$node."/src/{$unique_name}'>"
."<i class='icon {$icon} file__icon'></i>"
."<figcaption class='file__size overlay' hidden>"
."<span>{$finfo}</span>"
."<span>".Chrona::bytes($size)."</span>"
."</figcaption>"
."</a>"
."</div>"
."</figure>";
}
}
static function prepare_timestamp($timestamp){
$days = array('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday');
#$days = array('Воскресенье','Понедельник','Вторник','Среда','Четверг','Пятница','Суббота');
#$days = array('I','II','III','IV','V','VI','VII');
$months = array('Jan','Feb','Mar','Apr','May','June','July','Aug','Sep','Oct','Nov','Dec');
#$months = array('Ян','Фев','Мар','Апр','Мая','Июня','Июля','Авг','Сеп','Окт','Нов','Дек');
#$months = array('I','II','III','IV','V','VI','VII','VIII','IX','X','XI','XII');
#$months = array('0000','0001','0010','0011','0100','0101','0110','0111','1000','1001','1010','1011');
#$months = array('Сечня','Лютня','Березня','Цветня','Травня','Красня','Липня','Зарева','Вересня','Рюена','Листопада','Снежня');
#$months = array('Зари нового мира','Войны богов','Кровавого света','Трупного частокола','Предвестника хаоса','Тления тел','Пожирающего огня','Ужасающего воздаяния','Угасания смерти','Давящего безумия','Близости конца','Чертога вечного льда'); /* спонсировано церковью Кровавого Бога, приходите к нам ^_^ */
$nowdate = getdate($timestamp);
$time = sprintf("%02u",$nowdate['hours']).':'.sprintf("%02u",$nowdate['minutes']).':'.sprintf("%02u",$nowdate['seconds']);
return "{$nowdate['mday']} {$months[$nowdate['mon']-1]} {$nowdate['year']} at {$time}";
//return "{$days[$nowdate['wday']]}, {$nowdate['mday']} {$months[$nowdate['mon']-1]} {$nowdate['year']} {$time}";
}
static function all(){ # regenerate all boards
foreach(static::spawn("SELECT `lt` FROM `nodes`") as $literal){
static::board($literal);
}
debug("[generator::all] DONE");
return true;
}
static function board($literal, $override = false){ # rebuild one board
if(!isset(X::$tree[$literal])){
stop("[generator::board] /{$literal}/ Error in arguments: literal={$literal};");
}
Katana::chkfs($literal);
Katana::cata($literal);
if(static::catalist($literal)
&& static::chunks($literal)
&& static::threads($literal)
){
debug("[generator::board] /{$literal}/ DONE");
return true;
}
stop("[generator::board] /{$literal}/ Error in regeneration monad");
return false;
}
static function threads($literal, $full = true){
if(!isset(X::$tree[$literal])){
stop("[generator::threads] Error in arguments: literal={$literal};");
}
foreach(Q::wire__posts($literal, 0) as $thread_id){
static::thread($literal, $thread_id);
}
debug("[generator::threads] /{$literal}/ DONE");
return true;
}
static function thread($literal, $tid){
if(!isset(X::$tree[$literal])){
stop("[generator::thread] /{$literal}/{$tid}/ Error in arguments: literal={$literal};tid={$tid};");
}
$tid = abs(intval($tid));
$path = "static/node/{$literal}/thread/{$tid}.htm";
$thread = Q::post__read($literal, $tid);
if(empty($thread)){
stop("[generator::thread] /{$literal}/{$tid}/ Error fetching data for literal={$literal};tid={$tid};");
}
if(!X::$mode&Seal::NAME){ $thread['name'] = ''; $thread['trip'] = ''; }
$files = Q::post__files($literal, $tid, $tid);
if(commit($path, self::prepare($literal, $tid, $thread, $files, false))){
foreach(Q::wire__posts_enumerate($literal, $tid) as $post){
$files = Q::post__files($literal, $tid, $post['id']);
commit($path, self::prepare($literal, $tid, $post, $files, false), true);
}
} else stop("[generator::thread] /{$literal}/{$tid}/ Error writing in `{$path}` for literal={$literal};tid={$tid};");
debug("[generator::thread] /{$literal}/{$tid}/ DONE");
return true;
}
static function append($literal, $tid, $pid){
if(!isset($literal, $tid, $pid, X::$tree[$literal], X::$cata[$tid])){
stop("[generator::append] /{$literal}/ Error in arguments: literal={$literal};tid={$tid};pid={$pid};");
}
$tid = abs(intval($tid));
$pid = abs(intval($pid));
$post = Q::post__read($literal, $pid);
if(empty($post)){
stop("[generator::append] Failed calling post data ({$pid})");
}
if(!X::$mode&Seal::NAME){ $post['name'] = ''; $post['trip'] = ''; }
$files = Q::post__files($literal, $tid, $pid);
if(commit("static/node/{$literal}/thread/{$tid}.htm", self::prepare($literal, $tid, $post, $files, false), true)){
debug("[generator::append] /{$literal}/{$tid}/ DONE");
return true;
}
return false;
}
static function chunks($literal){
if(!isset(X::$tree[$literal])){
stop("[generator::chunks] /{$literal}/{$tid}/ Error in arguments: literal={$literal};");
}
foreach(Q::wire__posts($literal, 0) as $chunk_id){
static::chunk($literal, $chunk_id);
}
debug("[generator::chunks] /{$literal}/ DONE");
return true;
}
static function chunk($literal, $tid, $limit = null){
if(!isset(X::$tree[$literal])){
stop("[generator::chunk] /{$literal}/{$tid}/ Error in arguments: literal={$literal};tid={$tid};limit={$limit};");
}
$limit = empty($limit)
? intval(X::$conf['kt']['maxposts'])
: $limit;
$tid = abs(intval($tid));
$path = "static/node/{$literal}/chunk/{$tid}.htm";
$chunk = Q::post__read($literal, $tid);
if(empty($chunk)){
stop("[generator::chunk] /{$literal}/{$tid}/ Error fetching data for literal={$literal};tid={$tid};limit={$limit};");
}
if(!X::$mode&Seal::NAME){ $chunk['name'] = ''; $chunk['trip'] = ''; }
$files = Q::post__files($literal, $tid, $tid);
if(commit($path, self::prepare($literal, $tid, $chunk, $files, true))){
foreach(Q::wire__chunk($literal, $tid, $limit) as $post){
$files = Q::post__files($literal, $tid, $post['id']);
commit($path, self::prepare($literal, $tid, $post, $files, false), true);
}
} else {
stop("[generator::chunk] /{$literal}/{$tid}/ Error writing in `{$path}` for literal={$literal};tid={$tid};");
}
debug("[generator::chunk] /{$literal}/{$tid}/ DONE");
return true;
}
static function references($literal){
if(!isset(X::$tree[$literal])){
stop("[generator::references] /{$literal}/ Error in arguments: literal={$literal};");
}
Katana::cata($literal);
$limit = intval(X::$conf['kt']['maxpages']);
$limit = $limit > 0
? ($limit * intval(X::$conf['kt']['maxthrds']))
: 0;
import(Module::HANDLE);
foreach(Q::wire__posts($literal, 0) as $thread_id){
foreach(Q::wire__posts_text($literal, $thread_id) as $post_id){
Handle::references($literal, $thread_id, $post_id, $post[1]);
}
}
debug("[generator::references] /{$literal}/ DONE");
return true;
}
static function catalist($literal){
if(!isset(X::$tree[$literal])){
stop("[generator::catalist] /{$literal}/ Error in arguments: literal={$literal};id={$tid};shift={$shift};");
}
if(static::catalist_order($literal)){
Katana::cata($literal);
foreach(X::$cata as $tid => $data){
static::catalist_entry($literal, $tid);
}
}
return true;
}
static function catalist_entry($literal, $tid){
if(!isset(X::$tree[$literal])){
stop("[generator::catalist_entry] /{$literal}/ Error in arguments: literal={$literal};tid={$tid};");
}
$path = "static/node/{$literal}/catalist";
$g = decode($path);
if(empty($g)){
$g = array();
}
$_p = Q::cata__post($literal, $tid);
$p = empty($_p)
? 0
: $_p['count'];
$_f = Q::cata__file($literal, $tid);
$f = empty($_f)
? 0
: $_f['count'];
$s = y(intval($g[$tid][0]), 0); // specs
$g[$tid] = array($s, $p, $f);
if(encode($path, $g)){
Katana::cata($literal);
debug("[generator::catalist_entry] /{$literal}/{$tid}/ DONE");
return true;
}
stop("[generator::catalist_entry] /{$literal}/ Error encoding `{$path}` for literal={$literal};tid={$tid};");
return false;
}
static function catalist_order($literal){
if(!isset(X::$tree[$literal])){
stop("[generator::catalist_order] /{$literal}/ Error in arguments: literal={$literal};");
}
$path = "static/node/{$literal}/catalist";
$g = decode($path);
if(empty($g)){
$g = array();
}
$h = array();
$limit = intval(X::$conf['kt']['maxpages']);
$limit = $limit > 0
? $limit * intval(X::$conf['kt']['maxthrds'])
: 0;
$pinned = Q::wire__all_enchanted($literal, Spell::PINNED);
$normal = Q::wire__all_limited($literal, $limit);
foreach(array_merge($pinned, $normal) as $e){
$s = y(intval($e['spec']),0);
$p = y(intval($g[$e['id']][1]),1);
$f = y(intval($g[$e['id']][2]),0);
$h[$e['id']] = array($s,$p,$f);
}
if(encode($path,$h)){
Katana::cata($literal);
debug("[generator::catalist_order] /{$literal}/ DONE");
return true;
}
stop("[generator::catalist_order] /{$literal}/ Error encoding `{$path}` for literal={$literal};");
return false;
}
static function pagelist($ob = null){
$pages = decode(Data::PAGELIST);
$result = "";
if(empty($pages)){
debug("[kt\unity::pagelist] Error decoding");
} else foreach($pages as $address => $name){
$result .= "<a href='/{$address}' class='nav__link menu__link "
.(X::$path[0] == $literal ? "nav__link_active highlight" : "")
."'><i class='icon icon-{$address}'></i>&nbsp;{$name}</a>";
}
if(commit("static/node/pagelist.htm", $result)){
debug("[generator::pagelist] DONE");
return true;
}
stop("[generator::pagelist] Error writing pagelist");
return false;
}
static function nodelist($ob = null){
$divide = 0;
$result = "";
$graph = decode(Graph::SELECT);
$to = (null == $ob ? '' : "<{$ob}>");
$tc = (null == $ob ? '' : "</{$ob}>");
$link = function($literal, $desc) use ($to, $tc){
return $to
."<a smooth dynamic id='node_{$literal}' href='/{$literal}' class='nav__link menu__link"
.(X::$node == $literal ? " nav__link_active highlight" : "")
."'><span hidden>{$literal}</span><span>{$desc}</span></a>"
.$tc;
};
if(!empty($graph["private"])) foreach($graph["private"] as $literal => $data){
if($data['mode']&Seal::HIDE) continue;
$rang = intval($data['rg']);
if($divide < $rang){
$result.= "{$to}<hr/>{$tc}";
$divide = $rang;
}
$result.= $link($literal, $data['name']);
}
$result.= "{$to}<hr/>{$tc}";
if(!empty($graph["public"])) foreach($graph["public"] as $literal => $data){
if($data['mode']&Seal::HIDE) continue;
$rang = intval($data['rg']);
if($divide < $rang){
$result .= "{$to}<hr/>{$tc}";
$divide = $rang;
}
$result.= $link($literal, $data['name']);
}
if(commit("static/node/nodelist.htm", $result)){
debug("[generator::nodelist] DONE");
return true;
}
stop("[generator::nodelist] Error writing nodelist");
return false;
}
static function nodeinfo($literal){
if(!isset(X::$tree[$literal])){
stop("[generator::info] /{$literal}/ Error in arguments: literal={$literal};");
}
Katana::cata($literal);
X::$mode = X::$tree[$literal]['mode'];
$temp = "<h5>General information about /{$literal}/</h5><ul>";
$temp.= "<li>Captcha is turned ".(X::$conf['kt']['postcode'] ? 'on' : 'off').".</li>";
$temp.= "<li>Maximum message length is ".(intval(X::$conf['kt']['textsize']) > 0 ? X::$conf['kt']['textsize'] : '&infin;')." symbols.</li>";
$temp.= "<li>Maximum file size is ".X::$conf['kt']['filesize']." MiB.</li>";
$temp.= "<li>Minimum allowed delay between consequent posts from one user is ".(X::$conf['kt']['interval'] ? X::$conf['kt']['interval'].' seconds' : 'turned off').".</li>";
$temp.= "</ul><h5>Permissions</h5><ul>"
."<li><i class='icon-".(X::$mode&Seal::NAME?"on' on":"off' off")."></i><span>Names and tripcodes</span></li>"
."<li><i class='icon-".(X::$mode&Seal::LINK?"on' on":"off' off")."></i><span>External service embedding</span></li>"
."<li><i class='icon-".(X::$mode&Seal::FILE?"on' on":"off' off")."></i><span>File uploading</span></li>";
if(!(X::$mode&Seal::FILE)){
$temp.= "<li>No file types are allowed to post.</li>";
} else {
$list = [];
foreach([
Seal::IMG => 'images',
Seal::AUD => 'audio',
Seal::VID => 'video',
Seal::FLA => 'flash',
Seal::TXT => 'raw text and code sources',
Seal::ARC => 'archives',
Seal::DOC => 'documents',
Seal::ADD => 'some additional file formats',
] as $flag => $type){
if(X::$mode&$flag){
$list[] = $type;
}
}
$temp.= "<li>";
if(!empty($list)){
$temp.= "You are allowed to post any ";
$temp.= implode(', ',$list);
$temp.= " except those violating the rules.";
} else {
$temp.= "You are not allowed to post any file types.";
}
$temp.= "</li>";
}
$temp.= "</ul><h5>Last updated: ".date('l jS \of F Y h:i:s A')." (mod.".X::$mode.")</h5>";
if(commit("static/node/{$literal}/info.htm", $temp)){
debug("[generator::info] /{$literal}/ DONE");
return true;
}
stop("[generator::info] /{$literal}/ Error writing in `{$path}` for literal={$literal};");
return false;
}
}