Merge branch '457-category-descriptions' into '3-2-0'
Add Description to Categories See merge request !68master
commit
0dcac4f324
File diff suppressed because it is too large
Load Diff
@ -1,142 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
// Responsible for loading in all necessary classes. AKA a poor man's DI solution.
|
|
||||||
use BusinessLogic\Attachments\AttachmentHandler;
|
|
||||||
use BusinessLogic\Attachments\AttachmentRetriever;
|
|
||||||
use BusinessLogic\Categories\CategoryRetriever;
|
|
||||||
use BusinessLogic\Emails\BasicEmailSender;
|
|
||||||
use BusinessLogic\Emails\EmailSenderHelper;
|
|
||||||
use BusinessLogic\Emails\EmailTemplateParser;
|
|
||||||
use BusinessLogic\Emails\EmailTemplateRetriever;
|
|
||||||
use BusinessLogic\Emails\MailgunEmailSender;
|
|
||||||
use BusinessLogic\Navigation\CustomNavElementHandler;
|
|
||||||
use BusinessLogic\Security\BanRetriever;
|
|
||||||
use BusinessLogic\Security\UserContextBuilder;
|
|
||||||
use BusinessLogic\Security\UserToTicketChecker;
|
|
||||||
use BusinessLogic\Settings\ApiChecker;
|
|
||||||
use BusinessLogic\Settings\SettingsRetriever;
|
|
||||||
use BusinessLogic\Statuses\StatusRetriever;
|
|
||||||
use BusinessLogic\Tickets\Autoassigner;
|
|
||||||
use BusinessLogic\Tickets\TicketDeleter;
|
|
||||||
use BusinessLogic\Tickets\TicketEditor;
|
|
||||||
use BusinessLogic\Tickets\TicketRetriever;
|
|
||||||
use BusinessLogic\Tickets\TicketCreator;
|
|
||||||
use BusinessLogic\Tickets\NewTicketValidator;
|
|
||||||
use BusinessLogic\Tickets\TicketValidators;
|
|
||||||
use BusinessLogic\Tickets\TrackingIdGenerator;
|
|
||||||
use BusinessLogic\Tickets\VerifiedEmailChecker;
|
|
||||||
use DataAccess\Attachments\AttachmentGateway;
|
|
||||||
use DataAccess\Categories\CategoryGateway;
|
|
||||||
use DataAccess\Files\FileDeleter;
|
|
||||||
use DataAccess\Files\FileReader;
|
|
||||||
use DataAccess\Files\FileWriter;
|
|
||||||
use DataAccess\Logging\LoggingGateway;
|
|
||||||
use DataAccess\Navigation\CustomNavElementGateway;
|
|
||||||
use DataAccess\Security\BanGateway;
|
|
||||||
use DataAccess\Security\UserGateway;
|
|
||||||
use DataAccess\Settings\ModsForHeskSettingsGateway;
|
|
||||||
use DataAccess\Statuses\StatusGateway;
|
|
||||||
use DataAccess\Tickets\TicketGateway;
|
|
||||||
use DataAccess\Tickets\VerifiedEmailGateway;
|
|
||||||
|
|
||||||
|
|
||||||
class ApplicationContext {
|
|
||||||
public $get;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ApplicationContext constructor.
|
|
||||||
*/
|
|
||||||
function __construct() {
|
|
||||||
$this->get = array();
|
|
||||||
|
|
||||||
// Settings
|
|
||||||
$this->get[ModsForHeskSettingsGateway::class] = new ModsForHeskSettingsGateway();
|
|
||||||
|
|
||||||
// API Checker
|
|
||||||
$this->get[ApiChecker::class] = new ApiChecker($this->get[ModsForHeskSettingsGateway::class]);
|
|
||||||
|
|
||||||
// Custom Navigation
|
|
||||||
$this->get[CustomNavElementGateway::class] = new CustomNavElementGateway();
|
|
||||||
$this->get[CustomNavElementHandler::class] = new CustomNavElementHandler($this->get[CustomNavElementGateway::class]);
|
|
||||||
|
|
||||||
// Logging
|
|
||||||
$this->get[LoggingGateway::class] = new LoggingGateway();
|
|
||||||
|
|
||||||
// Verified Email Checker
|
|
||||||
$this->get[VerifiedEmailGateway::class] = new VerifiedEmailGateway();
|
|
||||||
$this->get[VerifiedEmailChecker::class] = new VerifiedEmailChecker($this->get[VerifiedEmailGateway::class]);
|
|
||||||
|
|
||||||
// Users
|
|
||||||
$this->get[UserGateway::class] = new UserGateway();
|
|
||||||
$this->get[UserContextBuilder::class] = new UserContextBuilder($this->get[UserGateway::class]);
|
|
||||||
|
|
||||||
// Categories
|
|
||||||
$this->get[CategoryGateway::class] = new CategoryGateway();
|
|
||||||
$this->get[CategoryRetriever::class] = new CategoryRetriever($this->get[CategoryGateway::class]);
|
|
||||||
|
|
||||||
// Bans
|
|
||||||
$this->get[BanGateway::class] = new BanGateway();
|
|
||||||
$this->get[BanRetriever::class] = new BanRetriever($this->get[BanGateway::class]);
|
|
||||||
|
|
||||||
// Statuses
|
|
||||||
$this->get[StatusGateway::class] = new StatusGateway();
|
|
||||||
|
|
||||||
// Email Sender
|
|
||||||
$this->get[EmailTemplateRetriever::class] = new EmailTemplateRetriever();
|
|
||||||
$this->get[EmailTemplateParser::class] = new EmailTemplateParser($this->get[StatusGateway::class],
|
|
||||||
$this->get[CategoryGateway::class],
|
|
||||||
$this->get[UserGateway::class],
|
|
||||||
$this->get[EmailTemplateRetriever::class]);
|
|
||||||
$this->get[BasicEmailSender::class] = new BasicEmailSender();
|
|
||||||
$this->get[MailgunEmailSender::class] = new MailgunEmailSender();
|
|
||||||
$this->get[EmailSenderHelper::class] = new EmailSenderHelper($this->get[EmailTemplateParser::class],
|
|
||||||
$this->get[BasicEmailSender::class],
|
|
||||||
$this->get[MailgunEmailSender::class]);
|
|
||||||
|
|
||||||
// Tickets
|
|
||||||
$this->get[UserToTicketChecker::class] = new UserToTicketChecker($this->get[UserGateway::class]);
|
|
||||||
$this->get[TicketGateway::class] = new TicketGateway();
|
|
||||||
$this->get[TicketRetriever::class] = new TicketRetriever($this->get[TicketGateway::class],
|
|
||||||
$this->get[UserToTicketChecker::class]);
|
|
||||||
$this->get[TicketValidators::class] = new TicketValidators($this->get[TicketGateway::class]);
|
|
||||||
$this->get[TrackingIdGenerator::class] = new TrackingIdGenerator($this->get[TicketGateway::class]);
|
|
||||||
$this->get[Autoassigner::class] = new Autoassigner($this->get[CategoryGateway::class], $this->get[UserGateway::class]);
|
|
||||||
$this->get[NewTicketValidator::class] = new NewTicketValidator($this->get[CategoryRetriever::class],
|
|
||||||
$this->get[BanRetriever::class],
|
|
||||||
$this->get[TicketValidators::class]);
|
|
||||||
$this->get[TicketCreator::class] = new TicketCreator($this->get[NewTicketValidator::class],
|
|
||||||
$this->get[TrackingIdGenerator::class],
|
|
||||||
$this->get[Autoassigner::class],
|
|
||||||
$this->get[StatusGateway::class],
|
|
||||||
$this->get[TicketGateway::class],
|
|
||||||
$this->get[VerifiedEmailChecker::class],
|
|
||||||
$this->get[EmailSenderHelper::class],
|
|
||||||
$this->get[UserGateway::class],
|
|
||||||
$this->get[ModsForHeskSettingsGateway::class]);
|
|
||||||
$this->get[FileWriter::class] = new FileWriter();
|
|
||||||
$this->get[FileReader::class] = new FileReader();
|
|
||||||
$this->get[FileDeleter::class] = new FileDeleter();
|
|
||||||
$this->get[AttachmentGateway::class] = new AttachmentGateway();
|
|
||||||
$this->get[AttachmentHandler::class] = new AttachmentHandler($this->get[TicketGateway::class],
|
|
||||||
$this->get[AttachmentGateway::class],
|
|
||||||
$this->get[FileWriter::class],
|
|
||||||
$this->get[UserToTicketChecker::class],
|
|
||||||
$this->get[FileDeleter::class]);
|
|
||||||
$this->get[AttachmentRetriever::class] = new AttachmentRetriever($this->get[AttachmentGateway::class],
|
|
||||||
$this->get[FileReader::class],
|
|
||||||
$this->get[TicketGateway::class],
|
|
||||||
$this->get[UserToTicketChecker::class]);
|
|
||||||
$this->get[TicketDeleter::class] =
|
|
||||||
new TicketDeleter($this->get[TicketGateway::class],
|
|
||||||
$this->get[UserToTicketChecker::class],
|
|
||||||
$this->get[AttachmentHandler::class]);
|
|
||||||
$this->get[TicketEditor::class] =
|
|
||||||
new TicketEditor($this->get[TicketGateway::class], $this->get[UserToTicketChecker::class]);
|
|
||||||
|
|
||||||
// Statuses
|
|
||||||
$this->get[StatusRetriever::class] = new StatusRetriever($this->get[StatusGateway::class]);
|
|
||||||
|
|
||||||
// Settings
|
|
||||||
$this->get[SettingsRetriever::class] = new SettingsRetriever($this->get[ModsForHeskSettingsGateway::class]);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,190 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace BusinessLogic\Categories;
|
||||||
|
|
||||||
|
|
||||||
|
use BusinessLogic\Exceptions\AccessViolationException;
|
||||||
|
use BusinessLogic\Exceptions\ValidationException;
|
||||||
|
use BusinessLogic\Navigation\Direction;
|
||||||
|
use BusinessLogic\Security\PermissionChecker;
|
||||||
|
use BusinessLogic\Security\UserPrivilege;
|
||||||
|
use BusinessLogic\ValidationModel;
|
||||||
|
use DataAccess\Categories\CategoryGateway;
|
||||||
|
use DataAccess\Settings\ModsForHeskSettingsGateway;
|
||||||
|
use DataAccess\Tickets\TicketGateway;
|
||||||
|
|
||||||
|
class CategoryHandler {
|
||||||
|
/* @var $categoryGateway CategoryGateway */
|
||||||
|
private $categoryGateway;
|
||||||
|
|
||||||
|
/* @var $ticketGateway TicketGateway */
|
||||||
|
private $ticketGateway;
|
||||||
|
|
||||||
|
/* @var $permissionChecker PermissionChecker */
|
||||||
|
private $permissionChecker;
|
||||||
|
|
||||||
|
/* @var $modsForHeskSettingsGateway ModsForHeskSettingsGateway */
|
||||||
|
private $modsForHeskSettingsGateway;
|
||||||
|
|
||||||
|
function __construct(CategoryGateway $categoryGateway,
|
||||||
|
TicketGateway $ticketGateway,
|
||||||
|
PermissionChecker $permissionChecker,
|
||||||
|
ModsForHeskSettingsGateway $modsForHeskSettingsGateway) {
|
||||||
|
$this->categoryGateway = $categoryGateway;
|
||||||
|
$this->ticketGateway = $ticketGateway;
|
||||||
|
$this->permissionChecker = $permissionChecker;
|
||||||
|
$this->modsForHeskSettingsGateway = $modsForHeskSettingsGateway;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $category Category
|
||||||
|
* @param $userContext
|
||||||
|
* @param $heskSettings array
|
||||||
|
* @return Category The newly created category with ID
|
||||||
|
* @throws ValidationException When validation fails
|
||||||
|
* @throws \Exception When the newly created category was not retrieved
|
||||||
|
*/
|
||||||
|
//TODO Test
|
||||||
|
function createCategory($category, $userContext, $heskSettings) {
|
||||||
|
$modsForHeskSettings = $this->modsForHeskSettingsGateway->getAllSettings($heskSettings);
|
||||||
|
|
||||||
|
$validationModel = $this->validate($category, $userContext);
|
||||||
|
|
||||||
|
if (count($validationModel->errorKeys) > 0) {
|
||||||
|
throw new ValidationException($validationModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = $this->categoryGateway->createCategory($category, $heskSettings);
|
||||||
|
|
||||||
|
$allCategories = $this->categoryGateway->getAllCategories($heskSettings, $modsForHeskSettings);
|
||||||
|
|
||||||
|
foreach ($allCategories as $innerCategory) {
|
||||||
|
if ($innerCategory->id === $id) {
|
||||||
|
return $innerCategory;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \Exception("Newly created category {$id} lost! :O");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $category Category
|
||||||
|
* @param $userContext
|
||||||
|
* @param $creating bool
|
||||||
|
* @return ValidationModel
|
||||||
|
* @throws AccessViolationException
|
||||||
|
*/
|
||||||
|
//TODO Test
|
||||||
|
private function validate($category, $userContext, $creating = true) {
|
||||||
|
$validationModel = new ValidationModel();
|
||||||
|
|
||||||
|
if (!$this->permissionChecker->doesUserHavePermission($userContext, UserPrivilege::CAN_MANAGE_CATEGORIES)) {
|
||||||
|
throw new AccessViolationException('User cannot manage categories!');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$creating && $category->id < 1) {
|
||||||
|
$validationModel->errorKeys[] = 'ID_MISSING';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($category->backgroundColor === null || trim($category->backgroundColor) === '') {
|
||||||
|
$validationModel->errorKeys[] = 'BACKGROUND_COLOR_MISSING';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($category->foregroundColor === null || trim($category->foregroundColor) === '') {
|
||||||
|
$validationModel->errorKeys[] = 'FOREGROUND_COLOR_MISSING';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($category->name === null || trim($category->name) === '') {
|
||||||
|
$validationModel->errorKeys[] = 'NAME_MISSING';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($category->priority === null || intval($category->priority) < 0 || intval($category->priority) > 3) {
|
||||||
|
$validationModel->errorKeys[] = 'INVALID_PRIORITY';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($category->autoAssign === null || !is_bool($category->autoAssign)) {
|
||||||
|
$validationModel->errorKeys[] = 'INVALID_AUTOASSIGN';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($category->displayBorder === null || !is_bool($category->displayBorder)) {
|
||||||
|
$validationModel->errorKeys[] = 'INVALID_DISPLAY_BORDER';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($category->type === null || (intval($category->type) !== 0 && intval($category->type) !== 1)) {
|
||||||
|
$validationModel->errorKeys[] = 'INVALID_TYPE';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $validationModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $category Category
|
||||||
|
* @param $userContext
|
||||||
|
* @param $heskSettings array
|
||||||
|
* @return Category
|
||||||
|
* @throws ValidationException
|
||||||
|
* @throws \Exception When the category is missing
|
||||||
|
*/
|
||||||
|
function editCategory($category, $userContext, $heskSettings) {
|
||||||
|
$modsForHeskSettings = $this->modsForHeskSettingsGateway->getAllSettings($heskSettings);
|
||||||
|
|
||||||
|
$validationModel = $this->validate($category, $userContext, false);
|
||||||
|
|
||||||
|
if (count($validationModel->errorKeys) > 0) {
|
||||||
|
throw new ValidationException($validationModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->categoryGateway->updateCategory($category, $heskSettings);
|
||||||
|
$this->categoryGateway->resortAllCategories($heskSettings);
|
||||||
|
|
||||||
|
$allCategories = $this->categoryGateway->getAllCategories($heskSettings, $modsForHeskSettings);
|
||||||
|
|
||||||
|
foreach ($allCategories as $innerCategory) {
|
||||||
|
if ($innerCategory->id === $category->id) {
|
||||||
|
return $innerCategory;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \Exception("Category {$category->id} vanished! :O");
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteCategory($id, $userContext, $heskSettings) {
|
||||||
|
if (!$this->permissionChecker->doesUserHavePermission($userContext, UserPrivilege::CAN_MANAGE_CATEGORIES)) {
|
||||||
|
throw new AccessViolationException('User cannot manage categories!');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($id === 1) {
|
||||||
|
throw new \Exception("Category 1 cannot be deleted!");
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->ticketGateway->moveTicketsToDefaultCategory($id, $heskSettings);
|
||||||
|
$this->categoryGateway->deleteCategory($id, $heskSettings);
|
||||||
|
$this->categoryGateway->resortAllCategories($heskSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sortCategory($id, $direction, $heskSettings) {
|
||||||
|
$modsForHeskSettings = $this->modsForHeskSettingsGateway->getAllSettings($heskSettings);
|
||||||
|
|
||||||
|
$categories = $this->categoryGateway->getAllCategories($heskSettings, $modsForHeskSettings);
|
||||||
|
$category = null;
|
||||||
|
foreach ($categories as $innerCategory) {
|
||||||
|
if ($innerCategory->id === intval($id)) {
|
||||||
|
$category = $innerCategory;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($category === null) {
|
||||||
|
throw new \Exception("Could not find category with ID {$id}!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($direction === Direction::UP) {
|
||||||
|
$category->catOrder -= 15;
|
||||||
|
} else {
|
||||||
|
$category->catOrder += 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->categoryGateway->updateCategory($category, $heskSettings);
|
||||||
|
$this->categoryGateway->resortAllCategories($heskSettings);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace BusinessLogic\Security;
|
||||||
|
|
||||||
|
|
||||||
|
class PermissionChecker {
|
||||||
|
/**
|
||||||
|
* @param $userContext UserContext
|
||||||
|
* @param $permission string
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
function doesUserHavePermission($userContext, $permission) {
|
||||||
|
if ($userContext->admin) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_array($permission, $userContext->permissions)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class RequestMethod {
|
||||||
|
const GET = 'GET';
|
||||||
|
const POST = 'POST';
|
||||||
|
const PUT = 'PUT';
|
||||||
|
const DELETE = 'DELETE';
|
||||||
|
const PATCH = 'PATCH';
|
||||||
|
const ALL = [self::GET, self::POST, self::PUT, self::DELETE, self::PATCH];
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace BusinessLogic\Categories;
|
||||||
|
|
||||||
|
|
||||||
|
use BusinessLogic\Security\PermissionChecker;
|
||||||
|
use BusinessLogic\Security\UserContext;
|
||||||
|
use Core\Constants\Priority;
|
||||||
|
use DataAccess\Categories\CategoryGateway;
|
||||||
|
use DataAccess\Settings\ModsForHeskSettingsGateway;
|
||||||
|
use DataAccess\Tickets\TicketGateway;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class CategoryHandlerTest extends TestCase {
|
||||||
|
/* @var $categoryGateway CategoryGateway|\PHPUnit_Framework_MockObject_MockObject */
|
||||||
|
private $categoryGateway;
|
||||||
|
|
||||||
|
/* @var $categoryHandler CategoryHandler */
|
||||||
|
private $categoryHandler;
|
||||||
|
|
||||||
|
/* @var $ticketGateway TicketGateway|\PHPUnit_Framework_MockObject_MockObject */
|
||||||
|
private $ticketGateway;
|
||||||
|
|
||||||
|
/* @var $permissionChecker PermissionChecker|\PHPUnit_Framework_MockObject_MockObject */
|
||||||
|
private $permissionChecker;
|
||||||
|
|
||||||
|
/* @var $modsForHeskSettingsGateway ModsForHeskSettingsGateway|\PHPUnit_Framework_MockObject_MockObject */
|
||||||
|
private $modsForHeskSettingsGateway;
|
||||||
|
|
||||||
|
/* @var $heskSettings array */
|
||||||
|
private $heskSettings;
|
||||||
|
|
||||||
|
protected function setUp() {
|
||||||
|
$this->categoryGateway = $this->createMock(CategoryGateway::class);
|
||||||
|
$this->ticketGateway = $this->createMock(TicketGateway::class);
|
||||||
|
$this->permissionChecker = $this->createMock(PermissionChecker::class);
|
||||||
|
$this->modsForHeskSettingsGateway = $this->createMock(ModsForHeskSettingsGateway::class);
|
||||||
|
|
||||||
|
$this->categoryHandler = new CategoryHandler($this->categoryGateway,
|
||||||
|
$this->ticketGateway,
|
||||||
|
$this->permissionChecker,
|
||||||
|
$this->modsForHeskSettingsGateway);
|
||||||
|
$this->heskSettings = array();
|
||||||
|
|
||||||
|
//TODO write proper tests!
|
||||||
|
$this->permissionChecker->method('doesUserHavePermission')->willReturn(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testCreateCallsTheGatewayWithTheCategory() {
|
||||||
|
//-- Arrange
|
||||||
|
$category = new Category();
|
||||||
|
$category->autoAssign = true;
|
||||||
|
$category->backgroundColor = 'a';
|
||||||
|
$category->foregroundColor = 'a';
|
||||||
|
$category->catOrder = 1000;
|
||||||
|
$category->description = 'd';
|
||||||
|
$category->displayBorder = false;
|
||||||
|
$category->name = 'n';
|
||||||
|
$category->priority = Priority::LOW;
|
||||||
|
$category->usage = 0;
|
||||||
|
$category->type = 0;
|
||||||
|
$category->id = 1;
|
||||||
|
$this->categoryGateway->method('getAllCategories')->willReturn([$category]);
|
||||||
|
|
||||||
|
//-- Assert
|
||||||
|
$this->categoryGateway->expects($this->once())->method('createCategory')
|
||||||
|
->willReturn(1)
|
||||||
|
->with($category, $this->heskSettings);
|
||||||
|
|
||||||
|
//-- Act
|
||||||
|
$this->categoryHandler->createCategory($category, new UserContext(), $this->heskSettings);
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,8 @@
|
|||||||
var mfhLang = {
|
var mfhLang = {
|
||||||
text: function(key) {
|
text: function(key) {
|
||||||
return $('#lang_' + key).text();
|
return $('#lang_' + key).text();
|
||||||
|
},
|
||||||
|
html: function(key) {
|
||||||
|
return $('#lang_' + key).html()
|
||||||
}
|
}
|
||||||
};
|
};
|
@ -0,0 +1,383 @@
|
|||||||
|
var categories = [];
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
loadTable();
|
||||||
|
bindEditModal();
|
||||||
|
bindModalCancelCallback();
|
||||||
|
bindFormSubmit();
|
||||||
|
bindDeleteButton();
|
||||||
|
bindCreateModal();
|
||||||
|
bindGenerateLinkModal();
|
||||||
|
bindSortButtons();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
function loadTable() {
|
||||||
|
$('#overlay').show();
|
||||||
|
var heskUrl = $('p#hesk-path').text();
|
||||||
|
var $tableBody = $('#table-body');
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
method: 'GET',
|
||||||
|
url: heskUrl + 'api/index.php/v1/categories/all',
|
||||||
|
headers: { 'X-Internal-Call': true },
|
||||||
|
success: function(data) {
|
||||||
|
$tableBody.html('');
|
||||||
|
|
||||||
|
if (data.length === 0) {
|
||||||
|
mfhAlert.error("No categories were found. This shouldn't happen.", "No categories found");
|
||||||
|
$('#overlay').hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var totalNumberOfTickets = 0;
|
||||||
|
$.each(data, function() {
|
||||||
|
totalNumberOfTickets += this.numberOfTickets;
|
||||||
|
});
|
||||||
|
|
||||||
|
var first = true;
|
||||||
|
var lastElement = null;
|
||||||
|
$.each(data, function() {
|
||||||
|
var $template = $($('#category-row-template').html());
|
||||||
|
|
||||||
|
$template.find('span[data-property="id"]').text(this.id).attr('data-value', this.id);
|
||||||
|
var $nameField = $template.find('span[data-property="category-name"]');
|
||||||
|
if (this.foregroundColor === 'AUTO') {
|
||||||
|
$nameField.addClass('background-volatile');
|
||||||
|
} else {
|
||||||
|
$nameField.css('color', this.foregroundColor);
|
||||||
|
}
|
||||||
|
$nameField.css('background', this.backgroundColor);
|
||||||
|
if (this.displayBorder && this.foregroundColor !== 'AUTO') {
|
||||||
|
$nameField.css('border', 'solid 1px ' + this.foregroundColor);
|
||||||
|
}
|
||||||
|
$nameField.html(this.name);
|
||||||
|
|
||||||
|
if (this.description === '' || this.description === null) {
|
||||||
|
$template.find('.fa-info-circle').hide();
|
||||||
|
} else {
|
||||||
|
$template.find('.fa-info-circle').attr('data-content', this.description);
|
||||||
|
}
|
||||||
|
var $priority = $template.find('span[data-property="priority"]');
|
||||||
|
if (this.priority === 0) {
|
||||||
|
// Critical
|
||||||
|
$priority.text(mfhLang.text('critical')).addClass('critical');
|
||||||
|
} else if (this.priority === 1) {
|
||||||
|
// High
|
||||||
|
$priority.text(mfhLang.text('high')).addClass('important');
|
||||||
|
} else if (this.priority === 2) {
|
||||||
|
// Medium
|
||||||
|
$priority.text(mfhLang.text('medium')).addClass('medium');
|
||||||
|
} else {
|
||||||
|
// Low
|
||||||
|
$priority.text(mfhLang.text('low')).addClass('normal');
|
||||||
|
}
|
||||||
|
var linkPattern = $('input[name="show-tickets-path"]').val();
|
||||||
|
$template.find('a[data-property="number-of-tickets"]')
|
||||||
|
.text(this.numberOfTickets)
|
||||||
|
.attr('href', linkPattern.replace('{0}', this.id));
|
||||||
|
var percentText = mfhLang.text('perat');
|
||||||
|
var percentage = Math.round(this.numberOfTickets / totalNumberOfTickets * 100);
|
||||||
|
$template.find('div.progress').attr('title', percentText.replace('%s', percentage + '%'));
|
||||||
|
$template.find('div.progress-bar').attr('aria-value-now', percentage).css('width', percentage + '%');
|
||||||
|
|
||||||
|
$template.find('[data-property="generate-link"]')
|
||||||
|
.attr('data-category-id', this.id)
|
||||||
|
.find('i').attr('title', mfhLang.text('geco'));
|
||||||
|
|
||||||
|
if (this.usage === 1) {
|
||||||
|
// Tickets only
|
||||||
|
$template.find('.fa-calendar').removeClass('fa-calendar');
|
||||||
|
} else if (this.usage === 2) {
|
||||||
|
// Events only
|
||||||
|
$template.find('.fa-ticket').removeClass('fa-ticket');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.autoAssign) {
|
||||||
|
$template.find('.fa-bolt').addClass('orange');
|
||||||
|
$template.find('[data-property="autoassign"]').text(mfhLang.text('enabled_title_case'));
|
||||||
|
} else {
|
||||||
|
$template.find('.fa-bolt').addClass('gray');
|
||||||
|
$template.find('[data-property="autoassign"]').text(mfhLang.text('disabled_title_case'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.type === 1) {
|
||||||
|
// Private
|
||||||
|
$template.find('[data-property="type"]').text(mfhLang.text('cat_private'));
|
||||||
|
$template.find('.fa-lock').show();
|
||||||
|
$template.find('[data-property="generate-link"]').find('i')
|
||||||
|
.addClass('fa-ban')
|
||||||
|
.addClass('red')
|
||||||
|
.attr('title', mfhLang.text('cpric'));
|
||||||
|
} else {
|
||||||
|
// Public
|
||||||
|
$template.find('[data-property="type"]').text(mfhLang.text('cat_public'));
|
||||||
|
$template.find('.fa-unlock-alt').show();
|
||||||
|
$template.find('[data-property="generate-link"]').find('i')
|
||||||
|
.addClass('fa-code')
|
||||||
|
.addClass('green')
|
||||||
|
.attr('title', mfhLang.text('geco'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.id === 1) {
|
||||||
|
$template.find('[data-action="delete"]').hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
$tableBody.append($template);
|
||||||
|
|
||||||
|
categories[this.id] = this;
|
||||||
|
|
||||||
|
lastElement = this;
|
||||||
|
|
||||||
|
if (first) {
|
||||||
|
$template.find('[data-direction="up"]').css('visibility', 'hidden');
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (lastElement) {
|
||||||
|
//-- Hide the down arrow on the last element
|
||||||
|
$('[data-value="' + lastElement.id + '"]').parent().parent()
|
||||||
|
.find('[data-direction="down"]').css('visibility', 'hidden');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function(data) {
|
||||||
|
mfhAlert.errorWithLog(mfhLang.text('error_retrieving_categories'), data.responseJSON);
|
||||||
|
console.error(data);
|
||||||
|
},
|
||||||
|
complete: function() {
|
||||||
|
refreshBackgroundVolatileItems();
|
||||||
|
$('[data-toggle="popover"]').popover({
|
||||||
|
trigger: 'hover',
|
||||||
|
container: 'body'
|
||||||
|
});
|
||||||
|
$('#overlay').hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function bindEditModal() {
|
||||||
|
$(document).on('click', '[data-action="edit"]', function() {
|
||||||
|
var element = categories[$(this).parent().parent().find('[data-property="id"]').text()];
|
||||||
|
var $modal = $('#category-modal');
|
||||||
|
|
||||||
|
$modal.find('#edit-label').show();
|
||||||
|
$modal.find('#create-label').hide();
|
||||||
|
|
||||||
|
$modal.find('input[name="name"]').val(element.name).end()
|
||||||
|
.find('select[name="priority"]').val(element.priority).end()
|
||||||
|
.find('input[name="id"]').val(element.id).end()
|
||||||
|
.find('select[name="usage"]').val(element.usage).end()
|
||||||
|
.find('input[name="display-border"][value="' + (element.displayBorder ? 1 : 0) + '"]')
|
||||||
|
.prop('checked', 'checked').end();
|
||||||
|
|
||||||
|
var backgroundColor = element.backgroundColor;
|
||||||
|
var foregroundColor = element.foregroundColor;
|
||||||
|
var colorpickerOptions = {
|
||||||
|
format: 'hex',
|
||||||
|
color: backgroundColor
|
||||||
|
};
|
||||||
|
$modal.find('input[name="background-color"]')
|
||||||
|
.colorpicker(colorpickerOptions).end().modal('show');
|
||||||
|
|
||||||
|
colorpickerOptions = {
|
||||||
|
format: 'hex'
|
||||||
|
};
|
||||||
|
if (foregroundColor != '' && foregroundColor !== 'AUTO') {
|
||||||
|
colorpickerOptions.color = foregroundColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
$modal.find('input[name="foreground-color"]')
|
||||||
|
.colorpicker(colorpickerOptions).end().modal('show');
|
||||||
|
|
||||||
|
if (foregroundColor === '' || foregroundColor === 'AUTO') {
|
||||||
|
$modal.find('input[name="foreground-color"]').colorpicker('setValue', '#fff');
|
||||||
|
$modal.find('input[name="foreground-color"]').val('');
|
||||||
|
}
|
||||||
|
|
||||||
|
$modal.find('input[name="cat-order"]').val(element.catOrder);
|
||||||
|
$modal.find('input[name="autoassign"][value="' + (element.autoAssign ? 1 : 0) + '"]')
|
||||||
|
.prop('checked', 'checked');
|
||||||
|
$modal.find('input[name="type"][value="' + (element.type ? 1 : 0) + '"]')
|
||||||
|
.prop('checked', 'checked');
|
||||||
|
$modal.find('textarea[name="description"]').val(element.description === null ? '' : element.description);
|
||||||
|
|
||||||
|
$modal.modal('show');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function bindCreateModal() {
|
||||||
|
$('#create-button').click(function() {
|
||||||
|
var $modal = $('#category-modal');
|
||||||
|
$modal.find('#edit-label').hide();
|
||||||
|
$modal.find('#create-label').show();
|
||||||
|
|
||||||
|
$modal.find('input[name="name"]').val('');
|
||||||
|
$modal.find('select[name="priority"]').val(3); // Low priority
|
||||||
|
$modal.find('select[name="usage"]').val(0); // Tickets and events
|
||||||
|
$modal.find('input[name="id"]').val(-1);
|
||||||
|
$modal.find('textarea[name="description"]').val('');
|
||||||
|
$modal.find('input[name="cat-order"]').val('');
|
||||||
|
$modal.find('input[name="type"][value="0"]').prop('checked', 'checked');
|
||||||
|
$modal.find('input[name="autoassign"][value="0"]').prop('checked', 'checked');
|
||||||
|
$modal.find('input[name="display-border"][value="0"]')
|
||||||
|
.prop('checked', 'checked');
|
||||||
|
|
||||||
|
var colorpickerOptions = {
|
||||||
|
format: 'hex',
|
||||||
|
color: '#fff'
|
||||||
|
};
|
||||||
|
$modal.find('input[name="background-color"]')
|
||||||
|
.colorpicker(colorpickerOptions).end().modal('show');
|
||||||
|
$modal.find('input[name="background-color"]').val('');
|
||||||
|
$modal.find('input[name="foreground-color"]')
|
||||||
|
.colorpicker(colorpickerOptions).end().modal('show');
|
||||||
|
$modal.find('input[name="foreground-color"]').val('');
|
||||||
|
|
||||||
|
$modal.modal('show');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function bindModalCancelCallback() {
|
||||||
|
$('.cancel-callback').click(function() {
|
||||||
|
var $editCategoryModal = $('#category-modal');
|
||||||
|
|
||||||
|
$editCategoryModal.find('input[name="background-color"]').val('').colorpicker('destroy').end();
|
||||||
|
$editCategoryModal.find('input[name="foreground-color"]').val('').colorpicker('destroy').end();
|
||||||
|
$editCategoryModal.find('input[name="display-border"][value="1"]').prop('checked');
|
||||||
|
$editCategoryModal.find('input[name="display-border"][value="0"]').prop('checked');
|
||||||
|
$editCategoryModal.find('input[name="autoassign"][value="1"]').prop('checked');
|
||||||
|
$editCategoryModal.find('input[name="autoassign"][value="0"]').prop('checked');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function bindFormSubmit() {
|
||||||
|
$('form#manage-category').submit(function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var heskUrl = $('p#hesk-path').text();
|
||||||
|
|
||||||
|
var $modal = $('#category-modal');
|
||||||
|
|
||||||
|
var data = {
|
||||||
|
autoassign: $modal.find('input[name="autoassign"]').val() === 'true',
|
||||||
|
backgroundColor: $modal.find('input[name="background-color"]').val(),
|
||||||
|
description: $modal.find('textarea[name="description"]').val(),
|
||||||
|
displayBorder: $modal.find('input[name="display-border"]:checked').val() === '1',
|
||||||
|
foregroundColor: $modal.find('input[name="foreground-color"]').val() === '' ? 'AUTO' : $modal.find('input[name="foreground-color"]').val(),
|
||||||
|
name: $modal.find('input[name="name"]').val(),
|
||||||
|
priority: parseInt($modal.find('select[name="priority"]').val()),
|
||||||
|
type: parseInt($modal.find('input[name="type"]:checked').val()),
|
||||||
|
usage: parseInt($modal.find('select[name="usage"]').val()),
|
||||||
|
catOrder: parseInt($modal.find('input[name="cat-order"]').val())
|
||||||
|
};
|
||||||
|
|
||||||
|
var url = heskUrl + 'api/index.php/v1/categories/';
|
||||||
|
var method = 'POST';
|
||||||
|
|
||||||
|
var categoryId = parseInt($modal.find('input[name="id"]').val());
|
||||||
|
if (categoryId !== -1) {
|
||||||
|
url += categoryId;
|
||||||
|
method = 'PUT';
|
||||||
|
}
|
||||||
|
|
||||||
|
$modal.find('#action-buttons').find('.cancel-button').attr('disabled', 'disabled');
|
||||||
|
$modal.find('#action-buttons').find('.save-button').attr('disabled', 'disabled');
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
method: 'POST',
|
||||||
|
url: url,
|
||||||
|
headers: {
|
||||||
|
'X-Internal-Call': true,
|
||||||
|
'X-HTTP-Method-Override': method
|
||||||
|
},
|
||||||
|
data: JSON.stringify(data),
|
||||||
|
success: function(data) {
|
||||||
|
var format = undefined;
|
||||||
|
if (categoryId === -1) {
|
||||||
|
format = mfhLang.html('cat_name_added');
|
||||||
|
mfhAlert.success(format.replace('%s', data.name));
|
||||||
|
} else {
|
||||||
|
format = mfhLang.html('category_updated');
|
||||||
|
mfhAlert.success(format.replace('%s', data.name));
|
||||||
|
}
|
||||||
|
$modal.modal('hide');
|
||||||
|
loadTable();
|
||||||
|
},
|
||||||
|
error: function(data) {
|
||||||
|
mfhAlert.errorWithLog(mfhLang.text('error_saving_updating_category'), data.responseJSON);
|
||||||
|
console.error(data);
|
||||||
|
},
|
||||||
|
complete: function(data) {
|
||||||
|
$modal.find('#action-buttons').find('.cancel-button').removeAttr('disabled');
|
||||||
|
$modal.find('#action-buttons').find('.save-button').removeAttr('disabled');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function bindDeleteButton() {
|
||||||
|
$(document).on('click', '[data-action="delete"]', function() {
|
||||||
|
$('#overlay').show();
|
||||||
|
|
||||||
|
var heskUrl = $('p#hesk-path').text();
|
||||||
|
var element = categories[$(this).parent().parent().find('[data-property="id"]').text()];
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
method: 'POST',
|
||||||
|
url: heskUrl + 'api/index.php/v1/categories/' + element.id,
|
||||||
|
headers: {
|
||||||
|
'X-Internal-Call': true,
|
||||||
|
'X-HTTP-Method-Override': 'DELETE'
|
||||||
|
},
|
||||||
|
success: function() {
|
||||||
|
mfhAlert.success(mfhLang.text('cat_removed'));
|
||||||
|
loadTable();
|
||||||
|
},
|
||||||
|
error: function(data) {
|
||||||
|
$('#overlay').hide();
|
||||||
|
mfhAlert.errorWithLog(mfhLang.text('error_deleting_category'), data.responseJSON);
|
||||||
|
console.error(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function bindGenerateLinkModal() {
|
||||||
|
var $modal = $('#generate-link-modal');
|
||||||
|
|
||||||
|
$modal.find('.input-group-addon').click(function() {
|
||||||
|
clipboard.copy($modal.find('input[type="text"]').val());
|
||||||
|
mfhAlert.success(mfhLang.text('copied_to_clipboard'));
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('click', '[data-property="generate-link"] i.fa-code', function () {
|
||||||
|
var heskUrl = $('p#hesk-url').text();
|
||||||
|
|
||||||
|
var url = heskUrl + '/index.php?a=add&catid=' + $(this).parent().data('category-id');
|
||||||
|
|
||||||
|
$modal.find('input[type="text"]').val(url).end().modal('show');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function bindSortButtons() {
|
||||||
|
$(document).on('click', '[data-action="sort"]', function() {
|
||||||
|
$('#overlay').show();
|
||||||
|
var heskUrl = $('p#hesk-path').text();
|
||||||
|
var direction = $(this).data('direction');
|
||||||
|
var element = categories[$(this).parent().parent().parent().find('[data-property="id"]').text()];
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
method: 'POST',
|
||||||
|
url: heskUrl + 'api/index.php/v1-internal/categories/' + element.id + '/sort/' + direction,
|
||||||
|
headers: { 'X-Internal-Call': true },
|
||||||
|
success: function() {
|
||||||
|
loadTable();
|
||||||
|
},
|
||||||
|
error: function(data) {
|
||||||
|
mfhAlert.errorWithLog(mfhLang.text('error_sorting_categories'), data.responseJSON);
|
||||||
|
console.error(data);
|
||||||
|
$('#overlay').hide();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
(function(e,c){"undefined"!==typeof module?module.exports=c():"function"===typeof define&&"object"===typeof define.amd?define(c):this[e]=c()})("clipboard",function(){if("undefined"===typeof document||!document.addEventListener)return null;var e={};e.copy=function(){function c(){d=!1;b=null;g&&window.getSelection().removeAllRanges();g=!1}var d=!1,b=null,g=!1;document.addEventListener("copy",function(c){if(d){for(var g in b)c.clipboardData.setData(g,b[g]);c.preventDefault()}});return function(f){return new Promise(function(m,
|
||||||
|
e){function k(b){try{if(document.execCommand("copy"))c(),m();else{if(b)throw c(),Error("Unable to copy. Perhaps it's not available in your browser?");var d=document.getSelection();if(!document.queryCommandEnabled("copy")&&d.isCollapsed){var f=document.createRange();f.selectNodeContents(document.body);d.removeAllRanges();d.addRange(f);g=!0}k(!0)}}catch(a){c(),e(a)}}d=!0;"string"===typeof f?b={"text/plain":f}:f instanceof Node?b={"text/html":(new XMLSerializer).serializeToString(f)}:f instanceof Object?
|
||||||
|
b=f:e("Invalid data type. Must be string, DOM node, or an object mapping MIME types to strings.");k(!1)})}}();e.paste=function(){var c=!1,d,b;document.addEventListener("paste",function(g){if(c){c=!1;g.preventDefault();var f=d;d=null;f(g.clipboardData.getData(b))}});return function(g){return new Promise(function(f,e){c=!0;d=f;b=g||"text/plain";try{document.execCommand("paste")||(c=!1,e(Error("Unable to paste. Pasting only works in Internet Explorer at the moment.")))}catch(h){c=!1,e(Error(h))}})}}();
|
||||||
|
"undefined"===typeof ClipboardEvent&&"undefined"!==typeof window.clipboardData&&"undefined"!==typeof window.clipboardData.setData&&(function(c){function d(a,b){return function(){a.apply(b,arguments)}}function b(a){if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof a)throw new TypeError("not a function");this._value=this._state=null;this._deferreds=[];l(a,d(f,this),d(e,this))}function g(a){var b=this;return null===this._state?void this._deferreds.push(a):
|
||||||
|
void n(function(){var c=b._state?a.onFulfilled:a.onRejected;if(null===c)return void(b._state?a.resolve:a.reject)(b._value);var d;try{d=c(b._value)}catch(e){return void a.reject(e)}a.resolve(d)})}function f(a){try{if(a===this)throw new TypeError("A promise cannot be resolved with itself.");if(a&&("object"==typeof a||"function"==typeof a)){var b=a.then;if("function"==typeof b)return void l(d(b,a),d(f,this),d(e,this))}this._state=!0;this._value=a;h.call(this)}catch(c){e.call(this,c)}}function e(a){this._state=
|
||||||
|
!1;this._value=a;h.call(this)}function h(){for(var a=0,b=this._deferreds.length;b>a;a++)g.call(this,this._deferreds[a]);this._deferreds=null}function k(a,b,c,d){this.onFulfilled="function"==typeof a?a:null;this.onRejected="function"==typeof b?b:null;this.resolve=c;this.reject=d}function l(a,b,c){var d=!1;try{a(function(a){d||(d=!0,b(a))},function(a){d||(d=!0,c(a))})}catch(e){d||(d=!0,c(e))}}var n=b.immediateFn||"function"==typeof setImmediate&&setImmediate||function(a){setTimeout(a,1)},p=Array.isArray||
|
||||||
|
function(a){return"[object Array]"===Object.prototype.toString.call(a)};b.prototype["catch"]=function(a){return this.then(null,a)};b.prototype.then=function(a,c){var d=this;return new b(function(b,e){g.call(d,new k(a,c,b,e))})};b.all=function(){var a=Array.prototype.slice.call(1===arguments.length&&p(arguments[0])?arguments[0]:arguments);return new b(function(b,c){function d(f,g){try{if(g&&("object"==typeof g||"function"==typeof g)){var h=g.then;if("function"==typeof h)return void h.call(g,function(a){d(f,
|
||||||
|
a)},c)}a[f]=g;0===--e&&b(a)}catch(k){c(k)}}if(0===a.length)return b([]);for(var e=a.length,f=0;f<a.length;f++)d(f,a[f])})};b.resolve=function(a){return a&&"object"==typeof a&&a.constructor===b?a:new b(function(b){b(a)})};b.reject=function(a){return new b(function(b,c){c(a)})};b.race=function(a){return new b(function(b,c){for(var d=0,e=a.length;e>d;d++)a[d].then(b,c)})};"undefined"!=typeof module&&module.exports?module.exports=b:c.Promise||(c.Promise=b)}(this),e.copy=function(c){return new Promise(function(d,
|
||||||
|
b){if("string"!==typeof c&&!("text/plain"in c))throw Error("You must provide a text/plain type.");window.clipboardData.setData("Text","string"===typeof c?c:c["text/plain"])?d():b(Error("Copying was rejected."))})},e.paste=function(){return new Promise(function(c,d){var b=window.clipboardData.getData("Text");b?c(b):d(Error("Pasting was rejected."))})});return e});
|
Loading…
Reference in New Issue