RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.php [QSA,L]
-<Limit GET POST PUT DELETE OPTIONS>
- <IfModule version_module>
- <IfVersion >= 2.4>
- Require all granted
- </IfVersion>
- <IfVersion < 2.4>
- Allow from all
- Deny from none
- </IfVersion>
- </IfModule>
-
- <IfModule !version_module>
- Require all granted
- </IfModule>
-</Limit>
-
-<LimitExcept GET POST PUT DELETE OPTIONS>
+<LimitExcept GET POST PUT DELETE PATCH OPTIONS>
<IfModule version_module>
<IfVersion >= 2.4>
Require all denied
$data['language'] = $this->getTypeLanguage($feedType);
$data['last_update'] = $this->getLatestDateFormatted($feedType);
$data['show_dates'] = !$this->hideDates || $this->isLoggedIn;
- // Remove leading slash from REQUEST_URI.
- $data['self_link'] = escape(server_url($this->serverInfo))
- . escape($this->serverInfo['REQUEST_URI']);
+ // Remove leading path from REQUEST_URI (already contained in $pageaddr).
+ $requestUri = preg_replace('#(.*?/)(feed.*)#', '$2', escape($this->serverInfo['REQUEST_URI']));
+ $data['self_link'] = $pageaddr . $requestUri;
$data['index_url'] = $pageaddr;
$data['usepermalinks'] = $this->usePermalinks === true;
$data['links'] = $linkDisplayed;
return $this->redirectFromReferer(
$request,
$response,
- ['add-shaare', 'shaare'], ['addlink', 'post', 'edit_link'],
+ ['/admin/add-shaare', '/admin/shaare'], ['addlink', 'post', 'edit_link'],
$bookmark->getShortUrl()
);
}
'date' => $dayDatetime,
'date_rss' => $dayDatetime->format(DateTime::RSS),
'date_human' => format_date($dayDatetime, false, true),
- 'absolute_url' => $indexUrl . '/daily?day=' . $day,
+ 'absolute_url' => $indexUrl . 'daily?day=' . $day,
'links' => [],
];
*/
function index_url($server)
{
- $scriptname = $server['SCRIPT_NAME'] ?? '';
+ if (defined('SHAARLI_ROOT_URL') && null !== SHAARLI_ROOT_URL) {
+ return rtrim(SHAARLI_ROOT_URL, '/') . '/';
+ }
+
+ $scriptname = !empty($server['SCRIPT_NAME']) ? $server['SCRIPT_NAME'] : '/';
if (endsWith($scriptname, 'index.php')) {
$scriptname = substr($scriptname, 0, -9);
}
$scriptname = substr($scriptname, 0, -9);
}
- $route = ltrim($server['REQUEST_URI'] ?? '', $scriptname);
+ $route = preg_replace('@^' . $scriptname . '@', '', $server['REQUEST_URI'] ?? '');
if (! empty($server['QUERY_STRING'])) {
return index_url($server) . $route . '?' . $server['QUERY_STRING'];
}
- its values override those defined in `index.php`
- it is wrapped in a PHP comment so that its contents are never served by the web server, regardless of configuration
-**Do not edit configuration options in index.php! Your changes would be lost.**
+**Do not edit configuration options in index.php! Your changes would be lost.**
## Tools menu
## Settings
### Credentials
-
+
_These settings should not be edited_
-- **login**: Login username.
-- **hash**: Generated password hash.
+- **login**: Login username.
+- **hash**: Generated password hash.
- **salt**: Password salt.
### General
-- **title**: Shaarli's instance title.
-- **header_link**: Link to the homepage.
-- **links_per_page**: Number of Shaares displayed per page.
-- **timezone**: See [the list of supported timezones](http://php.net/manual/en/timezones.php).
+- **title**: Shaarli's instance title.
+- **header_link**: Link to the homepage.
+- **links_per_page**: Number of Shaares displayed per page.
+- **timezone**: See [the list of supported timezones](http://php.net/manual/en/timezones.php).
- **enabled_plugins**: List of enabled plugins.
- **default_note_title**: Default title of a new note.
- **retrieve_description** (boolean): If set to true, for every new Shaare Shaarli will try to retrieve the description and keywords from the HTML meta tags.
+- **root_url**: Overrides automatic discovery of Shaarli instance's URL (e.g.) `https://sub.domain.tld/shaarli-folder/`.
### Security
-- **session_protection_disabled**: Disable session cookie hijacking protection (not recommended).
- It might be useful if your IP adress often changes.
-- **ban_after**: Failed login attempts before being IP banned.
-- **ban_duration**: IP ban duration in seconds.
-- **open_shaarli**: Anyone can add a new Shaare while logged out if enabled.
-- **trusted_proxies**: List of trusted IP which won't be banned after failed login attemps. Useful if Shaarli is behind a reverse proxy.
+- **session_protection_disabled**: Disable session cookie hijacking protection (not recommended).
+ It might be useful if your IP adress often changes.
+- **ban_after**: Failed login attempts before being IP banned.
+- **ban_duration**: IP ban duration in seconds.
+- **open_shaarli**: Anyone can add a new Shaare while logged out if enabled.
+- **trusted_proxies**: List of trusted IP which won't be banned after failed login attemps. Useful if Shaarli is behind a reverse proxy.
- **allowed_protocols**: List of allowed protocols in shaare URLs or markdown-rendered descriptions. Useful if you want to store `javascript:` links (bookmarklets) in Shaarli (default: `["ftp", "ftps", "magnet"]`).
### Resources
-- **data_dir**: Data directory.
-- **datastore**: Shaarli's Shaares database file path.
+- **data_dir**: Data directory.
+- **datastore**: Shaarli's Shaares database file path.
- **history**: Shaarli's operation history file path.
-- **updates**: File path for the ran updates file.
-- **log**: Log file path.
-- **update_check**: Last update check file path.
-- **raintpl_tpl**: Templates directory.
-- **raintpl_tmp**: Template engine cache directory.
-- **thumbnails_cache**: Thumbnails cache directory.
-- **page_cache**: Shaarli's internal cache directory.
+- **updates**: File path for the ran updates file.
+- **log**: Log file path.
+- **update_check**: Last update check file path.
+- **raintpl_tpl**: Templates directory.
+- **raintpl_tmp**: Template engine cache directory.
+- **thumbnails_cache**: Thumbnails cache directory.
+- **page_cache**: Shaarli's internal cache directory.
- **ban_file**: Banned IP file path.
### Translation
- **language**: translation language (also see [Translations](Translations))
- - **auto** (default): The translation language is chosen from the browser locale.
- It means that the language can be different for 2 different visitors depending on their locale.
+ - **auto** (default): The translation language is chosen from the browser locale.
+ It means that the language can be different for 2 different visitors depending on their locale.
- **en**: Use the English translation.
- **fr**: Use the French translation.
-- **mode**:
+- **mode**:
- **auto** or **php** (default): Use the PHP implementation of gettext (slower)
- - **gettext**: Use PHP builtin gettext extension
+ - **gettext**: Use PHP builtin gettext extension
(faster, but requires `php-gettext` to be installed and to reload the web server on update)
-- **extension**: Translation extensions for custom themes or plugins.
+- **extension**: Translation extensions for custom themes or plugins.
Must be an associative array: `translation domain => translation path`.
### Updates
-- **check_updates**: Enable or disable update check to the git repository.
-- **check_updates_branch**: Git branch used to check updates (e.g. `stable` or `master`).
+- **check_updates**: Enable or disable update check to the git repository.
+- **check_updates_branch**: Git branch used to check updates (e.g. `stable` or `master`).
- **check_updates_interval**: Look for new version every N seconds (default: every day).
### Privacy
-- **default_private_links**: Check the private checkbox by default for every new Shaare.
-- **hide_public_links**: All Shaares are hidden while logged out.
+- **default_private_links**: Check the private checkbox by default for every new Shaare.
+- **hide_public_links**: All Shaares are hidden while logged out.
- **force_login**: if **hide_public_links** and this are set to `true`, all anonymous users are redirected to the login page.
- **hide_timestamps**: Timestamps are hidden.
- **remember_user_default**: Default state of the login page's *remember me* checkbox
### Feed
-- **rss_permalinks**: Enable this to redirect RSS links to Shaarli's permalinks instead of shaared URL.
+- **rss_permalinks**: Enable this to redirect RSS links to Shaarli's permalinks instead of shaared URL.
- **show_atom**: Display ATOM feed button.
### Thumbnail
-- **enable_thumbnails**: Enable or disable thumbnail display.
+- **enable_thumbnails**: Enable or disable thumbnail display.
- **enable_localcache**: Enable or disable local cache.
## Plugins configuration
-See [Plugins](Plugins.md)
\ No newline at end of file
+See [Plugins](Plugins.md)
$conf = new ConfigManager();
+// Manually override root URL for complex server configurations
+define('SHAARLI_ROOT_URL', $conf->get('general.root_url', null));
+
// In dev mode, throw exception on any warning
if ($conf->get('dev.debug', false)) {
// See all errors (for debugging only)
namespace Shaarli\Feed;
use DateTime;
+use PHPUnit\Framework\TestCase;
use ReferenceLinkDB;
use Shaarli\Bookmark\Bookmark;
use Shaarli\Bookmark\BookmarkFileService;
*
* Unit tests for FeedBuilder.
*/
-class FeedBuilderTest extends \PHPUnit\Framework\TestCase
+class FeedBuilderTest extends TestCase
{
/**
* @var string locale Basque (Spain).
/**
* Called before every test method.
*/
- public static function setUpBeforeClass()
+ public static function setUpBeforeClass(): void
{
$conf = new ConfigManager('tests/utils/config/configJson');
$conf->set('resource.datastore', self::$testDatastore);
'SERVER_NAME' => 'host.tld',
'SERVER_PORT' => '80',
'SCRIPT_NAME' => '/index.php',
- 'REQUEST_URI' => '/index.php?do=feed',
+ 'REQUEST_URI' => '/feed/atom',
);
}
$this->assertEquals(self::$RSS_LANGUAGE, $data['language']);
$this->assertRegExp('/Wed, 03 Aug 2016 09:30:33 \+\d{4}/', $data['last_update']);
$this->assertEquals(true, $data['show_dates']);
- $this->assertEquals('http://host.tld/index.php?do=feed', $data['self_link']);
+ $this->assertEquals('http://host.tld/feed/atom', $data['self_link']);
$this->assertEquals('http://host.tld/', $data['index_url']);
$this->assertFalse($data['usepermalinks']);
$this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links']));
'SERVER_NAME' => 'host.tld',
'SERVER_PORT' => '8080',
'SCRIPT_NAME' => '/~user/shaarli/index.php',
- 'REQUEST_URI' => '/~user/shaarli/index.php?do=feed',
+ 'REQUEST_URI' => '/~user/shaarli/feed/atom',
);
$feedBuilder = new FeedBuilder(
self::$bookmarkService,
$data = $feedBuilder->buildData(FeedBuilder::$FEED_ATOM, null);
$this->assertEquals(
- 'http://host.tld:8080/~user/shaarli/index.php?do=feed',
+ 'http://host.tld:8080/~user/shaarli/feed/atom',
$data['self_link']
);
static::assertInstanceOf(BookmarkRawFormatter::class, $formatter);
static::assertSame($parameters['selection'], $selection);
static::assertTrue($prependNoteUrl);
- static::assertSame('http://shaarli', $indexUrl);
+ static::assertSame('http://shaarli/subfolder/', $indexUrl);
return $bookmarks;
}
use Slim\Http\Request;
use Slim\Http\Response;
-class ToolsControllerTestControllerTest extends TestCase
+class ToolsControllerTest extends TestCase
{
use FrontAdminControllerMockHelper;
static::assertSame(200, $result->getStatusCode());
static::assertSame('tools', (string) $result->getBody());
- static::assertSame('https://shaarli', $assignedVariables['pageabsaddr']);
+ static::assertSame('https://shaarli/', $assignedVariables['pageabsaddr']);
static::assertTrue($assignedVariables['sslenabled']);
}
static::assertSame(200, $result->getStatusCode());
static::assertSame('tools', (string) $result->getBody());
- static::assertSame('http://shaarli', $assignedVariables['pageabsaddr']);
+ static::assertSame('http://shaarli/', $assignedVariables['pageabsaddr']);
static::assertFalse($assignedVariables['sslenabled']);
}
}
static::assertStringContainsString('application/rss', $result->getHeader('Content-Type')[0]);
static::assertSame('dailyrss', (string) $result->getBody());
static::assertSame('Shaarli', $assignedVariables['title']);
- static::assertSame('http://shaarli', $assignedVariables['index_url']);
- static::assertSame('http://shaarli/daily-rss', $assignedVariables['page_url']);
+ static::assertSame('http://shaarli/subfolder/', $assignedVariables['index_url']);
+ static::assertSame('http://shaarli/subfolder/daily-rss', $assignedVariables['page_url']);
static::assertFalse($assignedVariables['hide_timestamps']);
static::assertCount(2, $assignedVariables['days']);
static::assertEquals($dates[0], $day['date']);
static::assertSame($dates[0]->format(\DateTime::RSS), $day['date_rss']);
static::assertSame(format_date($dates[0], false), $day['date_human']);
- static::assertSame('http://shaarli/daily?day='. $dates[0]->format('Ymd'), $day['absolute_url']);
+ static::assertSame('http://shaarli/subfolder/daily?day='. $dates[0]->format('Ymd'), $day['absolute_url']);
static::assertCount(1, $day['links']);
static::assertSame(1, $day['links'][0]['id']);
static::assertSame('http://domain.tld/1', $day['links'][0]['url']);
static::assertEquals($dates[1], $day['date']);
static::assertSame($dates[1]->format(\DateTime::RSS), $day['date_rss']);
static::assertSame(format_date($dates[1], false), $day['date_human']);
- static::assertSame('http://shaarli/daily?day='. $dates[1]->format('Ymd'), $day['absolute_url']);
+ static::assertSame('http://shaarli/subfolder/daily?day='. $dates[1]->format('Ymd'), $day['absolute_url']);
static::assertCount(2, $day['links']);
static::assertSame(2, $day['links'][0]['id']);
static::assertStringContainsString('application/rss', $result->getHeader('Content-Type')[0]);
static::assertSame('dailyrss', (string) $result->getBody());
static::assertSame('Shaarli', $assignedVariables['title']);
- static::assertSame('http://shaarli', $assignedVariables['index_url']);
- static::assertSame('http://shaarli/daily-rss', $assignedVariables['page_url']);
+ static::assertSame('http://shaarli/subfolder/', $assignedVariables['index_url']);
+ static::assertSame('http://shaarli/subfolder/daily-rss', $assignedVariables['page_url']);
static::assertFalse($assignedVariables['hide_timestamps']);
static::assertCount(0, $assignedVariables['days']);
}
$this->container->environment = [
'SERVER_NAME' => 'shaarli',
'SERVER_PORT' => '80',
- 'REQUEST_URI' => '/daily-rss',
+ 'REQUEST_URI' => '/subfolder/daily-rss',
'REMOTE_ADDR' => '1.2.3.4',
+ 'SCRIPT_NAME' => '/subfolder/index.php',
];
$this->container->basePath = '/subfolder';
static::assertSame('/subfolder/login', $result->getHeader('location')[0]);
static::assertSame('UTC', $confSettings['general.timezone']);
- static::assertSame('Shared bookmarks on http://shaarli', $confSettings['general.title']);
+ static::assertSame('Shared bookmarks on http://shaarli/subfolder/', $confSettings['general.title']);
+ }
+
+ /**
+ * Same test as testSaveInstallDefaultValues() but for an instance install in root directory.
+ */
+ public function testSaveInstallDefaultValuesWithoutSubfolder(): void
+ {
+ $confSettings = [];
+
+ $this->container->environment = [
+ 'SERVER_NAME' => 'shaarli',
+ 'SERVER_PORT' => '80',
+ 'REQUEST_URI' => '/install',
+ 'REMOTE_ADDR' => '1.2.3.4',
+ 'SCRIPT_NAME' => '/index.php',
+ ];
+
+ $this->container->basePath = '';
+
+ $request = $this->createMock(Request::class);
+ $response = new Response();
+
+ $this->container->conf->method('set')->willReturnCallback(function (string $key, $value) use (&$confSettings) {
+ $confSettings[$key] = $value;
+ });
+
+ $result = $this->controller->save($request, $response);
+
+ static::assertSame(302, $result->getStatusCode());
+ static::assertSame('/login', $result->getHeader('location')[0]);
+
+ static::assertSame('UTC', $confSettings['general.timezone']);
+ static::assertSame('Shared bookmarks on http://shaarli/', $confSettings['general.title']);
}
}
$result->getHeader('Content-Type')[0]
);
static::assertSame('opensearch', (string) $result->getBody());
- static::assertSame('http://shaarli', $assignedVariables['serverurl']);
+ static::assertSame('http://shaarli/subfolder/', $assignedVariables['serverurl']);
}
}
namespace Shaarli\Http;
+use PHPUnit\Framework\TestCase;
+
require_once 'application/http/HttpUtils.php';
/**
* Unitary tests for index_url()
*/
-class IndexUrlTest extends \PHPUnit\Framework\TestCase
+class IndexUrlTest extends TestCase
{
/**
* If on the main page, remove "index.php" from the URL resource
)
);
}
+
+ /**
+ * The route is stored in REQUEST_URI and subfolder
+ */
+ public function testPageUrlWithRouteUnderSubfolder()
+ {
+ $this->assertEquals(
+ 'http://host.tld/subfolder/picture-wall',
+ page_url(
+ array(
+ 'HTTPS' => 'Off',
+ 'SERVER_NAME' => 'host.tld',
+ 'SERVER_PORT' => '80',
+ 'SCRIPT_NAME' => '/subfolder/index.php',
+ 'REQUEST_URI' => '/subfolder/picture-wall',
+ )
+ )
+ );
+
+ $this->assertEquals(
+ 'http://host.tld/subfolder/admin/picture-wall',
+ page_url(
+ array(
+ 'HTTPS' => 'Off',
+ 'SERVER_NAME' => 'host.tld',
+ 'SERVER_PORT' => '80',
+ 'SCRIPT_NAME' => '/subfolder/admin/index.php',
+ 'REQUEST_URI' => '/subfolder/admin/picture-wall',
+ )
+ )
+ );
+ }
}
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace Shaarli\Http;
+
+use PHPUnit\Framework\TestCase;
+
+/**
+ * Test index_url with SHAARLI_ROOT_URL defined to override automatic retrieval.
+ * This should stay in its dedicated class to make sure to not alter other tests of the suite.
+ */
+class IndexUrlTestWithConstant extends TestCase
+{
+ public static function setUpBeforeClass(): void
+ {
+ define('SHAARLI_ROOT_URL', 'http://other-host.tld/subfolder/');
+ }
+
+ /**
+ * The route is stored in REQUEST_URI and subfolder
+ */
+ public function testIndexUrlWithConstantDefined()
+ {
+ $this->assertEquals(
+ 'http://other-host.tld/subfolder/',
+ index_url(
+ array(
+ 'HTTPS' => 'Off',
+ 'SERVER_NAME' => 'host.tld',
+ 'SERVER_PORT' => '80',
+ 'SCRIPT_NAME' => '/index.php',
+ 'REQUEST_URI' => '/picture-wall',
+ )
+ )
+ );
+
+ $this->assertEquals(
+ 'http://other-host.tld/subfolder/',
+ index_url(
+ array(
+ 'HTTPS' => 'Off',
+ 'SERVER_NAME' => 'host.tld',
+ 'SERVER_PORT' => '80',
+ 'SCRIPT_NAME' => '/admin/index.php',
+ 'REQUEST_URI' => '/admin/picture-wall',
+ )
+ )
+ );
+ }
+}