@ -5,6 +5,7 @@
@@ -5,6 +5,7 @@
# include <iostream>
# include <filesystem>
# include <sstream>
# include <stdexcept>
# include <string>
# include <cstdlib>
# include <optional>
@ -43,6 +44,23 @@ const std::string TEMPLATE_CODE_END = "}}";
@@ -43,6 +44,23 @@ const std::string TEMPLATE_CODE_END = "}}";
using json = nlohmann : : json ;
namespace fs = std : : filesystem ;
enum job_type {
COPY_FILE = 1 ,
MARKDOWN = 1 < < 1 ,
TEMPLATE = 1 < < 2 ,
DELETE_FILE = 1 < < 3 ,
MAKE_DIR = 1 < < 4
} ;
struct blog_item {
job_type type ;
fs : : path src ;
std : : map < std : : string , std : : string > properties ;
time_t post_date ;
} ;
template < typename TP >
std : : time_t to_time_t ( TP tp )
{
@ -325,6 +343,24 @@ class substitution_plugin {
@@ -325,6 +343,24 @@ class substitution_plugin {
virtual ~ substitution_plugin ( ) = default ;
} ;
class s2_substitution_plugin : public substitution_plugin {
public :
int
perform_substitution ( int start , int end , const std : : string & invocation ,
std : : string & file_text ,
const std : : map < std : : string , std : : string > & properties ) override
{
return 0 ;
}
/* Must return lengh of replaced text */
virtual int
perform_substitution ( int start , int end , const std : : string & invocation ,
std : : string & file_text ,
const std : : map < std : : string , std : : string > & properties ,
const std : : map < fs : : path , blog_item > & pages ) = 0 ;
} ;
/*
* Returns vector of arguments from string of the form " cmdname:arg1 :arg2 " .
@ -369,7 +405,6 @@ std::vector<std::string> substitution_plugin::get_arguments(const std::string &i
@@ -369,7 +405,6 @@ std::vector<std::string> substitution_plugin::get_arguments(const std::string &i
}
class file_transclude_plugin : public substitution_plugin {
public :
inline static const std : : string hook_name = " include " ;
@ -426,8 +461,6 @@ class mmd_snippet_transclude_plugin : public substitution_plugin {
@@ -426,8 +461,6 @@ class mmd_snippet_transclude_plugin : public substitution_plugin {
}
} ;
class variable_transclude_plugin final : public substitution_plugin {
public :
inline static const std : : string hook_name = " " ;
@ -823,16 +856,27 @@ class feed_builder {
@@ -823,16 +856,27 @@ class feed_builder {
tinyxml2 : : XMLElement * add_article ( std : : string const & url , std : : string const & title , const std : : string & category , time_t updated , time_t published ) {
auto article = add_article ( url , title , updated , published ) ;
auto e_category = article - > InsertNewChildElement ( " category " ) ;
e_category - > SetAttribute ( " term " , category . c_str ( ) ) ;
return article ;
}
tinyxml2 : : XMLElement * add_article ( std : : string const & url , std : : string const & title , const std : : string & category , time_t updated , time_t published , std : : string content ) {
auto article = add_article ( url , title , updated , published ) ;
auto e_category = article - > InsertNewChildElement ( " category " ) ;
e_category - > SetAttribute ( " term " , category . c_str ( ) ) ;
auto e_content = article - > InsertNewChildElement ( " content " ) ;
e_content - > SetText ( content . c_str ( ) ) ;
e_content - > SetAttribute ( " type " , " html " ) ;
return article ;
}
} ;
class rss_feed_plugin : public substitution_plugin {
class rss_feed_plugin : public s2_s ubstitution_plugin {
public :
@ -842,9 +886,8 @@ class rss_feed_plugin : public substitution_plugin {
@@ -842,9 +886,8 @@ class rss_feed_plugin : public substitution_plugin {
int perform_substitution ( int start , int end , const std : : string & invocation ,
std : : string & file_text ,
const std : : map < std : : string , std : : string > & properties ) override {
const std : : map < std : : string , std : : string > & properties , const std : : map < fs : : path , blog_item > & pages ) override {
auto args = get_arguments ( invocation , 1 ) ;
std : : string relpath = args . at ( 1 ) ;
@ -874,6 +917,26 @@ class rss_feed_plugin : public substitution_plugin {
@@ -874,6 +917,26 @@ class rss_feed_plugin : public substitution_plugin {
next = relpath . find ( " , " , first ) ;
}
std : : multimap < time_t , blog_item > feed_items ;
for ( std : : string dir : paths ) {
for ( auto & page : pages ) {
if ( ! ( page . second . type & ( job_type : : TEMPLATE | job_type : : MARKDOWN ) ) ) {
continue ;
}
if ( page . first . filename ( ) = = " index.html " ) {
continue ;
}
if ( page . second . src . string ( ) . find ( dir ) ! = std : : string : : npos ) {
// this is a page to add
feed_items . insert ( { page . second . post_date , page . second } ) ;
}
}
}
/*
file_index_plugin p { } ;
std : : multimap < time_t , file_index_plugin : : post_entry > entries ;
@ -897,22 +960,36 @@ class rss_feed_plugin : public substitution_plugin {
@@ -897,22 +960,36 @@ class rss_feed_plugin : public substitution_plugin {
}
}
}
*/
feed_builder f { compute_url ( properties . at ( " current_file " ) , properties ) , properties . at ( " name " ) , { properties . at ( " author " ) } } ;
for ( auto entry = entrie s. rbegin ( ) ; entry ! = entrie s. rend ( ) ; entry + + ) {
for ( auto entry = feed_item s. rbegin ( ) ; entry ! = feed_item s. rend ( ) ; entry + + ) {
// write into rss feed
if ( ! fs : : is_directory ( entry - > second . path ) ) {
std : : string category = entry - > second . path . parent_path ( ) . filename ( ) . string ( ) ;
f . add_article ( compute_url ( entry - > second . path , properties ) , entry - > second . title , category , entry - > first , entry - > first ) ;
std : : string category = entry - > second . src . parent_path ( ) . filename ( ) . string ( ) ;
std : : string title ;
if ( entry - > second . properties . count ( " title " ) )
title = entry - > second . properties . at ( " title " ) ;
else
title = entry - > second . src . filename ( ) . string ( ) ;
std : : string content ;
if ( entry - > second . properties . count ( " original " ) ) {
content = entry - > second . properties . at ( " original " ) ;
} else {
// fall back to full generated page.
content = entry - > second . properties . at ( " body " ) ;
}
f . add_article ( compute_url ( entry - > second . src , properties ) , title , category , entry - > second . post_date , entry - > second . post_date , content ) ;
}
std : : string text = f . str ( ) ;
file_text . replace ( start , end - start , text ) ;
std : : cout < < text ;
return text . length ( ) ;
}
} ;
@ -923,24 +1000,12 @@ class builder {
@@ -923,24 +1000,12 @@ class builder {
mmd : : markdown_parser parser ;
std : : map < std : : string , substitution_plugin * > substitution_commands ;
std : : map < std : : string , s2_substitution_plugin * > s2_substitution_commands ;
std : : map < std : : string , std : : string > default_templates ;
std : : map < std : : string , std : : string > properties ;
const std : : set < std : : string > apply_templates_exts { " md " , " html " , " txt " , " markdown " , " xml " } ;
enum job_type {
COPY_FILE = 1 ,
MARKDOWN = 1 < < 1 ,
TEMPLATE = 1 < < 2 ,
DELETE_FILE = 1 < < 3 ,
MAKE_DIR = 1 < < 4
} ;
const std : : set < std : : string > apply_templates_exts { " md " , " html " , " txt " , " markdown " , " xml " , " atom " , " rss " } ;
struct blog_item {
job_type type ;
fs : : path src ;
std : : map < std : : string , std : : string > properties ;
} ;
void
@ -952,14 +1017,15 @@ class builder {
@@ -952,14 +1017,15 @@ class builder {
substitution_commands [ ifndef_plugin : : hook_name ] = new ifndef_plugin { } ;
substitution_commands [ file_transclude_plugin : : hook_name ] = new file_transclude_plugin { } ;
substitution_commands [ mmd_snippet_transclude_plugin : : hook_name ] = new mmd_snippet_transclude_plugin { } ;
substitution_commands [ rss_feed_plugin : : hook_name ] = new rss_feed_plugin { } ;
s2_substitution_commands [ rss_feed_plugin : : hook_name ] = new rss_feed_plugin { } ;
}
int
do_substitution ( int start , int end , const std : : string & invocation ,
std : : string & file_text ,
const std : : map < std : : string , std : : string > & properties )
const std : : map < std : : string , std : : string > & properties ,
std : : optional < const std : : map < fs : : path , blog_item > > pages )
{
assert ( substitution_commands . size ( ) > 0 ) ;
@ -970,12 +1036,26 @@ class builder {
@@ -970,12 +1036,26 @@ class builder {
start , end , invocation , file_text , properties ) ;
}
if ( pages ) {
if ( s2_substitution_commands . count ( command_name ) ) {
return s2_substitution_commands . at ( command_name ) - > perform_substitution (
start , end , invocation , file_text , properties , * pages ) ;
}
}
return 0 ;
}
void
run_substitution_plugins ( std : : string & text ,
const std : : map < std : : string , std : : string > & properties )
void
run_substitution_plugins ( std : : string & text ,
const std : : map < std : : string , std : : string > & properties )
{
run_substitution_plugins ( text , properties , { } ) ;
}
void
run_substitution_plugins ( std : : string & text ,
const std : : map < std : : string , std : : string > & properties , std : : optional < const std : : map < fs : : path , blog_item > > pages )
{
std : : string : : size_type next = text . find ( TEMPLATE_CODE_START , 0 ) ;
@ -1036,8 +1116,7 @@ class builder {
@@ -1036,8 +1116,7 @@ class builder {
end + = TEMPLATE_CODE_END . length ( ) ;
int subst = do_substitution ( next , end , invocation , text , properties ) ;
int subst = do_substitution ( next , end , invocation , text , properties , pages ) ;
if ( ! subst ) {
// unsuccesful
next + = TEMPLATE_CODE_START . length ( ) ;
@ -1111,9 +1190,8 @@ class builder {
@@ -1111,9 +1190,8 @@ class builder {
}
}
/* DO NOTHING BEFORE HERE */
time_t dir_last_update_time = to_time_t ( fs : : last_write_time ( directory ) ) ;
compile_jobs [ compute_target ( directory , properties ) ] = { job_type : : MAKE_DIR , directory } ;
// first pass of directory
//
@ -1177,6 +1255,8 @@ class builder {
@@ -1177,6 +1255,8 @@ class builder {
for ( fs : : path entry : next_files ) {
time_t article_last_update = to_time_t ( fs : : last_write_time ( entry ) ) ;
if ( file_ext ( entry ) = = " draft " ) {
continue ;
}
@ -1204,8 +1284,27 @@ class builder {
@@ -1204,8 +1284,27 @@ class builder {
article_properties [ " target " ] = target . string ( ) ;
if ( article_properties . count ( " date " ) ) {
std : : istringstream in { article_properties . at ( " date " ) } ;
date : : sys_seconds timepoint ;
in > > date : : parse ( article_properties . at ( " date-in-format " ) , timepoint ) ;
time_t t = std : : chrono : : system_clock : : to_time_t ( timepoint ) ;
if ( t ! = ( time_t ) { } ) {
article_last_update = t ;
if ( t > dir_last_update_time ) {
dir_last_update_time = t ;
}
} else {
spdlog : : info ( " ba d date parse " ) ;
}
std : : string ndate = reformat_date ( article_properties . at ( " date " ) , article_properties ) ;
article_properties [ " date " ] = ndate ;
}
if ( ! article_properties . count ( " notemplating " ) ) {
@ -1231,6 +1330,8 @@ class builder {
@@ -1231,6 +1330,8 @@ class builder {
article_properties [ " body " ] = html ;
}
article_properties [ " original " ] = article_properties [ " body " ] ;
std : : string new_page { } ;
/*
if ( page_templates . count ( default_template_name ) ) {
@ -1259,7 +1360,7 @@ class builder {
@@ -1259,7 +1360,7 @@ class builder {
article_properties [ " body " ] = new_page ;
notify_file_write ( target ) ;
compile_jobs [ target ] = { job_type : : TEMPLATE , entry , std : : map < std : : string , std : : string > ( article_properties ) } ;
compile_jobs [ target ] = { job_type : : TEMPLATE , entry , std : : map < std : : string , std : : string > ( article_properties ) , article_last_update } ;
continue ;
}
@ -1267,7 +1368,7 @@ class builder {
@@ -1267,7 +1368,7 @@ class builder {
// else just copy
auto target = compute_target ( entry , article_properties ) ;
notify_file_write ( target ) ;
compile_jobs [ target ] = { job_type : : COPY_FILE , entry , std : : map < std : : string , std : : string > ( article_properties ) } ;
compile_jobs [ target ] = { job_type : : COPY_FILE , entry , std : : map < std : : string , std : : string > ( article_properties ) , dir_last_update_time } ;
// build
@ -1290,12 +1391,21 @@ class builder {
@@ -1290,12 +1391,21 @@ class builder {
}
compile_jobs [ compute_target ( directory , properties ) ] = { job_type : : MAKE_DIR , directory , std : : map < std : : string , std : : string > ( properties ) , dir_last_update_time } ;
// recurse
for ( auto d : next_directories ) {
build_recursive ( std : : map < std : : string , std : : string > ( page_templates ) ,
std : : map < std : : string , std : : string > ( properties ) , compile_jobs , d ) ;
}
if ( directory . string ( ) ! = properties . at ( " source_root " ) ) {
time_t * parent_date = & compile_jobs . at ( compute_target ( directory . parent_path ( ) , properties ) ) . post_date ;
if ( * parent_date < dir_last_update_time ) {
* parent_date = dir_last_update_time ;
}
}
}
void
@ -1361,6 +1471,9 @@ class builder {
@@ -1361,6 +1471,9 @@ class builder {
for ( auto e : substitution_commands ) {
delete e . second ;
}
for ( auto e : s2_substitution_commands ) {
delete e . second ;
}
}
@ -1386,43 +1499,47 @@ class builder {
@@ -1386,43 +1499,47 @@ class builder {
write_build ( std : : map < fs : : path , blog_item > & compile_jobs )
{
for ( auto e : compile_jobs ) {
// make directory structure
for ( auto & e : compile_jobs ) {
if ( job_type : : MAKE_DIR & e . second . type ) {
fs : : create_directories ( e . first ) ;
}
}
// do copy and deletions
for ( auto & e : compile_jobs ) {
auto src = e . second . src ;
auto dest = e . first ;
if ( job_type : : COPY_FILE & e . second . type ) {
fs : : copy ( src , dest , fs : : copy_options : : update_existing ) ;
}
if ( job_type : : MAKE_DIR & e . second . type ) {
fs : : create_directories ( e . first ) ;
}
if ( job_type : : DELETE_FILE & e . second . type ) {
fs : : remove_all ( e . first ) ;
}
}
for ( auto e : compile_jobs ) {
for ( auto & e : compile_jobs ) {
if ( job_type : : TEMPLATE & e . second . type ) {
// reapply stg1 and stg2 templates
std : : string page = e . second . properties . at ( " body " ) ;
run_substitution_plugins ( page ,
e . second . properties , compile_jobs ) ;
e . second . properties [ " body " ] = page ;
}
if ( job_type : : MARKDOWN & e . second . type ) {
// run markdown
}
}
for ( auto e : compile_jobs ) {
for ( auto & e : compile_jobs ) {
}
for ( auto e : compile_jobs ) {
for ( auto & e : compile_jobs ) {
if ( ( job_type : : MARKDOWN | job_type : : TEMPLATE ) & e . second . type ) {
write_file ( e . first , e . second . properties . at ( " body " ) ) ;
write_file ( e . first . string ( ) , e . second . properties . at ( " body " ) ) ;
}
}
}