├── .htaccess ├── LICENSE.txt ├── README.md ├── admin.php ├── changelog.txt ├── index.php └── sp_theme └── sedot_bootstrap ├── config.ini ├── template.php └── widget.php /.htaccess: -------------------------------------------------------------------------------- 1 | 2 | RewriteEngine On 3 | RewriteBase / 4 | RewriteRule ^index\.php$ - [L] 5 | RewriteCond %{REQUEST_FILENAME} !-f 6 | RewriteCond %{REQUEST_FILENAME} !-d 7 | RewriteRule . /index.php [L] 8 | 9 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) [2015] [sukualam] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [DISCONTINUED] 2 | 3 | # Sedotpress 4 | The new lightweight and low-resources blogging platform written in PHP. It is single-file Flat-file blogging engine. No database required! All data saved in JSON format. 5 | 6 | ## Features: 7 | * Posts & comments saved in JSON format 8 | * Lightweight & Low-Resource 9 | * Portable 10 | * URL Rewriting 11 | * Built-In Comment System + Gravatar 12 | * Auto Generate sitemap.xml & rss.xml 13 | * etc ... 14 | 15 | ## Downloads: 16 | https://github.com/sukualam/sedotpress/archive/master.zip 17 | 18 | ## Installing: 19 | * Extract all files in the zip to server (you can place it in root "/" or subdirectory "blog/") 20 | * Edit some config in index.php 21 | * Chmod the sp_lang folder `chmod 777 -R sp_lang` 22 | * Finish! 23 | 24 | ## URL Rewrites: 25 | ##### Lighttpd 26 | * Add this line in /etc/lighttpd/lighttpd.conf 27 | * `url.rewrite = ("^/(.*)\.(.+)$" => "$0","^/(.+)/?$" => "/index.php/$1")` 28 | 29 | ##### Apache 30 | * Use the .htaccess 31 | 32 | ## Latest News: 33 | * 8/5/2015: This script totaly beginning, so, dont expect too much like wordpress function, but this script still in development way, if you see any bugs or suggestions, i will happy if you report it here. 34 | * To get the latest news and development status, visit my blog http://sedot.space 35 | 36 | ## Recent Changelog: 37 | ##### (for full changelog, read changelog.txt) 38 | 39 | ##Managing and Other: 40 | ##### To enter the admin area (backstage) 41 | * Open `http://(your_blog_url)/backstage` 42 | 43 | ##### Theme & Modification: 44 | * You can directly edit the source code, since this is just single file. 45 | * But, if a newer version is available, you must re-edit again. 46 | 47 | Report any bugs here, and contribute :) 48 | 49 | Thank you! 50 | -------------------------------------------------------------------------------- /admin.php: -------------------------------------------------------------------------------- 1 | $val){ 33 | $filter[$key] = htmlentities($val); 34 | } 35 | return @$filter; 36 | }else{ 37 | return htmlentities($_POST[$str]); 38 | } 39 | } 40 | function nullStr($mixed,$prefix = 'untitled',$numbering = false){ 41 | if(is_array($mixed)){ 42 | foreach($mixed as $key => $val){ 43 | if(is_null($mixed[$key]) || $mixed[$key] == ""){ 44 | if(!$numbering){ 45 | $mixed[$key] = $prefix; 46 | }else{ 47 | $mixed[$key] = sedot::assignPostId($prefix); 48 | } 49 | } 50 | } 51 | }else{ 52 | if(is_null($mixed) || $mixed == ''){ 53 | if(!$numbering){ 54 | $mixed = $prefix; 55 | }else{ 56 | $mixed = sedot::assignPostId($prefix); 57 | } 58 | } 59 | } 60 | return $mixed; 61 | } 62 | function alertMessage($str,$type = 'info'){ 63 | $alert = ''; 67 | return sprintf($alert,$type,$str); 68 | } 69 | function panelBox($str,$type = 'default'){ 70 | $panel = '
71 |
72 | %s 73 |
74 |
'; 75 | return sprintf($panel,$type,$str); 76 | } 77 | function filterStr($str){ 78 | $str = strtolower(sedot::plainString($str)); 79 | $cleared = preg_replace('/[^a-zA-Z0-9\s]/u','',$str); 80 | return str_replace('\s','-',$cleared); 81 | } 82 | function setNavi($count = 5,$perpage = 5,$row = 5){ 83 | $dummy = ''; 84 | if($perpage <= 100){ 85 | if($perpage <= 50){ 86 | if($perpage <= 25){ 87 | if($perpage <= 10){ 88 | if($perpage <= 5){ 89 | $perpage = 5; 90 | }else{ 91 | $perpage = 10; 92 | }}else{ 93 | $perpage = 25; 94 | }}else{ 95 | $perpage = 50; 96 | }}else{ 97 | $perpage = 100; 98 | }}else{ 99 | $perpage = 100; 100 | } 101 | 102 | $countTimes = $count * 5; 103 | $total = ceil(ceil($countTimes) / $perpage); 104 | 105 | for($i = 0;$i < $total;$i++){ 106 | $dummy[$i] = $i; 107 | } 108 | 109 | @$split = array_chunk($dummy,$row,true); 110 | 111 | if(is_null($split)){ 112 | return false; 113 | }else{ 114 | return $split; 115 | } 116 | } 117 | function pageNavi($current,$stack,$url){ 118 | $out = ''; 119 | $out .= ''; 150 | return $out; 151 | } 152 | } 153 | 154 | $admin = new backstage; 155 | $section = @$getRequest[1]; 156 | $baseUrl = sprintf('%s/backstage/%s',SITE_URL,$section); 157 | 158 | $admin->createWidget('primary'); 159 | $admin->createWidget('secondary'); 160 | 161 | if($section == 'new' || $section == 'edit'){ 162 | if($section == 'edit'){ 163 | if(isset($getRequest[2]) && $getRequest[2] != '' || ! empty($getRequest[2])){ 164 | if(file_exists(sprintf('sp_post/post%s',$getRequest[2]))){ 165 | $isFileExist = true; 166 | }else{ 167 | $isFileExist = false; 168 | } 169 | }else{ 170 | $isFileExist = false; 171 | } 172 | 173 | if(! $isFileExist){ 174 | die(sprintf('Invalid Operation - Back',$admin->sectionLink(''))); 175 | } 176 | 177 | $loadPost = $admin->loadPost($getRequest[2]); 178 | } 179 | $form = sprintf('

Edit Post

180 |
181 |
182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 |
192 | 195 |
196 |
197 | 200 |
201 | 202 |
203 |
', 204 | $admin->sectionLink(sprintf('%s/%s',$section,@$getRequest[2])), 205 | @$loadPost['title'], 206 | @$loadPost['tag'], 207 | @$loadPost['entry'], 208 | @$loadPost['permalink']); 209 | 210 | $admin->addWidget('primary',$form); 211 | 212 | $reqs = $admin->filterPOST(); 213 | 214 | if(isset($reqs['submit'])){ 215 | 216 | $data['date'] = date(DateTime::ISO8601); 217 | $data['title'] = $admin->nullStr($reqs['title'],'untitled-',true); 218 | $data['permalink'] = $admin->nullStr($reqs['permalink'],$admin->filterStr($data['title'])); 219 | $data['tag'] = $admin->nullStr($reqs['tag'],'uncategorized'); 220 | $data['entry'] = $admin->nullStr($_POST['entry'],'Unfinished Story'); 221 | $data['status'] = $admin->nullStr($reqs['status'],'publish'); 222 | 223 | if($section == 'new'){ 224 | $postFileName = sprintf('sp_post/%s',$admin->assignPostId()); 225 | }else{ 226 | $postFileName = sprintf('sp_post/post%s',$getRequest[2]); 227 | } 228 | 229 | 230 | if($admin->toFile($postFileName,$admin->toJson($data),true)){ 231 | 232 | $admin->create_index(); 233 | $message = $admin->alertMessage('Post Published!','success'); 234 | $admin->addWidget('primary',$message,'before'); 235 | 236 | }else{ 237 | 238 | $message = $admin->alertMessage('Something Wrong','danger'); 239 | $admin->addWidget('primary',$message,'before'); 240 | 241 | } 242 | 243 | } 244 | } 245 | elseif($section == 'mypost' || $section == 'draft'){ 246 | 247 | if($section == 'mypost'){ 248 | $indexPublished = $admin->loadJson('index','post'); 249 | 250 | }else{ 251 | $indexPublished = $admin->loadJson('index','draft'); 252 | } 253 | 254 | $setNavi = $admin->setNavi(count($indexPublished),5,5); 255 | 256 | 257 | 258 | if(isset($getRequest[2]) && isset($getRequest[3])){ 259 | if($getRequest[2] == 'page'){ 260 | $indexKey = $getRequest[3]; 261 | } 262 | else{ 263 | $indexKey = 0; 264 | } 265 | } 266 | else{ 267 | $indexKey = 0; 268 | } 269 | 270 | $indexList = $admin->index_page($indexPublished[$indexKey]); 271 | 272 | foreach($indexList as $meta){ 273 | $tableRows = ' 274 | %s 275 | %s 276 | %s 277 | %s 278 | %s 279 | '; 280 | 281 | $editPostUrl = sprintf('%s/%s',$admin->sectionLink('edit'),$meta['id']); 282 | $removePostUrl = sprintf('%s/%s',$admin->sectionLink('delete'),$meta['id']); 283 | 284 | @$formatRows .= sprintf($tableRows, 285 | $meta['date'], 286 | $meta['title'], 287 | round(filesize("sp_post/post{$meta['id']}") / 1024, 2), 288 | $meta['id'], 289 | ' 290 | 291 | 292 | ' 293 | ); 294 | } 295 | 296 | $table = '

My Posts

297 |
298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | %s 307 |
DateTitleSize (Kb)Post IdAction
308 |
'; 309 | 310 | $sidebar = '

Options

311 | Sorting Order'; 312 | $formatTable = sprintf($table,$formatRows); 313 | 314 | $admin->addWidget('primary',$formatTable); 315 | $admin->addWidget('secondary',$sidebar); 316 | 317 | if($setNavi){ 318 | $admin->addWidget('primary',$admin->pageNavi($indexKey,$setNavi,$baseUrl)); 319 | } 320 | } 321 | elseif($section == 'delete' && isset($getRequest[2])){ 322 | /* 323 | * Only for valid filename 324 | */ 325 | if(! file_exists(sprintf('sp_post/post%s',$getRequest[2]))){ 326 | die(); 327 | } 328 | 329 | /* 330 | * Delete confirmation dialog 331 | */ 332 | 333 | $form = '

Delete Post

334 |
335 |
336 | 339 |
340 |
341 | 344 |
345 | 346 |
'; 347 | 348 | $msgError = $admin->alertMessage('Not Deleted','danger'); 349 | $msgSuccess = $admin->alertMessage('Post Deleted','success'); 350 | 351 | /* 352 | * Handling the message 353 | */ 354 | 355 | if(isset($_POST['del'])){ 356 | if($_POST['del'] == 'yes'){ 357 | if($admin->removeExistingFile(sprintf('sp_post/post%s',$getRequest[2]))){ 358 | $admin->addWidget('primary',$msgSuccess); 359 | $admin->create_index(); 360 | } 361 | else{ 362 | $admin->addWidget('primary',$msgError); 363 | } 364 | } 365 | else{ 366 | $admin->addWidget('primary',$msgError); 367 | } 368 | } 369 | else{ 370 | $admin->addWidget('primary',$form); 371 | } 372 | } 373 | elseif($section == 'widget'){ 374 | 375 | /* 376 | * Load widget areas on templates 377 | */ 378 | $widgetConf = parse_ini_file('sp_theme/'.THEME.'/config.ini'); 379 | $widgetFile = '_widget-'.THEME; 380 | $widgetPath = 'sp_static/misc/'.$widgetFile.'.json'; 381 | /* 382 | * Create default widget config in sp_json/ that store the widgets 383 | * Filename format: widget-{theme_name}.json 384 | * Still need to implemented 385 | */ 386 | if(! file_exists($widgetPath)){ 387 | foreach($widgetConf['area'] as $key => $widget){ 388 | $widgetSection[$widget] = ''; 389 | } 390 | $widgetSet = $admin->toJson($widgetSection); 391 | 392 | if($admin->toFile($widgetPath,$widgetSet)){ 393 | $msg = 'Default Config Created'; 394 | $admin->addWidget('primary',$admin->alertMessage($msg,'success')); 395 | } 396 | } 397 | 398 | $loadWidget = $admin->loadJson('misc',$widgetFile); 399 | 400 | if(isset($getRequest[2])){ 401 | /* 402 | * $getRequest[2] means widget area name, example: sidebar1 403 | * This handle following action: edit,remove,new 404 | */ 405 | if(@$_POST['widgetAction'] == 'edithtml'){ 406 | $wArea = $_POST['widgetArea']; 407 | $wItemId = $_POST['widgetItemId']; 408 | $loadWidget[$wArea][$wItemId]['title'] = $_POST['widgetTitle']; 409 | $loadWidget[$wArea][$wItemId]['code'] = $_POST['htmlCode']; 410 | $rePack = $admin->toJson($loadWidget); 411 | $admin->toFile($widgetPath,$rePack,true); 412 | } 413 | if(isset($_POST['addWidget'])){ 414 | if($_POST['addWidget'] == 'html'){ 415 | $code = $_POST['htmlCode']; 416 | }else{ 417 | $code = sprintf('#%1$s#',strtoupper($_POST['addWidget'])); 418 | } 419 | $key = $admin->endKey($loadWidget[$getRequest[2]],true); 420 | $loadWidget[$getRequest[2]][$key] = array( 421 | 'type' => $_POST['addWidget'], 422 | 'title' => $_POST['widgetTitle'], 423 | 'code' => $code); 424 | $rePack = $admin->toJson($loadWidget); 425 | $admin->toFile($widgetPath,$rePack,true); 426 | } 427 | } 428 | 429 | if(isset($getRequest[3]) && $getRequest[3] == 'add' && ! isset($_POST['widgetType'])){ 430 | # This will show the widget type for new widget action 431 | $form = sprintf('
432 | 433 |
434 | 438 |
439 |
440 | 444 |
445 | 446 | Cancel 447 |
', 448 | $admin->sectionLink(sprintf('widget/%1$s/add',$getRequest[2])), 449 | $getRequest[2], 450 | $admin->sectionLink('widget')); 451 | $admin->addWidget('primary',$admin->panelBox($form),'before'); 452 | } 453 | 454 | if(isset($getRequest[3]) && $getRequest[3] == 'add' && isset($_POST['widgetType'])){ 455 | #This handle the widget type 456 | if($_POST['widgetType'] == 'html'){ 457 | $htmlForm = sprintf(' 458 |

New Widget

459 |
460 | 461 |
462 | 463 | 464 | 465 |
466 | 467 | Cancel 468 |
469 |
', 470 | $_POST['widgetId'], 471 | $admin->sectionLink(sprintf('widget/%s',$getRequest[2]))); 472 | $admin->addWidget('primary',$admin->panelBox($htmlForm),'before'); 473 | } 474 | elseif($_POST['widgetType'] == 'own'){ 475 | $getWidgets = $admin->currentMethod('widget'); 476 | foreach($getWidgets as $widgetName){ 477 | $widgetNameList[] = ''; 478 | } 479 | $htmlForm = sprintf(' 480 |

New Widget

481 |
482 |
483 | 484 | 487 | 488 | 489 | 490 |
491 | 492 | Cancel 493 |
494 |
', 495 | $_POST['widgetId'], 496 | $admin->sectionLink(sprintf('widget/%s',$getRequest[2])), 497 | implode(' ',$widgetNameList) 498 | ); 499 | $admin->addWidget('primary',$admin->panelBox($htmlForm),'before'); 500 | } 501 | else{ 502 | $htmlForm = sprintf(' 503 |

New Widget

504 |
505 | 506 |
507 | 508 | 509 | 510 | Cancel 511 |
512 |
', 513 | $_POST['widgetId'], 514 | $admin->sectionLink(sprintf('widget/%s',$getRequest[2])), 515 | $_POST['widgetType']); 516 | $admin->addWidget('primary',$admin->panelBox($htmlForm),'before'); 517 | } 518 | } 519 | 520 | /* 521 | * Move up the widget item 522 | */ 523 | 524 | if(isset($getRequest[3]) && $getRequest[3] == 'up' && isset($getRequest[4])){ 525 | $loadWidget[$getRequest[2]] = $admin->up($loadWidget[$getRequest[2]],$getRequest[4]); 526 | $loadWidget[$getRequest[2]] = array_values($loadWidget[$getRequest[2]]); 527 | $rePack = $admin->toJson($loadWidget); 528 | $admin->toFile($widgetPath,$rePack,true); 529 | } 530 | 531 | /* 532 | * Move down the widget item 533 | */ 534 | 535 | if(isset($getRequest[3]) && $getRequest[3] == 'down' && isset($getRequest[4])){ 536 | $loadWidget[$getRequest[2]] = $admin->down($loadWidget[$getRequest[2]],$getRequest[4]); 537 | $loadWidget[$getRequest[2]] = array_values($loadWidget[$getRequest[2]]); 538 | $rePack = $admin->toJson($loadWidget); 539 | $admin->toFile($widgetPath,$rePack,true); 540 | } 541 | 542 | /* 543 | * Delete widget item 544 | */ 545 | 546 | if(isset($getRequest[3]) && $getRequest[3] == 'delete' && isset($getRequest[4])){ 547 | unset($loadWidget[$getRequest[2]][$getRequest[4]]); 548 | $loadWidget[$getRequest[2]] = array_values($loadWidget[$getRequest[2]]); 549 | $rePack = $admin->toJson($loadWidget); 550 | $admin->toFile($widgetPath,$rePack,true); 551 | } 552 | 553 | if(isset($getRequest[3]) && $getRequest[3] == 'edit' && isset($getRequest[4])){ 554 | /* 555 | * Edit the existing widget item in current widget area 556 | * $getRequest[4] means widget item id 557 | */ 558 | 559 | $htmlForm = sprintf(' 560 |

Edit Widget

561 |
562 |

Widget Type: %6$s

563 | 564 | 565 | 566 |
567 | 568 | 569 | 570 |
571 | 572 | Cancel 573 |
574 |
', 575 | $admin->sectionLink(sprintf('widget/%s',$getRequest[2])), 576 | $loadWidget[$getRequest[2]][$getRequest[4]]['title'], 577 | $loadWidget[$getRequest[2]][$getRequest[4]]['code'], 578 | $getRequest[2], 579 | $getRequest[4], 580 | $loadWidget[$getRequest[2]][$getRequest[4]]['type'] 581 | ); 582 | 583 | $admin->addWidget('primary',$admin->panelBox($htmlForm),'before'); 584 | 585 | } 586 | 587 | /* 588 | * Create a simple widget management 589 | */ 590 | 591 | foreach($widgetConf['area'] as $area){ 592 | $widgetList = ''; 593 | foreach($loadWidget[$area] as $keys => $widgets){ 594 | $widgetList .= sprintf(' 595 |
  • 596 | %5$s  597 | 598 | 599 | 600 |
  • ', 601 | $admin->sectionLink('widget') . '/' . $area . '/edit/' . $keys, 602 | $admin->sectionLink('widget') . '/' . $area . '/up/' . $keys, 603 | $admin->sectionLink('widget') . '/' . $area . '/down/' . $keys, 604 | $admin->sectionLink('widget') . '/' . $area . '/delete/' . $keys, 605 | $widgets['title'] == '' ? '(notitle)' : $widgets['title']); 606 | } 607 | $box = sprintf(' 608 |

    %s

    609 | 610 | ', 611 | $area, 612 | $admin->sectionLink('widget') . '/' . $area . '/add', 613 | $widgetList 614 | ); 615 | 616 | $admin->addWidget('primary',$box); 617 | 618 | unset($widgetList); 619 | } 620 | } 621 | elseif($section == 'option'){ 622 | $filename = 'config.json'; 623 | } 624 | elseif($section == 'import'){ 625 | $dialogBox = ' 626 | Import from Blogger Backup (xml) 627 |
    628 | 629 |
    '; 630 | 631 | if(isset($_POST['act']) && $_POST['act'] == 'ok'){ 632 | $filename = 'blog.xml'; 633 | $parse = simplexml_load_file($filename); 634 | $encode = json_encode($parse); 635 | $decode = json_decode($encode,true); 636 | $reverse = array_reverse($decode['entry'],true); 637 | $term = 'http://schemas.google.com/blogger/2008/kind#post'; 638 | foreach($reverse as $id => $content){ 639 | if($content['category'][0]['@attributes']['term'] == $term){ 640 | array_shift($content['category']); 641 | foreach($content['category'] as $keys => $tag){ 642 | $tags[] = $content['category'][$keys]['@attributes']['term']; 643 | } 644 | $link = $content['link'][4]['@attributes']['href']; 645 | $link = rtrim($link,'{.html}'); 646 | $permalink = explode('/',$link);end($permalink); 647 | $permalink = $permalink[key($permalink)]; 648 | $data['date'] = date(DateTime::ISO8601); 649 | $data['title'] = $content['title']; 650 | $data['permalink'] = $permalink; 651 | $data['tag'] = implode(',',$tags); 652 | $data['entry'] = $content['content']; 653 | $data['status'] = 'publish'; 654 | $postFileName = sprintf('sp_post/%s',$admin->assignPostId()); 655 | $admin->toFile($postFileName,$admin->toJson($data),true); 656 | unset($tags); 657 | } 658 | } 659 | $admin->create_index(); 660 | } 661 | $admin->addWidget('primary',$dialogBox); 662 | } 663 | ?> 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | <?php echo SITE_TITLE; ?> [Backstage] 672 | 673 | 674 | 675 | 676 | 677 | 678 | 682 | 683 | 684 | 708 | 713 |
    714 |
    715 |
    716 |
    717 |
    718 | widgetSection('primary'); ?> 719 |
    720 |
    721 |
    722 | 734 | 735 | 736 | 737 | 738 | 739 | 746 | 747 | 748 | -------------------------------------------------------------------------------- /changelog.txt: -------------------------------------------------------------------------------- 1 | Full Changelog 2 | 3 | v.0.1.7 4 | backstage now in separate file (admin.php) 5 | temporary disabled: builtin comment (can someone give me the idea??) 6 | new: custom template (create your own template) 7 | enhanced index caching by splitting index to separate file 8 | new: builtin widget 9 | major structure changes, not compatible to older version 10 | changed login method + blacklist ip 11 | enhanced url routing 12 | 13 | v.0.1.6 (??) 14 | code rewritten, skip to v.0.1.7 15 | 16 | v.0.1.5 (8/4/2015) 17 | major structure changes (it wont compatible with older version) 18 | optimized json files 19 | new: language files (you can create your own) 20 | new: menu editor 21 | new: saving post in published or draft mode 22 | new: threaded comment + reply 23 | new: gravatar icon for comments 24 | added "website", "email" field in comment format 25 | added 404 page 26 | some bugfixes 27 | 28 | v.0.1.4 (28/3/2015) 29 | now, post saved in json format 30 | permalink is auto generated (if field is empty) 31 | title is auto generated, like Untitled-1 (if field is empty) 32 | tag is auto generated, named "out-topic" (if field is empty) 33 | admin area layout changes 34 | added "edit" link every post 35 | added favicon.ico 36 | fixed meta description from cutted if there are html tag 37 | many bugfixes 38 | 39 | v.0.1.3 beta (26/3/2015) 40 | enabled RSS feeds 41 | enabled sitemap (auto generate) 42 | enabled post thumbnails 43 | added visual editor (WYSIWYG) 44 | enabled image upload (base64 encoded) 45 | minor style changes 46 | 47 | v.0.1.2 beta (25/3/2015) 48 | now support nested of directory, like this : (http://localhost/blog) 49 | minor backstage (admin area) fix 50 | now can delete post from backstage 51 | 52 | v.0.1.1 beta 53 | builtin comment system (beta) 54 | minor style change 55 | 56 | v0.1.0 beta 57 | (The current version is provide basic blogging features): 58 | Create/Edit a post 59 | Admin area (we call it backstage) 60 | Search a post 61 | Blog archives 62 | Tags 63 | Clean URL (permalink) / Url Rewrite 64 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | total = $a; 79 | $this->page = $b; 80 | } 81 | function currentMethod($class){ 82 | $allMethod = get_class_methods($class); 83 | if($parentClass = get_parent_class($class)){ 84 | $parentMethod = get_class_methods($parentClass); 85 | $currentMethod = array_diff($allMethod, $parentMethod); 86 | }else{ 87 | $currentMethod = $allMethod; 88 | } 89 | return($currentMethod); 90 | } 91 | function removeExistingFile($path){ 92 | if(file_exists($path)){ 93 | if(unlink($path)){ 94 | return true; 95 | }else{ 96 | return false; 97 | } 98 | }else{ 99 | return false; 100 | } 101 | } 102 | function toJson($str){ 103 | $jsonEncode = json_encode($str, 104 | JSON_HEX_TAG | JSON_HEX_APOS | 105 | JSON_HEX_QUOT | JSON_HEX_AMP | 106 | JSON_UNESCAPED_UNICODE); 107 | return $jsonEncode; 108 | } 109 | function createWidget($widgetId = 1){ 110 | $this->primaryHTML[$widgetId] = array(); 111 | } 112 | function addWidget($widgetId,$str,$order = 'after'){ 113 | if($order == 'after'){ 114 | array_push($this->primaryHTML[$widgetId],$str); 115 | }else{ 116 | array_unshift($this->primaryHTML[$widgetId],$str); 117 | } 118 | } 119 | function widgetSection($opt = '1'){ 120 | if(isset($opt)){ 121 | foreach($this->primaryHTML[$opt] as $key => $widget){ 122 | printf('%s',$widget); 123 | } 124 | } 125 | } 126 | function down($a,$x){ 127 | if(count($a)-1 > $x){ 128 | $b = array_slice($a,0,$x,true); 129 | $b[] = $a[$x+1]; 130 | $b[] = $a[$x]; 131 | $b += array_slice($a,$x+2,count($a),true); 132 | return($b); 133 | }else{ 134 | return $a; 135 | } 136 | } 137 | function up($a,$x){ 138 | if($x > 0 && $x < count($a)){ 139 | $b = array_slice($a,0,($x-1),true); 140 | $b[] = $a[$x]; 141 | $b[] = $a[$x-1]; 142 | $b += array_slice($a,($x+1),count($a),true); 143 | return($b); 144 | }else{ 145 | return $a; 146 | } 147 | } 148 | function enableWidget(){ 149 | $widgetContainer = '
    150 | %3$s 151 | %4$s 152 |
    '; 153 | 154 | $widgetTitle = '

    %s

    '; 155 | 156 | $templateWidget = self::loadJson('misc','_widget-'.THEME); 157 | $getConfig = parse_ini_file('sp_theme/'.THEME.'/config.ini'); 158 | foreach($getConfig['area'] as $widget){ 159 | self::createWidget($widget); 160 | foreach($templateWidget[$widget] as $widgetKey => $content){ 161 | if($content['title'] != ''){ 162 | $content['title'] = sprintf($widgetTitle,$content['title']); 163 | } 164 | if($content['type'] == 'html'){ 165 | $code = $content['code']; 166 | }else{ 167 | $code = widget::$content['type'](); 168 | } 169 | self::addWidget($widget,sprintf( 170 | $widgetContainer, 171 | $widget, 172 | $widgetKey, 173 | $content['title'], 174 | $code 175 | )); 176 | } 177 | } 178 | skipCreateWidget: 179 | } 180 | function toFile($path,$data,$truncate = false){ 181 | if($truncate){ 182 | self::removeExistingFile($path); 183 | } 184 | $openFile = fopen($path,"a+"); 185 | if($openFile == false){ 186 | return false; 187 | }else{ 188 | $writeData = fwrite($openFile,$data); 189 | fclose($openFile); 190 | return true; 191 | } 192 | } 193 | 194 | function loadFile($path,$stream = false){ 195 | @$openFile = fopen($path,"r"); 196 | if(!$stream){ 197 | @$readFile = fread($openFile,filesize($path)); 198 | }else{ 199 | @$readFile = fgets($openFile); 200 | } 201 | $containData = $readFile; 202 | @fclose($openFile); 203 | return $containData; 204 | } 205 | function loadPost($postnum){ 206 | $loadPost = self::loadFile("sp_post/post{$postnum}",false); 207 | $this->postPart = json_decode($loadPost,true); 208 | return $this->postPart; 209 | } 210 | function formatPost($filename){ 211 | $postPart = self::loadPost($filename); 212 | $postPart['tag'] = explode(',',$postPart['tag']); 213 | return $postPart; 214 | } 215 | function endKey($array, $increment = false, $trim = 'post'){ 216 | if(is_null($array)){ 217 | $key = 0; 218 | }else{ 219 | end($array); 220 | $key = trim(key($array),$trim); 221 | } 222 | if($increment){ 223 | $key += 1; 224 | } 225 | return $key; 226 | } 227 | function assignPostId($prefix = NULL){ 228 | /* 229 | * This actually used for assigning post Id's in "publish" and "draft" mode 230 | * This function open 'sp_static/index/post.json' 231 | */ 232 | $postList = self::loadJson('index','post'); 233 | $newKey = self::endKey(array_reverse($postList[0],true),true); 234 | if(is_null($newKey)){ 235 | $newKey = 1; 236 | } 237 | if(is_null($prefix)){ 238 | $prefix = 'post'; 239 | } 240 | /* Skip existing file */ 241 | while(file_exists(sprintf('sp_post/%s%s',$prefix,$newKey))){ 242 | $newKey += 1; 243 | } 244 | $key = $prefix . $newKey; 245 | return $key; 246 | } 247 | function getImg($string){ 248 | preg_match('/src="([^"]*)"/',$string,$matches); 249 | if(!isset($matches[1]) || $matches[1] == "" || empty($matches[1])){ 250 | return false; 251 | }else{ 252 | return $matches[1]; 253 | } 254 | } 255 | function plainString($text){ 256 | // Copyright (c) 2008, David R. Nadeau, NadeauSoftware.com. 257 | // All rights reserved. 258 | $text = preg_replace( 259 | array( 260 | '@]*?>.*?@siu', 261 | '@]*?>.*?@siu', 262 | '@]*?.*?@siu', 263 | '@]*?.*?@siu', 264 | '@]*?.*?@siu', 265 | '@]*?.*?@siu', 266 | '@]*?.*?@siu', 267 | '@]*?.*?@siu', 268 | '@]*?.*?@siu', 269 | '@<((br)|(hr))@iu', 270 | '@ 303 | %1$s 304 | '; 305 | $sitemapItem = ' 306 | %1$s/%2$s'; 307 | $rssContainer = ' 308 | 309 | 310 | 311 | %1$s 312 | %2$s 313 | %3$s%4$s 314 | 315 | '; 316 | $rssItem = ' 317 | 318 | %1$s 319 | %2$s 320 | %3$s 321 | '; 322 | $dir = "sp_post/"; 323 | $archiveDate = ''; 324 | $countFileSize = ''; 325 | $scandir = array_diff(scandir($dir),array('..','.')); 326 | natsort($scandir); 327 | $reverseOrder = array_reverse($scandir); 328 | foreach($reverseOrder as $file){ 329 | $countFileSize += filesize("sp_post/{$file}"); 330 | $dec = self::loadPost(ltrim($file,'post')); 331 | $postDate = explode(" ",date('Y m d',strtotime($dec['date']))); 332 | $splitTag = explode(",",$dec['tag']); 333 | $postTag[] = $splitTag; 334 | @$meta = array($dec['date'],$dec['title'],$dec['permalink'],$splitTag); 335 | if($dec['status'] == "publish"){ 336 | $container[trim($file,'post')] = $meta; 337 | $countAllPost += 1; 338 | }else{ 339 | $draft_container[trim($file,'post')] = $meta; 340 | } 341 | @$archiveDate[$postDate[0]][$postDate[1]][$postDate[2]] += 1; 342 | } 343 | foreach($postTag as $many){ 344 | foreach($many as $tag){ 345 | $tags[] = $tag; 346 | } 347 | } 348 | // split the container 349 | @$chunk = array_chunk($container,$itemPerIndex,TRUE); 350 | @$chunk_draft = array_chunk($draft_container,$itemPerIndex,TRUE); 351 | ## CREATE SITEMAP.XML 352 | foreach($container as $metadata){ 353 | $xmlItems[] = sprintf(trim($sitemapItem,"\t\n\r\0\x0B"),SITE_URL,$metadata[2]); 354 | } 355 | $xml = sprintf(trim($sitemapContainer,"\t\n\r\0\x0B"),implode('',$xmlItems)); 356 | ## CREATE RSS.XML 357 | // limit to (2 x $itemPerIndex) 358 | for($i = 0;$i<= 1;$i++){ 359 | foreach($chunk[$i] as $postid => $metadata){ 360 | $readPost = self::loadPost($postid); 361 | $rssItems[] = sprintf(trim($rssItem,"\t\n\r\0\x0B"),$readPost['title'], 362 | SITE_URL . '/'. $readPost['permalink'], 363 | htmlentities($readPost['entry'])); 364 | } 365 | } 366 | $rss = sprintf(trim($rssContainer,"\t\n\r\0\x0B"),SITE_TITLE,SITE_URL, 367 | SITE_DESC,implode('',$rssItems)); 368 | // count post each tag 369 | $counttag = array_count_values($tags); 370 | $reverse_year = array_reverse($archiveDate,true); 371 | $indexJson = self::toJson($chunk); 372 | $draftJson = self::toJson($chunk_draft); 373 | $tagJson = self::toJson($counttag); 374 | $archiveJson = self::toJson($reverse_year); 375 | ## SPLIT INDEX TO FILE 376 | foreach($chunk as $keyy => $chunkval){ 377 | $chunkpart = self::toJson($chunkval); 378 | self::toFile("sp_static/index/index-{$keyy}.json",$chunkpart,true); 379 | } 380 | ## WRITE NEW DATA 381 | self::toFile("sp_static/index/totalpost.json",$countAllPost,true); 382 | self::toFile("sp_static/index/archive.json",$archiveJson,true); 383 | self::toFile("sp_static/index/draft.json",$draftJson,true); 384 | self::toFile("sp_static/index/post.json",$indexJson,true); 385 | self::toFile("sp_static/index/tag.json",$tagJson,true); 386 | self::toFile("sp_static/index/filesize.json",$countFileSize,true); 387 | self::toFile("sp_static/xml/rss.xml",$rss,true); 388 | self::toFile("sp_static/xml/sitemap.xml",$xml,true); 389 | } 390 | // load json file and decode it into associative array 391 | function loadJson($type = NULL,$name = NULL){ 392 | $str = self::loadFile("sp_static/{$type}/{$name}.json",true); 393 | $jsonDecoded = json_decode($str,true); 394 | return $jsonDecoded; 395 | } 396 | // add ip address to blacklist 397 | function ipBlackList($specify = NULL){ 398 | if(is_null($specify)){ 399 | $ipBlackList = $_SERVER["REMOTE_ADDR"]; 400 | }else{ 401 | $ipBlackList = $specify; 402 | } 403 | $blackList = self::loadJson('misc','blacklist'); 404 | $blackList[self::endKey($blackList,true)] = $ipBlackList; 405 | self::toFile('sp_static/misc/blacklist.json',self::toJson($blackList),true); 406 | } 407 | function isBlacklisted($specify = NULL){ 408 | $blackList = self::loadJson('misc','blacklist'); 409 | if(is_null($specify)){ 410 | $ip = $_SERVER["REMOTE_ADDR"]; 411 | }else{ 412 | $ip = $specify; 413 | } 414 | if(@in_array($ip,$blackList)){ 415 | return true; 416 | }else{ 417 | return false; 418 | } 419 | } 420 | function totalPost(){ 421 | $size = self::loadFile('sp_static/index/totalpost.json'); 422 | return $size; 423 | } 424 | // create recent comment 425 | function create_recent_comment($permalink,$post_title,$comments){ 426 | // read/write 427 | $filename = "sp_static/misc/recent_comment.json"; 428 | $fgets = self::loadFile($filename,true); 429 | $decode = json_decode($fgets,true); 430 | $endkey = self::endKey($decode,true); 431 | if($endkey > 5){ 432 | array_shift($decode); 433 | $newKey = 5; 434 | }else{ 435 | $newKey = $endkey; 436 | } 437 | $decode[$newKey] = array($permalink,$post_title,$comments); 438 | $encode = self::toJson($decode); 439 | self::toFile($filename,$encode,true); 440 | } 441 | function search($mode,$query){ 442 | $index = self::loadJson('index','post'); 443 | $s = explode('+',strtolower(rtrim($query,'+'))); 444 | $count = count($s); 445 | if($count <= 1){$amount = 1;} 446 | else{$amount = $count;} 447 | for($i = 0;$i <= count($index);$i++){ 448 | foreach($index[$i] as $key => $val){ 449 | if($mode == 0){ 450 | $tgl = date('Y m d',strtotime($val[$mode])); 451 | $y = explode(' ',$tgl); 452 | if($amount == 1){ 453 | if($y[0] == $s[0]){ 454 | $found[$key] = ''; 455 | } 456 | } 457 | if($amount == 2){ 458 | if($y[0] == $s[0] && $y[1] == $s[1]){ 459 | $found[$key] = ''; 460 | } 461 | } 462 | if($amount >= 3){ 463 | if($y[0] == $s[0] && $y[1] == $s[1] && $y[2] == $s[2]){ 464 | $found[$key] = ''; 465 | } 466 | } 467 | } 468 | if($mode == 1){ 469 | $p = explode(' ',strtolower($val[$mode])); 470 | for($o = 0;$o < $amount;$o++){ 471 | if(in_array($s[0],$p)){$found[$key] = '';} 472 | } 473 | unset($p); 474 | } 475 | if($mode == 2){ 476 | $p = strtolower($val[$mode]); 477 | if($s[0] == $p){ 478 | $found[$key] = ''; 479 | break 2; 480 | } 481 | } 482 | if($mode == 3){ 483 | $val[$mode] = array_map('strtolower',$val[$mode]); 484 | for($o = 0;$o < $amount;$o++){ 485 | if(in_array(strtolower(urldecode($query)),$val[$mode])){$found[$key] = '';} 486 | } 487 | } 488 | unset($val); 489 | } 490 | } 491 | return $found; 492 | } 493 | function index_page($post_array,$read_meta = false){ 494 | foreach($post_array as $postid => $metadata){ 495 | $dec = self::loadPost($postid); 496 | if($read_meta){ 497 | $postmeta = array( 498 | 'date' => $dec['date'], 499 | 'title' => $dec['title'], 500 | 'permalink' => $dec['permalink'], 501 | 'tag' => explode(',',$dec['tag']) 502 | ); 503 | }else{ 504 | $postmeta = self::splitMeta($metadata); 505 | } 506 | $thumbImg = self::getImg($dec['entry']); 507 | $konten = $dec['entry']; 508 | /* grouping */ 509 | $group[$postid]["id"] = $postid; 510 | $group[$postid]["image"] = $thumbImg; 511 | $group[$postid]["tag"] = $postmeta['tag']; 512 | $group[$postid]["title"] = $postmeta['title']; 513 | $group[$postid]["url"] = self::_url($postmeta['permalink']); 514 | $group[$postid]["date"] = $postmeta['date']; 515 | $group[$postid]["entry"] = $konten; 516 | } 517 | return $group; 518 | } 519 | } # end sedot class 520 | 521 | include 'sp_theme/'.THEME.'/widget.php'; 522 | 523 | ### START LISTENING ### 524 | $post = new sedot; 525 | 526 | if(!isset($_FRONT_PAGE)){$_FRONT_PAGE = false;} 527 | if(!isset($_SINGLE_POST)){$_SINGLE_POST = false;} 528 | if(!isset($_PAGE_404)){$_PAGE_404 = false;} 529 | 530 | if(empty($getRequest[0])){ 531 | $_FRONT_PAGE = true; 532 | $pointer = $post->loadJson('index','index-0'); 533 | $entries = $post->index_page($pointer,false); 534 | $post->navi($post->totalpost(),1); 535 | } 536 | elseif($getRequest[0] == "page"){ 537 | $_FRONT_PAGE = true; 538 | $pagenum = $getRequest[1]; 539 | $pointer = $post->loadJson('index','index-'.($pagenum - 1)); 540 | $entries = $post->index_page($pointer,false); 541 | $post->navi($post->totalpost(),$pagenum); 542 | } 543 | elseif($getRequest[0] == "rss"){ 544 | header("Content-Type: application/rss+xml; charset=ISO-8859-1"); 545 | printf("%s",$post->loadFile("sp_static/xml/rss.xml")); 546 | exit; 547 | } 548 | elseif($getRequest[0] == "search" || $getRequest[0] == "tag" || $getRequest[0] == "timeline"){ 549 | $_FRONT_PAGE = true; 550 | $_SEARCH_MODE = true; 551 | if($getRequest[0] == 'search'){$mode = 1;} 552 | elseif($getRequest[0] == 'tag'){$mode = 3;} 553 | elseif($getRequest[0] == 'timeline'){ 554 | $mode = 0; 555 | $i = 1; 556 | while($getRequest[$i] != "page"){ 557 | $timeline[] = $getRequest[$i]; 558 | if($i > 6){break;} 559 | $i++; 560 | } 561 | $getRequest[1] = implode('+',$timeline); 562 | $getRequest[1] = rtrim($getRequest[1],'+'); 563 | 564 | for($a = 2;$a < $i;$a++){ 565 | unset($getRequest[$a]); 566 | } 567 | $getRequest = array_values($getRequest); 568 | } 569 | $find = array_chunk($post->search($mode,$getRequest[1]),5,true); 570 | $pagenum = $getRequest[3]; 571 | if($pagenum == ''){$pagenum = 1;} 572 | $pointer = $find[$pagenum - 1]; 573 | $entries = $post->index_page($pointer,true); 574 | $post->navi(count($find),$pagenum); 575 | } 576 | elseif($getRequest[0] == "backstage"){ 577 | $authUser = @$_SERVER['PHP_AUTH_USER']; 578 | $authPass = @$_SERVER['PHP_AUTH_PW']; 579 | if($post->isBlacklisted()){ 580 | die("IP Address Blocked"); 581 | } 582 | if($authUser == ADMIN_NICKNM && $authPass == ADMIN_PASSWD){ 583 | if(isset($getRequest[1]) && $getRequest[1] == "logout"){ 584 | $isValid = false; 585 | }else{ 586 | $isValid = true; 587 | } 588 | }else{ 589 | $_SESSION["ATTEMPT"] += 1; 590 | $isValid = false; 591 | } 592 | if(!$isValid){ 593 | if($_SESSION["ATTEMPT"] >= 10){ 594 | $post->ipBlackList(); 595 | die("Max Attempt"); 596 | }else{ 597 | header('WWW-Authenticate: Basic realm="sedotpress"'); 598 | header('HTTP/1.0 401 Unauthorized'); 599 | die("Not authorized"); 600 | } 601 | } 602 | die(include 'admin.php'); 603 | } 604 | else{ 605 | $_SINGLE_POST = true; 606 | $pointer = $post->search(2,$getRequest[0]); 607 | $pointer = key($pointer); 608 | if(! $pointer || $pointer == '' || is_null($pointer)){ 609 | header('HTTP/1.0 404 Not Found'); 610 | $_SINGLE_POST = false; 611 | $_PAGE_404 = true; 612 | goto bypass; 613 | } 614 | $entry = $post->formatPost($pointer); 615 | bypass: 616 | } 617 | require 'sp_theme/'.THEME.'/template.php'; 618 | ?> 619 | -------------------------------------------------------------------------------- /sp_theme/sedot_bootstrap/config.ini: -------------------------------------------------------------------------------- 1 | [widget] 2 | 3 | area[] = "index-top"; 4 | area[] = "index-bottom"; 5 | 6 | area[] = "post-top"; 7 | area[] = "post-bottom"; 8 | area[] = "sidebar1"; 9 | 10 | area[] = "sidebar2"; 11 | 12 | area[] = "footer"; 13 | 14 | area[] = "top"; -------------------------------------------------------------------------------- /sp_theme/sedot_bootstrap/template.php: -------------------------------------------------------------------------------- 1 | enableWidget(); 7 | 8 | /* override widget that not declared ini config.ini */ 9 | $post->createWidget('head'); 10 | $post->createWidget('header'); 11 | 12 | if($_PAGE_404){ 13 | $message = '
    14 |

    404 NOT FOUND

    15 |
    '; 16 | $post->addWidget('index-top',$message); 17 | } 18 | if($_FRONT_PAGE){ 19 | if($getRequest[0] == "page"){ 20 | $title = ''.SITE_TITLE.' | page '. $getRequest[1] . ''; 21 | }elseif($_SEARCH_MODE){ 22 | $_page = isset($getRequest[3]) ? ' | page '.$getRequest[3] : ''; 23 | $title = ''.SITE_TITLE.' - '. $getRequest[1] . $_page . ''; 24 | }else{ 25 | $title = ''.SITE_TITLE.' - '.SITE_DESC.''; 26 | } 27 | $metaDescription = ''; 28 | 29 | foreach($entries as $id => $entry){ 30 | foreach($entry['tag'] as $label){ 31 | $tags[] = ''.$label.''; 32 | } 33 | $format[] = '
    34 |
    35 |

    '.$entry['title'].'

    36 |
    37 | '.implode(' ',$tags).'
      38 |
    39 |

    40 | '.$entry['title'].' 41 | '.$post->getExcerpt($entry['entry'],250).'

    42 |
    43 |
    '; 44 | unset($tags); 45 | } 46 | $post->addWidget('index-top',implode('',$format)); 47 | $post->addWidget('head',$title); 48 | $post->addWidget('head',$metaDescription); 49 | } 50 | if($_SINGLE_POST){ 51 | foreach($entry['tag'] as $label){ 52 | $tags[] = ''.$label.''; 53 | } 54 | $title = ''.$entry['title'].' - '.SITE_TITLE.''; 55 | $metaDescription = ''; 56 | $format = '
    57 |
    58 |

    '.$entry['title'].'

    59 | '.implode(' ',$tags).'
    60 | '.$entry['entry'].' 61 |
    62 |
    '; 63 | $post->addWidget('head',$title); 64 | $post->addWidget('head',$metaDescription); 65 | $post->addWidget('post-top',$format); 66 | } 67 | 68 | $header = sprintf(' 69 |

    %s %s

    ', 70 | sprintf('%2$s',SITE_URL,SITE_TITLE),SITE_DESC); 71 | 72 | $post->addWidget('header',$header); 73 | $generator = ''; 74 | $post->addWidget('head',$generator); 75 | ?> 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | widgetSection('head'); ?> 84 | 85 | 86 | 87 | 88 | 92 | 93 | 94 | widgetSection('top'); ?> 95 | 100 |
    101 |
    102 |
    103 |
    104 | widgetSection('index-top'); 107 | $post->widgetSection('index-bottom'); 108 | }else{ 109 | $post->widgetSection('post-top'); 110 | $post->widgetSection('post-bottom'); 111 | } 112 | ?> 113 |
    114 |
    115 |
    116 |
    117 | widgetSection('sidebar1'); ?> 118 |
    119 |
    120 |
    121 |
    122 | widgetSection('sidebar2'); ?> 123 |
    124 |
    125 |
    126 |
    127 |
    128 |
    129 |
    130 | widgetSection('footer'); ?> 131 |
    132 | Powered by SEDOTPRESS 133 |
    134 |
    135 | 136 | 137 | 138 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /sp_theme/sedot_bootstrap/widget.php: -------------------------------------------------------------------------------- 1 | $count){ 19 | $link = trim(SITE_URL,'/').'/tag/'.$name; 20 | $tags[] = '
  • '.$name.'
  • '; 21 | } 22 | $container = '
      %1$s
    '; 23 | return sprintf($container,implode('',$tags)); 24 | } 25 | function archives(){ 26 | $archive = sedot::loadJson('index','archive'); 27 | foreach($archive as $year => $x){ 28 | foreach($x as $month => $y){ 29 | $link = trim(SITE_URL,'/').'/timeline/'.$year.'/'.$month; 30 | $list[] = '
  • '.date('F Y',strtotime($year.'-'.$month)).'
  • '; 31 | } 32 | } 33 | $container = '
      %1$s
    '; 34 | return sprintf($container,implode('',$list)); 35 | } 36 | function pageNavi(){ 37 | $url = ACTUAL_URL; 38 | $current = $this->page; 39 | $stack = $this->total; 40 | for($i = 1;$i <= $stack;$i++){$stack_temp[$i] = $i;} 41 | $stack = array_chunk($stack_temp,5,true); 42 | $out[] = ''; 62 | return implode('',$out); 63 | } 64 | } 65 | ?> --------------------------------------------------------------------------------