diff options
Diffstat (limited to 'public/admin')
-rwxr-xr-x | public/admin/administration.php | 59 | ||||
-rw-r--r-- | public/admin/changepassword.php | 12 | ||||
-rwxr-xr-x | public/admin/default.css | 232 | ||||
-rw-r--r-- | public/admin/img/button-add.png | bin | 0 -> 244 bytes | |||
-rw-r--r-- | public/admin/img/button-delete.png | bin | 0 -> 201 bytes | |||
-rw-r--r-- | public/admin/img/button-h.png | bin | 0 -> 212 bytes | |||
-rw-r--r-- | public/admin/img/button.png | bin | 0 -> 212 bytes | |||
-rw-r--r-- | public/admin/img/h3.png | bin | 0 -> 212 bytes | |||
-rw-r--r-- | public/admin/img/moonmoon.png | bin | 0 -> 564 bytes | |||
-rw-r--r-- | public/admin/img/nav-admin.png | bin | 0 -> 1519 bytes | |||
-rw-r--r-- | public/admin/img/nav-feed.png | bin | 0 -> 1090 bytes | |||
-rw-r--r-- | public/admin/inc/auth.inc.php | 9 | ||||
-rwxr-xr-x | public/admin/index.php | 149 | ||||
-rwxr-xr-x | public/admin/login.php | 51 | ||||
-rw-r--r-- | public/admin/logout.php | 10 | ||||
-rw-r--r-- | public/admin/purgecache.php | 24 | ||||
-rwxr-xr-x | public/admin/subscriptions.php | 84 | ||||
-rwxr-xr-x | public/admin/template.php | 54 |
18 files changed, 684 insertions, 0 deletions
diff --git a/public/admin/administration.php b/public/admin/administration.php new file mode 100755 index 0000000..09b5f85 --- /dev/null +++ b/public/admin/administration.php @@ -0,0 +1,59 @@ +<?php + +require_once __DIR__ . '/../../app/app.php'; +require_once __DIR__ . '/inc/auth.inc.php'; + + +$opml = OpmlManager::load($PlanetConfig->getOpmlFile()); +$opml_people = $opml->getPeople(); +$page_id = 'admin-admin'; +$header_extra = <<<"HTML" + <script> + window.onload = function(){ + var formManage = document.getElementById('frmPurge'); + formManage.onsubmit = function(){ + return confirm("{$l10n->getString('Are you sure you want to purge the cache?')}"); + } + } + </script> + +HTML; + +$repo_url = 'https://github.com/moonmoon/moonmoon'; +$releases_url = "$repo_url/releases"; +$link_url = "<a href='$releases_url'>$releases_url</a>"; +$version_current = str_replace('%s', $moon_version, $l10n->getString('Your moonmoon instance version is %s.')); +$version_action = str_replace('%s', $link_url, $l10n->getString('You can check for a more recent version on: %s.')); + +$page_content = <<<"FRAGMENT" + + <div class="widget"> + <h3>{$l10n->getString('Clear cache')}</h3> + <form action="purgecache.php" method="post" id="frmPurge"> + <input type="hidden" value="{$csrf->generate('frmPurge')}" name="_csrf"> + <p><label>{$l10n->getString('Clear cache:')}</label><input type="submit" class="submit delete" name="purge" id="purge" value="{$l10n->getString('Clear')}" /></p> + <p class="help">{$l10n->getString('Clearing the cache will make moonmoon reload all feeds.')}</p> + </form> + </div> + + <div class="widget"> + <h3>{$l10n->getString('Change administrator password')}</h3> + <form action="changepassword.php" method="post" id="frmPassword"> + <input type="hidden" value="{$csrf->generate('frmPassword')}" name="_csrf"> + <p><label for="password">{$l10n->getString('New password:')}</label> <input type="password" class="text" value="" name="password" id="password" size="20" /> <input type="submit" class="submit delete" name="changepwd" id="changepwd" value="{$l10n->getString('Change password')}" /></p> + </form> + </div> + + <div class="widget"> + <h3>{$l10n->getString('Upgrade moonmoon')}</h3> + <form> + <p>{$version_current}</p> + <p>{$version_action}</p> + </form> + </div> + +FRAGMENT; + +$footer_extra = ''; +$admin_access = 1; +require_once __DIR__ . '/template.php'; diff --git a/public/admin/changepassword.php b/public/admin/changepassword.php new file mode 100644 index 0000000..c1e61ff --- /dev/null +++ b/public/admin/changepassword.php @@ -0,0 +1,12 @@ +<?php + +require_once __DIR__.'/../app/app.php'; +require_once __DIR__.'/inc/auth.inc.php'; + +if ($csrf->verify($_POST['_csrf'], 'frmPassword') && isset($_POST['password']) && ('' != $_POST['password'])) { + $out = sprintf('<?php $login="admin"; $password="%s"; ?>', hash('sha256', $_POST['password'])); + file_put_contents(__DIR__.'/inc/pwd.inc.php', $out); + die("Password changed. <a href='administration.php'>Login</a>"); +} else { + die('Can not change password'); +} diff --git a/public/admin/default.css b/public/admin/default.css new file mode 100755 index 0000000..fcba3f4 --- /dev/null +++ b/public/admin/default.css @@ -0,0 +1,232 @@ +html, body{ + margin: 0; + padding: 0; +} +html{ + font-size: 62.5%; +} +body{ + background: #FFF; + color: #333; + font: 1.1em/1.6em "Lucida Grande", Tahoma, Arial, sans-serif; +} + + +fieldset{ + border: 0; + padding: 0; + margin: 1em 0; +} +input.text{ + border: 1px solid #BBB; + padding: 4px; +} +input.submit{ + font-size: 1em; + padding: 4px; + background: #D1D1D1 url(img/button.png) repeat-x left center; + border: 1px solid #FFF; + border-bottom: 1px solid #CECECE; + border-right: 1px solid #CECECE; + cursor: pointer; +} +input.submit:hover{ + background: #D1D1D1 url(img/button-h.png) repeat-x left center; + color: #000; + border: 1px solid #FFF; + border-bottom: 1px solid #C6C6C6; + border-right: 1px solid #C6C6C6; +} +input.delete{ + margin-left: 1em; +} +input.delete:hover{ + background: #ffabab url(img/button-delete.png) repeat-x left center; + color: #C00; + border: 1px solid #FCC; + border-bottom: 1px solid #F99; + border-right: 1px solid #F99; +} +input.add{ + font-weight: bold; +} +input.add:hover{ + background: #65a5e1 url(img/button-add.png) repeat-x left center; + color: #20466a; + border: 1px solid #a6c7e5; + border-bottom: 1px solid #4f90cd; + border-right: 1px solid #4f90cd; +} +p.help{ + font-size: 0.8em; + color: #999; + margin: 0; +} + +#page{ +} + + header{ + background: #E0E0E0 url(img/moonmoon.png) no-repeat right center; + border-top: 5px solid #D0D0D0; + margin: 0; + padding: 10px; + } + header h1{ + display: none; + margin: 0 0 0 20px; + padding: 0; + height: 14px; + width: 95px; + background: transparent url(img/moonmoon.png) no-repeat left center; + + overflow: hidden; + text-indent: -1000em; + } + header p{ + margin: 0 0 0 20px; + padding: 0; + } + header p a{ + color: #AAA; + } + header p a:hover{ + color: #111; + } + + + nav ul{ + margin: 20px 30px; + padding: 0; + list-style: none; + } + nav ul:after {content: "."; display: block; height: 0; clear: both; visibility: hidden; } + nav ul {display: inline-table;} + /* Hides from IE-mac \*/ + * html nav ul {height: 1%;} + nav ul {display: block;} + /* End hide from IE-mac */ + + nav ul li{ + display: block; + float: left; + } + nav ul li a{ + display: block; + border: 1px solid #DDD; + padding: 8px 8px 8px 36px; + margin-right: 8px; + text-decoration: none; + color: #666; + background-repeat: no-repeat; + background-position: 6px center; + } + nav ul li a:hover, + body#admin-feed #nav-feed a, + body#admin-admin #nav-admin a { + background-color: #f0f2f6; + border-color: #AAA; + color: #111; + } + #nav-feed a{ + background-image: url(img/nav-feed.png); + } + #nav-admin a{ + background-image: url(img/nav-admin.png); + } + + #content{ + margin: 20px 30px; + } + + .widget{ + margin: 20px 0; + } + .widget h3{ + background: #3e4350 url(img/h3.png) repeat-x left center; + color: #FFF; + text-transform: uppercase; + margin: 0; + padding: 10px; + } + .widget form{ + padding: 10px; + border: 1px solid #EEE; + border-top: 0; + } + + #feedmanage p.select{ + margin: 3px 0; + } + #feedmanage p.action span.count{ + float: right; + padding-right: 10px; + font-size: 1.3em; + } + #feedmanage table{ + width: 99%; + border-collapse: collapse; + border: 1px solid #f0f2f6; + } + #feedmanage thead th{ + background: #f0f2f6; + text-align: left; + padding: 6px; + } + #feedmanage thead span{ + display: none; + } + #feedmanage tr{ + border-bottom: 1px solid #e8ebf1; + } + #feedmanage tr.even td{ + background: #f0f2f6; + width: auto; + } + #feedmanage .text{ + background: transparent; + border: 1px solid #FFF; + width: 90%; + } + #feedmanage tr.even td .text{ + border: 1px solid #f0f2f6; + } + #feedmanage tr td input.text:focus{ + background: #FFF; + border: 1px solid #4392b7; + } + + #feedmanage tr.selected td{ + background: #ffabab; + background: #4392b7; + color: #FFF; + } + #feedmanage tr.selected td .text{ + color: #FFF; + border: 1px solid #4392b7; + } + #feedmanage tr td input.text:focus{ + background: #FFF; + color: #000; + border: 1px solid #4392b7; + } + +/* Login form +*******************************************************************************/ +form.login { + width: 400px; + margin: 0 auto; +} +form.login fieldset { + border: 1px solid #CCC; + text-align: center; +} + +p.logout { + float: right; + margin: 20px 30px 0 0; +} + p.logout a { + padding: 8px; + display: block; + } diff --git a/public/admin/img/button-add.png b/public/admin/img/button-add.png Binary files differnew file mode 100644 index 0000000..859364a --- /dev/null +++ b/public/admin/img/button-add.png diff --git a/public/admin/img/button-delete.png b/public/admin/img/button-delete.png Binary files differnew file mode 100644 index 0000000..7bfebc2 --- /dev/null +++ b/public/admin/img/button-delete.png diff --git a/public/admin/img/button-h.png b/public/admin/img/button-h.png Binary files differnew file mode 100644 index 0000000..0a43a6a --- /dev/null +++ b/public/admin/img/button-h.png diff --git a/public/admin/img/button.png b/public/admin/img/button.png Binary files differnew file mode 100644 index 0000000..fad6ccc --- /dev/null +++ b/public/admin/img/button.png diff --git a/public/admin/img/h3.png b/public/admin/img/h3.png Binary files differnew file mode 100644 index 0000000..0c86748 --- /dev/null +++ b/public/admin/img/h3.png diff --git a/public/admin/img/moonmoon.png b/public/admin/img/moonmoon.png Binary files differnew file mode 100644 index 0000000..26cc8f4 --- /dev/null +++ b/public/admin/img/moonmoon.png diff --git a/public/admin/img/nav-admin.png b/public/admin/img/nav-admin.png Binary files differnew file mode 100644 index 0000000..3077a54 --- /dev/null +++ b/public/admin/img/nav-admin.png diff --git a/public/admin/img/nav-feed.png b/public/admin/img/nav-feed.png Binary files differnew file mode 100644 index 0000000..00c896b --- /dev/null +++ b/public/admin/img/nav-feed.png diff --git a/public/admin/inc/auth.inc.php b/public/admin/inc/auth.inc.php new file mode 100644 index 0000000..32615e7 --- /dev/null +++ b/public/admin/inc/auth.inc.php @@ -0,0 +1,9 @@ +<?php + +include config_path('pwd.inc.php'); + +if (!Planet::authenticateUser($_COOKIE['auth'] ?? '', $password)) { + setcookie('auth', '', time() - 3600); + header('Location: login.php'); + die(); +} diff --git a/public/admin/index.php b/public/admin/index.php new file mode 100755 index 0000000..37939ea --- /dev/null +++ b/public/admin/index.php @@ -0,0 +1,149 @@ +<?php + +require_once __DIR__ . '/../../app/app.php'; +require_once __DIR__ . '/inc/auth.inc.php'; + +if (!$PlanetConfig::isInstalled()) { + die('<p>' . _g('You might want to <a href="../install.php">install moonmoon</a>.') . '</p>'); +} + +//Instantiate app +$Planet = new Planet($PlanetConfig); + +//Load +if (0 < $Planet->loadOpml($PlanetConfig->getOpmlFile())) { + $Planet->loadFeeds(); + $items = $Planet->getItems(); +} + +$everyone = $Planet->getPeople(); +$count_feeds = count($everyone); +$page_id = 'admin-feed'; +$footer_extra = <<<FRAGMENT + <script> + var allCheckboxes = function(status){ + var form = document.getElementById('feedmanage'); + var selectboxes = form.getElementsByTagName('input'); + for (var i=0; i<selectboxes.length; i++){ + if ('checkbox' == selectboxes[i].type){ + selectboxes[i].checked = status; + } + } + } + + window.onload = function(){ + //Select/unselect rows + var form = document.getElementById('feedmanage'); + var selectboxes = form.getElementsByTagName('input'); + for (var i=0; i<selectboxes.length; i++){ + if ('checkbox' == selectboxes[i].type) { + selectboxes[i].onchange = function() { + var tr = this.parentNode.parentNode; + if (this.checked) { + tr.className += ' selected'; + } else { + tr.className = tr.className.replace('selected',''); + } + } + } + } + + var btSelectall = document.getElementById('selectall'); + btSelectall.onclick = function(){ + allCheckboxes('checked'); + } + + var btSelectnone = document.getElementById('selectnone'); + btSelectnone.onclick = function(){ + allCheckboxes(''); + } + } + </script> +FRAGMENT; + +ob_start(); +?> + + <div class="widget"> + <h3><?=_g('Add Feed')?></h3> + <form action="subscriptions.php" method="post" id="feedimport"> + <fieldset> + <label for="url"><?=_g('Link:')?></label> + <input type="text" class="text" name="url" id="url" placeholder="http://" class="text" size="50" /> + <input type="submit" class="submit add" name="add" value="<?=_g('Add Feed')?>" /> + </fieldset> + <p class="help"><?=_g('Accepted formats are RSS and ATOM. If the link is not a feed, moonmoon will try to autodiscover the feed.')?></p> + <input type="hidden" value="<?php echo $csrf->generate('feedmanage'); ?>" name="_csrf"> + </form> + </div> + + <div class="widget"> + <h3><?=_g('Manage existing feeds')?></h3> + <form action="subscriptions.php" method="post" id="feedmanage"> + <p class="action"> + <span class="count"><?php echo sprintf(_g('Number of feeds: %s'), $count_feeds)?></span> + <input type="hidden" value="<?php echo $csrf->generate('feedmanage'); ?>" name="_csrf"> + <input type="submit" class="submit save" name="save" id="save" value="<?=_g('Save changes')?>" /> + <input type="submit" class="submit delete" name="delete" id="delete" value="<?=_g('Delete selected Feeds')?>" /> + </p> + <p class="select"><?=_g('Select :')?> <a href="javascript:void(0);" id="selectall"><?=_g('All')?></a>, <a href="javascript:void(0);" id="selectnone"><?=_g('None')?></a></p> + <table> + <thead> + <tr> + <th><span><?=_g('Selection')?></span></th> + <th><?=_g('Name')?></th> + <th><?=_g('Last entry')?></th> + <th><?=_g('Website link')?></th> + <th><?=_g('Feed link')?></th> + <th><?=_g('Unavailable')?></th> + </tr> + </thead> + <tbody> + <?php + $i = 0; + foreach ($everyone as $opml_person) { + $i++; + ?> + <tr class="<?=($i%2)?'odd':'even'; ?>"> + <td><input type="checkbox" class="checkbox" name="opml[<?=$i; ?>][delete]" /></td> + <td><input type="text" size="10" class="text" name="opml[<?=$i; ?>][name]" value="<?=$opml_person->getName(); ?>" /></td> + <td> + <?php + $items = $opml_person->get_items(); + if (count($items) > 0) { + echo $items[0]->get_date(); + } else { + echo _g('Not in cache'); + } + $check_is_down = $opml_person->getIsDown() === '1' ? 'checked="checked"' : ''; + ?> + </td> + <td><input type="text" size="30" class="text" name="opml[<?=$i; ?>][website]" value="<?=$opml_person->getWebsite(); ?>" /></td> + <td><input type="text" size="30" class="text" name="opml[<?=$i; ?>][feed]" value="<?=$opml_person->getFeed(); ?>" /></td> + <td><input type="checkbox" readonly="readonly" name="opml[<?=$i; ?>][isDown]" <?=$check_is_down?> value="1" /></td> + </tr> + <?php } ?> + </tbody> + </table> + </form> + </div> + + <div class="widget"> + <h3><?=_g('Upload OPML file')?></h3> + <form action="subscriptions.php" method="post" id="opmlimport" enctype="multipart/form-data"> + <p>Beware! This will totally erase and replace the list of your feeds just above.</p> + <fieldset> + <label for="url"><?=_g('File:')?></label> + <input type="file" class="text" name="opml" id="opml" class="text" size="50" /> + <input type="submit" class="submit add" name="upload" value="<?=_g('Upload OPML file and replace existing feeds')?>" /> + </fieldset> + <input type="hidden" value="<?php echo $csrf->generate('feedmanage'); ?>" name="_csrf"> + </form> + </div> + +<?php +$page_content = ob_get_contents(); +ob_end_clean(); + +$admin_access = 1; +require_once __DIR__ . '/template.php'; diff --git a/public/admin/login.php b/public/admin/login.php new file mode 100755 index 0000000..418bbe6 --- /dev/null +++ b/public/admin/login.php @@ -0,0 +1,51 @@ +<?php + +require_once __DIR__ . '/../../app/app.php'; + +if (isset($_POST['password'])) { + session_regenerate_id(); + + $hash_pwd = hash('sha256', $_POST['password']); + + // check if old moonmoon was installed and convert stored password + // from md5 to current hash function + $md5_pwd = md5($_POST['password']); + $passfile = config_path('pwd.inc.php'); + include($passfile); + + if ($md5_pwd == $password) { + error_log("Migrating password from md5 to sha256"); + file_put_contents($passfile, sprintf('<?php $login="admin"; $password="%s"; ?>', $hash_pwd)); + } + + setcookie('auth', $hash_pwd); + header('Location: index.php'); +} + +$page_content = <<<FRAGMENT + <form action="" method="post" class="login"> + <fieldset> + <p class="field"> + <label for="password">{$l10n->getString('Password:')}</label> + <input type="password" name="password" id="password"/> + <input type="submit" class="submit" value="{$l10n->getString('OK')}"/> + </p> + </fieldset> + </form> +FRAGMENT; + +$footer_extra = <<<FRAGMENT + <script type="text/javascript"> + <!-- + window.onload = function() { + document.getElementById('password').focus(); + } + --> + </script> + +FRAGMENT; + +$page_id = 'admin-login'; +$admin_access = 0; + +require_once __DIR__ . '/template.php'; diff --git a/public/admin/logout.php b/public/admin/logout.php new file mode 100644 index 0000000..25a3e4c --- /dev/null +++ b/public/admin/logout.php @@ -0,0 +1,10 @@ +<?php + +require_once __DIR__ . '/../../app/app.php'; + +setcookie('auth', '', time() - 3600); +session_destroy(); +session_regenerate_id(); + +header('Location: login.php'); +die(); diff --git a/public/admin/purgecache.php b/public/admin/purgecache.php new file mode 100644 index 0000000..d47f9a7 --- /dev/null +++ b/public/admin/purgecache.php @@ -0,0 +1,24 @@ +<?php + +require_once __DIR__.'/../../app/app.php'; +require_once __DIR__.'/inc/auth.inc.php'; + +if (isset($_POST['purge'])) { + $dir = __DIR__.'/../../cache/'; + + $dh = opendir($dir); + + while ($filename = readdir($dh)) { + if ($filename == '.' or $filename == '..') { + continue; + } + + $file = $dir . DIRECTORY_SEPARATOR . $filename; + if (is_file($file) && filemtime($file) < time()) { + unlink($file); + } + } +} + +header('Location: administration.php'); +die(); diff --git a/public/admin/subscriptions.php b/public/admin/subscriptions.php new file mode 100755 index 0000000..418e354 --- /dev/null +++ b/public/admin/subscriptions.php @@ -0,0 +1,84 @@ +<?php + +require_once __DIR__ . '/../../app/app.php'; +require_once __DIR__ . '/inc/auth.inc.php'; + +function removeSlashes(&$item, $key) +{ + $item = stripslashes($item); +} + +if (!$csrf->verify($_POST['_csrf'], 'feedmanage')) { + die('Invalid CSRF token!'); +} + +$opmlFile = $PlanetConfig->getOpmlFile(); + +if (isset($_POST['upload']) && + isset($_FILES['opml']) && + is_uploaded_file($_FILES['opml']['tmp_name']) && + $_FILES['opml']['size'] > 0) { + OpmlManager::backup($opmlFile); + $newOpml = new Opml(); + $newOpml->parse(file_get_contents($_FILES['opml']['tmp_name'])); + OpmlManager::save($newOpml, $opmlFile); +} elseif (isset($_POST['opml']) || isset($_POST['add'])) { + // Load old OPML + $oldOpml = OpmlManager::load($opmlFile); + if ($PlanetConfig->getName() === '') { + $PlanetConfig->setName($oldOpml->getTitle()); + } + $newOpml = new Opml(); + $newOpml->title = $PlanetConfig->getName(); + + // Remove slashes if needed + if (isset($_POST['opml'])) { + array_walk_recursive($_POST['opml'], 'removeSlashes'); + } + // Delete/Save feeds + if (isset($_POST['delete']) || isset($_POST['save'])) { + foreach ($_POST['opml'] as $person) { + if (isset($_POST['delete'])) { + //delete mode, check if to be deleted + if (!isset($person['delete'])) { + $newOpml->entries[] = $person; + } + } else { + $newOpml->entries[] = $person; + } + } + } + + // Add feed + if (isset($_POST['add'])) { + if ('http://' != $_POST['url']) { + //autodiscover feed + $feed = new SimplePie(); + $feed->enable_cache(false); + $feed->set_feed_url($_POST['url']); + if (!$PlanetConfig->checkCertificates()) { + $feed->set_curl_options([ + CURLOPT_SSL_VERIFYHOST => false, + CURLOPT_SSL_VERIFYPEER => false + ]); + } + $feed->init(); + $feed->handle_content_type(); + $person['name'] = html_entity_decode($feed->get_title()); + $person['website'] = $feed->get_permalink(); + $person['feed'] = $feed->feed_url; + $person['isDown'] = '0'; + + $oldOpml->entries[] = $person; + } + $newOpml->entries = $oldOpml->entries; + } + + // Backup old OPML + OpmlManager::backup($opmlFile); + + // Save new OPML + OpmlManager::save($newOpml, $opmlFile); +} +header("Location: index.php"); +die(); diff --git a/public/admin/template.php b/public/admin/template.php new file mode 100755 index 0000000..964c639 --- /dev/null +++ b/public/admin/template.php @@ -0,0 +1,54 @@ +<?php if (!isset($admin_access)) { + return; +} ?> +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title> + +<?php + echo _g('Administration') . ' — '; + printf(_g('moonmoon %s', '%s is the version number for moonmoon, this string appears as the tab title in the admin panel'), $moon_version); +?> + </title> + <link rel="stylesheet" media="screen" type="text/css" href="default.css"> +<!--[if lte IE 9]> +<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script> +<![endif]--> + + <?=@$header_extra ?: ''; ?> + +</head> + +<body id="<?=@$page_id ?: ''; ?>"> + <div id="page"> + <header> + <h1>moonmoon</h1> + <p><a href="../"><?=_g('Back to main page')?></a></p> + </header> + + <?php if ($admin_access == 1) : ?> + <p class="logout"><a href="logout.php"><?=_g('Logout')?></a></p> + <nav> + <ul> + <li id="nav-feed"><a href="index.php"><?=_g('Feeds')?></a></li> + <li id="nav-admin"><a href="administration.php"><?=_g('Administration')?></a></li> + </ul> + </nav> + + <?php endif; ?> + + + + <div id="content"> + + <?=@$page_content ?: ''; ?> + + </div> + </div> + +<?=@$footer_extra ?: ''; ?> + +</body> +</html> |