diff --git a/autoblogs/autoblog.php b/autoblogs/autoblog.php new file mode 100644 index 0000000..87a8b28 --- /dev/null +++ b/autoblogs/autoblog.php @@ -0,0 +1,908 @@ +=')) + die("This software requires PHP version 5.3.0 at least, yours is ".phpversion()); + +if (!class_exists('SQLite3')) + die("This software requires the SQLite3 PHP extension, and it can't be found on this system!"); + +libxml_disable_entity_loader(true); + +// Config and data file locations + +if (file_exists(__DIR__ . '/../config.php')) { + require_once __DIR__ . '/../config.php'; +} +//else die("Configuration file not found."); + +if (file_exists(__DIR__ . '/../functions.php')){ + require_once __DIR__ . '/../functions.php'; +} +else die("Functions file not found."); + +if (!defined('ROOT_DIR')) + define('ROOT_DIR', __DIR__); + +if (!defined('CONFIG_FILE')) define('CONFIG_FILE', ROOT_DIR . '/vvb.ini'); +if (!defined('ARTICLES_DB_FILE')) define('ARTICLES_DB_FILE', ROOT_DIR . '/articles.db'); +if (!defined('MEDIA_DIR')) define('MEDIA_DIR', ROOT_DIR . '/media'); + +if (!defined('LOCAL_URL')) +{ + // Automagic URL discover + define('LOCAL_URL', 'http' . (!empty($_SERVER['HTTPS']) ? 's' : '')."://{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}"); +} + +if (!defined('LOCAL_URI')) +{ + // filename + define('LOCAL_URI', (basename($_SERVER['SCRIPT_FILENAME']) == 'index.php' ? '' : basename($_SERVER['SCRIPT_FILENAME'])) . '?'); +} + +if (!function_exists('__')) +{ + // Translation? + function __($str) + { + if ($str == '_date_format') + return '%A %e %B %Y at %H:%M'; + else + return $str; + } +} + +// ERROR MANAGEMENT + +class VroumVroum_User_Exception extends Exception {} + +class VroumVroum_Feed_Exception extends Exception +{ + static public function getXMLErrorsAsString($errors) + { + $out = array(); + + foreach ($errors as $error) + { + $return = $xml[$error->line - 1] . "\n"; + $return .= str_repeat('-', $error->column) . "^\n"; + + switch ($error->level) { + case LIBXML_ERR_WARNING: + $return .= "Warning ".$error->code.": "; + break; + case LIBXML_ERR_ERROR: + $return .= "Error ".$error->code.": "; + break; + case LIBXML_ERR_FATAL: + $return .= "Fatal Error ".$error->code.": "; + break; + } + + $return .= trim($error->message) . + "\n Line: ".$error->line . + "\n Column: ".$error->column; + + if ($error->file) { + $return .= "\n File: ".$error->file; + } + + $out[] = $return; + } + + return $out; + } +} + +error_reporting(E_ALL); + +function exception_error_handler($errno, $errstr, $errfile, $errline ) +{ + // For @ ignored errors + if (error_reporting() === 0) return; + throw new ErrorException($errstr, 0, $errno, $errfile, $errline); +} + +function exception_handler($e) +{ + if ($e instanceOf VroumVroum_User_Exception) + { + echo '

'.$e->getMessage().'

'; + exit; + } + + $error = "Error happened !\n\n". + $e->getCode()." - ".$e->getMessage()."\n\nIn: ". + $e->getFile() . ":" . $e->getLine()."\n\n"; + + if (!empty($_SERVER['HTTP_HOST'])) + $error .= 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']."\n\n"; + + $error .= $e->getTraceAsString(); + //$error .= print_r($_SERVER, true); + + echo $error; + exit; +} + +set_error_handler("exception_error_handler"); +set_exception_handler("exception_handler"); + +// CONFIGURATION + +class VroumVroum_Config +{ + public $site_type = ''; + public $site_title = ''; + public $site_description = ''; + public $site_url = ''; + public $feed_url = ''; + public $articles_per_page = 10; + public $update_interval = 3600; + public $update_timeout = 10; + + public function __construct() + { + if (!file_exists(CONFIG_FILE)) + throw new VroumVroum_User_Exception("Missing configuration file '".basename(CONFIG_FILE)."'."); + + $ini = parse_ini_file(CONFIG_FILE); + + foreach ($ini as $key=>$value) + { + $key = strtolower($key); + + if (!property_exists($this, $key)) + continue; // Unknown config + + if (is_string($this->$key) || is_null($this->$key)) + $this->$key = trim((string) $value); + elseif (is_int($this->$key)) + $this->$key = (int) $value; + elseif (is_bool($this->$key)) + $this->$key = (bool) $value; + } + + // Check that all required values are filled + $check = array('site_type', 'site_title', 'site_url', 'feed_url', 'update_timeout', 'update_interval', 'articles_per_page'); + foreach ($check as $c) + { + if (!trim($this->$c)) + throw new VroumVroum_User_Exception("Missing or empty configuration value '".$c."' which is required!"); + } + + } + + public function __set($key, $value) + { + return; + } +} + +// BLOG + +class VroumVroum_Blog +{ + protected $articles = null; + protected $local = null; + + public $config = null; + + static public function removeHTML($str) + { + $str = strip_tags($str); + $str = html_entity_decode($str, ENT_QUOTES, 'UTF-8'); + return $str; + } + + static public function toURI($str) + { + $uri = self::removeHTML(trim($str)); + $uri = substr($uri, 0, 70); + $uri = preg_replace('/[^\w\d()\p{L}]+/u', '-', $uri); + $uri = preg_replace('/-{2,}/', '-', $uri); + $uri = preg_replace('/^-|-$/', '', $uri); + return $uri; + } + + public function __construct() + { + $this->config = new VroumVroum_Config; + + $create_articles_db = file_exists(ARTICLES_DB_FILE) ? false : true; + + $this->articles = new SQLite3(ARTICLES_DB_FILE); + + if ($create_articles_db) + { + $this->articles->exec(' + CREATE TABLE articles ( + id INTEGER PRIMARY KEY, + feed_id TEXT, + title TEXT, + uri TEXT, + url TEXT, + date INT, + content TEXT + ); + CREATE TABLE update_log ( + date INT PRIMARY KEY, + success INT, + log TEXT + ); + CREATE UNIQUE INDEX feed_id ON articles (feed_id); + CREATE INDEX date ON articles (date); + '); + } + + $this->articles->createFunction('countintegers', array($this, 'sql_countintegers')); + } + + public function getLocalURL($in) + { + return "./?".(is_array($in) ? $in['uri'] : $in); + } + + protected function log_update($success, $log = '') + { + $this->articles->exec('INSERT INTO update_log (date, success, log) VALUES (\''.time().'\', \''.(int)(bool)$success.'\', + \''.$this->articles->escapeString($log).'\');'); + + // Delete old log + $this->articles->exec('DELETE FROM update_log WHERE date > (SELECT date FROM update_log ORDER BY date DESC LIMIT 100,1);'); + + return true; + } + + public function insertOrUpdateArticle($feed_id, $title, $url, $date, $content) + { + $exists = $this->articles->querySingle('SELECT date, id, title, content FROM articles WHERE feed_id = \''.$this->articles->escapeString($feed_id).'\';', true); + + if (empty($exists)) + { + $uri = self::toURI($title); + + if ($this->articles->querySingle('SELECT 1 FROM articles WHERE uri = \''.$this->articles->escapeString($uri).'\';')) + { + $uri = date('Y-m-d-') . $uri; + } + + $content = $this->mirrorMediasForArticle($content, $url); + + $this->articles->exec('INSERT INTO articles (id, feed_id, title, uri, url, date, content) VALUES (NULL, + \''.$this->articles->escapeString($feed_id).'\', \''.$this->articles->escapeString($title).'\', + \''.$this->articles->escapeString($uri).'\', \''.$this->articles->escapeString($url).'\', + \''.(int)$date.'\', \''.$this->articles->escapeString($content).'\');'); + + $id = $this->articles->lastInsertRowId(); + + $title = self::removeHTML($title); + $content = self::removeHTML($content); + + } + else + { + // Doesn't need update + if ($date == $exists['date'] && $content == $exists['content'] && $title == $exists['title']) + { + return false; + } + + $id = $exists['id']; + + if ($content != $exists['content']) + $content = $this->mirrorMediasForArticle($content, $url); + + $this->articles->exec('UPDATE articles SET title=\''.$this->articles->escapeString($title).'\', + url=\''.$this->articles->escapeString($url).'\', content=\''.$this->articles->escapeString($content).'\', + date=\''.(int)$date.'\' WHERE id = \''.(int)$id.'\';'); + + $title = self::removeHTML($title); + $content = self::removeHTML($content); + + } + + return $id; + } + + public function mustUpdate() + { + if (isset($_GET['update'])) + return true; + + $last_update = $this->articles->querySingle('SELECT date FROM update_log ORDER BY date DESC LIMIT 1;'); + + if (!empty($last_update) && (int) $last_update > (time() - $this->config->update_interval)) + return false; + + return true; + } + + protected function _getStreamContext() + { + return stream_context_create( + array( + 'http' => array( + 'method' => 'GET', + 'timeout' => $this->config->update_timeout, + 'header' => "User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:20.0; Autoblogs; +https://github.com/mitsukarenai/Projet-Autoblog/) Gecko/20100101 Firefox/20.0\r\n", + ) + ) + ); + } + + public function update() + { + if (!$this->mustUpdate()) + return false; + + try { + $body = file_get_contents($this->config->feed_url, false, $this->_getStreamContext()); + } + catch (ErrorException $e) + { + $this->log_update(false, $e->getMessage() . "\n\n" . (!empty($http_response_header) ? implode("\n", $http_response_header) : '')); + throw new VroumVroum_Feed_Exception("Can't retrieve feed: ".$e->getMessage()); + } + + libxml_use_internal_errors(true); + $xml = @simplexml_load_string($body); + + if (!$xml) + { + $errors = VroumVroum_Feed_Exception::getXMLErrorsAsString(libxml_get_errors()); + $this->log_update(false, implode("\n", $errors) . "\n\n" . $body); + throw new VroumVroum_Feed_Exception("Feed is invalid - XML error: ".implode(" - ", $errors)); + } + + $updated = 0; + $this->articles->exec('BEGIN TRANSACTION;'); + + if (isset($xml->entry)) // ATOM feed + { + foreach ($xml->entry as $item) + { + $date = isset($item->published) ? (string) $item->published : (string) $item->updated; + $guid = !empty($item->id) ? (string)$item->id : (string)$item->link['href']; + + $id = $this->insertOrUpdateArticle($guid, (string)$item->title, + (string)$item->link['href'], strtotime($date), (string)$item->content); + + if ($id !== false) + $updated++; + } + } + elseif (isset($xml->item)) // RSS 1.0 /RDF + { + foreach ($xml->item as $item) + { + $guid = (string) $item->attributes('http://www.w3.org/1999/02/22-rdf-syntax-ns#')->about ?: (string)$item->link; + $date = (string) $item->children('http://purl.org/dc/elements/1.1/')->date; + + $id = $this->insertOrUpdateArticle($guid, (string)$item->title, (string)$item->link, + strtotime($date), (string) $item->children('http://purl.org/rss/1.0/modules/content/')); + + if ($id !== false) + $updated++; + } + } + elseif (isset($xml->channel->item)) // RSS 2.0 + { + foreach ($xml->channel->item as $item) + { + $content = (string) $item->children('http://purl.org/rss/1.0/modules/content/'); + $guid = !empty($item->guid) ? (string) $item->guid : (string) $item->link; + + if (empty($content) && !empty($item->description)) + $content = (string) $item->description; + + $id = $this->insertOrUpdateArticle($guid, (string)$item->title, (string)$item->link, + strtotime((string) $item->pubDate), $content); + + if ($id !== false) + $updated++; + } + } + else + { + throw new VroumVroum_Feed_Exception("Unknown feed type?!"); + } + + $this->log_update(true, $updated . " elements updated"); + + $this->articles->exec('END TRANSACTION;'); + + return $updated; + } + + public function listArticlesByPage($page = 1) + { + $nb = $this->config->articles_per_page; + $begin = ($page - 1) * $nb; + $res = $this->articles->query('SELECT * FROM articles ORDER BY date DESC LIMIT '.(int)$begin.','.(int)$nb.';'); + + $out = array(); + + while ($row = $res->fetchArray(SQLITE3_ASSOC)) + { + $out[] = $row; + } + + return $out; + } + + public function listLastArticles() + { + return array_merge($this->listArticlesByPage(1), $this->listArticlesByPage(2)); + } + + public function countArticles() + { + return $this->articles->querySingle('SELECT COUNT(*) FROM articles;'); + } + + public function getArticleFromURI($uri) + { + return $this->articles->querySingle('SELECT * FROM articles WHERE uri = \''.$this->articles->escapeString($uri).'\';', true); + } + + public function sql_countintegers($in) + { + return substr_count($in, ' '); + } + + public function searchArticles($query) + { + $res = $this->articles->query('SELECT id, uri, title, content + FROM articles + WHERE content LIKE \'%'.$this->articles->escapeString($query).'%\' + ORDER BY id DESC + LIMIT 0,100;'); + + $out = array(); + + while ($row = $res->fetchArray(SQLITE3_ASSOC)) + { + $row['url'] = $this->getLocalURL($this->articles->querySingle('SELECT uri FROM articles WHERE id = \''.(int)$row['id'].'\';')); + $out[] = $row; + } + + return $out; + } + + public function mirrorMediasForArticle($content, $url) + { + if (!file_exists(MEDIA_DIR)) + { + mkdir(MEDIA_DIR); + } + + $schemes = array('http', 'https'); + $extensions = explode(',', preg_quote('jpg,jpeg,png,apng,gif,svg,pdf,odt,ods,epub,webp,wav,mp3,ogg,aac,wma,flac,opus,mp4,webm', '!')); + $extensions = implode('|', $extensions); + + $from = parse_url($url); + if( isset($from['path']) ) { // not exist if http://exemple.com + $from['path'] = preg_replace('![^/]*$!', '', $from['path']); + }else{ + $from['path'] = ''; + } + + preg_match_all('!(src|href)\s*=\s*[\'"]?([^"\'<>\s]+\.(?:'.$extensions.'))[\'"]?!i', $content, $match, PREG_SET_ORDER); + + foreach ($match as $m) + { + $url = parse_url($m[2]); + + if (empty($url['scheme'])) + $url['scheme'] = $from['scheme']; + + if (empty($url['host'])) + $url['host'] = $from['host']; + + if (!in_array(strtolower($url['scheme']), $schemes)) + continue; + + if ($url['path'][0] != '/') + $url['path'] = $from['path'] . $url['path']; + + $filename = basename($url['path']); + $url = $url['scheme'] . '://' . $url['host'] . $url['path']; + + $filename = substr(sha1($url), -8) . '.' . substr(preg_replace('![^\w\d_.-]!', '', $filename), -64); + $copied = false; + + if (!file_exists(MEDIA_DIR . '/' . $filename)) + { + try { + $copied = $this->_copy($url, MEDIA_DIR . '/' . $filename); + } + catch (ErrorException $e) + { + // Ignore copy errors + } + } + $content = str_replace($m[0], $m[1] . '="'.'media/'.$filename.'" data-original-source="'.$url.'"', $content); + } + return $content; + } + + /* copy() is buggy with http streams and safe_mode enabled (which is bad), so here's a workaround */ + protected function _copy($from, $to) + { + $in = fopen($from, 'r', false, $this->_getStreamContext()); + $out = fopen($to, 'w', false); + $size = stream_copy_to_stream($in, $out); + fclose($in); + fclose($out); + return $size; + } +} + +// DISPLAY AND CONTROLLERS + +$vvb = new VroumVroum_Blog; +$config = $vvb->config; +$site_type = escape($config->site_type); + +if (isset($_GET['feed'])) // FEED +{ + header('Content-Type: application/atom+xml; charset=UTF-8'); + echo ' + + '.escape($config->site_title).' + '.escape(html_entity_decode(strip_tags($config->site_description), ENT_COMPAT, 'UTF-8')).' + '.date(DATE_ATOM, filemtime(ARTICLES_DB_FILE)).' + + '.LOCAL_URL.' + + Projet Autoblog'; + + foreach($vvb->listLastArticles() as $art) + { + echo ' + + + '.escape($config->site_title).' + '.escape($config->site_url).' + + <![CDATA['.escape($art['title']).']]> + + '.str_replace('?feed', '?', LOCAL_URL).urlencode(str_replace('./?', '', $vvb->getLocalURL($art))).' + '.date(DATE_ATOM, $art['date']).' + + + source)
'.escape_content($art['content']).']]> +
+
'; + } + + echo ' +
'; + exit; +} + +if (isset($_GET['opml'])) // OPML +{ + //header('Content-Type: application/octet-stream'); + header('Content-type: text/xml'); + header('Content-Disposition: attachment; filename="'.escape($config->site_title).'.xml"'); + $opmlfile = new SimpleXMLElement(''); + $opmlfile->addAttribute('version', '1.0'); + $opmlhead = $opmlfile->addChild('head'); + $opmlhead->addChild('title', escape($config->site_title)); + $opmlhead->addChild('dateCreated', date('r', time())); + $opmlbody = $opmlfile->addChild('body'); + $outline = $opmlbody->addChild('outline'); + $outline->addAttribute('title', escape($config->site_title)); + $outline->addAttribute('text', escape($config->site_type)); + $outline->addAttribute('htmlUrl', escape($config->site_url)); + $outline->addAttribute('xmlUrl', escape($config->feed_url)); + + echo $opmlfile->asXML(); + exit; +} + +if (isset($_GET['media'])) // MEDIA +{ + header('Content-Type: application/json'); + if(is_dir(MEDIA_DIR)) + { + $url = str_replace('?media', 'media/', LOCAL_URL); + $files = scandir(MEDIA_DIR); + unset($files[0]); // . + unset($files[1]); // .. + echo json_encode(array("url"=> $url, "files" => $files)); + } + exit; +} + +if (isset($_GET['update'])) +{ + $_SERVER['QUERY_STRING'] = ''; +} + +// CONTROLLERS +$search = !empty($_GET['q']) ? trim($_GET['q']) : ''; +$article = null; + +if (!$search && !empty($_SERVER['QUERY_STRING']) && !is_numeric($_SERVER['QUERY_STRING'])) +{ + $uri = rawurldecode($_SERVER['QUERY_STRING']); + $article = $vvb->getArticleFromURI($uri); + + if (!$article) + { + header('HTTP/1.1 404 Not Found', true, 404); + } +} + +// common CSS +$css=' * { margin: 0; padding: 0; } + body { font-family:sans-serif; background-color: #efefef; padding: 1%; color: #333; } + img { max-width: 100%; height: auto; } + a { text-decoration: none; color: #000;font-weight:bold; } + .header a { text-decoration: none; color: #000;font-weight:bold; } + .header { text-align:center; padding: 30px 3%; max-width:70em;margin:0 auto; } + .article .title { margin-bottom: 1em; } + .article .title h2 a:hover { color:#403976; } + .article h4 { font-weight: normal; font-size: small; color: #666; } + .article .source a { color: #666; } + .searchForm { float:right; } + .searchForm input { } + .pagination { background-color:white;padding: 12px 10px 12px 10px;border:1px solid #aaa;max-width:70em;margin:1em auto;box-shadow:0px 5px 7px #aaa; } + .pagination b { font-size: 1.2em; color: #333; } + .pagination a { color:#000; margin: 0 0.5em; } + .pagination a:hover { color:#333; } + .footer a { color:#000; } + .footer a:hover { color:#333; } + .content ul, .content ol { margin-left: 2em; } + .content h1, .content h2, .content h3, .content h4, .content h5, .content h6, + .content ul, .content ol, .content p, .content object, .content div, .content blockquote, + .content dl, .content pre { margin-bottom: 0.8em; } + .content pre, .content blockquote { background: #ddd; border: 1px solid #999; padding: 0.2em; max-width: 100%; overflow: auto; } + .content h1 { font-size: 1.5em; } + .content h2 { font-size: 1.4em;color:#000; } + .result h3 a { color: darkblue; text-decoration: none; text-shadow: 1px 1px 1px #fff; } + #error { position: fixed; top: 0; left: 0; right: 0; padding: 1%; background: #fff; border-bottom: 2px solid red; color: darkred; } +'; + +if($site_type == 'generic') // custom CSS for generic + { + $css = $css.'.header h1 a { color: #333;font-size:40pt;text-shadow: #ccc 0px 5px 5px;text-transform:uppercase; } + .article .title h2 { margin: 0; color:#333; text-shadow: 1px 1px 1px #fff; } + .article .title h2 a { color:#000; text-decoration:none; } + .article .source { font-size: 0.8em; color: #666; } + .article { background-color:white;padding: 12px 10px 12px 10px;border:1px solid #aaa;max-width:70em;margin:1em auto;box-shadow:0px 5px 7px #aaa; } + .footer { text-align:center; font-size: small; color:#333; clear: both; }'; + } + else if($site_type == 'microblog' || $site_type == 'twitter' || $site_type == 'identica') // custom CSS for microblog + { + $css = $css.'.header h1 a { color: #333;font-size:40pt;text-shadow: #ccc 0px 5px 5px; } + .article .title h2 { width: 10em;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;font-size: 0.7em;margin: 0; color:#333; text-shadow: 1px 1px 1px #fff; } + .article .title h2 a { color:#333; text-decoration:none; } + .article { background-color:white;padding: 12px 10px 12px 10px;border:1px solid #aaa;max-width:70em;margin:0 auto;box-shadow:0px 5px 7px #aaa; } + .article .source { font-size: 0.8em; color: #666; } + .footer { margin-top:1em;text-align:center; font-size: small; color:#333; clear: both; } + .content {font-size:0.9em;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;}'; + } + else if($site_type == 'shaarli') // custom CSS for shaarli + { + $css = $css.'.header h1 a { color: #333;font-size:40pt;text-shadow: #ccc 0px 5px 5px; } + .article .title h2 { margin: 0; color:#333; text-shadow: 1px 1px 1px #fff; } + .article .title h2 a { color:#000; text-decoration:none; } + .article { background-color:white;padding: 12px 10px 12px 10px;border:1px solid #aaa;max-width:70em;margin:1em auto;box-shadow:0px 5px 7px #aaa; } + .article .source { margin-top:1em;font-size: 0.8em; color: #666; } + .footer { text-align:center; font-size: small; color:#333; clear: both; }'; + } + + +// HTML HEADER +echo ' + + + + + '.escape($config->site_title).' + + + + + +
+

PROJET AUTOBLOG'. (strlen(HEAD_TITLE) > 0 ? ' ~ '. HEAD_TITLE : '') .'

+
+

'.escape($config->site_title).'

'; + +if (!empty($config->site_description)) + echo '

'.$config->site_description.'
⇐ retour index

'; + +echo ' +
+
+ + +
+
+
+'; + +if ($vvb->mustUpdate()) +{ + echo ' +
+
+

'.__('Update').'

+
+
+ '.__('Updating database... Please wait.').' +
+
'; +} + +if (!empty($search)) +{ + $results = $vvb->searchArticles($search); + $text = sprintf(__('%d results for %s'), count($results), escape($search)); + echo ' +
+
+

'.__('Search').'

+ '.$text.' +
+
'; + + foreach ($results as $art) + { + echo ' +
+

'.escape($art['title']).'

+

'.$art['content'].'

+
'; + } +} +elseif (!is_null($article)) +{ + if (!$article) + { + echo ' +
+
+

'.__('Not Found').'

+ '.(!empty($uri) ? '

'.escape($vvb->getLocalURL($uri)) . '

' : '').' + '.__('Article not found.').' +
+
'; + } + else + { + display_article($article); + } +} +else +{ + if (!empty($_SERVER['QUERY_STRING']) && is_numeric($_SERVER['QUERY_STRING'])) + $page = (int) $_SERVER['QUERY_STRING']; + else + $page = 1; + + $list = $vvb->listArticlesByPage($page); + + foreach ($list as $article) + { + display_article($article); + } + + $max = $vvb->countArticles(); + if ($max > $config->articles_per_page) + { + echo ''; + } +} + +echo ' +'; + +if ($vvb->mustUpdate()) +{ + try { + ob_end_flush(); + flush(); + } + catch (Exception $e) + { + // Silent, not critical + } + + try { + $updated = $vvb->update(); + } + catch (VroumVroum_Feed_Exception $e) + { + echo ' +
+ '.escape($e->getMessage()).' +
'; + $updated = 0; + } + + if ($updated > 0) + { + echo ' + '; + } + else + { + echo ' + '; + } +} + +echo ' + +'; + + +function escape_content($str) +{ + $str = preg_replace('!<\s*(style|script|link)!', '<\\1', $str); + $str = str_replace('="media/', '="./media/', $str); + return $str; +} + +// ARTICLE HTML CODE +function display_article($article) +{ + global $vvb, $config; + echo ' +
+
+

'.escape($article['title']).'

+ '.strftime(__('_date_format'), $article['date']).' +
+
'.escape_content($article['content']).'
+

'.__('Source:').' '.escape($article['url']).'

+
+
'; +} + +?> diff --git a/class_rssfeed.php b/class_rssfeed.php new file mode 100755 index 0000000..59fd431 --- /dev/null +++ b/class_rssfeed.php @@ -0,0 +1,277 @@ + + * + * + 03/2013 + * Few changes, AutoblogRSS and FileRSSFeed + * @author Arthur Hoaro + */ +class RSSFeed { + protected $xml; + + /** + * Construct a RSS feed + */ + public function __construct() { + $template = << + + + + +END; + + $this->xml = new SimpleXMLElement($template); + } + + /** + * Set RSS Feed headers + * @param $title the title of the feed + * @param $link link to the website where you can find the RSS feed + * @param $description a description of the RSS feed + * @param $rsslink the link to this RSS feed + */ + public function setHeaders($title, $link, $description, $rsslink) { + $atomlink = $this->xml->channel->addChild("atom:link","","http://www.w3.org/2005/Atom"); + $atomlink->addAttribute("href",$rsslink); + $atomlink->addAttribute("rel","self"); + $atomlink->addAttribute("type","application/rss+xml"); + + $this->xml->channel->title = $title; + $this->xml->channel->link = $link; + $this->xml->channel->description = $description; + } + + /** + * Set the language of the RSS feed + * @param $lang the language of the RSS feed + */ + public function setLanguage($lang) { + $this->xml->channel->addChild("language",$lang); + } + /** + * Adds a picture to the RSS feed + * @param $url URL to the image + * @param $title The image title. Usually same as the RSS feed's title + * @param $link Where the image should link to. Usually same as the RSS feed's link + */ + public function setImage($url, $title, $link) { + $image = $this->xml->channel->addChild("image"); + $image->url = $url; + $image->title = $title; + $image->link = $link; + } + /** + * Add a item to the RSS feed + * @param $title The title of the RSS feed + * @param $link Link to the item's url + * @param $description The description of the item + * @param $author The author who wrote this item + * @param $guid Unique ID for this post + * @param $timestamp Unix timestamp for making a date + */ + public function addItem($title, $link, $description, $author, $guid, $timestamp) { + $item = $this->xml->channel->addChild("item"); + $item->title = $title; + $item->description = $description; + $item->link = $link; + $item->guid = $guid; + if( isset($guid['isPermaLink'])) + $item->guid['isPermaLink'] = $guid['isPermaLink']; + if( !empty( $author) ) + $item->author = $author; + $item->pubDate = date(DATE_RSS,intval($timestamp)); + } + /** + * Displays the RSS feed + */ + public function displayXML() { + header('Content-type: application/rss+xml; charset=utf-8'); + echo $this->xml->asXML(); + exit; + } + + public function getXML() { + return $this->xml; + } +} + +class RSSMerger { + private $feeds = array(); + + /** + * Constructs a RSSmerger object + */ + function __construct() { + + } + + /** + * Populates the feeds array from the given url which is a rss feed + * @param $url + */ + function add($xml) { + + foreach($xml->channel->item as $item) { + $item->sitetitle = $xml->channel->title; + $item->sitelink = $xml->channel->link; + + preg_match("/^[A-Za-z]{3}, ([0-9]{2}) ([A-Za-z]{3}) ([0-9]{4}) ([0-9]{2}):([0-9]{2}):([0-9]{2}) ([\+|\-]?[0-9]{4})$/", $item->pubDate, $match); + $item->time = time($match[4]+($match[6]/100),$match[5],$match[6],date("m",strtotime($match[2])),$match[1],$match[3]); + + $this->feeds[] = $item; + } + } + /** + * Comparing function for sorting the feeds + * @param $value1 + * @param $value2 + */ + function feeds_cmp($value1,$value2) { + if(intval($value1->time) == intval($value2->time)) + return 0; + + return (intval($value1->time) < intval($value2->time)) ? +1 : -1; + } + + /** + * Sorts the feeds array using the Compare function feeds_cmp + */ + function sort() { + usort($this->feeds,Array("RssMerger","feeds_cmp")); + } + + /** + * This function return the feed items. + * @param $limit how many feed items that should be returned + * @return the feeds array + */ + function getFeeds($limit) { + return array_slice($this->feeds,0,$limit); + } +} + +class FileRSSFeed extends RSSFeed { + protected $filename; + + public function __construct($filename) { + parent::__construct(); + $this->filename = $filename; + + $this->load(); + } + + public function load() { + if ( file_exists( $this->filename )) { + $this->xml = simplexml_load_file($this->filename); + } + } + + public function create($title, $link, $description, $rsslink) { + parent::setHeaders($title, $link, $description, $rsslink); + $this->write(); + } + + public function addItem($title, $link, $description, $author, $guid, $timestamp) { + parent::addItem($title, $link, $description, $author, $guid, $timestamp); + $this->write(); + } + + private function write() { + if ( file_exists( $this->filename )) { + unlink($this->filename); + } + + $outputXML = new RSSFeed(); + foreach($this->xml->channel->item as $f) { + $item = $outputXML->addItem($f->title,$f->link,$f->description,$f->author,$f->guid, strtotime($f->pubDate)); + } + + $merger = new RssMerger(); + $merger->add($outputXML->getXML()); + $merger->sort(); + + unset($this->xml->channel->item); + foreach($merger->getFeeds(20) as $f) { + parent::addItem($f->title,$f->link,$f->description,$f->author,$f->guid,$f->time); + } + + file_put_contents( $this->filename, $this->xml->asXML(), LOCK_EX ); + } +} + +class AutoblogRSS extends FileRSSFeed { + public function __construct($filename) { + parent::__construct($filename); + } + + public function addUnavailable($title, $folder, $siteurl, $rssurl) { + $path = pathinfo( $_SERVER['PHP_SELF'] ); + $autobHref = 'http'.(!empty($_SERVER['HTTPS'])?'s':'').'://'. + $_SERVER["SERVER_NAME"].':'.$_SERVER["SERVER_PORT"]. $path['dirname'].'/'.$folder; + + parent::addItem( 'L\'autoblog "'. $title.'" est indisponible', $autobHref, + 'Autoblog: '.$title.'
+ Site: '. $siteurl .'
+ RSS: '.$rssurl.'
+ Folder: '. $folder , + 'admin@'.$_SERVER['SERVER_NAME'], + $autobHref, + time() + ); + } + + public function addAvailable($title, $folder, $siteurl, $rssurl) { + $path = pathinfo( $_SERVER['PHP_SELF'] ); + $autobHref = 'http'.(!empty($_SERVER['HTTPS'])?'s':'').'://'. + $_SERVER["SERVER_NAME"].':'.$_SERVER["SERVER_PORT"]. $path['dirname'].'/'.$folder; + + parent::addItem( 'L\'autoblog "'. $title.'" est de nouveau disponible', $autobHref, + 'Autoblog : '.$title.'
+ Site: '. $siteurl .'
+ RSS: '.$rssurl.'
+ Folder: '. $folder , + 'admin@'.$_SERVER['SERVER_NAME'], + $autobHref, + time() + ); + } + + public function addCodeChanged($title, $folder, $siteurl, $rssurl, $code) { + $path = pathinfo( $_SERVER['PHP_SELF'] ); + $autobHref = 'http'.(!empty($_SERVER['HTTPS'])?'s':'').'://'. + $_SERVER["SERVER_NAME"].':'.$_SERVER["SERVER_PORT"]. $path['dirname'].'/'.$folder; + + parent::addItem( 'L\'autoblog "'. $title.'" a renvoyé un code imprévu', $autobHref, + 'Code: '. $code .'
+ Autoblog : '.$title.'
+ Site: '. $siteurl .'
+ RSS: '.$rssurl.'
+ Folder: '. $folder , + 'admin@'.$_SERVER['SERVER_NAME'], + $autobHref, + time() + ); + } + + public function addNewAutoblog($title, $folder, $siteurl, $rssurl) { + $path = pathinfo( $_SERVER['PHP_SELF'] ); + $autobHref = 'http'.(!empty($_SERVER['HTTPS'])?'s':'').'://'. + $_SERVER["SERVER_NAME"].':'.$_SERVER["SERVER_PORT"]. $path['dirname'].'/'.$folder; + + parent::addItem( 'L\'autoblog "'. $title.'" a été ajouté à la ferme', $autobHref, + 'Autoblog : '.$title.'
+ Site: '. $siteurl .'
+ RSS: '.$rssurl.'
+ Folder: '. $folder , + 'admin@'.$_SERVER['SERVER_NAME'], + $autobHref, + time() + ); + } +} + +?> diff --git a/config.php b/config.php new file mode 100755 index 0000000..8f98017 --- /dev/null +++ b/config.php @@ -0,0 +1,43 @@ +SebSauvage et Bohwaz.'); + +// define( 'ALLOW_FULL_UPDATE', TRUE ); +// define( 'ALLOW_CHECK_UPDATE', TRUE ); + +/** + * If you set ALLOW_NEW_AUTOBLOGS to FALSE, the following options do not matter. + **/ +// define( 'ALLOW_NEW_AUTOBLOGS', TRUE ); +// define( 'ALLOW_NEW_AUTOBLOGS_BY_LINKS', TRUE ); +// define( 'ALLOW_NEW_AUTOBLOGS_BY_SOCIAL', TRUE ); +// define( 'ALLOW_NEW_AUTOBLOGS_BY_BUTTON', TRUE ); +// define( 'ALLOW_NEW_AUTOBLOGS_BY_OPML_FILE', TRUE ); +// define( 'ALLOW_NEW_AUTOBLOGS_BY_OPML_LINK', TRUE ); +// define( 'ALLOW_NEW_AUTOBLOGS_BY_XSAF', TRUE ); + +/** + * More about TwitterBridge : https://github.com/mitsukarenai/twitterbridge + **/ +// define( 'API_TWITTER', FALSE ); + +/** + * Import autoblogs from friend's autoblog farm - Add a link to the JSON export + **/ +$friends_autoblog_farm = array( + 'https://raw.github.com/mitsukarenai/xsaf-bootstrap/master/3.json', + // 'https://www.ecirtam.net/autoblogs/?export', + // 'https://autoblog.suumitsu.eu/?export', + // 'http://streisand.hoa.ro/?export', +); +?> diff --git a/docs/docs.txt b/docs/docs.txt new file mode 100644 index 0000000..a4c763e --- /dev/null +++ b/docs/docs.txt @@ -0,0 +1,4 @@ +You can manually add files in the /docs/ directory, such as PDF, docs, images, etc. +You can also add subfolders in /docs/ for website mirroring. Be sure that your subfolder contains a file named index.html. + +Delete this file to hide the 'Autres documents' block in your autoblogs homepage. diff --git a/functions.php b/functions.php new file mode 100755 index 0000000..b1f68d1 --- /dev/null +++ b/functions.php @@ -0,0 +1,302 @@ +SebSauvage et Bohwaz.'); + +// Functions +function NoProtocolSiteURL($url) { + $protocols = array("http://", "https://"); + $siteurlnoproto = str_replace($protocols, "", $url); + + // Remove the / at the end of string + if ( $siteurlnoproto[strlen($siteurlnoproto) - 1] == '/' ) + $siteurlnoproto = substr($siteurlnoproto, 0, -1); + + // Remove index.php/html at the end of string + if( strpos($url, 'index.php') || strpos($url, 'index.html') ) { + $siteurlnoproto = preg_replace('#(.*)/index\.(html|php)$#', '$1', $siteurlnoproto); + } + + return $siteurlnoproto; +} + + +function DetectRedirect($url) +{ + if(parse_url($url, PHP_URL_HOST)==FALSE) { + //die('Not a URL'); + throw new Exception('Not a URL: '. escape ($url) ); + } + $response = get_headers($url, 1); + if(!empty($response['Location'])) { + $response2 = get_headers($response['Location'], 1); + if(!empty($response2['Location'])) { + //die('too much redirection'); + throw new Exception('too much redirection: '. escape ($url) ); + } + else { return $response['Location']; } + } + else { + return $url; + } +} + +function urlToFolder($url) { + return sha1(NoProtocolSiteURL($url)); +} + +function urlToFolderSlash($url) { + return sha1(NoProtocolSiteURL($url).'/'); +} + +function folderExists($url) { + return file_exists(AUTOBLOGS_FOLDER . urlToFolder($url)) || file_exists(AUTOBLOGS_FOLDER . urlToFolderSlash($url)); +} + +function escape($str) { + return htmlspecialchars($str, ENT_COMPAT, 'UTF-8', false); +} + +function createAutoblog($type, $sitename, $siteurl, $rssurl, $error = array()) { + if( $type == 'generic' || empty( $type )) { + $var = updateType( $siteurl ); + $type = $var['type']; + if( !empty( $var['name']) ) { + if( !stripos($siteurl, $var['name'] === false) ) + $sitename = ucfirst($var['name']) . ' - ' . $sitename; + } + } + + if(folderExists($siteurl)) { + $error[] = 'Erreur : l\'autoblog '. $sitename .' existe déjà.'; + return $error; + } + + $foldername = AUTOBLOGS_FOLDER . urlToFolderSlash($siteurl); + + if ( mkdir($foldername, 0755, false) ) { + + /** + * RSS + **/ + try { // à déplacer après la tentative de création de l'autoblog crée avec succès ? + require_once('class_rssfeed.php'); + $rss = new AutoblogRSS(RSS_FILE); + $rss->addNewAutoblog($sitename, $foldername, $siteurl, $rssurl); + } + catch (Exception $e) { + ; + } + + $fp = fopen($foldername .'/index.php', 'w+'); + if( !fwrite($fp, "") ) + $error[] = "Impossible d'écrire le fichier index.php"; + fclose($fp); + + $fp = fopen($foldername .'/vvb.ini', 'w+'); + if( !fwrite($fp, '[VroumVroumBlogConfig] +SITE_TYPE="'. $type .'" +SITE_TITLE="'. $sitename .'" +SITE_DESCRIPTION="Site original : '. $sitename .'" +SITE_URL="'. $siteurl .'" +FEED_URL="'. $rssurl .'" +ARTICLES_PER_PAGE="'. getArticlesPerPage( $type ) .'" +UPDATE_INTERVAL="'. getInterval( $type ) .'" +UPDATE_TIMEOUT="'. getTimeout( $type ) .'"') ) + $error[] = "Impossible d'écrire le fichier vvb.ini"; + fclose($fp); + } + else + $error[] = "Impossible de créer le répertoire."; + updateXML('new_autoblog_added', 'new', $foldername, $sitename, $siteurl, $rssurl); /* éventuellement une conditionnelle ici: if(empty($error)) ? */ + return $error; +} + +function getArticlesPerPage( $type ) { + switch( $type ) { + case 'microblog': + return 20; + case 'shaarli': + return 20; + default: + return 5; + } +} + +function getInterval( $type ) { + switch( $type ) { + case 'microblog': + return 300; + case 'shaarli': + return 1800; + default: + return 3600; + } +} + +function getTimeout( $type ) { + switch( $type ) { + case 'microblog': + return 30; + case 'shaarli': + return 30; + default: + return 30; + } +} + +function updateType($siteurl) { + if( strpos($siteurl, 'twitter.com') !== FALSE ) { + return array('type' => 'twitter', 'name' => 'twitter'); + } + elseif ( strpos( $siteurl, 'identi.ca') !== FALSE ) { + return array('type' => 'identica', 'name' => 'identica'); + } + elseif( strpos( $siteurl, 'shaarli' ) !== FALSE ) { + return array('type' => 'shaarli', 'name' => 'shaarli'); + } + else + return array('type' => 'generic', 'name' => ''); +} + +function debug($data) +{ + echo '
';
+	var_dump($data);
+	echo '
'; +} + +function __($str) +{ + switch ($str) + { + case 'Search': + return 'Recherche'; + case 'Update': + return 'Mise à jour'; + case 'Updating database... Please wait.': + return 'Mise à jour de la base de données, veuillez patienter...'; + case '%d results for %s': + return '%d résultats pour la recherche %s'; + case 'Not Found': + return 'Introuvable'; + case 'Article not found.': + return 'Cet article n\'a pas été trouvé.'; + case 'Older': + return 'Plus anciens'; + case 'Newer': + return 'Plus récents'; + case 'ATOM Feed': + return 'Flux ATOM'; + case 'Update complete!': + return 'Mise à jour terminée !'; + case 'Click here to reload this webpage.': + return 'Cliquez ici pour recharger cette page.'; + case 'Source:': + return 'Source :'; + case '_date_format': + return '%A %e %B %Y à %H:%M'; + case 'configuration': + case 'articles': + return $str; + case 'Media export': + return 'Export fichiers media'; + default: + return $str; + } +} + +function updateXML($status, $response_code, $autoblog_url, $autoblog_title, $autoblog_sourceurl, $autoblog_sourcefeed) +{ +$json = json_decode(file_get_contents(RESOURCES_FOLDER.'rss.json'), true); +$json[] = array( + 'timestamp'=>time(), + 'autoblog_url'=>$autoblog_url, + 'autoblog_title'=>$autoblog_title, + 'autoblog_sourceurl'=>$autoblog_sourceurl, + 'autoblog_sourcefeed'=>$autoblog_sourcefeed, + 'status'=>$status, + 'response_code'=>$response_code + ); +file_put_contents(RESOURCES_FOLDER.'rss.json', json_encode($json), LOCK_EX); +} + +function displayXMLstatus_tmp($status, $response_code, $autoblog_url, $autoblog_title, $autoblog_sourceurl, $autoblog_sourcefeed) { + switch ($status) + { + case 'unavailable': + return 'Autoblog "'.$autoblog_title.'": site distant inaccessible (code '.$response_code.')
Autoblog: '.$autoblog_title.'
Site: '. $autoblog_sourceurl .'
RSS: '.$autoblog_sourcefeed.''; + case 'moved': + return 'Autoblog "'.$autoblog_title.'": site distant redirigé (code '.$response_code.')
Autoblog: '.$autoblog_title.'
Site: '. $autoblog_sourceurl .'
RSS: '.$autoblog_sourcefeed.''; + case 'not_found': + return 'Autoblog "'.$autoblog_title.'": site distant introuvable (code '.$response_code.')
Autoblog: '.$autoblog_title.'
Site: '. $autoblog_sourceurl .'
RSS: '.$autoblog_sourcefeed.''; + case 'remote_error': + return 'Autoblog "'.$autoblog_title.'": site distant a problème serveur (code '.$response_code.')
Autoblog: '.$autoblog_title.'
Site: '. $autoblog_sourceurl .'
RSS: '.$autoblog_sourcefeed.''; + case 'available': + return 'Autoblog "'.$autoblog_title.'": site distant à nouveau opérationnel (code '.$response_code.')
Autoblog: '.$autoblog_title.'
Site: '. $autoblog_sourceurl .'
RSS: '.$autoblog_sourcefeed.''; + case 'new_autoblog_added': + return 'Autoblog "'.$autoblog_title.'" ajouté (code '.$response_code.')
Autoblog: '.$autoblog_title.'
Site: '. $autoblog_sourceurl .'
RSS: '.$autoblog_sourcefeed.''; + } +} + +function displayXML_tmp() { +header('Content-type: application/rss+xml; charset=utf-8'); +echo ' +'.serverUrl(true).''; +echo 'Projet Autoblog'. ((strlen(HEAD_TITLE)>0) ? ' | '. HEAD_TITLE : '').''.serverUrl(true),"Projet Autoblog - RSS : Ajouts et changements de disponibilité.".''; +if(file_exists(RESOURCES_FOLDER.'rss.json')) +{ + $json = json_decode(file_get_contents(RESOURCES_FOLDER.'rss.json'), true); + foreach ($json as $item) + { + $description = displayXMLstatus_tmp($item['status'],$item['response_code'],$item['autoblog_url'],$item['autoblog_title'],$item['autoblog_sourceurl'],$item['autoblog_sourcefeed']); + $link = serverUrl(true).AUTOBLOGS_FOLDER.$item['autoblog_url']; + $date = date("r", $item['timestamp']); + print << + {$item['autoblog_title']} + + {$link} + {$item['timestamp']} + admin@{$_SERVER['SERVER_NAME']} + {$date} + +EOT; + } +} +echo ''; +} +?> diff --git a/index.php b/index.php new file mode 100755 index 0000000..be3c7e4 --- /dev/null +++ b/index.php @@ -0,0 +1,926 @@ +loadXML($data) or die('xml malformé'); + $title = $dom->getElementsByTagName('title'); + return $title->item(0)->nodeValue; +} + +function get_link_from_feed($url) { + return get_link_from_datafeed(file_get_contents($url)); +} + +function get_link_from_datafeed($data) { + if($data === false) { return 'url inaccessible'; } + $xml = simplexml_load_string($data); // quick feed check + + // ATOM feed && RSS 1.0 /RDF && RSS 2.0 + if (!isset($xml->entry) && !isset($xml->item) && !isset($xml->channel->item)) + die('le flux n\'a pas une syntaxe valide'); + + $check = substr($data, 0, 5); + if($check !== 'channel->link; + if($channel['link'] === NULL) { + $dom = new DOMDocument; + $dom->loadXML($data) or die('xml malformé'); + $link = $dom->getElementsByTagName('uri'); + return $link->item(0)->nodeValue; + } + else { + return $channel['link']; + } +} + +function serverUrl($return_subfolder = false) +{ + $https = (!empty($_SERVER['HTTPS']) && (strtolower($_SERVER['HTTPS'])=='on')) || $_SERVER["SERVER_PORT"]=='443'; // HTTPS detection. + $serverport = ($_SERVER["SERVER_PORT"]=='80' || ($https && $_SERVER["SERVER_PORT"]=='443') ? '' : ':'.$_SERVER["SERVER_PORT"]); + if($return_subfolder === true) { + $path = pathinfo( $_SERVER['PHP_SELF'] ); + $subfolder = $path['dirname'] .'/'; + } else $subfolder = ''; + return 'http'.($https?'s':'').'://'.$_SERVER["SERVER_NAME"].$serverport.$subfolder; +} + +function objectCmp($a, $b) { + return strcasecmp ($a->site_title, $b->site_title); +} + +function generate_antibot() { + $letters = array('zéro', 'un', 'deux', 'trois', 'quatre', 'cinq', 'six', 'sept', 'huit', 'neuf', 'dix', 'onze', 'douze', 'treize', 'quatorze', 'quinze', 'seize', 'dix-sept', 'dix-huit', 'dix-neuf', 'vingt'); + return $letters[mt_rand(1, 20)]; +} + +function check_antibot($number, $text_number) { + $letters = array('zéro', 'un', 'deux', 'trois', 'quatre', 'cinq', 'six', 'sept', 'huit', 'neuf', 'dix', 'onze', 'douze', 'treize', 'quatorze', 'quinze', 'seize', 'dix-sept', 'dix-huit', 'dix-neuf', 'vingt'); + return ( array_search( $text_number, $letters ) === intval($number) ) ? true : false; +} + +function create_from_opml($opml) { + global $error, $success; + + foreach( $opml->body->outline as $outline ) { + if ( !empty( $outline['title'] ) && !empty( $outline['text'] ) && !empty( $outline['xmlUrl']) && !empty( $outline['htmlUrl'] )) { + try { + $rssurl = DetectRedirect(escape( $outline['xmlUrl'])); + + $sitename = escape( $outline['title'] ); + $siteurl = escape($outline['htmlUrl']); + $sitetype = escape($outline['text']); if ( $sitetype == 'generic' or $sitetype == 'microblog' or $sitetype == 'shaarli') { } else { $sitetype = 'generic'; } + + $error = array_merge( $error, createAutoblog( $sitetype, $sitename, $siteurl, $rssurl, $error ) ); + + if( empty ( $error )) + $success[] = 'Autoblog "'. $sitename .'" crée avec succès. → afficher l\'autoblog.'; + } + catch (Exception $e) { + $error[] = $e->getMessage(); + } + } + } +} + +/** + * Simple version check + **/ +function versionCheck() { + $versionfile = 'version'; + $lastestUrl = 'https://raw.github.com/mitsukarenai/Projet-Autoblog/master/0.3/version'; + + $expire = time() - 84600 ; // 23h30 en secondes + $lockfile = '.versionlock'; + + if (file_exists($lockfile) && filemtime($lockfile) > $expire) { + if( file_get_contents($lockfile) == 'NEW' ) { + // No new version installed + if( filemtime( $lockfile ) > filemtime( $versionfile ) ) + return true; + else unlink($lockfile); + } + else return false; + } + + if (file_exists($lockfile) && filemtime($lockfile) < $expire) { unlink($lockfile); } + + if( file_get_contents($versionfile) != file_get_contents($lastestUrl) ) { + file_put_contents($lockfile, 'NEW'); + return true; + } + file_put_contents($lockfile, '.'); + return false; + } + $update_available = (ALLOW_CHECK_UPDATE) ? versionCheck() : false; + +/** +* RSS Feed +**/ +if( !file_exists(RSS_FILE)) { + require_once('class_rssfeed.php'); + $rss = new AutoblogRSS(RSS_FILE); + $rss->create('Projet Autoblog'. ((strlen(HEAD_TITLE)>0) ? ' | '. HEAD_TITLE : ''), serverUrl(true),"Projet Autoblog - RSS : Ajouts et changements de disponibilité.", serverUrl(true) . RSS_FILE); +} +if (isset($_GET['rss'])) { + require_once('class_rssfeed.php'); + $rss = new AutoblogRSS(RSS_FILE); + $rss->displayXML(); + die; +} + +if( !file_exists(RESOURCES_FOLDER.'rss.json')) { + file_put_contents(RESOURCES_FOLDER.'rss.json', '', LOCK_EX); +} + +if (isset($_GET['rss_tmp'])) { + displayXML_tmp(); + die; +} + +/** + * SVG + **/ +if (isset($_GET['check'])) +{ + //echo "1"; + header('Content-type: image/svg+xml'); + $randomtime=rand(86400, 259200); /* intervalle de mise à jour: de 1 à 3 jours (pour éviter que le statut de tous les autoblogs soit rafraichi en bloc et bouffe le CPU) */ + $expire=time() -$randomtime ; + + /* SVG minimalistes */ + $svg_vert='OK'; + $svg_jaune='mv'; + $svg_rouge='err'; + $svg_twitter=''; + $svg_identica=''; + $svg_statusnet=''; + + $errorlog="./".escape( $_GET['check'] ) ."/error.log"; + + $oldvalue = null; + if(file_exists($errorlog)) { $oldvalue = file_get_contents($errorlog); }; + if(file_exists($errorlog) && filemtime($errorlog) < $expire) { unlink($errorlog); } /* errorlog périmé ? Suppression. */ + if(file_exists($errorlog)) /* errorlog existe encore ? se contenter de lire sa taille pour avoir le statut */ + { + if(filesize($errorlog) == "0") {die($svg_vert);} + else if(filesize($errorlog) == "1") {die($svg_jaune);} + else {die($svg_rouge);} + } + else /* ..sinon, lancer la procédure de contrôle */ + { + $ini = parse_ini_file("./". escape( $_GET['check'] ) ."/vvb.ini") or die; + + if(strpos(strtolower($ini['SITE_TITLE']), 'twitter') !== FALSE) { die($svg_twitter); } /* Twitter */ + if(strpos(strtolower($ini['SITE_TITLE']), 'identica') !== FALSE) { die($svg_identica); } /* Identica */ + if(strpos(strtolower($ini['SITE_TYPE']), 'microblog') !== FALSE) { die($svg_statusnet); } /* Statusnet */ + + $headers = get_headers($ini['FEED_URL']); + /* le flux est indisponible (typiquement: erreur DNS ou possible censure) - à vérifier */ + if(empty($headers) || $headers === FALSE ) { + if( $oldvalue !== null && $oldvalue != '..' ) { + require_once('class_rssfeed.php'); + $rss = new AutoblogRSS(RSS_FILE); + $rss->addUnavailable($ini['SITE_TITLE'], escape($_GET['check']), $ini['SITE_URL'], $ini['FEED_URL']); + updateXML('unavailable', 'nxdomain', escape($_GET['check']), $ini['SITE_TITLE'], $ini['SITE_URL'], $ini['FEED_URL']); + } + file_put_contents($errorlog, '..'); + die($svg_rouge); + } + $code=explode(" ", $headers[0]); + /* code retour 200: flux disponible */ + if($code[1] == "200") { + if( $oldvalue !== null && $oldvalue != '' ) { + require_once('class_rssfeed.php'); + $rss = new AutoblogRSS(RSS_FILE); + $rss->addAvailable($ini['SITE_TITLE'], escape($_GET['check']), $ini['SITE_URL'], $ini['FEED_URL']); + updateXML('available', '200', escape($_GET['check']), $ini['SITE_TITLE'], $ini['SITE_URL'], $ini['FEED_URL']); + } + file_put_contents($errorlog, ''); + die($svg_vert); + } + /* autre code retour: un truc a changé (redirection, changement de CMS, .. bref vvb.ini doit être corrigé) */ + else { + if( $oldvalue !== null && $oldvalue != '.' ) { + require_once('class_rssfeed.php'); + $rss = new AutoblogRSS(RSS_FILE); + $rss->addCodeChanged($ini['SITE_TITLE'], escape($_GET['check']), $ini['SITE_URL'], $ini['FEED_URL'], $code[1]); + updateXML('moved', '3xx', escape($_GET['check']), $ini['SITE_TITLE'], $ini['SITE_URL'], $ini['FEED_URL']); + } + file_put_contents($errorlog, '.'); + die($svg_jaune); + } + } +} + +/** + * JSON Export + **/ +if (isset($_GET['export'])) { + header('Content-Type: application/json'); + $subdirs = glob(AUTOBLOGS_FOLDER . "*"); + + foreach($subdirs as $unit) { + if(is_dir($unit)) { + $unit=substr($unit, 2); + $ini = parse_ini_file($unit.'/vvb.ini'); + $config = new stdClass; + + foreach ($ini as $key=>$value) { + $key = strtolower($key); + $config->$key = $value; + } + unset($ini); + + $feed=$config->feed_url; + $type=$config->site_type; + $title=$config->site_title; + $url=$config->site_url; + $reponse[$unit] = array("SITE_TYPE"=>"$type", "SITE_TITLE"=>"$title", "SITE_URL"=>"$url", "FEED_URL"=>"$feed"); + + } + } + echo json_encode( array( "meta"=> array("xsaf-version"=>XSAF_VERSION,"xsaf-db_transfer"=>"true","xsaf-media_transfer"=>"true"), + "autoblogs"=>$reponse)); + die; +} + +/** + * JSON Allowed Twitter accounts export + **/ +if (isset($_GET['export_twitter'])) { + header('Content-Type: application/json'); + $subdirs = glob(AUTOBLOGS_FOLDER . "*"); + $response = array(); + + foreach($subdirs as $unit) { + if(is_dir($unit)) { + $unit=substr($unit, 2); + $ini = parse_ini_file($unit.'/vvb.ini'); + if( $ini['SITE_TYPE'] == 'twitter' ) { + preg_match('#twitter\.com/(.+)#', $ini['SITE_URL'], $username); + $response[] = $username[1]; + } + unset($ini); + } + } + echo json_encode( $response ); + die; +} + +/** + * OPML Full Export + **/ +if (isset($_GET['exportopml'])) // OPML +{ + //header('Content-Type: application/octet-stream'); + header('Content-type: text/xml'); + header('Content-Disposition: attachment; filename="autoblogs-'. $_SERVER['SERVER_NAME'] .'.xml"'); + + $opmlfile = new SimpleXMLElement(''); + $opmlfile->addAttribute('version', '1.0'); + $opmlhead = $opmlfile->addChild('head'); + $opmlhead->addChild('title', 'Autoblog OPML export from '. $_SERVER['SERVER_NAME'] ); + $opmlhead->addChild('dateCreated', date('r', time())); + $opmlbody = $opmlfile->addChild('body'); + + $subdirs = glob(AUTOBLOGS_FOLDER . "*"); + + foreach($subdirs as $unit) { + if(is_dir($unit)) { + $unit=substr($unit, 2); + $ini = parse_ini_file($unit.'/vvb.ini'); + $config = new stdClass; + + foreach ($ini as $key=>$value) { + $key = strtolower($key); + $config->$key = $value; + } + unset($ini); + + $outline = $opmlbody->addChild('outline'); + $outline->addAttribute('title', escape($config->site_title)); + $outline->addAttribute('text', escape($config->site_type)); + $outline->addAttribute('htmlUrl', escape($config->site_url)); + $outline->addAttribute('xmlUrl', escape($config->feed_url)); + } + } + echo $opmlfile->asXML(); + exit; +} + +/** + * Site map + * NEW AUTOBLOG FOLDER - Need update + **/ +if (isset($_GET['sitemap'])) +{ + header('Content-Type: application/xml'); + $proto=(!empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS'])=='on')?"https://":"http://"; + echo ''; + echo ''.$proto."{$_SERVER['HTTP_HOST']}".str_replace('?sitemap', '', $_SERVER['REQUEST_URI'])."\n"; + echo ''.date('c', time())."\n"; + echo 'daily'; + $subdirs = glob(AUTOBLOGS_FOLDER . "*"); + foreach($subdirs as $unit) { + if(is_dir($unit)) { + $unit=substr($unit, 2); + echo ''.$proto.$_SERVER['SERVER_NAME'].substr($_SERVER['PHP_SELF'], 0, -9)."$unit/"."\n"; + echo ''.date('c', filemtime($unit))."\n"; + echo 'hourly'; + } + } + echo ''; + die; +} + +/** + * Update ALL autblogs (except .disabled) + * This action can be very slow and consume CPU if you have a lot of autoblogs + **/ +if( isset($_GET['updateall']) && ALLOW_FULL_UPDATE) { + + $expire = time() - 84600 ; // 23h30 en secondes + $lockfile = ".updatealllock"; + if (file_exists($lockfile) && filemtime($lockfile) > $expire) { + echo "too early"; + die; + } + else { + if( file_exists($lockfile) ) + unlink($lockfile); + + if( file_put_contents($lockfile, date(DATE_RFC822)) ===FALSE) { + echo "Merci d'ajouter des droits d'écriture sur le fichier."; + die; + } + } + + $subdirs = glob(AUTOBLOGS_FOLDER . "*"); + foreach($subdirs as $unit) { + if(is_dir($unit)) { + if( !file_exists(ROOT_DIR . '/' . $unit . '/.disabled')) { + file_get_contents(serverUrl() . substr($_SERVER['PHP_SELF'], 0, -9) . $unit . '/index.php'); + } + } + } +} + +$antibot = generate_antibot(); +$form = '
+
+
+ + +
'; + +/** + * ADD BY BOOKMARK BUTTON + **/ +if(!empty($_GET['via_button']) && $_GET['number'] === '17' && ALLOW_NEW_AUTOBLOGS && ALLOW_NEW_AUTOBLOGS_BY_BUTTON ) +{ + $form = ''; + + if( empty($_GET['rssurl']) ) { + $form .= '

URL du flux RSS incorrect.
Fermer la fenêtre.

'; + } + else { + if(isset($_GET['add']) && $_GET['add'] === '1' && !empty($_GET['siteurl']) && !empty($_GET['sitename'])) { + try { + $rssurl = DetectRedirect(escape($_GET['rssurl'])); + + $siteurl = escape($_GET['siteurl']); + $sitename = escape($_GET['sitename']); + $sitetype = updateType($siteurl); // Disabled input doesn't send POST data + $sitetype = $sitetype['type']; + + $error = array_merge( $error, createAutoblog($sitetype, $sitename, $siteurl, $rssurl, $error)); + if( empty($error)) { + $form .= ''; + $form .= '

Autoblog '. $sitename .' ajouté avec succès.
'; + } + else { + $form .= '

'; + } + } + catch (Exception $e) { + $form .= $e->getMessage(); + } + $form .= 'Fermer la fenêtre.

'; + } + else { + try { + $rssurl = DetectRedirect(escape($_GET['rssurl'])); + $datafeed = file_get_contents($rssurl); + if( $datafeed !== false ) { + $siteurl = get_link_from_datafeed($datafeed); + $sitename = get_title_from_datafeed($datafeed); + $sitetype = updateType($siteurl); + $sitetype = $sitetype['type']; + + $form .= 'Merci de vérifier les informations suivantes, corrigez si nécessaire.
+
+ +
+
+
+
+
'; + } + else { + $form .= '

URL du flux RSS incorrecte.
Fermer la fenêtre.

'; + } + } + catch (Exception $e) { + $form .= $e->getMessage() .'
Fermer la fenêtre.

'; + } + } + } + $form .= ''; + echo $form; die; +} + +/** + * ADD BY SOCIAL / SHAARLI + **/ +if(!empty($_POST['socialaccount']) && !empty($_POST['socialinstance']) && ALLOW_NEW_AUTOBLOGS && ALLOW_NEW_AUTOBLOGS_BY_SOCIAL) +{ + if( !empty($_POST['number']) && !empty($_POST['antibot']) && check_antibot($_POST['number'], $_POST['antibot']) ) { + + $socialaccount = strtolower(escape($_POST['socialaccount'])); + $socialinstance = strtolower(escape($_POST['socialinstance'])); + + if($socialinstance === 'twitter') { + if( API_TWITTER !== FALSE ) { + $sitetype = 'twitter'; + $siteurl = "http://twitter.com/$socialaccount"; + $rssurl = API_TWITTER.$socialaccount; + } + else + $error[] = "Twitter veut mettre à mort son API ouverte. Du coup on peut plus faire ça comme ça."; + } + elseif($socialinstance === 'identica') { + $sitetype = 'identica'; + $siteurl = "http://identi.ca/$socialaccount"; + $rssurl = "http://identi.ca/api/statuses/user_timeline/$socialaccount.rss"; + } + elseif($socialinstance === 'statusnet' && !empty($_POST['statusneturl'])) { + $sitetype = 'microblog'; + $siteurl= NoProtocolSiteURL(escape($_POST['statusneturl'])); + try { + $rssurl = DetectRedirect("http://".$siteurl."/api/statuses/user_timeline/$socialaccount.rss"); + $siteurl = DetectRedirect("http://".$siteurl."/$socialaccount"); + } + catch (Exception $e) { + echo $error[] = $e->getMessage(); + } + } + elseif($socialinstance === 'shaarli' && !empty($_POST['shaarliurl'])) { + $sitetype = 'shaarli'; + $siteurl = NoProtocolSiteURL(escape($_POST['shaarliurl'])); + try { + $siteurl = DetectRedirect("http://".$siteurl."/"); + } + catch (Exception $e) { + echo $error[] = $e->getMessage(); + } + $rssurl = $siteurl."?do=rss"; + $socialaccount = get_title_from_feed($rssurl); + } + + if( empty($error) ) { + // Twitterbridge do NOT allow this user yet => No check + if( $sitetype != 'twitter' ) { + $headers = get_headers($rssurl, 1); + if (strpos($headers[0], '200') == FALSE) { + $error[] = "Flux inaccessible (compte inexistant ?)"; + } + } + if( empty($error) ) { + $error = array_merge( $error, createAutoblog($sitetype, ucfirst($socialinstance) .' - '. $socialaccount, $siteurl, $rssurl, $error)); + if( empty($error)) + $success[] = ''.ucfirst($socialinstance) .' - '. $socialaccount.' ajouté avec succès.'; + } + } + } + else + $error[] = 'Antibot : Chiffres incorrects.'; +} + +/** + * ADD BY GENERIC LINK + **/ +if( !empty($_POST['generic']) && ALLOW_NEW_AUTOBLOGS && ALLOW_NEW_AUTOBLOGS_BY_LINKS) { + if(empty($_POST['rssurl'])) + {$error[] = "Veuillez entrer l'adresse du flux.";} + if(empty($_POST['number']) || empty($_POST['antibot']) ) + {$error[] = "Vous êtes un bot ?";} + elseif(! check_antibot($_POST['number'], $_POST['antibot'])) + {$error[] = "Antibot : Ce n'est pas le bon nombre.";} + + if(empty($error)) { + try { + $rssurl = DetectRedirect(escape($_POST['rssurl'])); + + if(!empty($_POST['siteurl'])) { + + $siteurl = escape($_POST['siteurl']); + $sitename = get_title_from_feed($rssurl); + + $error = array_merge( $error, createAutoblog('generic', $sitename, $siteurl, $rssurl, $error)); + + if( empty($error)) + $success[] = 'Autoblog '. $sitename .' crée avec succès.afficher l\'autoblog'; + } + else { + // checking procedure + + $datafeed = file_get_contents($rssurl); + if( $datafeed === false ) { + $error[] = 'URL "'. $rssurl .'" inaccessible.'; + } + $sitetype = 'generic'; + $siteurl = get_link_from_datafeed($datafeed); + $sitename = get_title_from_datafeed($datafeed); + + $form = 'Merci de vérifier les informations suivantes, corrigez si nécessaire.
+
+
+
+
+
+
+
'; + + } + } + catch (Exception $e) { + echo $error[] = $e->getMessage(); + } + } +} + +/** + * ADD BY OPML File + **/ +if( !empty($_POST['opml_file']) && ALLOW_NEW_AUTOBLOGS && ALLOW_NEW_AUTOBLOGS_BY_OPML_FILE) { + if(empty($_POST['number']) || empty($_POST['antibot']) ) + {$error[] = "Vous êtes un bot ?";} + elseif(! check_antibot($_POST['number'], $_POST['antibot'])) + {$error[] = "Antibot : Ce n'est pas le bon nombre.";} + + if( empty( $error)) { + if (is_uploaded_file($_FILES['file']['tmp_name'])) { + $opml = null; + if( ($opml = simplexml_load_file( $_FILES['file']['tmp_name'])) !== false ) { + create_from_opml($opml); + } + else + $error[] = "Impossible de lire le contenu du fichier OPML."; + unlink($_FILES['file']['tmp_name']); + } else { + $error[] = "Le fichier n'a pas été envoyé."; + } + } +} + +/** + * ADD BY OPML Link + **/ + if( !empty($_POST['opml_link']) && ALLOW_NEW_AUTOBLOGS && ALLOW_NEW_AUTOBLOGS_BY_OPML_LINK) { + if(empty($_POST['number']) || empty($_POST['antibot']) ) + {$error[] = "Vous êtes un bot ?";} + elseif(! check_antibot($_POST['number'], $_POST['antibot'])) + {$error[] = "Antibot : Ce n'est pas le bon nombre.";} + if( empty( $_POST['opml_url'] )) + {$error[] = 'Le lien est incorrect.';} + + if( empty( $error)) { + $opml_url = escape($_POST['opml_url']); + if(parse_url($opml_url, PHP_URL_HOST)==FALSE) { + $error[] = "URL du fichier OPML non valide."; + } else { + if ( ($opml = simplexml_load_file( $opml_url )) !== false ) { + create_from_opml($opml); + } else { + $error[] = "Impossible de lire le contenu du fichier OPML ou d'accéder à l'URL donnée."; + } + } + + } +} + +?> + + + + + Projet Autoblog<?php if(strlen(HEAD_TITLE)>0) echo " | " . HEAD_TITLE; ?> + + + '; + } + ?> + + +

+ PROJET AUTOBLOG + 0) echo " | " . HEAD_TITLE; ?> +

+ +
+ '; + ?> +

Présentation

+ +

+ Le Projet Autoblog a pour objectif de répliquer les articles d'un blog ou d'un site site web.
+ Si l'article source est supprimé, et même si le site d'origine disparaît, les articles restent lisibles sur l'autoblog.
+ L'objectif premier de ce projet est de lutter contre la censure et toute sorte de pression... +

+ +

+ Voici une liste d'autoblogs hébergés sur + (plus d'infos sur le projet). +

+
+ + +
+

Mise à jour

+

+ Une mise à jour du Projet Autoblog est disponible !
+ → Télécharger la dernière version
+ → Important : Consulter la documentation - mise à jour +

+
+ + + +
+ +

Ajouter un autoblog

+ + Message'. (count($error) ? 's' : '') .' :

    '; + foreach ( $error AS $value ) { + echo '
  • '. $value .'
  • '; + } + foreach ( $success AS $value ) { + echo '
  • '. $value .'
  • '; + } + echo '
'; + } + + $button_list = '

Ajouter un autoblog via : '; + if(ALLOW_NEW_AUTOBLOGS_BY_LINKS) + $button_list .= 'Flux RSS '; + if(ALLOW_NEW_AUTOBLOGS_BY_SOCIAL) { + $button_list .= 'Compte réseau social '; + $button_list .= 'Shaarli '; + } + if(ALLOW_NEW_AUTOBLOGS_BY_OPML_FILE) + $button_list .= 'Fichier OPML '; + if(ALLOW_NEW_AUTOBLOGS_BY_OPML_LINK) + $button_list .= 'Lien vers OPML '; + if(ALLOW_NEW_AUTOBLOGS_BY_BUTTON) + $button_list .= 'Marque page '; + $button_list .= '

'; + echo $button_list; + + if(ALLOW_NEW_AUTOBLOGS_BY_LINKS == TRUE) { ?> +
+

Ajouter un site web

+

+ Si vous souhaitez que héberge un autoblog d'un site,
+ remplissez le formulaire suivant: +

+ + +
+ +
+

Ajouter un compte social

+ +
+
+ Twitter
'; + else echo 'Twitter
'; ?> + Identica
+ +
+
+ + +
+
+ +
+

Ajouter un Shaarli

+ +
+ +
+
+ + +
+
+ +
+

Ajouter par fichier OPML

+ +
+ +
+
+ + +
+
+ + + + + + + +
+ + + '. substr($unit, (strrpos($unit, '/')) + 1 ) .''; + } + } + } + if(!empty( $docs )) { + echo '

Autres documents

    '; + foreach( $docs as $value ) + echo '
  • '. $value .'
  • '; + echo '
'; + } + ?> + +
+

Autoblogs hébergés rss

+

+ Autres fermes + → Rechercher +

+ + +
+ $value) + { + $key = strtolower($key); + $config->$key = $value; + } + $autoblogs[$unit] = $config; + unset($ini); + } + } + } + } + } + + uasort($autoblogs, "objectCmp"); + $autoblogs_display = ''; + + if(!empty($autoblogs)){ + foreach ($autoblogs as $key => $autoblog) { + $opml_link='opml'; + $autoblogs_display .= '
+ +
config ini '.$opml_link.' | '.escape($autoblog->site_type).' source: '.escape($autoblog->site_url).'
+
'; + } + } + echo $autoblogs_display; + ?> +
+
+ + ".count($autoblogs)." autoblogs hébergés

"; ?> +
+ Propulsé par Projet Autoblog 0.3 de Mitsu, Oros et Arthur Hoaro (Domaine Public) + 0 ){ echo "
".FOOTER; } ?> + + + + + + + diff --git a/resources/autoblog.css b/resources/autoblog.css new file mode 100644 index 0000000..259e2e8 --- /dev/null +++ b/resources/autoblog.css @@ -0,0 +1,48 @@ +/** + * autoblog.css + * ------------ + * Please do NOT edit this file. Updating your Autoblogs farm will be easier. + * If you want to add your own CSS, use the file user.css + * + */ + +body {background-color:#efefef;text-align:center;color:#333;font-family:sans-serif;} +a {color:black;text-decoration:none;font-weight:bold;} +a:hover {color:darkred;} +h1 {text-align:center;font-size:40pt;text-shadow: #ccc 0px 5px 5px;} +h2 {text-align:center;font-size: 16pt;margin:0 0 1em 0;font-style:italic;text-shadow: #ccc 0px 5px 5px; } +.pbloc {background-color:white;padding: 12px 10px 12px 10px;border:1px solid #aaa;max-width:70em;margin:1em auto;text-align:justify;box-shadow:0px 5px 7px #aaa;} +input[type="text"]{width:20em;} +input[type="radio"] {width:1em;} +input[type="submit"] {width:8em;} +div.form {padding:0.2em;border:1px solid #fff;} +div.form:hover {background-color:#FAF4DA;border:1px dotted;} +#contentVignette {text-align: center;} +.vignette {width:27%;height:2em;display: inline-block;text-align:justify;margin:0; padding:20px;background-color:#eee;border: 1px solid #888;} +.vignette:hover {background-color:#fff;} +.vignette .title {font-size: 14pt;text-shadow: #ccc 0px 5px 5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;} +.vignette .title a:hover {color:darkred; text-decoration:none;} +.vignette .source {font-size:x-small;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;} +.vignette .source a:hover {color:darkred; text-decoration:none;} +.clear {clear:both;text-align:right;font-size:small;} +#logo {float: right;} +.bouton{background: -moz-linear-gradient(center top , #EDEDED 5%, #DFDFDF 100%) repeat scroll 0 0 #EDEDED;border: 1px none;padding: 10px;border: 1px solid #7777777;border-radius: 8px 8px 8px 8px;box-shadow: 0 1px 0 0 #FFFFFF inset;display: inline-block;} +.success {color: green;} +.error {color: red;} +.button_list{display:none;} +.button{-moz-box-shadow:inset 0 1px 0 0 #d9fbbe;-webkit-box-shadow:inset 0 1px 0 0 #d9fbbe;box-shadow:inset 0 1px 0 0 #d9fbbe;background:0;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#b8e356',endColorstr='#a5cc52');background-color:#b8e356;-moz-border-radius:6px;-webkit-border-radius:6px;border-radius:6px;border:1px solid #83c41a;display:inline-block;color:#fff;font-family:arial;font-size:14px;font-weight:700;text-decoration:none;text-shadow:1px 1px 0 #86ae47;padding:6px 24px;} +.button:hover{background:0;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#a5cc52',endColorstr='#b8e356');background-color:#a5cc52;} +.button:active{position:relative;top:1px;} +.buttonactive{background-color:#aaa;-moz-border-radius:6px;-webkit-border-radius:6px;border-radius:6px;border:1px solid #83c41a;display:inline-block;color:#fff;font-family:arial;font-size:14px;font-weight:700;text-decoration:none;text-shadow:1px 1px 0 #86ae47;padding:6px 24px;} +@media screen and (max-width:1024px) { + .vignette { width: 40%; } +} +@media screen and (max-width:640px) { + h1 { font-size:20pt; } + .button, .button:hover, .button:active, .buttonactive { display: block; margin: auto; text-align:center; } + .vignette { width: 80%; } +} +@media screen and (max-width:480px) { + #logo { max-width: 250px; } + input[type="text"]{width:15em;} +} \ No newline at end of file diff --git a/resources/icon-logo.svg b/resources/icon-logo.svg new file mode 100644 index 0000000..e3045dd --- /dev/null +++ b/resources/icon-logo.svg @@ -0,0 +1,218 @@ + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/rss.png b/resources/rss.png new file mode 100644 index 0000000..315c4f4 Binary files /dev/null and b/resources/rss.png differ diff --git a/resources/user.css.example b/resources/user.css.example new file mode 100644 index 0000000..ca67340 --- /dev/null +++ b/resources/user.css.example @@ -0,0 +1,10 @@ +/** + * user.css + * ------------ + * Feel free to add your custom CSS and override original CSS in this file. + * Don't forget to rename user.css.example to user.css to make it works. + */ + +body { + background-color: red; +} \ No newline at end of file diff --git a/version b/version new file mode 100644 index 0000000..1d807ef --- /dev/null +++ b/version @@ -0,0 +1 @@ +0.3.0-DEV Build 0 \ No newline at end of file diff --git a/xsaf3.php b/xsaf3.php new file mode 100755 index 0000000..47704b3 --- /dev/null +++ b/xsaf3.php @@ -0,0 +1,169 @@ + $expire) { + echo "too early"; + die; +} +else { + if( file_exists($lockfile) ) + unlink($lockfile); + + if( file_put_contents($lockfile, date(DATE_RFC822)) ===FALSE) { + echo "Merci d'ajouter des droits d'écriture sur le dossier."; + die; + } +} + +define('ROOT_DIR', __DIR__); +if(file_exists("functions.php")){ + include "functions.php"; +}else{ + echo "functions.php not found !"; + die; +} + +if(file_exists("config.php")){ + include "config.php"; +}else{ + echo "config.php not found !"; + die; +} + +function serverUrl() { + $https = (!empty($_SERVER['HTTPS']) && (strtolower($_SERVER['HTTPS'])=='on')) || $_SERVER["SERVER_PORT"]=='443'; // HTTPS detection. + $serverport = ($_SERVER["SERVER_PORT"]=='80' || ($https && $_SERVER["SERVER_PORT"]=='443') ? '' : ':'.$_SERVER["SERVER_PORT"]); + return 'http'.($https?'s':'').'://'.$_SERVER["SERVER_NAME"].$serverport; +} + +libxml_use_internal_errors(true); + +// $max_exec_time = temps max d'exécution en seconde +function xsafimport($xsafremote, $max_exec_time) { + if( DEBUG ) + echo "\n*Traitement $xsafremote en maximum $max_exec_time secondes"; + + $max_exec_time+=time()-1; // -1 car l'import prend environ 1 seconde + + /* détection de ferme autoblog */ + $json_import = file_get_contents($xsafremote); + if(!empty($json_import)) { + $to_update=array(); + $json_import = json_decode($json_import, true); + + if(!isset($json_import['meta']) || !isset($json_import['meta']['xsaf-version']) || $json_import['meta']['xsaf-version'] != XSAF_VERSION){ + if(DEBUG){ + echo "\nxsaf-version différentes !"; + } + return false; + } + + $get_remote_db = ($json_import['meta']['xsaf-db_transfer'] == "true") ? true : false; + $get_remote_media = ($json_import['meta']['xsaf-media_transfer'] == "true") ? true : false; + + if(!empty($json_import['autoblogs'])) { + foreach ($json_import['autoblogs'] as $value) { + + if(count($value)==4 && !empty($value['SITE_TYPE']) && !empty($value['SITE_TITLE']) && !empty($value['SITE_URL']) && !empty($value['FEED_URL'])) { + $sitetype = escape($value['SITE_TYPE']); + $sitename = escape($value['SITE_TITLE']); + $siteurl = escape($value['SITE_URL']); + // Do not use DetectRedirect because it's slow and it has been used when the feed was added + //$rssurl = DetectRedirect(escape($value['FEED_URL'])); + $rssurl = escape($value['FEED_URL']); + } + + + /* TOO SLOW + $xml = simplexml_load_file($rssurl); // quick feed check + // ATOM feed && RSS 1.0 /RDF && RSS 2.0 + $result = (!isset($xml->entry) && !isset($xml->item) && !isset($xml->channel->item)) ? false : true; */ + $result = true; + + /* autoblog */ + if( $result === true ) { + $foldername = urlToFolderSlash($siteurl); + + $errors = createAutoblog($sitetype, $sitename, $siteurl, $rssurl); + foreach( $errors AS $value) { + if( DEBUG ) + echo '

'. $value .'

'; + } + if( empty($errors) && DEBUG ) { + echo '

autoblog '. $sitename .' crée avec succès (DL DB : '. var_dump($get_remote_db) .' - DL media : '. var_dump($get_remote_media) .') : '. $foldername .'

'; + if( !ALLOW_REMOTE_DB_DL && !ALLOW_REMOTE_MEDIA_DL ) + echo ''; + } + + /* ============================================================================================================================================================================== */ + /* récupération de la DB distante */ + if($get_remote_db == true && ALLOW_REMOTE_DB_DL ) { + $remote_db = str_replace("?export", $foldername."/articles.db", $xsafremote); + copy($remote_db, './'. $foldername .'/articles.db'); + } + + if($get_remote_media == true && ALLOW_REMOTE_MEDIA_DL ) { + $remote_media=str_replace("?export", $foldername."/?media", $xsafremote); + $json_media_import = file_get_contents($remote_media); + if(!empty($json_media_import)) + { + mkdir('./'.$foldername.'/media/'); + $json_media_import = json_decode($json_media_import, true); + $media_path=$json_media_import['url']; + if(!empty($json_media_import['files'])) { + foreach ($json_media_import['files'] as $value) { + copy($media_path.$value, './'.$foldername.'/media/'.$value); + } + } + } + } + + /* ============================================================================================================================================================================== */ + //TODO : tester si articles.db est une DB valide + //$to_update[] = serverUrl().preg_replace("/(.*)\/(.*)$/i","$1/".$foldername , $_SERVER['SCRIPT_NAME']); // url of the new autoblog + } + + if( DEBUG ) + echo '

time : '.($max_exec_time - time()) .'

'; + if(time() >= $max_exec_time) { + if( DEBUG ) + echo "

Time out !

"; + break; + } + } + } + else { + if( DEBUG ) + echo "Format JSON incorrect."; + return false; + } + } + return; +} + +if( DEBUG ) echo ''; +if( ALLOW_NEW_AUTOBLOGS and ALLOW_NEW_AUTOBLOGS_BY_XSAF && !empty($friends_autoblog_farm) ) { + foreach( $friends_autoblog_farm AS $value ) { + if( !empty($value) ) + xsafimport($value, EXEC_TIME); + } + if(DEBUG) echo "

XSAF import finished

"; +} +elseif( DEBUG ) + echo "

XSAF désactivé. Positionnez les variables ALLOW_NEW_AUTOBLOGS et ALLOW_NEW_AUTOBLOGS_BY_XSAF à TRUE dans le fichier config.php pour l'activer.

"; + +if( DEBUG ) echo ''; +?>