1
0
Fork 0
mirror of https://github.com/YunoHost-Apps/shaarli_ynh.git synced 2024-09-03 20:26:10 +02:00

apply modification for multiuser from https://github.com/Roultabie/Shaarli

This commit is contained in:
Julien Malik 2013-12-17 01:07:15 +01:00
parent edadc45397
commit 1890330cc0
11 changed files with 462 additions and 69 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 541 B

View file

@ -0,0 +1,108 @@
/*
* jQuery Highlight plugin
*
* Based on highlight v3 by Johann Burkard
* http://johannburkard.de/blog/programming/javascript/highlight-javascript-text-higlighting-jquery-plugin.html
*
* Code a little bit refactored and cleaned (in my humble opinion).
* Most important changes:
* - has an option to highlight only entire words (wordsOnly - false by default),
* - has an option to be case sensitive (caseSensitive - false by default)
* - highlight element tag and class names can be specified in options
*
* Usage:
* // wrap every occurrance of text 'lorem' in content
* // with <span class='highlight'> (default options)
* $('#content').highlight('lorem');
*
* // search for and highlight more terms at once
* // so you can save some time on traversing DOM
* $('#content').highlight(['lorem', 'ipsum']);
* $('#content').highlight('lorem ipsum');
*
* // search only for entire word 'lorem'
* $('#content').highlight('lorem', { wordsOnly: true });
*
* // don't ignore case during search of term 'lorem'
* $('#content').highlight('lorem', { caseSensitive: true });
*
* // wrap every occurrance of term 'ipsum' in content
* // with <em class='important'>
* $('#content').highlight('ipsum', { element: 'em', className: 'important' });
*
* // remove default highlight
* $('#content').unhighlight();
*
* // remove custom highlight
* $('#content').unhighlight({ element: 'em', className: 'important' });
*
*
* Copyright (c) 2009 Bartek Szopka
*
* Licensed under MIT license.
*
*/
jQuery.extend({
highlight: function (node, re, nodeName, className) {
if (node.nodeType === 3) {
var match = node.data.match(re);
if (match) {
var highlight = document.createElement(nodeName || 'span');
highlight.className = className || 'highlight';
var wordNode = node.splitText(match.index);
wordNode.splitText(match[0].length);
var wordClone = wordNode.cloneNode(true);
highlight.appendChild(wordClone);
wordNode.parentNode.replaceChild(highlight, wordNode);
return 1; //skip added node in parent
}
} else if ((node.nodeType === 1 && node.childNodes) && // only element nodes that have children
!/(script|style)/i.test(node.tagName) && // ignore script and style nodes
!(node.tagName === nodeName.toUpperCase() && node.className === className)) { // skip if already highlighted
for (var i = 0; i < node.childNodes.length; i++) {
i += jQuery.highlight(node.childNodes[i], re, nodeName, className);
}
}
return 0;
}
});
jQuery.fn.unhighlight = function (options) {
var settings = { className: 'highlight', element: 'span' };
jQuery.extend(settings, options);
return this.find(settings.element + "." + settings.className).each(function () {
var parent = this.parentNode;
parent.replaceChild(this.firstChild, this);
parent.normalize();
}).end();
};
jQuery.fn.highlight = function (words, options) {
var settings = { className: 'highlight', element: 'span', caseSensitive: false, wordsOnly: false };
jQuery.extend(settings, options);
if (words.constructor === String) {
words = [words];
}
words = jQuery.grep(words, function(word, i){
return word != '';
});
words = jQuery.map(words, function(word, i) {
return word.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
});
if (words.length == 0) { return this; };
var flag = settings.caseSensitive ? "" : "i";
var pattern = "(" + words.join("|") + ")";
if (settings.wordsOnly) {
pattern = "\\b" + pattern + "\\b";
}
var re = new RegExp(pattern, flag);
return this.each(function () {
jQuery.highlight(this, re, settings.element, settings.className);
});
};

View file

@ -8,7 +8,7 @@ version: 2.8.2r1
html{color:#000;background:#FFF;}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var,optgroup{font-style:inherit;font-weight:inherit;}del,ins{text-decoration:none;}li{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym{border:0;font-variant:normal;}sup{vertical-align:baseline;}sub{vertical-align:baseline;}legend{color:#000;}input,button,textarea,select,optgroup,option{font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;}input,button,textarea,select{*font-size:100%;}
body { font-family: "Trebuchet MS",Verdana,Arial,Helvetica,sans-serif; font-size:10pt; background-color: #ffffff; }
input, textarea {
input, textarea, select {
background-color: #dedede;
background: -webkit-gradient(linear, 0 0, 0 bottom, from(#dedede), to(#ffffff));
background: -webkit-linear-gradient(#dedede, #ffffff);
@ -227,6 +227,7 @@ cursor:pointer;
}
*/
#linklist li.private { background: url('../images/private.png') no-repeat 10px center; padding-left:60px; }
#linklist li.contributor { background: url('../images/contributor.png') no-repeat 10px center; padding-left:60px; }
#linklist li { padding-left:26px; }
.private .linktitle a {color:#969696;}
.linktitle { font-size:14pt; font-weight:bold; }
@ -236,6 +237,7 @@ cursor:pointer;
.linkdate a { background-image:url('../images/calendar.png');padding:2px 0 3px 20px;background-repeat:no-repeat;text-decoration: none; color:#E28E3F; }
.linkdate a:hover { color: #F57900 }
.linkurl { font-size:8pt; color:#4BAA74; }
.linkauthor { font-size:8pt; color:#4BAA74; font-style: italic;}
.linkdescription { color:#000; margin-top:0; margin-bottom:12px; font-weight:normal; max-height:400px; overflow:auto; }
.linkdescription a { text-decoration: none; color:#3465A4; }
.linkdescription a:hover { color:#F57900; }
@ -464,3 +466,76 @@ div.dailyEntryDescription { font-size:10pt; }
}
/* Highlight search results */
.highlight { background-color: #FFFF33; }
/* Manage users */
#manageusers {
margin: 10px 10px 10px 0px;
}
#manageusers form {
width: 170px;
float: left;
margin-left: 15px;
margin-bottom: 10px;
background: linear-gradient(#555555, #222222);
border-radius: 5px 5px 5px 5px;
padding: 10px;
box-shadow: -1px 2px 5px 1px rgba(0, 0, 0, 0.7),
-1px 2px 15px rgba(255, 255, 255, 0.4) inset;
}
#manageusers .sysadmin {
background-color: #555555;
background-image: repeating-linear-gradient(135deg, transparent, transparent 20px, rgba(255,255,255,.5) 20px, rgba(255,255,255,.5) 40px);
}
#manageusers #createuserbadge {
padding-bottom: 11px;
background: linear-gradient(#777777, #444444);
}
#manageusers h4 {
font-weight: bold;
color: #ffffff;
}
#manageusers select, #manageusers input {
margin-top: 6px;
}
#manageusers select {
width: 170px;
height: 25px;
cursor: pointer;
}
#manageusers input[type=text] {
width: 160px;
cursor: pointer;
}
#manageusers input[type=submit] {
font-style: italic;
cursor: pointer;
}
#manageusers input[type=submit]:disabled {
font-style: italic;
color: grey;
cursor: default;
}
#manageusers input[type=submit]:hover {
box-shadow: 0px 0px 5px rgba(255, 255, 255, 0.3) inset;
}
#manageusers input[type=submit]:disabled {
box-shadow: 0px 0px 0px rgba(255, 255, 255, 0) inset;
}
#manageusers .updateuser {
width: 170px;
background: linear-gradient(#2b552e, #112212);
color: #ffffff;
}
#manageusers .smallsubmit {
width: 83px;
}
#manageusers .resetpassword {
background: linear-gradient(#555555, #222222);
color: #ffffff;
}
#manageusers .deleteuser {
background: linear-gradient(#552b2b, #221111);
color: #ffffff;
}

View file

@ -5,6 +5,11 @@
// Licence: http://www.opensource.org/licenses/zlib-license.php
// Requires: php 5.1.x (but autocomplete fields will only work if you have php 5.2.x)
// -----------------------------------------------------------------------------------------------
// NEVER TRUST IN PHP.INI
// Some hosts do not define a default timezone in php.ini,
// so we have to do this for avoid the strict standard error.
date_default_timezone_set('UTC');
// -----------------------------------------------------------------------------------------------
// Hardcoded parameter (These parameters can be overwritten by creating the file /config/options.php)
$GLOBALS['config']['DATADIR'] = 'data'; // Data subdirectory
$GLOBALS['config']['CONFIG_FILE'] = $GLOBALS['config']['DATADIR'].'/config.php'; // Configuration file (user login/password)
@ -15,6 +20,7 @@ $GLOBALS['config']['BAN_AFTER'] = 4; // Ban IP after this many failures.
$GLOBALS['config']['BAN_DURATION'] = 1800; // Ban duration for IP address after login failures (in seconds) (1800 sec. = 30 minutes)
$GLOBALS['config']['OPEN_SHAARLI'] = false; // If true, anyone can add/edit/delete links without having to login
$GLOBALS['config']['HIDE_TIMESTAMPS'] = false; // If true, the moment when links were saved are not shown to users that are not logged in.
$GLOBALS['config']['HIDE_QRCODE'] = false; // If true, qrcodes are not shown.
$GLOBALS['config']['ENABLE_THUMBNAILS'] = true; // Enable thumbnails in links.
$GLOBALS['config']['CACHEDIR'] = 'cache'; // Cache directory for thumbnails for SLOW services (like flickr)
$GLOBALS['config']['PAGECACHE'] = 'pagecache'; // Page cache directory.
@ -23,6 +29,13 @@ $GLOBALS['config']['PUBSUBHUB_URL'] = ''; // PubSubHubbub support. Put an empty
$GLOBALS['config']['UPDATECHECK_FILENAME'] = $GLOBALS['config']['DATADIR'].'/lastupdatecheck.txt'; // For updates check of Shaarli.
$GLOBALS['config']['UPDATECHECK_INTERVAL'] = 86400 ; // Updates check frequency for Shaarli. 86400 seconds=24 hours
// Note: You must have publisher.php in the same directory as Shaarli index.php
// // -----------------------------------------------------------------------------------------------
// Levels for multi users
$GLOBALS['level']['administrator'] = 4;
$GLOBALS['level']['moderator'] = 3;
$GLOBALS['level']['contributor'] = 2;
$GLOBALS['level']['reader'] = 1;
// -----------------------------------------------------------------------------------------------
// You should not touch below (or at your own risks !)
// Optionnal config file.
@ -103,7 +116,7 @@ if (empty($GLOBALS['privateLinkByDefault'])) $GLOBALS['privateLinkByDefault']=fa
if (!is_file($GLOBALS['config']['CONFIG_FILE'])) install();
require $GLOBALS['config']['CONFIG_FILE']; // Read login/password hash into $GLOBALS.
define('SHAARLI_OWNER', $GLOBALS['login'][0]);
autoLocale(); // Sniff browser language and set date format accordingly.
header('Content-Type: text/html; charset=utf-8'); // We use UTF-8 for proper international characters handling.
@ -292,19 +305,26 @@ function allIPs()
}
// Check that user/password is correct.
function check_auth($login,$password)
function check_auth($username,$password)
{
$hash = sha1($password.$login.$GLOBALS['salt']);
if ($login==$GLOBALS['login'] && $hash==$GLOBALS['hash'])
{ // Login/password is correct.
$_SESSION['uid'] = sha1(uniqid('',true).'_'.mt_rand()); // generate unique random number (different than phpsessionid)
$_SESSION['ip']=allIPs(); // We store IP address(es) of the client to make sure session is not hijacked.
$_SESSION['username']=$login;
$_SESSION['expires_on']=time()+INACTIVITY_TIMEOUT; // Set session expiration.
logm('Login successful');
return True;
//$currentHash = sha1($password.$username.$GLOBALS['salt']);
if (is_array($GLOBALS['login']))
{
if (in_array($username, $GLOBALS['login'])) {
if ($GLOBALS['password'][$username] === sha1($password . $username . $GLOBALS['salt'])) {
// Login/password is correct.
$_SESSION['uid'] = sha1(uniqid('',true).'_'.mt_rand()); // generate unique random number (different than phpsessionid)
$_SESSION['ip'] = allIPs(); // We store IP address(es) of the client to make sure session is not hijacked.
$_SESSION['username'] = $username;
$_SESSION['level'] = $GLOBALS['level'][$username];
$_SESSION['email'] = $GLOBALS['email'][$username];
$_SESSION['expires_on']=time()+INACTIVITY_TIMEOUT; // Set session expiration.
logm('Login successful');
return True;
}
}
}
logm('Login failed for user '.$login);
logm('Login failed for user '.$username);
return False;
}
@ -328,7 +348,7 @@ function isLoggedIn()
}
// Force logout.
function logout() { if (isset($_SESSION)) { unset($_SESSION['uid']); unset($_SESSION['ip']); unset($_SESSION['username']); unset($_SESSION['privateonly']); } }
function logout() { if (isset($_SESSION)) { unset($_SESSION['uid']); unset($_SESSION['ip']); unset($_SESSION['username']); unset($_SESSION['level']); unset($_SESSION['privateonly']); } }
// ------------------------------------------------------------------------------------------
@ -636,6 +656,7 @@ class pageBuilder
$this->tpl->assign('version',shaarli_version);
$this->tpl->assign('scripturl',indexUrl());
$this->tpl->assign('pagetitle','Shaarli');
$this->tpl->assign('shaarliOwner', SHAARLI_OWNER);
$this->tpl->assign('privateonly',!empty($_SESSION['privateonly'])); // Show only private links ?
if (!empty($GLOBALS['title'])) $this->tpl->assign('pagetitle',$GLOBALS['title']);
if (!empty($GLOBALS['pagetitle'])) $this->tpl->assign('pagetitle',$GLOBALS['pagetitle']);
@ -647,6 +668,7 @@ class pageBuilder
public function assign($what,$where)
{
if ($this->tpl===false) $this->initialize(); // Lazy initialization
$this->initializeUser();
$this->tpl->assign($what,$where);
}
@ -657,6 +679,28 @@ class pageBuilder
if ($this->tpl===false) $this->initialize(); // Lazy initialization
$this->tpl->draw($page);
}
private function initializeUser()
{
if (!empty($_SESSION['username'])) {
$this->tpl->assign('currentUser',$_SESSION['username']);
}
else {
$this->tpl->assign('currentUser','');
}
if (!empty($_SESSION['level'])) {
$this->tpl->assign('currentUserLevel',$_SESSION['level']);
}
else {
$this->tpl->assign('currentUserLevel','');
}
if (!empty($_SESSION['email'])) {
$this->tpl->assign('currentUserEmail',$_SESSION['email']);
}
else {
$this->tpl->assign('currentUserEmail','');
}
}
}
// ------------------------------------------------------------------------------------------
@ -688,6 +732,7 @@ class linkdb implements Iterator, Countable, ArrayAccess
private $keys; // List of linkdate keys (for the Iterator interface implementation)
private $position; // Position in the $this->keys array. (for the Iterator interface implementation.)
private $loggedin; // Is the used logged in ? (used to filter private links)
public static $editLink; // If user edit link (preserve private links)
// Constructor:
function __construct($isLoggedIn)
@ -732,9 +777,11 @@ class linkdb implements Iterator, Countable, ArrayAccess
if (!file_exists($GLOBALS['config']['DATASTORE'])) // Create a dummy database for example.
{
$this->links = array();
$link = array('title'=>'Shaarli - sebsauvage.net','url'=>'http://sebsauvage.net/wiki/doku.php?id=php:shaarli','description'=>'Welcome to Shaarli ! This is a bookmark. To edit or delete me, you must first login.','private'=>0,'linkdate'=>'20110914_190000','tags'=>'opensource software');
$link = array('title'=>'Shaarli - sebsauvage.net','url'=>'http://sebsauvage.net/wiki/doku.php?id=php:shaarli','description'=>'Welcome to Shaarli ! This is a bookmark. To edit or delete me, you must first login.','author'=>SHAARLI_OWNER, 'private'=>'0','linkdate'=>'20110914_190000','tags'=>'opensource software');
$this->links[$link['linkdate']] = $link;
$link = array('title'=>'My secret stuff... - Pastebin.com','url'=>'http://pastebin.com/smCEEeSn','description'=>'SShhhh!! I\'m a private link only YOU can see. You can delete me too.','private'=>1,'linkdate'=>'20110914_074522','tags'=>'secretstuff');
$link = array('title'=>'My secret stuff... - Pastebin.com','url'=>'http://pastebin.com/smCEEeSn','description'=>'SShhhh!! I\'m a private link only YOU can see. You can delete me too.','author'=>SHAARLI_OWNER, 'private'=>'2','linkdate'=>'20110914_074522','tags'=>'secretstuff');
$this->links[$link['linkdate']] = $link;
$link = array('title'=>'Contributor link','url'=>'http://sebsauvage.net','description'=>'Here is a link viewed only by contributors and logged user.','author'=>SHAARLI_OWNER, 'private'=>'1','linkdate'=>'20111007_233200','tags'=>'contributor link');
$this->links[$link['linkdate']] = $link;
file_put_contents($GLOBALS['config']['DATASTORE'], PHPPREFIX.base64_encode(gzdeflate(serialize($this->links))).PHPSUFFIX); // Write database to disk
}
@ -744,17 +791,29 @@ class linkdb implements Iterator, Countable, ArrayAccess
private function readdb()
{
// Read data
$this->links=(file_exists($GLOBALS['config']['DATASTORE']) ? unserialize(gzinflate(base64_decode(substr(file_get_contents($GLOBALS['config']['DATASTORE']),strlen(PHPPREFIX),-strlen(PHPSUFFIX))))) : array() );
$links=(file_exists($GLOBALS['config']['DATASTORE']) ? unserialize(gzinflate(base64_decode(substr(file_get_contents($GLOBALS['config']['DATASTORE']),strlen(PHPPREFIX),-strlen(PHPSUFFIX))))) : array() );
// Note that gzinflate is faster than gzuncompress. See: http://www.php.net/manual/en/function.gzdeflate.php#96439
// If user is not logged in, filter private links.
if (!$this->loggedin)
{
$toremove=array();
foreach($this->links as $link) { if ($link['private']!=0) $toremove[]=$link['linkdate']; }
foreach($toremove as $linkdate) { unset($this->links[$linkdate]); }
foreach($links as $link) { if ($link['private']!=0) $toremove[]=$link['linkdate']; }
foreach($toremove as $linkdate) { unset($links[$linkdate]); }
$this->links = $links;
}
// If user edit a link, preserve private links of others.
elseif (self::$editLink)
{
$this->links = $links;
}
// If user is logged in, filter private links.
else {
$toremove=array();
foreach($links as $link) { if ($link['private']!=0 && ($link['private']==2 && $link['author'] != $_SESSION['username'])) $toremove[]=$link['linkdate']; }
foreach($toremove as $linkdate) { unset($links[$linkdate]); }
$this->links = $links;
}
// Keep the list of the mapping URLs-->linkdate up-to-date.
$this->urls=array();
foreach($this->links as $link) { $this->urls[$link['url']]=$link['linkdate']; }
@ -764,7 +823,7 @@ class linkdb implements Iterator, Countable, ArrayAccess
public function savedb()
{
if (!$this->loggedin) die('You are not authorized to change the database.');
file_put_contents($GLOBALS['config']['DATASTORE'], PHPPREFIX.base64_encode(gzdeflate(serialize($this->links))).PHPSUFFIX);
file_put_contents($GLOBALS['config']['DATASTORE'], PHPPREFIX.base64_encode(gzdeflate(serialize($this->links))).PHPSUFFIX, LOCK_EX);
invalidateCaches();
}
@ -1166,8 +1225,8 @@ function showDaily()
// Render HTML page (according to URL parameters and user rights)
function renderPage()
{
if (isset($_POST['save_edit'])) linkdb::$editLink = TRUE;
$LINKSDB=new linkdb(isLoggedIn() || $GLOBALS['config']['OPEN_SHAARLI']); // Read links from database (and filter private links if used it not logged in).
// -------- Display login form.
if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=login'))
{
@ -1335,11 +1394,11 @@ function renderPage()
if (!tokenOk($_POST['token'])) die('Wrong token.'); // Go away !
// Make sure old password is correct.
$oldhash = sha1($_POST['oldpassword'].$GLOBALS['login'].$GLOBALS['salt']);
if ($oldhash!=$GLOBALS['hash']) { echo '<script language="JavaScript">alert("The old password is not correct.");document.location=\'?do=changepasswd\';</script>'; exit; }
$oldhash = sha1($_POST['oldpassword'].$_SESSION['username'].$GLOBALS['salt']);
if ($oldhash != $GLOBALS['password'][$_SESSION['username']]) { echo '<script language="JavaScript">alert("The old password is not correct.");document.location=\'?do=changepasswd\';</script>'; exit; }
// Save new password
$GLOBALS['salt'] = sha1(uniqid('',true).'_'.mt_rand()); // Salt renders rainbow-tables attacks useless.
$GLOBALS['hash'] = sha1($_POST['setpassword'].$GLOBALS['login'].$GLOBALS['salt']);
//$GLOBALS['salt'] = sha1(uniqid('',true).'_'.mt_rand()); // Salt renders rainbow-tables attacks useless.
$GLOBALS['password'][$_SESSION['username']] = sha1($_POST['setpassword'].$_SESSION['username'].$GLOBALS['salt']);
writeConfig();
echo '<script language="JavaScript">alert("Your password has been changed.");document.location=\'?do=tools\';</script>';
exit;
@ -1354,6 +1413,59 @@ function renderPage()
}
}
// -------- User wants to manage users.
if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=users')) {
$PAGE = new pageBuilder;
foreach ($GLOBALS['login'] as $key => $username) {
if ($key === 0) $users[$key]['sysAdmin'] = TRUE;
else $users[$key]['sysAdmin'] = FALSE;
$users[$key]['username'] = $username;
$users[$key]['level'] = $GLOBALS['level'][$username];
$users[$key]['email'] = $GLOBALS['email'][$username];
}
$PAGE->assign('users', $users);
$PAGE->renderPage('manageusers');
exit;
}
// -------- Create / Editing user.
if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=saveUser')) {
$saveUserMessage = 'user successfully updated !';
if (isset($_GET['deleteUser'])) {
if (is_array($GLOBALS['login'])) {
if (in_array($_GET['username'], $GLOBALS['login'])) {
$sUkey = array_search($_GET['username'], $GLOBALS['login']);
if ($sUkey !== 0) {
unset($GLOBALS['login'][$sUkey], $GLOBALS['password'][$_GET['username']],
$GLOBALS['level'][$_GET['username']], $GLOBALS['email'][$_GET['username']]);
$GLOBALS['login'] = array_values($GLOBALS['login']);
writeConfig();
echo '<script language="JavaScript">alert("' . $_GET['username'] . ' was deleted.");document.location=\'?do=users\';</script>';
}
}
}
exit;
}
if (is_array($GLOBALS['login'])) {
if (!in_array($_GET['username'], $GLOBALS['login'])) {
$newUserKey = count($GLOBALS['login']);
$newUser = array($newUserKey => $_GET['username']);
$GLOBALS['login'] = $GLOBALS['login'] + $newUser;
$saveUserMessage = 'user successfully created !';
}
if (is_array($newUser) || isset($_GET['resetPassword'])) {
$newPassword = smallHash(sha1(uniqid('',true).'_'.mt_rand()));
$GLOBALS['password'][$_GET['username']] = sha1($newPassword . $_GET['username'] . $GLOBALS['salt']);
$saveUserMessage .= ' His new password is ' . $newPassword;
}
$GLOBALS['level'][$_GET['username']] = (int) $_GET['userlevel'];
$GLOBALS['email'][$_GET['username']] = $_GET['email'];
}
writeConfig();
echo '<script language="JavaScript">alert("' . $saveUserMessage . '");document.location=\'?do=users\';</script>';
exit;
}
// -------- User wants to change configuration
if (isset($_SERVER["QUERY_STRING"]) && startswith($_SERVER["QUERY_STRING"],'do=configure'))
{
@ -1452,11 +1564,19 @@ function renderPage()
if (!tokenOk($_POST['token'])) die('Wrong token.'); // Go away !
$tags = trim(preg_replace('/\s\s+/',' ', $_POST['lf_tags'])); // Remove multiple spaces.
$linkdate=$_POST['lf_linkdate'];
// If user is not an admin and try to edit other link of him and keep author when updating link
if (isset($LINKSDB[$linkdate])) {
if ($_SESSION['level'] < 3 && $LINKSDB[$linkdate]['author'] !== $_SESSION['username']) die('You cannot edit link of other !');
$author = $LINKSDB[$linkdate]['author'];
}
else {
$author = $_SESSION['username'];
}
$url = trim($_POST['lf_url']);
if (!startsWith($url,'http:') && !startsWith($url,'https:') && !startsWith($url,'ftp:') && !startsWith($url,'magnet:') && !startsWith($url,'?'))
$url = 'http://'.$url;
$link = array('title'=>trim($_POST['lf_title']),'url'=>$url,'description'=>trim($_POST['lf_description']),'private'=>(isset($_POST['lf_private']) ? 1 : 0),
'linkdate'=>$linkdate,'tags'=>str_replace(',',' ',$tags));
$url = 'http://'.$url;
$link = array('title'=>trim($_POST['lf_title']),'url'=>$url,'description'=>trim($_POST['lf_description']),'private'=>$_POST['lf_private'],
'author' => $author,'linkdate'=>$linkdate,'tags'=>str_replace(',',' ',$tags));
if ($link['title']=='') $link['title']=$link['url']; // If title is empty, use the URL as title.
$LINKSDB[$linkdate] = $link;
$LINKSDB->savedb(); // save to disk
@ -1489,6 +1609,7 @@ function renderPage()
// - confirmation is handled by javascript
// - we are protected from XSRF by the token.
$linkdate=$_POST['lf_linkdate'];
if ($_SESSION['level'] < 3 && $LINKSDB[$linkdate]['author'] !== $_SESSION['username']) die('You cannot delete link of other !');
unset($LINKSDB[$linkdate]);
$LINKSDB->savedb(); // save to disk
@ -1505,6 +1626,7 @@ function renderPage()
{
$link = $LINKSDB[$_GET['edit_link']]; // Read database
if (!$link) { header('Location: ?'); exit; } // Link not found in database.
if ($_SESSION['level'] < 3 && $LINKSDB[$_GET['edit_link']]['author'] !== $_SESSION['username']) exit; // If user is not an admin and try to edit other link of him
$PAGE = new pageBuilder;
$PAGE->assign('linkcount',count($LINKSDB));
$PAGE->assign('link',$link);
@ -1532,7 +1654,7 @@ function renderPage()
$link_is_new = true; // This is a new link
$linkdate = strval(date('Ymd_His'));
$title = (empty($_GET['title']) ? '' : $_GET['title'] ); // Get title if it was provided in URL (by the bookmarklet).
$description=''; $tags=''; $private=0;
$description=''; $tags=''; $private=2; $author = $_SESSION['username'];
if (($url!='') && parse_url($url,PHP_URL_SCHEME)=='') $url = 'http://'.$url;
// If this is an HTTP link, we try go get the page to extact the title (otherwise we will to straight to the edit form.)
if (empty($title) && parse_url($url,PHP_URL_SCHEME)=='http')
@ -1543,7 +1665,7 @@ function renderPage()
}
if ($url=='') $url='?'.smallHash($linkdate); // In case of empty URL, this is just a text (with a link that point to itself)
$link = array('linkdate'=>$linkdate,'title'=>$title,'url'=>$url,'description'=>$description,'tags'=>$tags,'private'=>0);
$link = array('linkdate'=>$linkdate,'title'=>$title,'url'=>$url,'description'=>$description,'tags'=>$tags,'private'=>2, 'author' => $author);
}
$PAGE = new pageBuilder;
@ -1784,7 +1906,9 @@ function buildLinkList($PAGE,$LINKSDB)
$link['description']=nl2br(keepMultipleSpaces(text2clickable(htmlspecialchars($link['description']))));
$title=$link['title'];
$classLi = $i%2!=0 ? '' : 'publicLinkHightLight';
$link['class'] = ($link['private']==0 ? $classLi : 'private');
if ($link['private']==="2") $classLi = 'private';
elseif ($link['private']==="1") $classLi = 'contributor';
$link['class'] = $classLi;
$link['localdate']=linkdate2locale($link['linkdate']);
$taglist = explode(' ',$link['tags']);
uasort($taglist, 'strcasecmp');
@ -1979,7 +2103,6 @@ function lazyThumbnail($url,$href=false)
else
$html.='<img class="lazyimage" src="#" data-original="'.htmlspecialchars($t['src']).'"';
$html.='<img class="lazyimage" src="#" data-original="'.htmlspecialchars($t['src']).'"';
if (!empty($t['width'])) $html.=' width="'.htmlspecialchars($t['width']).'"';
if (!empty($t['height'])) $html.=' height="'.htmlspecialchars($t['height']).'"';
if (!empty($t['style'])) $html.=' style="'.htmlspecialchars($t['style']).'"';
@ -1997,6 +2120,27 @@ function lazyThumbnail($url,$href=false)
return $html;
}
// -----------------------------------------------------------------------------------------------
// Create user
function createUser($username, $password, $level = '', $email = '')
{
$currentLogin = array($username);
$currentPassword = array($username => sha1($password . $username . $GLOBALS['salt']));
$currentLevel = array($username => $level);
$currentEmail = array($username => $email);
if (is_array($GLOBALS['login'])) {
$GLOBALS['login'] = array_merge($GLOBALS['login'], $currentLogin);
$GLOBALS['password'] = array_merge($GLOBALS['password'], $currentPassword);
$GLOBALS['level'] = array_merge($GLOBALS['level'], $currentLevel);
$GLOBALS['email'] = array_merge($GLOBALS['email'] , $currentEmail);
}
else {
$GLOBALS['login'] = $currentLogin;
$GLOBALS['password'] = $currentPassword;
$GLOBALS['level'] = $currentLevel;
$GLOBALS['email'] = $currentEmail;
}
}
// -----------------------------------------------------------------------------------------------
// Installation
@ -2031,18 +2175,19 @@ function install()
if (!empty($_POST['setlogin']) && !empty($_POST['setpassword']))
{
$tz = 'UTC';
if (!empty($_POST['continent']) && !empty($_POST['city']))
if (isTZvalid($_POST['continent'],$_POST['city']))
if (!empty($_POST['continent']) && !empty($_POST['city'])) {
if (isTZvalid($_POST['continent'],$_POST['city'])) {
$tz = $_POST['continent'].'/'.$_POST['city'];
$GLOBALS['timezone'] = $tz;
// Everything is ok, let's create config file.
$GLOBALS['login'] = $_POST['setlogin'];
$GLOBALS['salt'] = sha1(uniqid('',true).'_'.mt_rand()); // Salt renders rainbow-tables attacks useless.
$GLOBALS['hash'] = sha1($_POST['setpassword'].$GLOBALS['login'].$GLOBALS['salt']);
$GLOBALS['title'] = (empty($_POST['title']) ? 'Shared links on '.htmlspecialchars(indexUrl()) : $_POST['title'] );
writeConfig();
echo '<script language="JavaScript">alert("Shaarli is now configured. Please enter your login/password and start shaaring your links !");document.location=\'?do=login\';</script>';
exit;
$GLOBALS['timezone'] = $tz;
// Everything is ok, let's create config file.
$GLOBALS['salt'] = sha1(uniqid('',true).'_'.mt_rand()); // Salt renders rainbow-tables attacks useless.
$GLOBALS['title'] = $_POST['settitle'];
createUser($_POST['setlogin'], $_POST['setpassword'], $GLOBALS['level']['administrator'], $_POST['setemail']);
writeConfig();
echo '<script language="JavaScript">alert("Shaarli is now configured. Please enter your login/password and start shaaring your links !");document.location=\'?do=login\';</script>';
exit;
}
}
}
// Display config form:
@ -2165,13 +2310,20 @@ function processWS()
function writeConfig()
{
if (is_file($GLOBALS['config']['CONFIG_FILE']) && !isLoggedIn()) die('You are not authorized to alter config.'); // Only logged in user can alter config.
$config='<?php $GLOBALS[\'login\']='.var_export($GLOBALS['login'],true).'; $GLOBALS[\'hash\']='.var_export($GLOBALS['hash'],true).'; $GLOBALS[\'salt\']='.var_export($GLOBALS['salt'],true).'; ';
$config .='$GLOBALS[\'timezone\']='.var_export($GLOBALS['timezone'],true).'; date_default_timezone_set('.var_export($GLOBALS['timezone'],true).'); $GLOBALS[\'title\']='.var_export($GLOBALS['title'],true).';';
$config .= '$GLOBALS[\'redirector\']='.var_export($GLOBALS['redirector'],true).'; ';
$config .= '$GLOBALS[\'disablesessionprotection\']='.var_export($GLOBALS['disablesessionprotection'],true).'; ';
$config .= '$GLOBALS[\'disablejquery\']='.var_export($GLOBALS['disablejquery'],true).'; ';
$config .= '$GLOBALS[\'privateLinkByDefault\']='.var_export($GLOBALS['privateLinkByDefault'],true).'; ';
$config .= ' ?>';
$config ='<?php' .PHP_EOL;
$config .= '$GLOBALS[\'login\'] = ' . var_export($GLOBALS['login'],true) . ';' . PHP_EOL;
$config .= '$GLOBALS[\'password\'] = ' . var_export($GLOBALS['password'],true) . ';' . PHP_EOL;
$config .= '$GLOBALS[\'level\'] = ' . var_export($GLOBALS['level'],true) . ';' . PHP_EOL;
$config .= '$GLOBALS[\'email\'] = ' . var_export($GLOBALS['email'],true) . ';' . PHP_EOL;
$config .= '$GLOBALS[\'salt\'] = ' . var_export($GLOBALS['salt'],true) . ';' . PHP_EOL;
$config .= '$GLOBALS[\'timezone\'] = ' . var_export($GLOBALS['timezone'],true) . ';' . PHP_EOL;
$config .= 'date_default_timezone_set(' . var_export($GLOBALS['timezone'],true) . ');' . PHP_EOL;
$config .= '$GLOBALS[\'title\'] = '.var_export($GLOBALS['title'],true).';' . PHP_EOL;
$config .= '$GLOBALS[\'redirector\'] = '.var_export($GLOBALS['redirector'],true).';' . PHP_EOL;
$config .= '$GLOBALS[\'disablesessionprotection\'] = ' . var_export($GLOBALS['disablesessionprotection'],true) . ';' . PHP_EOL;
$config .= '$GLOBALS[\'disablejquery\'] = ' . var_export($GLOBALS['disablejquery'],true) . ';' . PHP_EOL;
$config .= '$GLOBALS[\'privateLinkByDefault\'] = ' . var_export($GLOBALS['privateLinkByDefault'],true) . ';' . PHP_EOL;
$config .= '?>';
if (!file_put_contents($GLOBALS['config']['CONFIG_FILE'],$config) || strcmp(file_get_contents($GLOBALS['config']['CONFIG_FILE']),$config)!=0)
{
echo '<script language="JavaScript">alert("Shaarli could not create the config file. Please make sure Shaarli has the right to write in the folder is it installed in.");document.location=\'?\';</script>';

View file

@ -14,13 +14,12 @@
<i>Title</i><br><input type="text" name="lf_title" value="{$link.title|htmlspecialchars}" style="width:100%"><br>
<i>Description</i><br><textarea name="lf_description" rows="4" cols="25" style="width:100%">{$link.description|htmlspecialchars}</textarea><br>
<i>Tags</i><br><input type="text" id="lf_tags" name="lf_tags" value="{$link.tags|htmlspecialchars}" style="width:100%"><br>
{if condition="($link_is_new && $GLOBALS['privateLinkByDefault']==true) || $link.private == true"}
<input type="checkbox" checked="checked" name="lf_private" id="lf_private">
&nbsp;<label for="lf_private"><i>Private</i></label><br>
{else}
<input type="checkbox" name="lf_private" id="lf_private">
&nbsp;<label for="lf_private"><i>Private</i></label><br>
{/if}
&nbsp;<label for="lf_private"><i>State of your link</i></label><br>
<select name="lf_private" id="lf_private">
<option value="2"{if condition="($link_is_new && $GLOBALS['privateLinkByDefault']==true) || $link.private == 2"} selected{/if}>Private</option>
<option value="1"{if condition="$link.private == 1"} selected{/if}>Collaborators</option>
<option value="0">Public</option>
</select>
<input type="submit" value="Save" name="save_edit" class="bigbutton" style="margin-left:40px;">
<input type="submit" value="Cancel" name="cancel_edit" class="bigbutton" style="margin-left:40px;">
{if condition="!$link_is_new"}<input type="submit" value="Delete" name="delete_link" class="bigbutton" style="margin-left:180px;" onClick="return confirmDeleteLink();">{/if}

View file

@ -33,7 +33,7 @@
<a name="{$value.linkdate|smallHash}" id="{$value.linkdate|smallHash}"></a>
<div class="thumbnail">{$value.url|thumbnail}</div>
<div class="linkcontainer">
{if="isLoggedIn()"}
{if="$currentUserLevel > 3 || $currentUser === $value.author"}
<div class="linkeditbuttons">
<form method="GET" class="buttoneditform"><input type="hidden" name="edit_link" value="{$value.linkdate}"><input type="image" alt="Edit" src="images/edit_icon.png#" title="Edit" class="button_edit"></form><br>
<form method="POST" class="buttoneditform"><input type="hidden" name="lf_linkdate" value="{$value.linkdate}">
@ -43,14 +43,18 @@
<span class="linktitle"><a href="{$redirector}{$value.url|htmlspecialchars}">{$value.title|htmlspecialchars}</a></span>
<br>
{if="$value.description"}<div class="linkdescription"{if condition="$search_type=='permalink'"} style="max-height:none !important;"{/if}>{$value.description}</div>{/if}
{if="!$GLOBALS['config']['HIDE_TIMESTAMPS'] || isLoggedIn()"}
<span class="linkdate" title="Permalink"><a href="?{$value.linkdate|smallHash}">{$value.localdate|htmlspecialchars} - permalink</a> - </span>
{else}
<span class="linkdate" title="Short link here"><a href="?{$value.linkdate|smallHash}">permalink</a> - </span>
{/if}
{if="!$GLOBALS['config']['HIDE_QRCODE']"}
<div style="position:relative;display:inline;"><a href="http://invx.com/code/qrcode/?code={$scripturl|urlencode}%3F{$value.linkdate|smallHash}&width=200&height=200"
{if="empty($GLOBALS['disablejquery'])"}onclick="return false;"{/if} class="qrcode"><img src="images/qrcode.png#" width="13" height="13" title="QR-Code"></a></div> -
<span class="linkurl" title="Short link">{$value.url|htmlspecialchars}</span><br>
{/if}
<span class="linkurl" title="Short link">{$value.url|htmlspecialchars} - </span>
<span class="linkauthor" title="Author">Added by {$value.author}</span><br>
{if="$value.tags"}
<div class="linktaglist">
{loop="value.taglist"}<span class="linktag" title="Add tag"><a href="?addtag={$value|urlencode}">{$value|htmlspecialchars}</a></span> {/loop}

View file

@ -0,0 +1,43 @@
<!DOCTYPE html>
<html>
<head>{include="includes"}</head>
<body>
<div id="pageheader">
{include="page.header"}
</div>
<div id="manageusers">
<form method="GET" name="edituser" id="createuserbadge">
<h4>New user</h4>
<input type="hidden" name="do" value="saveUser">
<input type="text" name="username" value="" placeholder="username">
<select name="userlevel" id="eu_level">
<option value="4">Administrator</option>
<option value="3">Moderator</option>
<option value="2">Contributor</option>
<option value="1" selected>Reader</option>
</select>
<input type="text" name="email" value="" placeholder="email">
<input type="submit" name="createUser" value="create user" class="updateuser">
</form>
{loop="users"}
<form method="GET" name="edituser" class="userBadge {if="$value.username === $shaarliOwner"}sysadmin{/if}">
<h4>{$value.username}</h4>
<input type="hidden" name="do" value="saveUser">
<input type="hidden" name="username" value="{$value.username}">
<select name="userlevel" id="userlevel">
<option value="4"{if="$value.level == 4"} selected{/if}>Administrator</option>
<option value="3"{if="$value.level == 3"} selected{/if}>Moderator</option>
<option value="2"{if="$value.level == 2"} selected{/if}>Contributor</option>
<option value="1"{if="$value.level == 1"} selected{/if}>Reader</option>
</select>
<input type="text" name="email" value="{$value.email}">
<input type="submit" name="updateUser" value="update card" class="updateuser">
<input type="submit" name="resetPassword" value="reset pass" class="smallsubmit resetpassword">
<input type="submit" name="deleteUser" value="delete user" class="smallsubmit deleteuser" {if="$value.username === $shaarliOwner"}disabled{/if} onClick="return confirm('Do you really want to delete {$value.username} ?')">
</form>
{/loop}
<div class="clear"></div>
</div>
{include="page.footer"}
</body>
</html>

View file

@ -18,3 +18,13 @@ $(document).ready(function()
});
</script>
{/if}
{if="empty($GLOBALS['disablejquery']) && isset($_GET['searchterm'])"}
<script src="inc/jquery.highlight.js#"></script>
<script language="JavaScript">
$(document).ready(function()
{
$('#linklist li').highlight("{$search_crits}");
});
</script>
{/if}

View file

@ -9,7 +9,7 @@
{else}
<a href="?" class="nomobile">Home</a>
{if="isLoggedIn()"}
<a href="?do=logout">Logout</a><a href="?do=tools">Tools</a><a href="?do=addlink"><b>Add link</b></a>
<a href="?do=logout">Logout <i>({$currentUser})</i></a><a href="?do=tools">Tools</a><a href="?do=addlink"><b>Add link</b></a>
{elseif="$GLOBALS['config']['OPEN_SHAARLI']"}
<a href="?do=tools">Tools</a><a href="?do=addlink"><b>Add link</b></a>
{else}

View file

@ -17,7 +17,7 @@
</div>
</center>
{include="page.footer"}
</body>
{if="empty($GLOBALS['disablejquery'])"}
<script>
$(document).ready(function() {
@ -25,4 +25,5 @@ $(document).ready(function() {
});
</script>
{/if}
</body>
</html>

View file

@ -5,12 +5,13 @@
<div id="pageheader">
{include="page.header"}
<div id="toolsdiv">
{if="!$GLOBALS['config']['OPEN_SHAARLI']"}<a href="?do=changepasswd"><b>Change password</b> <span>: Change your password.</span></a><br><br>{/if}
<a href="?do=configure"><b>Configure your Shaarli</b> <span>: Change Title, timezone...</span></a><br><br>
<a href="?do=changetag"><b>Rename/delete tags</b> <span>: Rename or delete a tag in all links</span></a><br><br>
<a href="?do=import"><b>Import</b> <span>: Import Netscape html bookmarks (as exported from Firefox, Chrome, Opera, delicious...)</span></a> <br><br>
<a href="?do=export"><b>Export</b> <span>: Export Netscape html bookmarks (which can be imported in Firefox, Chrome, Opera, delicious...)</span></a><br><br>
<a class="smallbutton" onclick="alert('Drag this link to your bookmarks toolbar, or right-click it and choose Bookmark This Link...');return false;" href="javascript:javascript:(function(){var%20url%20=%20location.href;var%20title%20=%20document.title%20||%20url;window.open('{$pageabsaddr}?post='%20+%20encodeURIComponent(url)+'&amp;title='%20+%20encodeURIComponent(title)+'&amp;source=bookmarklet','_blank','menubar=no,height=390,width=600,toolbar=no,scrollbars=no,status=no,dialog=1');})();"><b>Shaare link</b></a> <a href="#" style="clear:none;"><span>&#x21D0; Drag this link to your bookmarks toolbar (or right-click it and choose Bookmark This Link....).<br>&nbsp;&nbsp;&nbsp;&nbsp;Then click "Shaare link" button in any page you want to share.</span></a><br><br>
{if="count($GLOBALS['login'])>=1 || !$GLOBALS['config']['OPEN_SHAARLI']"}<a href="?do=changepasswd"><b>Change password</b> <span>: Change your password.</span></a><br><br>{/if}
{if="$currentUserLevel >= 3"}<a href="?do=users"><b>Manage users</b> <span>: Reset password / create or delete user.</span></a><br><br>{/if}
{if="$currentUserLevel == 4"}<a href="?do=configure"><b>Configure your Shaarli</b> <span>: Change Title, timezone...</span></a><br><br>{/if}
{if="$currentUserLevel >= 3"}<a href="?do=changetag"><b>Rename/delete tags</b> <span>: Rename or delete a tag in all links</span></a><br><br>{/if}
{if="$currentUserLevel == 4"}<a href="?do=import"><b>Import</b> <span>: Import Netscape html bookmarks (as exported from Firefox, Chrome, Opera, delicious...)</span></a> <br><br>{/if}
{if="$currentUserLevel == 4"}<a href="?do=export"><b>Export</b> <span>: Export Netscape html bookmarks (which can be imported in Firefox, Chrome, Opera, delicious...)</span></a><br><br>{/if}
<a class="smallbutton" onclick="alert('Drag this link to your bookmarks toolbar, or right-click it and choose Bookmark This Link...');return false;" href="javascript:javascript:(function(){var%20url%20=%20location.href;var%20title%20=%20document.title%20||%20url;window.open('{$pageabsaddr}?post='%20+%20encodeURIComponent(url)+'&amp;title='%20+%20encodeURIComponent(title)+'&amp;source=bookmarklet','_blank','menubar=no,height=390,width=600,toolbar=no,scrollbars=no,status=no,dialog=1');})();"><b>Shaare link</b></a> <a href="#" style="clear:none;"><span>&#x21D0; Drag this link to your bookmarks toolbar (or right-click it and choose Bookmark This Link....).<br>&nbsp;&nbsp;&nbsp;&nbsp;Then click "Shaare link" button in any page you want to share.</span></a><br><br>
<div class="clear"></div>
</div>
</div>