social_event_invite_flow-1.0.0-beta3/src/Form/EnrollInviteSendEmailsFromFileForm.php
src/Form/EnrollInviteSendEmailsFromFileForm.php
<?php
namespace Drupal\social_event_invite_flow\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\File\FileUrlGenerator;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\TempStore\PrivateTempStoreFactory;
use Drupal\Core\Utility\Token;
use Drupal\file\Entity\File;
use Drupal\node\NodeInterface;
use Drupal\social_event\Entity\Node\Event;
use Drupal\social_event\EventEnrollmentInterface;
use Drupal\social_event\Service\SocialEventEnrollServiceInterface;
use Drupal\social_event_max_enroll\Service\EventMaxEnrollService;
use Drupal\social_event_invite_flow\Service\EventInviteFlowService;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\social_event_invite_flow\Form\InviteFlowBaseForm;
use Drupal\Component\Utility\Environment;
use Drupal\Core\File\FileSystemInterface;
/**
* Class EnrollInviteForm.
*/
class EnrollInviteSendEmailsFromFileForm extends InviteFlowBaseForm {
/*
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The logger factory.
*
* @var \Drupal\Core\Logger\LoggerChannelFactoryInterface
*/
protected $loggerFactory;
/**
* The current group from route.
*
* @var \Drupal\Core\Logger\LoggerChannelFactoryInterface
*/
protected $group;
/**
* The node storage for event enrollments.
*/
protected EntityStorageInterface $entityStorage;
/**
* Drupal\Core\TempStore\PrivateTempStoreFactory definition.
*/
protected PrivateTempStoreFactory $tempStoreFactory;
/**
* The token service.
*/
protected Token $token;
/**
* The social event enroll.
*/
protected SocialEventEnrollServiceInterface $eventEnrollService;
/**
* The module handler service.
*/
protected ModuleHandlerInterface $moduleHandler;
/**
* The event maximum enroll service.
*/
protected EventMaxEnrollService $eventMaxEnrollService;
/**
* File URL Generator services.
*
* @var \Drupal\Core\File\FileUrlGenerator
*/
protected FileUrlGenerator $fileUrlGenerator;
/**
* The invite flow service.
*/
protected EventInviteFlowService $eventInviteFlowService;
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'enroll_invite_upload_form';
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
$instance = parent::create($container);
$instance->entityStorage = $instance->entityTypeManager->getStorage('event_enrollment');
$instance->tempStoreFactory = $container->get('tempstore.private');
$instance->token = $container->get('token');
$instance->eventEnrollService = $container->get('social_event.enroll');
$instance->moduleHandler = $container->get('module_handler');
if ($instance->moduleHandler->moduleExists('social_event_max_enroll')) {
$instance->eventMaxEnrollService = $container->get('social_event_max_enroll.service');
}
$instance->fileUrlGenerator = $container->get('file_url_generator');
$instance->eventInviteFlowService = $container->get('social_event_invite_flow.invite_flow_service');
return $instance;
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$form = parent::buildForm($form, $form_state);
$tempstore = $this->tempStoreFactory->get('social_event_invite_flow_enroll_invite_upload_form_values');
$params = $tempstore->get('params');
$email_column_default_value = $params['email_column'] ?? '';
$delimiter_default_value = $params['delimiter'] ?? '';
$form['#attributes']['enctype'] = 'multipart/form-data';
$form['#attributes']['class'][] = 'form--default';
/** @var \Drupal\node\Entity\Node $node */
$node = $this->routeMatch->getParameter('node');
$params = [
'user' => $this->currentUser(),
'node' => $node,
];
// Load event invite configuration.
$invite_config = $this->config('social_event_invite.settings');
$form['importdata'] = [
'#type' => 'fieldset',
'#title' => $this->t('Importdata'),
];
$form['importdata']['csvfile'] = [
'#title' => $this->t('CSV file'),
'#type' => 'file',
'#description' => ($max_size = Environment::getUploadMaxSize()) ? $this->t('Due to server restrictions, the <strong>maximum upload file size is @max_size</strong>. Files that exceed this size will be disregarded.', ['@max_size' => format_size($max_size)]) : '',
'#element_validate' => ['::validateFileupload'],
];
$form['importdata']['delimiter'] = array(
'#title' => $this->t('CSV delimiter'),
'#type' => 'select',
'#options' => [';', ','],
'#default_value' => $delimiter_default_value
);
$form['importdata']['email_column'] = [
'#title' => $this->t('Email column name'),
'#type' => 'textfield',
'#description' => $this->t('Enter the exact column name from which to import email addresses.'),
'#required' => TRUE,
'#default_value' => $email_column_default_value
];
$form['invite_messages'] = [
'#type' => 'fieldset',
'#title' => $this->t('Invitee Messages'),
'#collapsible' => FALSE,
'#collapsed' => TRUE,
];
/*
$elements = $this->eventInviteFlowService->getEventInviteMessageFormElements();
foreach ($elements as $key => $element) {
$form['invite_messages'][$key] = [
'#type' => $element['type'],
'#title' => $element['title'],
'#required' => $element['required'],
'#default_value' => $element['default_value']
];
}
*/
$form['event'] = [
'#type' => 'hidden',
'#value' => $this->routeMatch->getRawParameter('node'),
];
$form['actions']['#type'] = 'actions';
$form['actions']['submit_cancel'] = [
'#type' => 'submit',
'#weight' => 999,
'#value' => $this->t('Back to event'),
'#submit' => [[$this, 'cancelForm']],
'#limit_validation_errors' => [],
];
$form['actions']['submit'] = [
'#type' => 'submit',
'#value' => $this->t('Send your invite(s) by email'),
'#attributes' => [
'class' => [
'btn-primary',
'btn-raised',
'button--primary'
]
]
];
// We should prevent invite enrollments if social_event_max_enroll is
// enabled and there are no left spots.
if ($this->moduleHandler->moduleExists('social_event_max_enroll')) {
if (
$node instanceof NodeInterface &&
$this->eventMaxEnrollService->isEnabled($node) &&
$this->eventMaxEnrollService->getEnrollmentsLeft($node) === 0
) {
$form['actions']['submit']['#attributes'] = [
'disabled' => 'disabled',
'title' => $this->t('There are no spots left'),
];
}
}
return $form;
}
/**
* Cancel form taking you back to an event.
*
* @param array $form
* An associative array containing the structure of the form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*/
public function cancelForm(array &$form, FormStateInterface $form_state) {
$form_state->setRedirect('view.event_manage_enrollments.page_manage_enrollments', [
'node' => $this->routeMatch->getRawParameter('node'),
]);
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
$delimiter = $form_state->getValue('delimiter');
$csvfile = $form_state->getValue('csvfile');
$email_column_name = $form_state->getValue('email_column');
if (empty($csvfile)) {
$form_state->setErrorByName('csvfile', $this->t('You have to provide a text or upload a file!'));
}
else {
if (!empty($csvfile) && !empty($email_column_name)) {
if ($delimiter == '0') {
$delimiter = ';';
} else {
$delimiter = ',';
}
if ($handle = fopen($csvfile, 'r')) {
if ($headers = fgetcsv($handle, 4096, $delimiter)) {
if (!in_array($email_column_name,$headers)) {
$form_state->setErrorByName('email_column', $this->t('No Email field available'));
}
}
fclose($handle);
}
else {
$form_state->setErrorByName('csvfile', $this->t('Unable to read uploaded file @filepath', ['@filepath' => $csvupload]));
}
}
}
}
/**
* Validate the file upload.
*/
public static function validateFileupload(&$element, FormStateInterface $form_state, &$complete_form) {
$validators = [
'file_validate_extensions' => ['csv'],
];
// @TODO: File_save_upload will probably be deprecated soon as well.
// @see https://www.drupal.org/node/2244513.
if ($file = file_save_upload('csvfile', $validators, FALSE, 0, FileSystemInterface::EXISTS_REPLACE)) {
// The file was saved using file_save_upload() and was added to the
// files table as a temporary file. We'll make a copy and let the
// garbage collector delete the original upload.
$csv_dir = 'temporary://csvfile';
$directory_exists = \Drupal::service('file_system')
->prepareDirectory($csv_dir, FileSystemInterface::CREATE_DIRECTORY);
if ($directory_exists) {
$destination = $csv_dir . '/' . $file->getFilename();
if (\Drupal::service('file.repository')
->copy($file, $destination, FileSystemInterface::EXISTS_REPLACE)) {
$form_state->setValue('csvfile', $destination);
}
else {
$form_state->setErrorByName('enroll_invite_import', t('Unable to copy upload file to @dest', ['@dest' => $destination]));
}
}
}
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
parent::submitForm($form, $form_state);
$email_column = $form_state->getValue('email_column');
$delimiter = $form_state->getValue('delimiter');
$nid = $form_state->getValue('event');
$params['email_column'] = $email_column;
$params['delimiter'] = $delimiter;
$tempstore = $this->tempStoreFactory->get('social_event_invite_flow_enroll_invite_upload_form_values');
try {
$tempstore->set('params', $params);
}
catch (\Exception $error) {
$this->loggerFactory->get('event_invite_form_values')->alert(t('@err', ['@err' => $error]));
$this->messenger->addWarning(t('Unable to proceed, please try again.'));
}
// BOM as a string for comparison.
$bom = "\xef\xbb\xbf";
$csvfile = $form_state->getValue('csvfile');
$delimiter = $form_state->getValue('delimiter');
if ($delimiter == '0') {
$delimiter = ';';
} else {
$delimiter = ',';
}
// Prepare batch.
$batch = [
'title' => $this->t('Prepare sending emails from CSV ...'),
'operations' => [],
'init_message' => $this->t('Prepare sending emails.'),
'progress_message' => $this->t('Processed @current out of @total.'),
'error_message' => $this->t('An error occurred during processing'),
'finished' => '\Drupal\social_event_invite_flow\Service\SendEmails::csvUploadFinished'
];
if ($handle = fopen($csvfile, 'r')) {
// Progress file pointer and get first 3 characters to compare to the BOM string.
if (fgets($handle, 4) !== $bom) {
// BOM not found - rewind pointer to start of file.
rewind($handle);
}
$batch['operations'][] = [
'\Drupal\social_event_invite_flow\Service\SendEmails::csvUploadRememberFilename',
[$csvfile]
];
$counter = 0;
while ($line = fgetcsv($handle, 4096, $delimiter)) {
//\Drupal::logger('debug')->debug('count line numbers: <pre><code>' . print_r($counter, TRUE) . '</code></pre>');
if ( $counter === 0 ) {
$email_key = array_search($email_column, $line);
//\Drupal::logger('debug')->debug('Line number: <pre><code>' . print_r($line, TRUE) . '</code></pre>');
}
else {
$email = $line[$email_key];
//\Drupal::logger('debug')->debug('LINE: <pre><code>' . print_r($line[$email_key], TRUE) . '</code></pre>');
// Use base64_encode to ensure we don't overload the batch
// processor by stuffing complex objects into it.
$batch['operations'][] = [
'\Drupal\social_event_invite_flow\Service\SendEmails::csvUploadLine',
[$line,$email,$nid]
];
}
$counter++;
}
fclose($handle);
}
batch_set($batch);
}
/**
* Returns access to the invite page.
*
* @param \Drupal\node\NodeInterface $node
* The node entity.
* @param \Drupal\Core\Session\AccountInterface $account
* The currently logged in account.
*
* @return \Drupal\Core\Access\AccessResultInterface
* The access results.
*/
public function inviteAccess(NodeInterface $node, AccountInterface $account) {
// Anonymous users should not have the possibility to invite users.
if ($account->isAnonymous()) {
return AccessResult::forbidden();
}
// Allow for event managers/organizers.
if (social_event_manager_or_organizer()) {
return AccessResult::allowed();
}
// Disable access for non-enrolled users, invite by users was disabled in
// the event invite settings, enrolment is disabled for this event or event
// is visible only for group members.
$event_invite_settings = $this->config('social_event_invite.settings');
$enrollment = $this->entityTypeManager->getStorage('event_enrollment')
->loadByProperties([
'status' => TRUE,
'field_account' => $account->id(),
'field_event' => $node->id(),
'field_enrollment_status' => '1',
]);
if (
!$event_invite_settings->get('invite_by_users') ||
!($node instanceof Event && $node->isEnrollmentEnabled()) ||
empty($enrollment) ||
$node->get('field_content_visibility')->getString() === 'group'
) {
return AccessResult::forbidden();
}
// Allow sharing/invites for users only if allowed by the event manager.
if (
$node->hasField('field_event_send_invite_by_user') &&
!$node->get('field_event_send_invite_by_user')->isEmpty() &&
$node->get('field_event_send_invite_by_user')->getString() === '1'
) {
return AccessResult::allowed();
}
return AccessResult::neutral();
}
}
