From 9c27b01d6e863e209d00e4e75fb81d008fa2e8ba Mon Sep 17 00:00:00 2001
From: Torsten Oppermann <torsten@sgalinski.de>
Date: Fri, 10 Mar 2017 08:13:07 +0100
Subject: [PATCH] [TASK] Starting integrating new backend look&feel

---
 .../ViewHelpers/Backend/ControlViewHelper.php |  54 +++++++
 .../Backend/EditOnClickViewHelper.php         |  47 ++++++
 .../ViewHelpers/Backend/IconViewHelper.php    |  61 ++++++++
 .../Widget/Controller/PaginateController.php  |  96 ++++++++++++
 .../Backend/Widget/PaginateViewHelper.php     |  64 ++++++++
 Classes/ViewHelpers/SetViewHelper.php         |  73 +++++++++
 Classes/ViewHelpers/Widget/UriViewHelper.php  |  66 ++++++++
 .../Private/Backend/Partials/ButtonBar.html   |  16 ++
 Resources/Private/Templates/Queue/Index.html  | 145 +++++++-----------
 .../Backend/Widget/Paginate/Index.html        | 135 ++++++++++++++++
 Resources/Public/Scripts/Backend.js           |  25 +++
 11 files changed, 696 insertions(+), 86 deletions(-)
 create mode 100644 Classes/ViewHelpers/Backend/ControlViewHelper.php
 create mode 100644 Classes/ViewHelpers/Backend/EditOnClickViewHelper.php
 create mode 100644 Classes/ViewHelpers/Backend/IconViewHelper.php
 create mode 100644 Classes/ViewHelpers/Backend/Widget/Controller/PaginateController.php
 create mode 100644 Classes/ViewHelpers/Backend/Widget/PaginateViewHelper.php
 create mode 100644 Classes/ViewHelpers/SetViewHelper.php
 create mode 100644 Classes/ViewHelpers/Widget/UriViewHelper.php
 create mode 100644 Resources/Private/Backend/Partials/ButtonBar.html
 create mode 100644 Resources/Private/Templates/ViewHelpers/Backend/Widget/Paginate/Index.html

diff --git a/Classes/ViewHelpers/Backend/ControlViewHelper.php b/Classes/ViewHelpers/Backend/ControlViewHelper.php
new file mode 100644
index 00000000..52e14975
--- /dev/null
+++ b/Classes/ViewHelpers/Backend/ControlViewHelper.php
@@ -0,0 +1,54 @@
+<?php
+
+namespace SGalinski\SgMail\ViewHelpers\Backend;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) sgalinski Internet Services (https://www.sgalinski.de)
+ *
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+use TYPO3\CMS\Backend\Utility\BackendUtility;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper;
+use TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList;
+
+/**
+ * Class ControlViewHelper
+ **/
+class ControlViewHelper extends AbstractViewHelper {
+	/**
+	 * Renders the control buttons for the specified record
+	 *
+	 * @param string $table
+	 * @param mixed $row
+	 * @return string
+	 * @throws \InvalidArgumentException
+	 * @throws \UnexpectedValueException
+	 */
+	public function render($table, $row) {
+		/** @var DatabaseRecordList $databaseRecordList */
+		$databaseRecordList = GeneralUtility::makeInstance(DatabaseRecordList::class);
+		$pageInfo = BackendUtility::readPageAccess($row['pid'], $GLOBALS['BE_USER']->getPagePermsClause(1));
+		$databaseRecordList->calcPerms = $GLOBALS['BE_USER']->calcPerms($pageInfo);
+		return $databaseRecordList->makeControl($table, $row);
+	}
+}
diff --git a/Classes/ViewHelpers/Backend/EditOnClickViewHelper.php b/Classes/ViewHelpers/Backend/EditOnClickViewHelper.php
new file mode 100644
index 00000000..4df9631d
--- /dev/null
+++ b/Classes/ViewHelpers/Backend/EditOnClickViewHelper.php
@@ -0,0 +1,47 @@
+<?php
+
+namespace SGalinski\SgMail\ViewHelpers\Backend;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) sgalinski Internet Services (https://www.sgalinski.de)
+ *
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+use TYPO3\CMS\Backend\Utility\BackendUtility;
+use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper;
+
+/**
+ * Class EditOnClickViewHelper
+ **/
+class EditOnClickViewHelper extends AbstractViewHelper {
+	/**
+	 * Renders the onclick script for editing a record
+	 *
+	 * @param string $table
+	 * @param int $uid
+	 * @param boolean $new
+	 * @return string
+	 */
+	public function render($table, $uid, $new = FALSE) {
+		return BackendUtility::editOnClick('&edit[' . $table . '][' . $uid . ']=' . ($new ? 'new' : 'edit'), '', -1);
+	}
+}
diff --git a/Classes/ViewHelpers/Backend/IconViewHelper.php b/Classes/ViewHelpers/Backend/IconViewHelper.php
new file mode 100644
index 00000000..37d0e5d3
--- /dev/null
+++ b/Classes/ViewHelpers/Backend/IconViewHelper.php
@@ -0,0 +1,61 @@
+<?php
+
+namespace SGalinski\SgMail\ViewHelpers\Backend;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) sgalinski Internet Services (https://www.sgalinski.de)
+ *
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+use TYPO3\CMS\Backend\Utility\BackendUtility;
+use TYPO3\CMS\Core\Imaging\Icon;
+use TYPO3\CMS\Core\Imaging\IconFactory;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper;
+
+/**
+ * Class IconViewHelper
+ **/
+class IconViewHelper extends AbstractViewHelper {
+	/**
+	 * Renders the icon for the specified record
+	 *
+	 * @param string $table
+	 * @param mixed $row
+	 * @param boolean $clickMenu
+	 * @return string
+	 * @throws \InvalidArgumentException
+	 */
+	public function render($table, $row, $clickMenu = TRUE) {
+		/** @var IconFactory $iconFactory */
+		$iconFactory = GeneralUtility::makeInstance(IconFactory::class);
+		$toolTip = BackendUtility::getRecordToolTip($row, $table);
+		$iconImg = '<span ' . $toolTip . '>'
+			. $iconFactory->getIconForRecord($table, $row, Icon::SIZE_SMALL)->render()
+			. '</span>';
+		if ($clickMenu) {
+			return BackendUtility::wrapClickMenuOnIcon($iconImg, $table, $row['uid']);
+		} else {
+			return $iconImg;
+		}
+	}
+}
diff --git a/Classes/ViewHelpers/Backend/Widget/Controller/PaginateController.php b/Classes/ViewHelpers/Backend/Widget/Controller/PaginateController.php
new file mode 100644
index 00000000..c2206979
--- /dev/null
+++ b/Classes/ViewHelpers/Backend/Widget/Controller/PaginateController.php
@@ -0,0 +1,96 @@
+<?php
+
+namespace SGalinski\SgMail\ViewHelpers\Backend\Widget\Controller;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) sgalinski Internet Services (https://www.sgalinski.de)
+ *
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+/**
+ * Class PaginateController
+ */
+class PaginateController extends \TYPO3\CMS\Fluid\ViewHelpers\Be\Widget\Controller\PaginateController {
+
+	/**
+	 * @var mixed
+	 */
+	protected $objects;
+
+	/**
+	 * Renders the paginator
+	 *
+	 * @param int $currentPage
+	 * @return void
+	 */
+	public function indexAction($currentPage = 1) {
+		// set current page
+		$this->currentPage = (int) $currentPage;
+		if ($this->currentPage < 1) {
+			$this->currentPage = 1;
+		}
+		if ($this->currentPage > $this->numberOfPages) {
+			// set $modifiedObjects to NULL if the page does not exist
+			$modifiedObjects = NULL;
+		} else {
+			// modify query
+			$this->itemsPerPage = (int) $this->configuration['itemsPerPage'];
+			$this->offset = $this->itemsPerPage * ($this->currentPage - 1);
+			if (is_array($this->objects)) {
+				$modifiedObjects = [];
+				for ($index = $this->offset; $index < $this->offset + $this->itemsPerPage; $index++) {
+					if (isset($this->objects[$index])) {
+						$modifiedObjects[] = $this->objects[$index];
+					} else {
+						break;
+					}
+				}
+			} else {
+				$query = $this->objects->getQuery();
+				$query->setLimit($this->itemsPerPage);
+				if ($this->currentPage > 1) {
+					$query->setOffset($this->offset);
+				}
+				$modifiedObjects = $query->execute();
+			}
+		}
+		$this->view->assign(
+			'contentArguments', [
+				$this->widgetConfiguration['as'] => $modifiedObjects
+			]
+		);
+		$this->view->assign('configuration', $this->configuration);
+		$this->view->assign('pagination', $this->buildPagination());
+	}
+
+	/**
+	 * Returns an array with the keys "pages", "current", "numberOfPages",
+	 * "nextPage" & "previousPage"
+	 *
+	 * @return array
+	 */
+	protected function buildPagination() {
+		$pagination = parent::buildPagination();
+		$pagination['totalObjects'] = count($this->objects);
+		return $pagination;
+	}
+}
diff --git a/Classes/ViewHelpers/Backend/Widget/PaginateViewHelper.php b/Classes/ViewHelpers/Backend/Widget/PaginateViewHelper.php
new file mode 100644
index 00000000..952d1dc7
--- /dev/null
+++ b/Classes/ViewHelpers/Backend/Widget/PaginateViewHelper.php
@@ -0,0 +1,64 @@
+<?php
+namespace SGalinski\SgMail\ViewHelpers\Backend\Widget;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) sgalinski Internet Services (https://www.sgalinski.de)
+ *
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+use SGalinski\SgRoutes\ViewHelpers\Backend\Widget\Controller\PaginateController;
+use TYPO3\CMS\Fluid\Core\Widget\AbstractWidgetViewHelper;
+
+/**
+ * Class PaginateViewHelper
+ */
+class PaginateViewHelper extends AbstractWidgetViewHelper {
+	/**
+	 * @var \SGalinski\SgRoutes\ViewHelpers\Backend\Widget\Controller\PaginateController
+	 */
+	protected $controller;
+
+	/**
+	 * Initializes the controller
+	 *
+	 * @param PaginateController $controller
+	 */
+	public function injectPaginateController(
+		PaginateController $controller
+	) {
+		$this->controller = $controller;
+	}
+
+	/**
+	 * Renders the paginator
+	 *
+	 * @param mixed $objects
+	 * @param string $as
+	 * @param array $configuration
+	 * @return string
+	 */
+	public function render(
+		$objects, $as,
+		array $configuration = ['itemsPerPage' => 10, 'insertAbove' => FALSE, 'insertBelow' => TRUE, 'recordsLabel' => '']
+	) {
+		return $this->initiateSubRequest();
+	}
+}
diff --git a/Classes/ViewHelpers/SetViewHelper.php b/Classes/ViewHelpers/SetViewHelper.php
new file mode 100644
index 00000000..83a8b2e6
--- /dev/null
+++ b/Classes/ViewHelpers/SetViewHelper.php
@@ -0,0 +1,73 @@
+<?php
+
+namespace SGalinski\SgMail\ViewHelpers;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) sgalinski Internet Services (https://www.sgalinski.de)
+ *
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+use TYPO3\CMS\Extbase\Reflection\ObjectAccess;
+use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper;
+
+/**
+ * Class SetViewHelper
+ **/
+class SetViewHelper extends AbstractViewHelper {
+
+	/**
+	 * Set (override) the variable in $name.
+	 *
+	 * @param string $name
+	 * @param mixed $value
+	 * @return void
+	 * @throws \TYPO3\CMS\Fluid\Core\ViewHelper\Exception\InvalidVariableException
+	 */
+	public function render($name, $value = NULL) {
+		if (NULL === $value) {
+			$value = $this->renderChildren();
+		}
+		if (FALSE === strpos($name, '.')) {
+			if (TRUE === $this->templateVariableContainer->exists($name)) {
+				$this->templateVariableContainer->remove($name);
+			}
+			$this->templateVariableContainer->add($name, $value);
+		} elseif (1 === substr_count($name, '.')) {
+			$parts = explode('.', $name);
+			$objectName = array_shift($parts);
+			$path = implode('.', $parts);
+			if (FALSE === $this->templateVariableContainer->exists($objectName)) {
+				return NULL;
+			}
+			$object = $this->templateVariableContainer->get($objectName);
+			try {
+				ObjectAccess::setProperty($object, $path, $value);
+				// Note: re-insert the variable to ensure unreferenced values like arrays also get updated
+				$this->templateVariableContainer->remove($objectName);
+				$this->templateVariableContainer->add($objectName, $object);
+			} catch (\Exception $error) {
+				return NULL;
+			}
+		}
+		return NULL;
+	}
+}
diff --git a/Classes/ViewHelpers/Widget/UriViewHelper.php b/Classes/ViewHelpers/Widget/UriViewHelper.php
new file mode 100644
index 00000000..a34f5185
--- /dev/null
+++ b/Classes/ViewHelpers/Widget/UriViewHelper.php
@@ -0,0 +1,66 @@
+<?php
+
+namespace SGalinski\SgMail\ViewHelpers\Widget;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) sgalinski Internet Services (https://www.sgalinski.de)
+ *
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+
+/**
+ * Class UriViewHelper
+ */
+class UriViewHelper extends \TYPO3\CMS\Fluid\ViewHelpers\Widget\UriViewHelper {
+
+	/**
+	 * Get the URI for a non-AJAX Request.
+	 *
+	 * @return string the Widget URI
+	 */
+	protected function getWidgetUri() {
+		$uriBuilder = $this->controllerContext->getUriBuilder();
+		$argumentPrefix = $this->controllerContext->getRequest()->getArgumentPrefix();
+		$arguments = $this->hasArgument('arguments') ? $this->arguments['arguments'] : [];
+		if ($this->hasArgument('action')) {
+			$arguments['action'] = $this->arguments['action'];
+		}
+		if ($this->arguments['format'] !== '' && $this->hasArgument('format')) {
+			$arguments['format'] = $this->arguments['format'];
+		}
+		$parentNamespace = $this->controllerContext->getRequest()->getWidgetContext()->getParentPluginNamespace();
+		$parentArguments = GeneralUtility::_GP($parentNamespace);
+		$allArguments = [$argumentPrefix => $arguments];
+		if ($parentArguments && isset($parentArguments['filters'])) {
+			$allArguments[$parentNamespace . '[filters]'] = $parentArguments['filters'];
+		}
+		return $uriBuilder->reset()
+			->setArguments($allArguments)
+			->setSection($this->arguments['section'])
+			->setAddQueryString(TRUE)
+			->setAddQueryStringMethod($this->arguments['addQueryStringMethod'])
+			->setArgumentsToBeExcludedFromQueryString([$argumentPrefix, 'cHash'])
+			->setFormat($this->arguments['format'])
+			->build();
+	}
+}
diff --git a/Resources/Private/Backend/Partials/ButtonBar.html b/Resources/Private/Backend/Partials/ButtonBar.html
new file mode 100644
index 00000000..128d6a5c
--- /dev/null
+++ b/Resources/Private/Backend/Partials/ButtonBar.html
@@ -0,0 +1,16 @@
+<div class="btn-toolbar" role="toolbar" aria-label="">
+	<f:for each="{buttons}" as="buttonGroup">
+		<f:if condition="{buttonGroup -> f:count()} > 1">
+			<f:then>
+				<div class="btn-group" role="group" aria-label="">
+					<f:for each="{buttonGroup}" as="button">
+						{button -> f:format.raw()}
+					</f:for>
+				</div>
+			</f:then>
+			<f:else>
+				{buttonGroup.0 -> f:format.raw()}
+			</f:else>
+		</f:if>
+	</f:for>
+</div>
diff --git a/Resources/Private/Templates/Queue/Index.html b/Resources/Private/Templates/Queue/Index.html
index 6da59377..02acd3a5 100644
--- a/Resources/Private/Templates/Queue/Index.html
+++ b/Resources/Private/Templates/Queue/Index.html
@@ -7,92 +7,65 @@
 
 <f:section name="main">
 	<f:flashMessages renderMode="div" />
-	<f:widget.paginate objects="{queue}" as="paginatedMails" configuration="{itemsPerPage: 10, insertAbove: 1, insertBelow: 1, maximumNumberOfLinks: 10}">
-		<table class="table table-hover">
-			<thead>
-				<tr>
-					<th><f:translate key="backend.toAddress" /></th>
-					<th><f:translate key="backend.fromAddress" /></th>
-					<th><f:translate key="backend.fromName" /></th>
-					<th><f:translate key="backend.sent" /></th>
-					<th><f:translate key="backend.sending_time" /></th>
-					<th>cc</th>
-					<th>bcc</th>
-					<th><f:translate key="backend.replyTo" /></th>
-					<th><f:translate key="backend.priority" /></th>
-					<th><f:translate key="backend.subject" /></th>
-					<th></th>
-					<th></th>
-				</tr>
-			</thead>
 
-			<f:for each="{paginatedMails}" as="mail">
-				<tr class="accordion">
-					<td>{mail.toAddress}</td>
-					<td>{mail.fromAddress}</td>
-					<td>{mail.fromName}</td>
-					<td>
-						<f:if condition="{mail.sent} == '0'">
-							<f:then>
-								<f:translate key="backend.not_sent" />
-							</f:then>
-							<f:else>
-								<f:translate key="backend.sent" />
-							</f:else>
-						</f:if>
-					</td>
-					<td>
-						<f:format.date format="d.m.Y h:i:s">{mail.sendingTime}</f:format.date>
-					</td>
-					<td>
-						{mail.ccAddresses}
-					</td>
-					<td>
-						{mail.bccAddresses}
-					</td>
-					<td>
-						{mail.replyTo}
-					</td>
-					<td>
-						{mail.priority}
-					</td>
-					<td>
-						{mail.mailSubject}
-					</td>
-					<td>
-						<f:if condition="{mail.sent} == '0'">
-							<f:then>
-								<f:link.action class="btn-send-now btn btn-primary" controller="Queue" action="sendMail" arguments="{uid: mail.uid}">Send Now</f:link.action>
-							</f:then>
-							<f:else>
-								<f:link.action class="btn-resend btn btn-warning" controller="Queue" action="sendMail" arguments="{uid: mail.uid}">Send Again</f:link.action>
-							</f:else>
-						</f:if>
-					</td>
-					<td>
-						<button class="btn-toggle btn btn-primary" data-uid="{mail.uid}"><f:translate key="backend.showBody" /></button>
-					</td>
-				</tr>
-				<!-- Modal -->
-				<div class="modal fade" id="toggle-{mail.uid}" tabindex="-1" role="dialog" aria-labelledby="login-modal-label">
-					<div class="modal-dialog" role="document">
-						<div class="modal-content">
-							<div class="modal-header">
-								<button type="button" class="close" data-dismiss="modal" aria-label="Close">
-									<span aria-hidden="true">&times;</span></button>
-								<h4 class="modal-title" id="login-modal-label">{mail.mailSubject}</h4>
-							</div>
-							<div class="modal-body" id="login-modal-body">
-								<div class="modalcol modalcol-left">
-									<f:format.html>
-										{mail.mailBody}
-									</f:format.html>
+	<f:if condition="{queue}">
+		<div class="panel panel-default recordlist">
+			<div class="table-fit">
+				<table data-table="tx_sgmail_domain_model_mail" class="table table-striped table-hover">
+					<sg:backend.widget.paginate objects="{queue}" as="paginatedMails" configuration="{insertAbove: 1, itemsPerPage: 20}">
+						<tbody>
+							<f:for each="{paginatedMails}" as="mail">
+								{sg:backend.editOnClick(table: 'tx_sgmail_domain_model_mail', uid: mail.uid) -> sg:set(name: 'editOnClick')}
+								<tr data-uid="{mail.uid}">
+									<td nowrap="nowrap" class="col-icon">
+										<sg:backend.icon table="tx_sgmail_domain_model_mail" row="{mail}" />
+									</td>
+									<td nowrap="nowrap">
+										<a href="#" onclick="{editOnClick}">
+											<span>{mail.uid}</span>
+										</a>
+									</td>
+									<td nowrap="nowrap" class="col-control">
+										<sg:backend.control table="tx_sgmail_domain_model_mail" row="{mail}" />
+									</td>
+									<td>
+										<f:if condition="{mail.sent} == '0'">
+											<f:then>
+												<f:link.action class="btn-send-now btn btn-primary" controller="Queue" action="sendMail" arguments="{uid: mail.uid}">Send Now</f:link.action>
+											</f:then>
+											<f:else>
+												<f:link.action class="btn-resend btn btn-warning" controller="Queue" action="sendMail" arguments="{uid: mail.uid}">Send Again</f:link.action>
+											</f:else>
+										</f:if>
+									</td>
+									<td>
+										<button class="btn-toggle btn btn-primary" data-uid="{mail.uid}">
+											<f:translate key="backend.showBody" /></button>
+									</td>
+								</tr><!-- Modal -->
+								<div class="modal fade" id="toggle-{mail.uid}" tabindex="-1" role="dialog" aria-labelledby="login-modal-label">
+									<div class="modal-dialog" role="document">
+										<div class="modal-content">
+											<div class="modal-header">
+												<button type="button" class="close" data-dismiss="modal" aria-label="Close">
+													<span aria-hidden="true">&times;</span></button>
+												<h4 class="modal-title" id="login-modal-label">{mail.mailSubject}</h4>
+											</div>
+											<div class="modal-body" id="login-modal-body">
+												<div class="modalcol modalcol-left">
+													<f:format.html>
+														{mail.mailBody}
+													</f:format.html>
+												</div>
+											</div>
+										</div>
+									</div>
 								</div>
-							</div>
-						</div>
-					</div>
-				</div>
-			</f:for>
-		</table>
-	</f:widget.paginate>
+							</f:for>
+						</tbody>
+					</sg:backend.widget.paginate>
+				</table>
+			</div>
+		</div>
+	</f:if>
 </f:section>
diff --git a/Resources/Private/Templates/ViewHelpers/Backend/Widget/Paginate/Index.html b/Resources/Private/Templates/ViewHelpers/Backend/Widget/Paginate/Index.html
new file mode 100644
index 00000000..6ee74c1a
--- /dev/null
+++ b/Resources/Private/Templates/ViewHelpers/Backend/Widget/Paginate/Index.html
@@ -0,0 +1,135 @@
+{namespace core=TYPO3\CMS\Core\ViewHelpers}
+{namespace sg=SGalinski\SgMail\ViewHelpers}
+
+<f:if condition="{configuration.insertAbove}">
+	<thead>
+		<tr>
+			<td colspan="6">
+				<f:render section="paginator" arguments="{pagination: pagination, position:'top', recordsLabel: configuration.recordsLabel}" />
+			</td>
+		</tr>
+	</thead>
+</f:if>
+
+<f:renderChildren arguments="{contentArguments}" />
+
+<f:if condition="{configuration.insertBelow}">
+	<tfoot>
+		<tr>
+			<td colspan="6">
+				<f:render section="paginator" arguments="{pagination: pagination, position:'bottom', recordsLabel: configuration.recordsLabel}" />
+			</td>
+		</tr>
+	</tfoot>
+</f:if>
+
+<f:section name="paginator">
+	<nav class="pagination-wrap">
+		<ul class="pagination pagination-block">
+			<f:if condition="{pagination.hasLessPages}">
+				<f:then>
+					<li>
+						<a href="{sg:widget.uri(arguments:{currentPage: 1})}" title="{f:translate(key:'widget.pagination.first')}">
+							<core:icon identifier="actions-view-paging-first" />
+						</a>
+					</li>
+					<li>
+						<a href="{sg:widget.uri(arguments:{currentPage: pagination.previousPage})}" title="{f:translate(key:'widget.pagination.previous')}">
+							<core:icon identifier="actions-view-paging-previous" />
+						</a>
+					</li>
+				</f:then>
+				<f:else>
+					<li class="disabled">
+						<span>
+							<core:icon identifier="actions-view-paging-first" />
+						</span>
+					</li>
+					<li class="disabled">
+						<span>
+							<core:icon identifier="actions-view-paging-previous" />
+						</span>
+					</li>
+				</f:else>
+			</f:if>
+			<li>
+				<span>
+					<f:if condition="{recordsLabel}">
+						<f:then>
+							{recordsLabel}
+						</f:then>
+						<f:else>
+							<f:translate key="widget.pagination.records" />
+						</f:else>
+					</f:if>
+					{pagination.startRecord} - {pagination.endRecord} / {pagination.totalObjects}
+				</span>
+			</li>
+			<li>
+				<span>
+					<f:translate key="widget.pagination.page" />
+
+					<form id="paginator-form-{position}" onsubmit="goToPage{position}(this); return false;" style="display:inline;">
+					<script type="text/javascript">
+						function goToPage {
+							position
+						}
+						(formObject)
+						{
+							var url = '{sg:widget.uri(arguments:{currentPage: 987654321})}';
+							var page = formObject.elements['paginator-target-page'].value;
+							if (page > {pagination.numberOfPages}
+						)
+							{
+								page = {pagination.numberOfPages
+							}
+								;
+							}
+						else
+							if (page < 1) {
+								page = 1;
+							}
+							url = url.replace('987654321', page);
+							self.location.href = url;
+						}
+					</script>
+					<f:form.textfield id="paginator-{position}" name="paginator-target-page" additionalAttributes="{min: '1'}" class="form-control input-sm paginator-input" size="5" value="{pagination.current}" type="number" />
+					</form>
+
+					/ {pagination.numberOfPages}
+				</span>
+			</li>
+			<f:if condition="{pagination.hasMorePages}">
+				<f:then>
+					<li>
+						<a href="{sg:widget.uri(arguments:{currentPage: pagination.nextPage})}" title="{f:translate(key:'widget.pagination.next')}">
+							<core:icon identifier="actions-view-paging-next" />
+						</a>
+					</li>
+					<li>
+						<a href="{sg:widget.uri(arguments:{currentPage: pagination.numberOfPages})}" title="{f:translate(key:'widget.pagination.last')}">
+							<core:icon identifier="actions-view-paging-last" />
+						</a>
+					</li>
+				</f:then>
+				<f:else>
+					<li class="disabled">
+						<span>
+							<core:icon identifier="actions-view-paging-next" />
+						</span>
+					</li>
+					<li class="disabled">
+						<span>
+							<core:icon identifier="actions-view-paging-last" />
+						</span>
+					</li>
+				</f:else>
+			</f:if>
+			<li>
+				<a href="{sg:widget.uri(arguments:{currentPage: pagination.current})}" title="{f:translate(key:'widget.pagination.refresh')}">
+					<core:icon identifier="actions-refresh" />
+				</a>
+			</li>
+		</ul>
+	</nav>
+</f:section>
diff --git a/Resources/Public/Scripts/Backend.js b/Resources/Public/Scripts/Backend.js
index 5cbcd2bd..74effae1 100644
--- a/Resources/Public/Scripts/Backend.js
+++ b/Resources/Public/Scripts/Backend.js
@@ -39,3 +39,28 @@
 	});
 })
 (TYPO3.jQuery);
+
+// functions for backend docheader functionality
+function jumpExt(URL, anchor) {    //
+	var anc = anchor ? anchor : "";
+	window.location.href = URL + (T3_THIS_LOCATION ? "&returnUrl=" + T3_THIS_LOCATION : "") + anc;
+	return false;
+}
+
+function jumpSelf(URL) {    //
+	window.location.href = URL + (T3_RETURN_URL ? "&returnUrl=" + T3_RETURN_URL : "");
+	return false;
+}
+
+function jumpToUrl(URL) {
+	window.location.href = URL;
+	return false;
+}
+
+function setHighlight(id) {    //
+	top.fsMod.recentIds["web"] = id;
+	top.fsMod.navFrameHighlightedID["web"] = "pages" + id + "_" + top.fsMod.currentBank;    // For highlighting
+	if (top.content && top.content.nav_frame && top.content.nav_frame.refresh_nav) {
+		top.content.nav_frame.refresh_nav();
+	}
+}
-- 
GitLab