Commit c2938505 authored by kent@passingphase.nz's avatar kent@passingphase.nz

merge in fixes and improvements from github

parent e9497141
......@@ -5,14 +5,17 @@
"license": "GPL-2.0+",
"minimum-stability": "dev",
"prefer-stable": true,
"homepage": "https://www.drupal.org/project/message_private",
"homepage": "https://www.drupal.org/project/message_thread",
"require": {
"php": "^5.5|^7.0",
"drupal/core": "^8.6",
"drupal/message": "^1.0",
"drupal/message_history": "dev-1.x",
"drupal/message_notify": "^1.0",
"drupal/message_ui": "^1.0",
"drupal/message_private": "^1.0"
"drupal/message_private": "^1.0",
"drupal/message_thread_history": "1.0-alpha1",
"squizlabs/php_codesniffer": "*"
},
"require-dev": {
"composer/installers": "^1.2",
......
id: message_thread_delete_action
label: 'Delete selected message threads'
status: true
langcode: en
type: message_thread
plugin: message_thread_delete_action
dependencies:
module:
- message_thread
This diff is collapsed.
......@@ -7,3 +7,4 @@ configure: message_thread.admin_settings
dependencies:
- drupal:views
- message_private:message_private
- message_history:message_history
......@@ -2,9 +2,103 @@
/**
* @file
* Message Thread un-installer.
* Message Thread installer.
*/
use Drupal\Core\Database\Database;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\field\Entity\FieldStorageConfig;
/**
* Implements hook_install().
*/
function message_thread_install() {
// By default, maintain entity statistics for messages.
// @see \Drupal\message_thread\MessageStatisticsInterface
\Drupal::state()->set('message.maintain_entity_statistics', TRUE);
}
/**
* Implements hook_schema().
*/
function message_thread_schema() {
$schema['message_thread_statistics'] = [
'description' => 'Maintains statistics of message threads and messages to show "new" and "updated" flags.',
'fields' => [
'entity_id' => [
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
'description' => 'The entity_id of the entity for which the statistics are compiled.',
],
'entity_type' => [
'type' => 'varchar_ascii',
'not null' => TRUE,
'default' => 'message_thread',
'length' => EntityTypeInterface::ID_MAX_LENGTH,
'description' => 'The entity_type of the entity to which this message is a reply.',
],
'field_name' => [
'type' => 'varchar_ascii',
'not null' => TRUE,
'default' => '',
'length' => FieldStorageConfig::NAME_MAX_LENGTH,
'description' => 'The field_name of the field that was used to add this message.',
],
'mid' => [
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'description' => 'The {message}.mid of the last message.',
],
'last_message_timestamp' => [
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'description' => 'The Unix timestamp of the last message that was posted within this entity, from {message}.changed.',
],
'last_message_name' => [
'type' => 'varchar',
'length' => 60,
'not null' => FALSE,
'description' => 'The name of the latest author to post a message on this message thread, from {message}.name.',
],
'last_message_uid' => [
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
'description' => 'The user ID of the latest author to post a message on this message thread, from {message}.uid.',
],
'message_count' => [
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
'description' => 'The total number of messages on this entity.',
],
],
'primary key' => ['entity_id', 'entity_type', 'field_name'],
'indexes' => [
'last_message_timestamp' => ['last_message_timestamp'],
'message_count' => ['message_count'],
'last_message_uid' => ['last_message_uid'],
],
'foreign keys' => [
'last_message_author' => [
'table' => 'users',
'columns' => [
'last_message_uid' => 'uid',
],
],
],
];
return $schema;
}
/**
* Implements hook_uninstall().
*
......@@ -16,6 +110,31 @@ function message_thread_uninstall() {
/**
* Implements hook_update_N().
*
* Add new table for statistics.
*/
function message_thread_update_8002() {
if (db_table_exists('message_thread_statistics')) {
return;
}
$schema_manager = Database::getConnection()->schema();
$schema = drupal_get_module_schema('message_thread');
$schema_manager->createTable('message_thread_statistics', $schema['message_thread_statistics']);
}
/**
* Implements hook_update_N().
*
* Turn on statistics.
*/
function message_thread_update_8003() {
\Drupal::state()->set('message.maintain_entity_statistics', TRUE);
}
/**
* Implements hook_update_N().
*
* Add new field definitions.
*/
function message_thread_update_8001() {
$entity_definition_update_manager = \Drupal::service('entity.definition_update_manager');
......
......@@ -29,6 +29,7 @@ task.message_thread.delete_form:
base_route: message_thread.threads
title: Delete
## Provide dynamic local tasks.
#message_thread.dynamic_tasks:
# deriver: 'Drupal\message_thread\Plugin\Derivative\DynamicLocalTasks'
task.message_thread.admin:
title: Message Threads
route_name: message_thread.message_threads
base_route: node.content_overview
......@@ -25,9 +25,10 @@ use Drupal\Core\Access\AccessResultForbidden;
/**
* Implements hook_installed().
*
* Flush cache after install to ensure that all routes are read.
*/
function message_thread_modules_installed() {
// Flush cache after install to ensure that all routes are read.
drupal_flush_all_caches();
}
......@@ -40,8 +41,7 @@ function message_thread_help($route_name, RouteMatchInterface $arg) {
$output = file_get_contents(drupal_get_path('module', 'message_thread') . '/README.md');
return \Drupal::moduleHandler()->moduleExists('markdown') ? Xss::filterAdmin(\Drupal::moduleHandler()->invoke('markdown', 'filter', [
'process', 0, -1, $output,
]
)) : '<h3>Message Private README</h3><pre>' . Html::escape($output) . '</pre>';
])) : '<h3>Message Private README</h3><pre>' . Html::escape($output) . '</pre>';
}
}
......@@ -54,10 +54,10 @@ function message_thread_form_alter(array &$form, FormStateInterface $form_state,
return;
}
// When a message is created we need to ensure that the participants
// are included.
// This is best done in the validation hook
// to ensure participants are not missed from submit functions.
// When a message is created we need to ensure
// that the participants are included.
// This is best done in the validation hook to ensure participants
// are not missed from submit functions.
$thread_templates = \Drupal::entityTypeManager()->getListBuilder('message_thread_template')->load();
foreach ($thread_templates as $name => $template) {
$settings = $template->getSettings();
......@@ -117,7 +117,7 @@ function message_thread_form_alter(array &$form, FormStateInterface $form_state,
}
/**
* Submit handler.
* Submit message callback.
*/
function message_thread_submit_message(array &$form, FormStateInterface $form_state) {
$thread_id = $form_state->getValue('message_thread');
......@@ -131,6 +131,10 @@ function message_thread_submit_message(array &$form, FormStateInterface $form_st
]
)->execute();
// Update statistics.
$entity = $form_state->getFormObject()->getEntity();
\Drupal::service('message.statistics')->update($entity);
// Redirect to the thread not the message.
$params = [
'message_thread' => $thread_id,
......@@ -231,8 +235,8 @@ function message_thread_preprocess_links(&$variables) {
if (substr($route_name, 0, 15) == 'entity.message.') {
// $message_access_manager =
// new MessagePrivateAccessControlHandler($entity->getEntityType());.
// $access = $message_access_manager->checkAccess(
// $entity, $operation, $account);.
// $access =
// $message_access_manager->checkAccess($entity, $operation, $account);.
$access = message_private_message_access($entity, $operation, $account);
// Disable link if user does not have access.
if ($access instanceof AccessResultForbidden) {
......@@ -275,8 +279,8 @@ function message_thread_theme() {
* Default template: message_thread.html.twig.
*
* Most themes use their own copy of message_thread.html.twig.
* The default is located
* inside "/core/modules/message_thread/templates/message_thread.html.twig".
* The default is located inside
* "/core/modules/message_thread/templates/message_thread.html.twig".
* Look in there for the full list of variables.
*
* @param array $variables
......@@ -380,10 +384,10 @@ function message_thread_entity_view_alter(array &$build, EntityInterface $entity
* Helper function to display the messages as a View.
*
* @param array $settings
* Template settings.
* Message thread settings.
*
* @return array|bool|false
* Display inof or false.
* @return array|bool|null
* The view or False.
*/
function message_thread_get_messages_display(array $settings, $entity) {
......@@ -446,7 +450,6 @@ function message_thread_reply_link($entity, $component) {
function message_thread_reply_form($entity, $component) {
$user = \Drupal::currentUser();
$account = User::load($user->id());
$ref = User::load(1);
$message = \Drupal::entityTypeManager()->getStorage('message')->create([
'template' => 'private_message',
'uid' => $account->id(),
......@@ -540,7 +543,7 @@ function message_thread_local_tasks_alter(&$local_tasks) {
* Views Plugin access callback.
*
* @return \Drupal\Core\Access\AccessResult
* Access result object.
* Whether or not to allow access.
*/
function message_thread_tab_access_check() {
$account = \Drupal::currentUser();
......@@ -589,10 +592,9 @@ function message_thread_get_messages($thread_id) {
/**
* Implements hook_ENTITY_TYPE_view_alter() for message thread entities.
*/
function message_thread_message_thread_view_alter(array &$build,
function message_history_message_thread_view_alter(array &$build,
EntityInterface $message_thread,
EntityViewDisplayInterface $display) {
// Update the message_history table,
// stating that this user viewed all messages in this thread.
if (!\Drupal::service('module_handler')->moduleExists('message_history')) {
......@@ -633,3 +635,34 @@ function message_thread_theme_suggestions_message_thread(array $variables) {
return $suggestions;
}
/**
* Implements hook_entity_insert().
*/
function message_thread_entity_insert(EntityInterface $entity) {
// On insert of a message thread create the statistics entry.
switch ($entity->getEntityType()->id()) {
case 'message_thread':
if (!\Drupal::state()->get('message.maintain_entity_statistics')) {
return;
}
\Drupal::service('message.statistics')->create($entity);
break;
}
}
/**
* Implements hook_entity_predelete().
*/
function message_thread_entity_predelete(EntityInterface $entity) {
switch ($entity->getEntityTypeId()) {
case 'message_thread':
\Drupal::service('message.statistics')->delete($entity);
break;
case 'message':
\Drupal::service('message.statistics')->update($entity);
break;
}
}
......@@ -128,5 +128,12 @@ message_thread.reply:
message_template:
with_config_overrides: FALSE
message_thread.multiple_delete_confirm:
path: '/admin/content/message-thread/delete'
defaults:
_form: '\Drupal\message_thread\Form\DeleteMultiple'
requirements:
_permission: 'administer message threads'
route_callbacks:
- 'Drupal\message_thread\Routing\MessageThreadRoutes::routes'
......@@ -3,3 +3,9 @@ services:
class: Drupal\message_thread\Plugin\Breadcrumbs\MessageThreadBreadcrumbs
tags:
- { name: breadcrumb_builder, priority: 1020 }
message.statistics:
class: Drupal\message_thread\MessageStatistics
arguments: ['@database', '@current_user', '@entity.manager', '@state']
tags:
- { name: backend_overridable }
......@@ -6,9 +6,7 @@ use Drupal\Core\Url;
use Drupal\Core\Controller\ControllerBase;
use Drupal\message_thread\Entity\MessageThread;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\message_thread\MessageThreadTemplateInterface;
use Drupal\message\Entity\Message;
use Drupal\message\MessageTemplateInterface;
use Drupal\Component\Utility\Xss;
use Drupal\views\Views;
......@@ -77,15 +75,15 @@ class MessageThreadController extends ControllerBase implements ContainerInjecti
}
/**
* Form output to add a new message entity of message_thread_template.
* Generates form output for adding a new message_thread_template.
*
* @param \Drupal\message\MessageThreadTemplateInterface $message_thread_template
* The message template object.
* @param string $message_thread_template
* The message template name.
*
* @return array
* An array as expected by drupal_render().
*/
public function add(MessageThreadTemplateInterface $message_thread_template) {
public function add($message_thread_template) {
$message_thread = MessageThread::create(['template' => $message_thread_template]);
$form = $this->entityFormBuilder()->getForm($message_thread);
......@@ -93,9 +91,17 @@ class MessageThreadController extends ControllerBase implements ContainerInjecti
}
/**
* Form output to add a message entity of message_template inside a thread.
* Generates form output for adding a new message entity inside a thread.
*
* @param string $message_template
* The message template name.
* @param string $message_thread
* The message thread id.
*
* @return array
* An array as expected by drupal_render().
*/
public function reply(MessageTemplateInterface $message_template, $message_thread) {
public function reply($message_template, $message_thread) {
$message = Message::create(['template' => $message_template]);
$form = $this->entityFormBuilder()->getForm($message);
......@@ -134,7 +140,7 @@ class MessageThreadController extends ControllerBase implements ContainerInjecti
* Generates output of all threads belonging to the current user.
*
* @return array
* A render array for a list of the messages;
* A render array for a list of the messages.
*/
public function inBox() {
// Get threads that the current user belongs to.
......@@ -167,7 +173,6 @@ class MessageThreadController extends ControllerBase implements ContainerInjecti
$view->preExecute();
$view->execute($display_id);
$message_threads = $view->buildRenderable($display_id);
// Return build array.
......@@ -177,7 +182,8 @@ class MessageThreadController extends ControllerBase implements ContainerInjecti
else {
$url = Url::fromRoute('message.template_add');
return [
'#markup' => 'You have no messages in your inbox. Try sending a message to someone <a href="/' . $url->getInternalPath() . '">sending a message to someone</a>.',
'#markup' => 'You have no messages in your inbox. Try sending a message to someone <a href="/' .
$url->getInternalPath() . '">sending a message to someone</a>.',
];
}
}
......@@ -185,18 +191,21 @@ class MessageThreadController extends ControllerBase implements ContainerInjecti
/**
* Message thread title.
*
* @param Drupal\message_thread\Entity\MessageThread|null $message_thread
* The message thread object.
* @param \Drupal\message_thread\Entity\MessageThread $message_thread
* Message thread object.
*
* @return array|string
* The title.
* Markup.
*/
public function messageThreadTitle(MessageThread $message_thread = NULL) {
return $message_thread ? ['#markup' => $message_thread->get('field_thread_title')->getValue()[0]['value'], '#allowed_tags' => Xss::getHtmlTagList()] : '';
}
/**
* Display sent message threads using a view.
* Generates form output for adding a new message entity of message_template.
*
* @return array
* An array as expected by drupal_render().
*/
public function sent() {
$view_name = 'conversations';
......
<?php
namespace Drupal\message_thread\Form;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Form\ConfirmFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\message_thread\Entity\MessageThread;
use Drupal\Core\TempStore\PrivateTempStoreFactory;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a message thread deletion confirmation form.
*/
class DeleteMultiple extends ConfirmFormBase {
/**
* The array of message threads to delete.
*
* @var array
*/
protected $messageThreads = [];
/**
* The tempstore factory.
*
* @var \Drupal\Core\TempStore\PrivateTempStoreFactory
*/
protected $tempStoreFactory;
/**
* The message storage.
*
* @var \Drupal\Core\Entity\EntityStorageInterface
*/
protected $manager;
/**
* Constructs a DeleteMultiple form object.
*
* @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory
* The tempstore factory.
* @param \Drupal\Core\Entity\EntityManagerInterface $manager
* The entity manager.
*/
public function __construct(PrivateTempStoreFactory $temp_store_factory, EntityManagerInterface $manager) {
$this->tempStoreFactory = $temp_store_factory;
$this->storage = $manager->getStorage('message_thread');
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('tempstore.private'),
$container->get('entity.manager')
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'message_thread_multiple_delete_confirm';
}
/**
* {@inheritdoc}
*/
public function getQuestion() {
return \Drupal::translation()->formatPlural(count($this->messageThreads), 'Are you sure you want to delete this item?', 'Are you sure you want to delete these items?');
}
/**
* {@inheritdoc}
*/
public function getCancelRoute() {
}
/**
* {@inheritdoc}
*/
public function getConfirmText() {
return t('Delete');
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$this->messageThreads = $this->tempStoreFactory->get('message_thread_multiple_delete_confirm')->get(\Drupal::currentUser()->id());
if (empty($this->messageThreads)) {
return new RedirectResponse($this->getCancelUrl()->setAbsolute()->toString());
}
$form['message_threads'] = [
'#theme' => 'item_list',
'#items' => array_map(function (MessageThread $message_thread) {
$params = [
'@id' => $message_thread->id(),
'@template' => $message_thread->getTemplate()->label(),
];
return t('Delete message thread ID @id for template @template', $params);
}, $this->messageThreads),
];
$form = parent::buildForm($form, $form_state);
$form['actions']['cancel']['#href'] = $this->getCancelRoute();
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
if ($form_state->getValue('confirm') && !empty($this->messageThreads)) {
$this->storage->delete($this->messageThreads);
$this->tempStoreFactory->get('message_thread_multiple_delete_confirm')->delete(\Drupal::currentUser()->id());
$count = count($this->messageThreads);
$this->logger('message_thread')->notice('Deleted @count message threads.', ['@count' => $count]);
drupal_set_message(\Drupal::translation()->formatPlural($count, 'Deleted 1 message thread.', 'Deleted @count message threads.'));
}
$form_state->setRedirect('message_thread.message_threads');
}
/**
* {@inheritdoc}
*/
public function getCancelUrl() {
return new Url('message_thread.message_threads');
}
}
......@@ -34,11 +34,38 @@ class MessageForm extends MessageMessageForm {
return $element;
}
/**
* {@inheritdoc}
*
* Updates the message object by processing the submitted values.
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
// Build the node object from the submitted values.
parent::submitForm($form, $form_state);
/* @var $message Message */
$message = $this->entity;
$values = $form_state->getValues();
}
/**
* {@inheritdoc}
*/
public function save(array $form, FormStateInterface $form_state) {
parent::submitForm($form, $form_state);
// Redirect to message view display if user has access.
if ($message->access('view')) {
$form_state->setRedirect('entity.message.canonical', ['message' => $message->id()]);
}
else {
$form_state->setRedirect('<front>');
}
// @todo : for node they clear temp store here, but perhaps unused with
// message.
// In the unlikely case something went wrong on save, the message will be
// rebuilt and message form redisplayed.
drupal_set_message(t('The message could not be saved.'), 'error');
$form_state->setRebuild();
}
}
......@@ -56,6 +56,7 @@ class MessageThreadForm extends ContentEntityForm {
$form['created']['#group'] = 'owner';
}
// @todo: assess the best way to access and create tokens tab from D7.
// @todo : add similar to node/from library, adding css for
// 'message-form-owner' class.
// $form['#attached']['library'][] = 'node/form';
......@@ -105,7 +106,7 @@ class MessageThreadForm extends ContentEntityForm {
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
// Add owner as a participant.
// Add owner as a participant
// We do this before the parent::submit to ensure the value is saved.
$values = $form_state->getValues();;
$found = FALSE;
......
......@@ -35,26 +35,19 @@ class MessageThreadTemplateForm extends EntityForm {
/**
* The template storage manager.
*
* @var \Drupal\Core\Entity\EntityStorageInterface
* @var \Drupal\Core\Entity\\EntityStorageInterface
*/
protected $templateStorage;
/**
* The entity type manager.
* The purge plugin manager.
*
* @var \Drupal\Core\Entity\EntityTypeManager
* @var \Drupal\message\MessagePurgePluginManager
*/
protected $entityTypeManager;
/**
* Constructs the message thread template form.
*
* @param \Drupal\message\MessagePurgePluginManager $purge_manager
* The message purge plugin manager service.
* @param \Drupal\Core\Entity\EntityStorageInterface $template_storage
* The message purge plugin manager service.
* @param \Drupal\Core\Entity\EntityTypeManager $entity_type_manager
* The message purge plugin manager service.
*/
public function __construct(MessagePurgePluginManager $purge_manager, EntityStorageInterface $template_storage, EntityTypeManager $entity_type_manager) {
$this->purgeManager = $purge_manager;
......@@ -128,7 +121,6 @@ class MessageThreadTemplateForm extends EntityForm {
]),
];
// Message thread views.
$options = ['_none' => 'None'];
$options += $this->getMessageViews('message_thread_field_data');
$form['settings']['thread_view_id'] = [
......@@ -175,7 +167,9 @@ class MessageThreadTemplateForm extends EntityForm {
];
// Message views.
/*
* Message Views
*/
$form['settings']['view_id'] = [
'#title' => $this->t('Message View'),
'#type' => 'select',
......@@ -292,7 +286,7 @@ class MessageThreadTemplateForm extends EntityForm {
}
/**
* Get display ids of View.
* Get display ids for a View.
*/
public function getThreadDisplayIds(array &$form, FormStateInterface $form_state) {
return $this->getDisplayIds($form, $form_state,