*/
public function export(Request $request, Response $response): Response
{
+ $this->checkToken($request);
+
$selection = $request->getParam('selection');
if (empty($selection)) {
return $response->write($this->render('export.bookmarks'));
}
-
- /**
- * @param mixed[] $data Variables passed to the template engine
- *
- * @return mixed[] Template data after active plugins render_picwall hook execution.
- */
- protected function executeHooks(array $data): array
- {
- $this->container->pluginManager->executeHooks(
- 'render_tools',
- $data
- );
-
- return $data;
- }
}
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace Shaarli\Front\Controller\Admin;
+
+use Psr\Http\Message\UploadedFileInterface;
+use Slim\Http\Request;
+use Slim\Http\Response;
+
+/**
+ * Class ImportController
+ *
+ * Slim controller used to display Shaarli data import page,
+ * and import bookmarks from Netscape Bookmarks file.
+ */
+class ImportController extends ShaarliAdminController
+{
+ /**
+ * GET /admin/import - Display import page
+ */
+ public function index(Request $request, Response $response): Response
+ {
+ $this->assignView(
+ 'maxfilesize',
+ get_max_upload_size(
+ ini_get('post_max_size'),
+ ini_get('upload_max_filesize'),
+ false
+ )
+ );
+ $this->assignView(
+ 'maxfilesizeHuman',
+ get_max_upload_size(
+ ini_get('post_max_size'),
+ ini_get('upload_max_filesize'),
+ true
+ )
+ );
+ $this->assignView('pagetitle', t('Import') .' - '. $this->container->conf->get('general.title', 'Shaarli'));
+
+ return $response->write($this->render('import'));
+ }
+
+ /**
+ * POST /admin/import - Process import file provided and create bookmarks
+ */
+ public function import(Request $request, Response $response): Response
+ {
+ $this->checkToken($request);
+
+ $file = ($request->getUploadedFiles() ?? [])['filetoupload'] ?? null;
+ if (!$file instanceof UploadedFileInterface) {
+ $this->saveErrorMessage(t('No import file provided.'));
+
+ return $this->redirect($response, '/admin/import');
+ }
+
+
+ // Import bookmarks from an uploaded file
+ if (0 === $file->getSize()) {
+ // The file is too big or some form field may be missing.
+ $msg = sprintf(
+ t(
+ 'The file you are trying to upload is probably bigger than what this webserver can accept'
+ .' (%s). Please upload in smaller chunks.'
+ ),
+ get_max_upload_size(ini_get('post_max_size'), ini_get('upload_max_filesize'))
+ );
+ $this->saveErrorMessage($msg);
+
+ return $this->redirect($response, '/admin/import');
+ }
+
+ $status = $this->container->netscapeBookmarkUtils->import($request->getParams(), $file);
+
+ $this->saveSuccessMessage($status);
+
+ return $this->redirect($response, '/admin/import');
+ }
+}
use DateTimeZone;
use Exception;
use Katzgrau\KLogger\Logger;
+use Psr\Http\Message\UploadedFileInterface;
use Psr\Log\LogLevel;
use Shaarli\Bookmark\Bookmark;
use Shaarli\Bookmark\BookmarkServiceInterface;
/**
* Imports Web bookmarks from an uploaded Netscape bookmark dump
*
- * @param array $post Server $_POST parameters
- * @param array $files Server $_FILES parameters
+ * @param array $post Server $_POST parameters
+ * @param UploadedFileInterface $file File in PSR-7 object format
*
* @return string Summary of the bookmark import status
*/
- public function import($post, $files)
+ public function import($post, UploadedFileInterface $file)
{
$start = time();
- $filename = $files['filetoupload']['name'];
- $filesize = $files['filetoupload']['size'];
- $data = file_get_contents($files['filetoupload']['tmp_name']);
+ $filename = $file->getClientFilename();
+ $filesize = $file->getSize();
+ $data = (string) $file->getStream();
if (preg_match('/<!DOCTYPE NETSCAPE-Bookmark-file-1>/i', $data) === 0) {
- return self::importStatus($filename, $filesize);
+ return $this->importStatus($filename, $filesize);
}
// Overwrite existing bookmarks?
http://<replace_domain>/daily
http://<replace_domain>/?post
http://<replace_domain>/admin/export
-http://<replace_domain>/?do=import
+http://<replace_domain>/admin/import
http://<replace_domain>/login
http://<replace_domain>/picture-wall
http://<replace_domain>/?do=pluginadmin
}
if ($targetPage == Router::$PAGE_IMPORT) {
- // Upload a Netscape bookmark dump to import its contents
-
- if (! isset($_POST['token']) || ! isset($_FILES['filetoupload'])) {
- // Show import dialog
- $PAGE->assign(
- 'maxfilesize',
- get_max_upload_size(
- ini_get('post_max_size'),
- ini_get('upload_max_filesize'),
- false
- )
- );
- $PAGE->assign(
- 'maxfilesizeHuman',
- get_max_upload_size(
- ini_get('post_max_size'),
- ini_get('upload_max_filesize'),
- true
- )
- );
- $PAGE->assign('pagetitle', t('Import') .' - '. $conf->get('general.title', 'Shaarli'));
- $PAGE->renderPage('import');
- exit;
- }
-
- // Import bookmarks from an uploaded file
- if (isset($_FILES['filetoupload']['size']) && $_FILES['filetoupload']['size'] == 0) {
- // The file is too big or some form field may be missing.
- $msg = sprintf(
- t(
- 'The file you are trying to upload is probably bigger than what this webserver can accept'
- .' (%s). Please upload in smaller chunks.'
- ),
- get_max_upload_size(ini_get('post_max_size'), ini_get('upload_max_filesize'))
- );
- echo '<script>alert("'. $msg .'");document.location=\'./?do='.Router::$PAGE_IMPORT .'\';</script>';
- exit;
- }
- if (! $sessionManager->checkToken($_POST['token'])) {
- die('Wrong token.');
- }
- $netscapeBookmarkUtils = new NetscapeBookmarkUtils($bookmarkService, $conf, $history);
- $status = $netscapeBookmarkUtils->import($_POST, $_FILES);
- echo '<script>alert("'.$status.'");document.location=\'./?do='
- .Router::$PAGE_IMPORT .'\';</script>';
+ header('Location: ./admin/import');
exit;
}
$this->get('/admin/shaare/{id:[0-9]+}/pin', '\Shaarli\Front\Controller\Admin\ManageShaareController:pinBookmark');
$this->get('/admin/export', '\Shaarli\Front\Controller\Admin\ExportController:index');
$this->post('/admin/export', '\Shaarli\Front\Controller\Admin\ExportController:export');
+ $this->get('/admin/import', '\Shaarli\Front\Controller\Admin\ImportController:index');
+ $this->post('/admin/import', '\Shaarli\Front\Controller\Admin\ImportController:import');
$this->get('/links-per-page', '\Shaarli\Front\Controller\Admin\SessionFilterController:linksPerPage');
$this->get('/visibility/{visibility}', '\Shaarli\Front\Controller\Admin\SessionFilterController:visibility');
declare(strict_types=1);
-namespace front\controller\admin;
+namespace Shaarli\Front\Controller\Admin;
use PHPUnit\Framework\TestCase;
use Shaarli\Bookmark\Bookmark;
use Shaarli\Formatter\BookmarkFormatter;
use Shaarli\Formatter\BookmarkRawFormatter;
-use Shaarli\Front\Controller\Admin\ExportController;
-use Shaarli\Front\Controller\Admin\FrontAdminControllerMockHelper;
use Shaarli\Netscape\NetscapeBookmarkUtils;
use Shaarli\Security\SessionManager;
use Slim\Http\Request;
$request = $this->createMock(Request::class);
$response = new Response();
- $this->container->sessionManager = $this->createMock(SessionManager::class);
$this->container->sessionManager
->expects(static::once())
->method('setSessionParameter')
->willThrowException(new \Exception($message = 'error message'));
;
- $this->container->sessionManager = $this->createMock(SessionManager::class);
$this->container->sessionManager
->expects(static::once())
->method('setSessionParameter')
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace Shaarli\Front\Controller\Admin;
+
+use PHPUnit\Framework\TestCase;
+use Psr\Http\Message\UploadedFileInterface;
+use Shaarli\Netscape\NetscapeBookmarkUtils;
+use Shaarli\Security\SessionManager;
+use Slim\Http\Request;
+use Slim\Http\Response;
+use Slim\Http\UploadedFile;
+
+class ImportControllerTest extends TestCase
+{
+ use FrontAdminControllerMockHelper;
+
+ /** @var ImportController */
+ protected $controller;
+
+ public function setUp(): void
+ {
+ $this->createContainer();
+
+ $this->controller = new ImportController($this->container);
+ }
+
+ /**
+ * Test displaying import page
+ */
+ public function testIndex(): void
+ {
+ $assignedVariables = [];
+ $this->assignTemplateVars($assignedVariables);
+
+ $request = $this->createMock(Request::class);
+ $response = new Response();
+
+ $result = $this->controller->index($request, $response);
+
+ static::assertSame(200, $result->getStatusCode());
+ static::assertSame('import', (string) $result->getBody());
+
+ static::assertSame('Import - Shaarli', $assignedVariables['pagetitle']);
+ static::assertIsInt($assignedVariables['maxfilesize']);
+ static::assertRegExp('/\d+[KM]iB/', $assignedVariables['maxfilesizeHuman']);
+ }
+
+ /**
+ * Test importing a file with default and valid parameters
+ */
+ public function testImportDefault(): void
+ {
+ $parameters = [
+ 'abc' => 'def',
+ 'other' => 'param',
+ ];
+
+ $requestFile = new UploadedFile('file', 'name', 'type', 123);
+
+ $request = $this->createMock(Request::class);
+ $request->method('getParams')->willReturnCallback(function () use ($parameters) {
+ return $parameters;
+ });
+ $request->method('getUploadedFiles')->willReturn(['filetoupload' => $requestFile]);
+ $response = new Response();
+
+ $this->container->netscapeBookmarkUtils = $this->createMock(NetscapeBookmarkUtils::class);
+ $this->container->netscapeBookmarkUtils
+ ->expects(static::once())
+ ->method('import')
+ ->willReturnCallback(
+ function (
+ array $post,
+ UploadedFileInterface $file
+ ) use ($parameters, $requestFile): string {
+ static::assertSame($parameters, $post);
+ static::assertSame($requestFile, $file);
+
+ return 'status';
+ }
+ )
+ ;
+
+ $this->container->sessionManager
+ ->expects(static::once())
+ ->method('setSessionParameter')
+ ->with(SessionManager::KEY_SUCCESS_MESSAGES, ['status'])
+ ;
+
+ $result = $this->controller->import($request, $response);
+
+ static::assertSame(302, $result->getStatusCode());
+ static::assertSame(['/subfolder/admin/import'], $result->getHeader('location'));
+ }
+
+ /**
+ * Test posting an import request - without import file
+ */
+ public function testImportFileMissing(): void
+ {
+ $request = $this->createMock(Request::class);
+ $response = new Response();
+
+ $this->container->sessionManager
+ ->expects(static::once())
+ ->method('setSessionParameter')
+ ->with(SessionManager::KEY_ERROR_MESSAGES, ['No import file provided.'])
+ ;
+
+ $result = $this->controller->import($request, $response);
+
+ static::assertSame(302, $result->getStatusCode());
+ static::assertSame(['/subfolder/admin/import'], $result->getHeader('location'));
+ }
+
+ /**
+ * Test posting an import request - with an empty file
+ */
+ public function testImportEmptyFile(): void
+ {
+ $requestFile = new UploadedFile('file', 'name', 'type', 0);
+
+ $request = $this->createMock(Request::class);
+ $request->method('getUploadedFiles')->willReturn(['filetoupload' => $requestFile]);
+ $response = new Response();
+
+ $this->container->netscapeBookmarkUtils = $this->createMock(NetscapeBookmarkUtils::class);
+ $this->container->netscapeBookmarkUtils->expects(static::never())->method('filterAndFormat');
+
+ $this->container->sessionManager
+ ->expects(static::once())
+ ->method('setSessionParameter')
+ ->willReturnCallback(function (string $key, array $value): SessionManager {
+ static::assertSame(SessionManager::KEY_ERROR_MESSAGES, $key);
+ static::assertStringStartsWith('The file you are trying to upload is probably bigger', $value[0]);
+
+ return $this->container->sessionManager;
+ })
+ ;
+
+ $result = $this->controller->import($request, $response);
+
+ static::assertSame(302, $result->getStatusCode());
+ static::assertSame(['/subfolder/admin/import'], $result->getHeader('location'));
+ }
+}
use DateTime;
use PHPUnit\Framework\TestCase;
+use Psr\Http\Message\UploadedFileInterface;
use Shaarli\Bookmark\Bookmark;
use Shaarli\Bookmark\BookmarkFileService;
use Shaarli\Bookmark\BookmarkFilter;
use Shaarli\Config\ConfigManager;
use Shaarli\History;
+use Slim\Http\UploadedFile;
/**
* Utility function to load a file's metadata in a $_FILES-like array
*
* @param string $filename Basename of the file
*
- * @return array A $_FILES-like array
+ * @return UploadedFileInterface Upload file in PSR-7 compatible object
*/
function file2array($filename)
{
- return array(
- 'filetoupload' => array(
- 'name' => $filename,
- 'tmp_name' => __DIR__ . '/input/' . $filename,
- 'size' => filesize(__DIR__ . '/input/' . $filename)
- )
+ return new UploadedFile(
+ __DIR__ . '/input/' . $filename,
+ $filename,
+ null,
+ filesize(__DIR__ . '/input/' . $filename)
);
}
<body>
{include="page.header"}
-<form method="POST" action="{$base_path}/?do=import" enctype="multipart/form-data" name="uploadform" id="uploadform">
+<form method="POST" action="{$base_path}/admin/import" enctype="multipart/form-data" name="uploadform" id="uploadform">
<div class="pure-g">
<div class="pure-u-lg-1-4 pure-u-1-24"></div>
<div class="pure-u-lg-1-2 pure-u-22-24 page-form page-form-complete">
</a>
</div>
<div class="tools-item">
- <a href="{$base_path}/?do=import"
+ <a href="{$base_path}/admin/import"
title="{'Import Netscape HTML bookmarks (as exported from Firefox, Chrome, Opera, delicious...)'|t}">
<span class="pure-button pure-u-lg-2-3 pure-u-3-4">{'Import links'|t}</span>
</a>
{include="page.header"}
<div id="uploaddiv">
Import Netscape HTML bookmarks (as exported from Firefox/Chrome/Opera/Delicious/Diigo...) (Max: {$maxfilesize}).
- <form method="POST" action="{$base_path}/?do=import" enctype="multipart/form-data"
+ <form method="POST" action="{$base_path}/admin/import" enctype="multipart/form-data"
name="uploadform" id="uploadform">
<input type="hidden" name="token" value="{$token}">
<input type="hidden" name="MAX_FILE_SIZE" value="{$maxfilesize}">
<br><br>{/if}
<a href="{$base_path}/admin/tags"><b>Rename/delete tags</b><span>: Rename or delete a tag in all links</span></a>
<br><br>
- <a href="{$base_path}/?do=import"><b>Import</b><span>: Import Netscape html bookmarks (as exported from Firefox, Chrome, Opera, delicious...)</span></a>
+ <a href="{$base_path}/admin/import"><b>Import</b><span>: Import Netscape html bookmarks (as exported from Firefox, Chrome, Opera, delicious...)</span></a>
<br><br>
<a href="{$base_path}/admin/export"><b>Export</b><span>: Export Netscape html bookmarks (which can be imported in Firefox, Chrome, Opera, delicious...)</span></a>
<br><br>