Commit 7785139e authored by Tomas Norre Mikkelsen's avatar Tomas Norre Mikkelsen

Merge branch 'feature/add-notification-for-liked-extensions' into 'develop'

[FEATURE] Add notifications for a new extension release

See merge request !420
parents c026a0c9 a300c4ba
Pipeline #7108 passed with stages
in 4 minutes and 8 seconds
......@@ -210,6 +210,7 @@ class ExtensionController extends \T3o\TerFe2\Controller\AbstractController
$currentUser = $this->ownerRepository->findByUid((int)$GLOBALS['TSFE']->fe_user->user['uid']);
if ($currentUser instanceof \T3o\TerFe2\Domain\Model\FrontendUser) {
$this->view->assign('hasLiked', $currentUser->getLikedExtensions()->contains($extension));
$this->view->assign('notification', $currentUser->getNotifiedExtensions()->contains($extension));
}
}
}
......@@ -522,14 +523,12 @@ class ExtensionController extends \T3o\TerFe2\Controller\AbstractController
public function likeAction(\T3o\TerFe2\Domain\Model\Extension $extension)
{
$liked = false;
if ($GLOBALS['TSFE'] && $GLOBALS['TSFE']->loginUser) {
$currentUser = $this->ownerRepository->findByUid((int)$GLOBALS['TSFE']->fe_user->user['uid']);
if ($currentUser instanceof \T3o\TerFe2\Domain\Model\FrontendUser) {
$currentUser->addLikedExtension($extension);
$this->ownerRepository->update($currentUser);
$this->extensionRepository->update($extension);
$liked = true;
}
$currentUser = $this->getLoggedInUser();
if ($currentUser instanceof \T3o\TerFe2\Domain\Model\FrontendUser) {
$currentUser->addLikedExtension($extension);
$this->ownerRepository->update($currentUser);
$this->extensionRepository->update($extension);
$liked = true;
}
return json_encode($liked);
......@@ -538,19 +537,45 @@ class ExtensionController extends \T3o\TerFe2\Controller\AbstractController
public function disLikeAction(\T3o\TerFe2\Domain\Model\Extension $extension)
{
$disliked = false;
if ($GLOBALS['TSFE'] && $GLOBALS['TSFE']->loginUser) {
$currentUser = $this->ownerRepository->findByUid((int)$GLOBALS['TSFE']->fe_user->user['uid']);
if ($currentUser instanceof \T3o\TerFe2\Domain\Model\FrontendUser) {
$currentUser->removeLikedExtension($extension);
$this->ownerRepository->update($currentUser);
$this->extensionRepository->update($extension);
$disliked = true;
}
$currentUser = $this->getLoggedInUser();
if ($currentUser instanceof \T3o\TerFe2\Domain\Model\FrontendUser) {
$currentUser->removeLikedExtension($extension);
$this->ownerRepository->update($currentUser);
$this->extensionRepository->update($extension);
$disliked = true;
}
return json_encode($disliked);
}
public function activateNotificationAction(\T3o\TerFe2\Domain\Model\Extension $extension): string
{
$notified = false;
$currentUser = $this->getLoggedInUser();
if ($currentUser instanceof \T3o\TerFe2\Domain\Model\FrontendUser) {
$currentUser->addNotifiedExtension($extension);
$this->ownerRepository->update($currentUser);
$this->extensionRepository->update($extension);
$notified = true;
}
return json_encode($notified);
}
public function deactivateNotificationAction(\T3o\TerFe2\Domain\Model\Extension $extension): string
{
$unnotified = false;
$currentUser = $this->getLoggedInUser();
if ($currentUser instanceof \T3o\TerFe2\Domain\Model\FrontendUser) {
$currentUser->removeNotifiedExtension($extension);
$this->ownerRepository->update($currentUser);
$this->extensionRepository->update($extension);
$unnotified = true;
}
return json_encode($unnotified);
}
/**
* Check if current frontend user can upload given extension
*
......@@ -574,6 +599,18 @@ class ExtensionController extends \T3o\TerFe2\Controller\AbstractController
return !empty($isAllowedToUploadKey);
}
/**
* @return \T3o\TerFe2\Domain\Model\FrontendUser|null
*/
protected function getLoggedInUser(): ?\T3o\TerFe2\Domain\Model\FrontendUser
{
if ($GLOBALS['TSFE'] && $GLOBALS['TSFE']->fe_user && $GLOBALS['TSFE']->fe_user->user) {
return $this->ownerRepository->findByUid((int)$GLOBALS['TSFE']->fe_user->user['uid']);
}
return null;
}
/**
* Check if an version does not exist for extension
*
......
......@@ -121,6 +121,11 @@ class Extension extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity
*/
protected $likes = 0;
/**
* @var int
*/
protected $notifications = 0;
/**
* Initialize all ObjectStorage instances.
*/
......@@ -600,4 +605,14 @@ class Extension extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity
$this->likes--;
}
}
public function getNotifications(): int
{
return $this->notifications;
}
public function setNotifications(int $notifications): void
{
$this->notifications = $notifications;
}
}
......@@ -21,10 +21,16 @@ class FrontendUser extends \TYPO3\CMS\Extbase\Domain\Model\FrontendUser
*/
protected $likedExtensions;
/**
* @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\T3o\TerFe2\Domain\Model\Extension>
*/
protected $notifiedExtensions;
public function __construct()
{
parent::__construct();
$this->likedExtensions = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
$this->notifiedExtensions = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
}
/**
......@@ -61,4 +67,28 @@ class FrontendUser extends \TYPO3\CMS\Extbase\Domain\Model\FrontendUser
$this->likedExtensions->detach($extension);
}
}
public function getNotifiedExtensions(): \TYPO3\CMS\Extbase\Persistence\ObjectStorage
{
if ($this->notifiedExtensions === null) {
$this->notifiedExtensions = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
}
return $this->notifiedExtensions;
}
public function setNotifiedExtensions(\TYPO3\CMS\Extbase\Persistence\ObjectStorage $notifiedExtensions): void
{
$this->notifiedExtensions = $notifiedExtensions;
}
public function addNotifiedExtension(Extension $extension): void
{
$this->notifiedExtensions->attach($extension);
}
public function removeNotifiedExtension(Extension $extension): void
{
$this->notifiedExtensions->detach($extension);
}
}
......@@ -79,6 +79,7 @@ class ImportExtensionsFromQueueTask extends Task
$indexQueue = GeneralUtility::makeInstance(Queue::class);
$indexQueue->updateItem('tx_terfe2_domain_model_extension', $extUid);
}
$this->sendNotifications($extUid, $extensionData);
}
$this->logger->info(sprintf(
'Extension "%s" still exists in ter_fe2 with version %s',
......@@ -470,6 +471,52 @@ class ImportExtensionsFromQueueTask extends Task
->execute();
}
protected function sendNotifications(int $extensionUid, array $extensionData)
{
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
->getQueryBuilderForTable('fe_users');
$notifications = $queryBuilder
->select(...['u.email', 'u.name', 'e.ext_key', 'e.composer_name'])
->from('tx_terfe2_extension_feuser_notification_mm', 'notify')
->join(
'notify',
'fe_users',
'u',
'u.uid = notify.uid_foreign'
)
->join(
'notify',
'tx_terfe2_domain_model_extension',
'e',
'e.uid = notify.uid_local'
)
->where(
$queryBuilder->expr()->eq('notify.uid_local', (int)$extensionUid)
)
->execute()
->fetchAll();
if (!empty($notifications)) {
foreach ($notifications as $notification) {
$mail = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Mail\MailMessage::class);
$mail->setTo($notification['email'], $notification['name']);
$mail->setSubject('Release of extension "' . $notification['ext_key'] . '", version ' . $extensionData['version']);
$standalone = GeneralUtility::makeInstance(\TYPO3\CMS\Fluid\View\StandaloneView::class);
$standalone->setTemplatePathAndFilename('EXT:ter_fe2/Resources/Private/Templates/Mail/NewVersionNotify.html');
$standalone->assignMultiple(
[
'notify' => $notification,
'extData' => $extensionData
]
);
$mail->setBody($standalone->render());
$mail->send();
}
}
}
/**
* @param string $extKey
*
......
......@@ -11,6 +11,17 @@ $columns = [
'MM' => 'tx_terfe2_extension_feuser_mm',
'MM_opposite_field' => 'likes'
]
],
'notified_extensions' => [
'exclude' => 1,
'label' => 'Liked extensions',
'config' => [
'type' => 'select',
'renderType' => 'selectMultipleSideBySide',
'foreign_table' => 'tx_terfe2_domain_model_extension',
'MM' => 'tx_terfe2_extension_feuser_notification_mm',
'MM_opposite_field' => 'notifications'
]
]
];
......
......@@ -234,6 +234,16 @@ return [
'foreign_table' => 'fe_users',
'MM' => 'tx_terfe2_extension_feuser_mm'
]
],
'notifications' => [
'exclude' => 1,
'label' => 'Notifications for users',
'config' => [
'type' => 'select',
'renderType' => 'selectMultipleSideBySide',
'foreign_table' => 'fe_users',
'MM' => 'tx_terfe2_extension_feuser_notification_mm'
]
]
],
];
......@@ -151,6 +151,8 @@ plugin.tx_terfe2 {
like.typeNum = 4500
dislike.typeNum = 4501
activateNotification.typeNum = 4502
deactivateNotification.typeNum = 4503
}
const.page.ter_fe = 1
......@@ -160,6 +160,14 @@ dislike < like
dislike.typeNum = {$plugin.tx_terfe2.dislike.typeNum}
dislike.10.switchableControllerActions.Extension.1 = disLike
activateNotification < like
activateNotification.typeNum = {$plugin.tx_terfe2.activateNotification.typeNum}
activateNotification.10.switchableControllerActions.Extension.1 = activateNotification
deactivateNotification < like
deactivateNotification.typeNum = {$plugin.tx_terfe2.deactivateNotification.typeNum}
deactivateNotification.10.switchableControllerActions.Extension.1 = deactivateNotification
plugin.tx_terfe2_rating {
features.requireCHashArgumentForActionArguments = 0
}
......
......@@ -155,6 +155,20 @@
</f:if>
</f:else>
</f:if>
<f:security.ifAuthenticated>
<f:if condition="{notification}">
<f:then>
<button class="btn btn-success btn-block" data-rating-type="deactivateNotification" data-rating-extension="{extension.uid}" title="Do not notify me anymore of new extension releases">
<strong><i class="fa fa-bell"></i> Notify me via email on new releases</strong>
</button>
</f:then>
<f:else>
<button class="btn btn-danger btn-block" data-rating-type="activateNotification" data-rating-extension="{extension.uid}" title="Notify me on new extension releases">
<strong><i class="fa fa-bell-slash"></i> Notify me via email on new releases</strong>
</button>
</f:else>
</f:if>
</f:security.ifAuthenticated>
<f:if condition="{extension.forgeLink}">
<f:link.external class="btn btn-secondary btn-block" rel="nofollow" uri="{extension.forgeLink}" target="_blank">
<i class="fa fa-hand-o-right"></i> Found an Issue?
......
Dear {notify.name},
there was just a new release of the extension "{notify.ext_key}" with version {extData.version}.
Upload comment:
{extData.uploadcomment -> f:format.nl2br()}
URL: https://extensions.typo3.org/extension/{notify.ext_key}
Download as ZIP: https://extensions.typo3.org/extension/download/{notify.ext_key}/{extData.version}/
<f:if condition="{notify.composer_name}">Composer command: composer update {notify.composer_name} --with-all-dependencies</f:if>
If you don't want to get notified anymore, go to https://extensions.typo3.org/extension/{notify.ext_key}, login and deactivate the notifications for this extension.
Best regards,
Your typo3.org maintenance team
--
This is an automatic message from the typo3.org system
Contact us: https://typo3.org/teams-committees/typo3org/ or just reply to this email
......@@ -78,6 +78,22 @@ jQuery(document).ready(function ($) {
likes--;
newRatingType = 'like';
}
if (ratingType === 'activateNotification') {
pageType = 4502;
removeClass = 'btn-danger';
addClass = 'btn-success';
subRemoveClass = 'fa-bell-slash';
subAddClass = 'fa-bell';
newRatingType = 'deactivateNotification';
}
if (ratingType === 'deactivateNotification') {
pageType = 4503;
removeClass = 'btn-success';
addClass = 'btn-danger';
subRemoveClass = 'fa-bell';
subAddClass = 'fa-bell-slash';
newRatingType = 'activateNotification';
}
$.ajax({
url: '/index.php?type=' + pageType,
data: {
......@@ -87,7 +103,11 @@ jQuery(document).ready(function ($) {
success: function (returnData) {
$(item).removeClass(removeClass).addClass(addClass);
$(item).data('rating-type', newRatingType);
item.lastChild.nodeValue = " " + likes.toLocaleString('en');
if (subRemoveClass && subAddClass) {
$(item).find('i').removeClass(subRemoveClass).addClass(subAddClass);
} else {
item.lastChild.nodeValue = " " + likes.toLocaleString('en');
}
}
});
})
......
......@@ -27,10 +27,10 @@ if (!defined('TYPO3_MODE')) {
'T3o.ter_fe2',
'Rating',
[
'Extension' => 'like,disLike'
'Extension' => 'like,disLike,activateNotification,deactivateNotification'
],
[
'Extension' => 'like,disLike'
'Extension' => 'like,disLike,activateNotification,deactivateNotification'
]
);
......
......@@ -20,6 +20,7 @@ CREATE TABLE tx_terfe2_domain_model_extension (
paypal_url varchar(255) DEFAULT '' NOT NULL,
expire int(11) unsigned default '0' NOT NULL,
likes int(11) unsigned default '0' NOT NULL,
notifications int(11) unsigned default '0' NOT NULL,
tstamp int(11) unsigned DEFAULT '0' NOT NULL,
crdate int(11) unsigned DEFAULT '0' NOT NULL,
......@@ -222,7 +223,8 @@ CREATE TABLE tx_terfe2_domain_model_author (
);
CREATE TABLE fe_users (
liked_extensions int(11) unsigned DEFAULT '0' NOT NULL
liked_extensions int(11) unsigned DEFAULT '0' NOT NULL,
notified_extensions int(11) unsigned DEFAULT '0' NOT NULL
);
# ======================================================================
......@@ -238,6 +240,7 @@ CREATE TABLE tx_terfe2_extension_tag_mm (
KEY uid_foreign (uid_foreign)
);
# MM table for likes
CREATE TABLE `tx_terfe2_extension_feuser_mm` (
`uid_local` int(10) unsigned DEFAULT '0' NOT NULL,
`uid_foreign` int(10) unsigned DEFAULT '0' NOT NULL,
......@@ -247,3 +250,14 @@ CREATE TABLE `tx_terfe2_extension_feuser_mm` (
KEY `uid_local` (`uid_local`),
KEY `uid_foreign` (`uid_foreign`)
);
# MM table for notifications
CREATE TABLE `tx_terfe2_extension_feuser_notification_mm` (
`uid_local` int(10) unsigned DEFAULT '0' NOT NULL,
`uid_foreign` int(10) unsigned DEFAULT '0' NOT NULL,
`sorting` int(10) unsigned DEFAULT '0' NOT NULL,
`sorting_foreign` int(10) unsigned DEFAULT '0' NOT NULL,
KEY `uid_local` (`uid_local`),
KEY `uid_foreign` (`uid_foreign`)
);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment