Commit dd316b1c authored by JosephK's avatar JosephK
Browse files

Shaarli + template frama + plugins tags_advanced, recovery, framanav + scripts...

Shaarli + template frama + plugins tags_advanced, recovery, framanav + scripts de création de compte
parents
u/*
\ No newline at end of file
[![](https://framagit.org/assets/favicon-075eba76312e8421991a0c1f89a89ee81678bcde72319dd3e8047e2a47cd3a42.ico)](https://framagit.org)
![English:](https://upload.wikimedia.org/wikipedia/commons/thumb/a/ae/Flag_of_the_United_Kingdom.svg/20px-Flag_of_the_United_Kingdom.svg.png) **Framasoft uses GitLab** for the development of its free softwares. Our Github repositories are only mirrors.
If you want to work with us, **fork us on [framagit.org](https://framagit.org)**. (no registration needed, you can sign in with your Github account)
![Français :](https://upload.wikimedia.org/wikipedia/commons/thumb/c/c3/Flag_of_France.svg/20px-Flag_of_France.svg.png) **Framasoft utilise GitLab** pour le développement de ses logiciels libres. Nos dépôts Github ne sont que des miroirs.
Si vous souhaitez travailler avec nous, **forkez-nous sur [framagit.org](https://framagit.org)**. (l'inscription n'est pas nécessaire, vous pouvez vous connecter avec votre compte Github)
* * *
MyFrama est une instance multi-utilisateur de [Shaarli](https://github.com/shaarli/Shaarli/).
Elle contient un template Bootstrap (dans `shaarli/tpl/frama`), une page d'accueil (`index.php`, `layout.php` et `setconfig.php`) pour créer des comptes ainsi que les plugins :
* `tags_advanced` pour tagger automatiquement des liens selon l'url et épingler les tags les plus fréquement utilisé
* `recovery` pour mémoriser une adresse email dans la configuration et permettre de récupérer un accès à son compte
* `framanav` pour ajouter la [barre de navigation de Framasoft](https://framagit.org/framasoft/framanav)
Chaque compte utilisateur se trouve dans le dossier `/u/`.
Il s'agit à chaque fois d'une instance autonome qui se crée depuis le formulaire en page d'accueil.
Les principaux dossiers de Shaarli sont des liens symboliques relatifs qui pointent vers `/shaarli/`.
Seuls les dossiers `cache`, `data`, `pagecache`, `tmp` appartiennent réellement à l'utilisateur.
Il ne faut pas tenter une installation depuis le dossier `/shaarli/`.
<?php
// remplacer http par https
define('URL_BASE', str_replace($_SERVER['DOCUMENT_ROOT'], 'http://'.$_SERVER['HTTP_HOST'], dirname($_SERVER['SCRIPT_FILENAME'])) );
define('TITLE', 'MyFrama');
/**
* Build a directory path by concatenating a list of directory names.
*
* You can call this function with as many params as you want.
* Example: join_path('home', 'test', 'Downloads') returns home/test/Downloads
*
* @param string [...] a list of directory names
* @return a string corresponding to the final pathname
*/
function join_path() {
$path_parts = func_get_args();
return join(DIRECTORY_SEPARATOR, $path_parts);
}
define('PATH_ROOT', dirname(__FILE__));
define('PATH_ACCOUNTS', join_path(PATH_ROOT, 'u'));
define('PATH_SHAARLI', join_path(PATH_ROOT, 'shaarli'));
require_once( 'setconfig.php');
/**
* Test if input values are correct.
*
* Tested values are:
* - account: max 25 chars from [a-z0-9_-]{1,25}
* - password: not empty
* - email: valid email address
* All values are required.
*
* @param array $values input values to validate.
* @return true if $values are correct, false otherwise.
*/
function values_are_valid($values) {
if ( empty($values['account'])
|| empty($values['password'])
|| empty($values['email'])) {
return 'empty';
}
if (!preg_match('/^[a-z0-9_-]{1,25}?$/', strtolower($values['account']))) {
return 'account';
}
if (strlen($values['password']) <= 0) {
// Already tested by empty(...) but if one day we want to set
// a minimal number of chars, we'll just have to change the 0.
return 'password';
}
return true;
}
/**
* Check if an account is already existing.
*
* @param string $account the name of the subdomain to check.
* @return bool True if the account exists, false otherwise.
*/
function check_account_exists($account) {
$account_path = join_path(PATH_ACCOUNTS, $account);
if (file_exists($account_path)) {
return true;
} else {
return false;
}
}
/**
* Create a new account.
*
* This function creates files under PATH_ACCOUNTS and set the database with
* correct values.
* Note values must be verified before calling this function.
*
* @param string $account name of the account to create.
* @param string $password the account password.
* @param string $email an email address.
* @throws Exception if any error occured during process.
*/
function create_account($account, $password, $email) {
$account_path = join_path(PATH_ACCOUNTS, strtolower($account));
mkdir($account_path);
$userData = array('data', 'tmp', 'cache', 'pagecache');
// Relative symlink to shaarli's content
if ($handle = opendir('shaarli')) {
$blacklist = array('.', '..', '.git');
$blacklist = array_merge($blacklist, $userData);
while (false !== ($file = readdir($handle))) {
if (!in_array($file, $blacklist)) {
symlink( '../../shaarli/' . $file, 'u/' . $account . '/' . $file);
}
}
closedir($handle);
}
// Create userData folders
foreach ($userData as $file) {
mkdir($account_path . '/' . $file);
chmod('u/' . $account . '/' . $file , 0777);
copy('shaarli/' . $file . '/.htaccess', 'u/' . $account . '/' . $file . '/.htaccess');
copy('shaarli/plugins/tags_advanced/example.php', 'u/' . $account . '/data/tags_advanced.php');
}
//~~> (vérifier s'il y a plus secure que 777 :-/ )
//--Config writing (from shaarli/index.php install() )--------------
$GLOBALS['timezone'] = 'Europe/Paris'; // There is no reliable way to guess timezone so we force Paris.;
// Everything is ok, let's create config file.
$GLOBALS['login'] = $account;
$GLOBALS['config']['RECOVERY'] = array (
'email' => $email,
'noreply' => 'no-reply@framasoft.org',
'attempt' => 0,
'token' => ''
);
$GLOBALS['salt'] = sha1(uniqid('',true).'_'.mt_rand()); // Salt renders rainbow-tables attacks useless.
$GLOBALS['hash'] = sha1($password.$GLOBALS['login'].$GLOBALS['salt']);
$GLOBALS['title'] = 'MyFrama';
$GLOBALS['config']['ENABLE_UPDATECHECK'] = false;
$GLOBALS['config']['ENABLED_PLUGINS'] = array(
0 => 'qrcode',
1 => 'addlink_toolbar',
2 => 'markdown',
3 => 'recovery',
4 => 'tags_advanced'
);
writeConfig($GLOBALS, true, $account_path . '/data/config.php');
//~~> Config ok
//------------------------------------------------------------------
}
$test_values = true;
if (!is_writable(PATH_ACCOUNTS)) {
$error = 'Le répertoire contenant les données doit pouvoir être écrit par le serveur web.';
//~~> Dossier /u/ ok !
} elseif (isset($_POST['subdomain_redirect'])) {
// Handle account redirection.
$account = strtolower($_POST['subdomain_redirect']);
if (check_account_exists($account)) {
header('Location: u/' . $account . '?do=login' );
} else {
$error_redirection = 'Ce compte ne semble pas exister. Êtes-vous sûr du nom de votre compte&nbsp;?';
}
//~~> Redirection vers la page de login ok !
} elseif (!empty($_POST)) {
$test_values = values_are_valid($_POST);
$account_exists = true;
$error = null;
$account = strtolower($_POST['account']);
$password = $_POST['password'];
$email = $_POST['email'];
if ($test_values === true) {
try {
$account_exists = check_account_exists($account);
} catch (Exception $e) {
$error = $e->getMessage();
}
} else {
$error = 'Veuillez vérifier que les informations que vous avez entrées correspondent au format demandé.';
}
if ($account_exists === false) {
// If $account_exists is false, there was no error catched earlier.
try {
create_account($account, $password, $email);
} catch (Exception $e) {
$error = $e->getMessage();
}
}
if (is_null($error) && $account_exists) {
$error = 'Ce compte semble déjà exister, veuillez choisir un autre nom de compte.';
$test_values = 'account';
} elseif (is_null($error)) {
// Everything is fine, we can redirect user to its new board!
header('Location: u/' . $account . '?do=login');
}
}
include 'layout.php';
<!DOCTYPE html>
<html>
<head>
<title>MyFrama</title>
<meta charset="UTF-8" />
<meta name="viewport" content="initial-scale=1.0" />
<script src="https://framasoft.org/nav/lib/jquery/jquery.min.js" type="text/javascript"></script>
<script src="https://framasoft.org/nav/lib/bootstrap/js/bootstrap.min.js" type="text/javascript"></script>
</head>
<body class="fb_g2">
<script src="https://framasoft.org/nav/nav.js" type="text/javascript"></script>
<div class="container ombre">
<header>
<h1 class="big-title"><a href="/"><b class="violet">My</b><b class="vert">Frama</b></a></h1>
<p class="lead">Marquez et partagez vos liens</p>
<hr class="trait" role="presentation" />
</header>
<div class="row <?php echo isset($error) ? 'active' : ''; ?>" id="registration">
<div class="col-md-12">
<h2>Créer un compte</h2>
<?php if (isset($error)) { ?>
<div class="col-md-8 col-md-offset-2">
<div class="alert alert-danger"><strong>Mince&nbsp;!</strong> <?php echo $error; ?></div>
</div>
<?php } ?>
<div class="col-md-8 col-md-offset-2">
<form class="form-horizontal" method="POST" action="#" role="form">
<div class="alert alert-info">Merci de remplir tous les champs.</div>
<div class="form-group <?php echo $test_values === 'account' ? 'has-error' : ''; ?>">
<label class="col-sm-4 control-label" for="account">Nom d&rsquo;utilisateur<br /><span class="small">(max. 25 caractères)</span></label>
<div class="col-sm-8">
<input type="text" class="form-control" required="" id="account" name="account" pattern="[a-zA-Z0-9_-]{1,25}" value="<?php echo isset($_POST['account']) ? $_POST['account'] : ''; ?>" >
</div>
</div>
<div class="form-group <?php echo $test_values === 'email' ? 'has-error' : ''; ?>">
<label class="col-sm-4 control-label" for="email">Adresse courriel</label>
<div class="col-sm-8">
<input type="email" class="form-control" required="" id="email" name="email" value="<?php echo isset($_POST['email']) ? $_POST['email'] : ''; ?>">
</div>
</div>
<div class="form-group <?php echo $test_values === 'password' ? 'has-error' : ''; ?>">
<label class="col-sm-4 control-label" for="password">Mot de passe</label>
<div class="col-sm-8">
<input type="password" class="form-control" required="" id="password" name="password" value="<?php echo isset($_POST['password']) ? $_POST['password'] : ''; ?>">
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-4 col-sm-8">
<input class="btn btn-primary" type="submit" value="Créer mon compte" />
</div>
</div>
</form>
</div>
</div>
</div>
<div class="row <?php echo isset($error_redirection) ? 'active' : ''; ?>" id="login">
<div class="col-md-12">
<h2>Vous possédez déjà un compte&nbsp;?</h2>
<?php if (isset($error_redirection)) { ?>
<div class="col-md-8 col-md-offset-2">
<div class="alert alert-danger"><strong>Mince&nbsp;!</strong> <?php echo $error_redirection; ?></div>
</div>
<?php } ?>
<div class="col-md-8 col-md-offset-2">
<form class="form-horizontal" method="POST" action="#" role="form">
<div class="form-group">
<label class="col-sm-4 control-label" for="subdomain_redirect">Votre compte</label>
<div class="col-sm-8">
<div class="input-group">
<div class="input-group-addon"><?php echo URL_BASE; ?>/u/</div>
<input type="text" class="form-control" required="" id="subdomain_redirect" name="subdomain_redirect" pattern="[a-zA-Z0-9_-]{1,25}" value="<?php echo isset($_POST['subdomain_redirect']) ? $_POST['subdomain_redirect'] : ''; ?>">
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-4 col-sm-8">
<input class="btn btn-primary" type="submit" value="Accéder à mon compte" />
</div>
</div>
</form>
</div>
</div>
</div>
<hr role="presentation" />
<div class="row">
<div id="tuto-faq" class="col-md-4">
<h2>Prise en main</h2>
<p class="text-center" role="presentation"><span class="glyphicon glyphicon-question-sign"></span></p>
<div id="aboutbox">
<p><b class="violet">My</b><b class="vert">Frama</b> est un service en ligne libre de partage de lien.</p>
<ol>
<li>Créez votre compte&nbsp;;</li>
<li>Connectez-vous à votre nouveau compte&nbsp;;</li>
<li>Marquez des pages et partagez-les&nbsp;;</li>
</ol>
</div>
</div>
<div class="col-md-4" id="le-logiciel">
<h2>Le logiciel</h2>
<p class="text-center" role="presentation"><span class="glyphicon glyphicon-cloud"></span></p>
<p><b class="violet">My</b><b class="vert">Frama</b> est une instance du logiciel <a href="https://github.com/shaarli/Shaarli/">Shaarli</a> développé par la communauté Shaarli à partir d’un projet initié par <a href="http://sebsauvage.net/">Sébastien Sauvage</a>.</p>
<p>Shaarli est sous licence <a href="https://github.com/shaarli/Shaarli/blob/master/COPYING">ZLIB/LIBPNG</a>.</p>
</div>
<div class="col-md-4" id="jardin">
<h2>Cultivez votre jardin</h2>
<p class="text-center" role="presentation"><span class="glyphicon glyphicon-tree-deciduous"></span></p>
<p>Pour participer au développement du logiciel, proposer des améliorations
ou simplement le télécharger, rendez-vous sur <a href="https://github.com/shaarli/Shaarli/">le site de développement</a>.</p>
<p>Si vous souhaitez installer ce logiciel pour votre propre usage et ainsi gagner en autonomie, nous vous aidons sur&nbsp;:</p>
<p class="text-center"><a href="http://framacloud.org/cultiver-son-jardin/installation-de-shaarli/" class="btn btn-success"><i class="glyphicon glyphicon-tree-deciduous"></i> framacloud.org</a></p>
</div>
</div>
</div>
</div>
</body>
</html>
<?php
//-- From shaarli/index.php --------------------------------------------
// Data subdirectory
$GLOBALS['config']['DATADIR'] = 'data';
// Main configuration file
$GLOBALS['config']['CONFIG_FILE'] = $GLOBALS['config']['DATADIR'].'/config.php';
// Link datastore
$GLOBALS['config']['DATASTORE'] = $GLOBALS['config']['DATADIR'].'/datastore.php';
// Banned IPs
$GLOBALS['config']['IPBANS_FILENAME'] = $GLOBALS['config']['DATADIR'].'/ipbans.php';
// Processed updates file.
$GLOBALS['config']['UPDATES_FILE'] = $GLOBALS['config']['DATADIR'].'/updates.txt';
// Access log
$GLOBALS['config']['LOG_FILE'] = $GLOBALS['config']['DATADIR'].'/log.txt';
// For updates check of Shaarli
$GLOBALS['config']['UPDATECHECK_FILENAME'] = $GLOBALS['config']['DATADIR'].'/lastupdatecheck.txt';
// Set ENABLE_UPDATECHECK to disabled by default.
$GLOBALS['config']['ENABLE_UPDATECHECK'] = false;
// RainTPL cache directory (keep the trailing slash!)
$GLOBALS['config']['RAINTPL_TMP'] = 'tmp/';
// Raintpl template directory (keep the trailing slash!)
$GLOBALS['config']['RAINTPL_TPL'] = 'tpl/frama/';
// Thumbnail cache directory
$GLOBALS['config']['CACHEDIR'] = 'cache';
// Atom & RSS feed cache directory
$GLOBALS['config']['PAGECACHE'] = 'pagecache';
/*
* Global configuration
*/
// Ban IP after this many failures
$GLOBALS['config']['BAN_AFTER'] = 4;
// Ban duration for IP address after login failures (in seconds)
$GLOBALS['config']['BAN_DURATION'] = 1800;
// Feed options
// Enable RSS permalinks by default.
// This corresponds to the default behavior of shaarli before this was added as an option.
$GLOBALS['config']['ENABLE_RSS_PERMALINKS'] = true;
// If true, an extra "ATOM feed" button will be displayed in the toolbar
$GLOBALS['config']['SHOW_ATOM'] = false;
// Link display options
$GLOBALS['config']['HIDE_PUBLIC_LINKS'] = false;
$GLOBALS['config']['HIDE_TIMESTAMPS'] = false;
$GLOBALS['config']['LINKS_PER_PAGE'] = 20;
// Open Shaarli (true): anyone can add/edit/delete links without having to login
$GLOBALS['config']['OPEN_SHAARLI'] = false;
// Thumbnails
// Display thumbnails in links
$GLOBALS['config']['ENABLE_THUMBNAILS'] = true;
// Store thumbnails in a local cache
$GLOBALS['config']['ENABLE_LOCALCACHE'] = true;
// Update check frequency for Shaarli. 86400 seconds=24 hours
$GLOBALS['config']['UPDATECHECK_BRANCH'] = 'stable';
$GLOBALS['config']['UPDATECHECK_INTERVAL'] = 86400;
/*
* Plugin configuration
*
* Warning: order matters!
*
* These settings may be be overriden in:
* - data/config.php
* - each plugin's configuration file
*/
//$GLOBALS['config']['ENABLED_PLUGINS'] = array(
// 'qrcode', 'archiveorg', 'readityourself', 'demo_plugin', 'playvideos',
// 'wallabag', 'markdown', 'addlink_toolbar',
//);
$GLOBALS['config']['ENABLED_PLUGINS'] = array('qrcode', 'addlink_toolbar', 'markdown'); //'framanav'
// Initialize plugin parameters array.
$GLOBALS['plugins'] = array();
// PubSubHubbub support. Put an empty string to disable, or put your hub url here to enable.
$GLOBALS['config']['PUBSUBHUB_URL'] = '';
if (empty($GLOBALS['redirector'])) $GLOBALS['redirector']='';
if (empty($GLOBALS['disablesessionprotection'])) $GLOBALS['disablesessionprotection']=false;
if (empty($GLOBALS['privateLinkByDefault'])) $GLOBALS['privateLinkByDefault']=false;
if (empty($GLOBALS['titleLink'])) $GLOBALS['titleLink']='?';
//-- From shaarli/application/Config.php -------------------------------
// $config['config']['CONFIG_FILE'] replace by $config_file
/**
* Re-write configuration file according to given array.
* Requires mandatory fields listed in $MANDATORY_FIELDS.
*
* @param array $config contains all configuration fields.
* @param bool $isLoggedIn true if user is logged in.
*
* @return void
*
* @throws MissingFieldConfigException: a mandatory field has not been provided in $config.
* @throws UnauthorizedConfigException: user is not authorize to change configuration.
* @throws Exception: an error occured while writing the new config file.
*/
function writeConfig($config, $isLoggedIn, $config_file)
{
// These fields are required in configuration.
$MANDATORY_FIELDS = array(
'login', 'hash', 'salt', 'timezone', 'title', 'titleLink',
'redirector', 'disablesessionprotection', 'privateLinkByDefault'
);
if (!isset($config_file)) {
throw new MissingFieldConfigException('CONFIG_FILE');
}
// Only logged in user can alter config.
if (is_file($config_file) && !$isLoggedIn) {
throw new UnauthorizedConfigException();
}
// Check that all mandatory fields are provided in $config.
foreach ($MANDATORY_FIELDS as $field) {
if (!isset($config[$field])) {
throw new MissingFieldConfigException($field);
}
}
$configStr = '<?php '. PHP_EOL;
$configStr .= '$GLOBALS[\'login\'] = '.var_export($config['login'], true).';'. PHP_EOL;
$configStr .= '$GLOBALS[\'hash\'] = '.var_export($config['hash'], true).';'. PHP_EOL;
$configStr .= '$GLOBALS[\'salt\'] = '.var_export($config['salt'], true).'; '. PHP_EOL;
$configStr .= '$GLOBALS[\'timezone\'] = '.var_export($config['timezone'], true).';'. PHP_EOL;
$configStr .= 'date_default_timezone_set('.var_export($config['timezone'], true).');'. PHP_EOL;
$configStr .= '$GLOBALS[\'title\'] = '.var_export($config['title'], true).';'. PHP_EOL;
$configStr .= '$GLOBALS[\'titleLink\'] = '.var_export($config['titleLink'], true).'; '. PHP_EOL;
$configStr .= '$GLOBALS[\'redirector\'] = '.var_export($config['redirector'], true).'; '. PHP_EOL;
$configStr .= '$GLOBALS[\'disablesessionprotection\'] = '.var_export($config['disablesessionprotection'], true).'; '. PHP_EOL;
$configStr .= '$GLOBALS[\'privateLinkByDefault\'] = '.var_export($config['privateLinkByDefault'], true).'; '. PHP_EOL;
// Store all $config['config']
foreach ($config['config'] as $key => $value) {
$configStr .= '$GLOBALS[\'config\'][\''. $key .'\'] = '.var_export($config['config'][$key], true).';'. PHP_EOL;
}
if (isset($config['plugins'])) {
foreach ($config['plugins'] as $key => $value) {
$configStr .= '$GLOBALS[\'plugins\'][\''. $key .'\'] = '.var_export($config['plugins'][$key], true).';'. PHP_EOL;
}
}
if (!file_put_contents($config_file, $configStr)
|| strcmp(file_get_contents($config_file), $configStr) != 0
) {
throw new Exception(
'Shaarli could not create the config file.
Please make sure Shaarli has the right to write in the folder is it installed in.'
);
}
}
?>
\ No newline at end of file
# Set default behavior
* text=auto eol=lf
# Ensure sources are processed
*.conf text
*.css text
*.html text diff=html
*.js text
*.md text
*.php text diff=php
Dockerfile text
# Do not alter images nor minified scripts
*.ico binary
*.jpg binary
*.png binary
*.min.css binary
*.min.js binary
# Exclude from Git archives
.gitattributes export-ignore
.gitignore export-ignore
.travis.yml export-ignore
composer.json export-ignore
doc/**/*.json export-ignore
doc/**/*.md export-ignore
docker/ export-ignore
Doxyfile export-ignore
Makefile export-ignore
phpunit.xml export-ignore
tests/ export-ignore
# Ignore data/, tmp/, cache/ and pagecache/
data
tmp
cache
pagecache
# Eclipse project files
.settings
.buildpath
.project
# Ignore raintpl generated pages
*.rtpl.php
# Ignore test dependencies
composer.lock
/vendor/
# Ignore development and test resources
coverage
doxygen
sandbox
phpmd.html
# Ignore user plugin configuration
plugins/*/config.php
sudo: false
language: php
php:
- 7.0
- 5.6
- 5.5
- 5.4
- 5.3
install:
- composer self-update
- composer install
script:
- make clean
- make check_permissions
- make test
## Contributing to Shaarli (community repository)
### Bugs and feature requests
**Reporting bugs, feature requests: issues management**
You can look through existing bugs/requests and help reporting them [here](https://github.com/shaarli/Shaarli/issues).
Constructive input/experience reports/helping other users is welcome.
The general guideline of the fork is to keep Shaarli simple (project and code maintenance, and features-wise), while providing customization capabilities (plugin system, making more settings configurable).
Check the [milestones](https://github.com/shaarli/Shaarli/milestones) to see what issues have priority.
* The issues list should preferably contain **only tasks that can be actioned immediately**. Anyone should be able to open the issues list, pick one and start working on it immediately.