'newsletter/%s' ); $this->config = array_merge($config, $this->config); if (empty($this->config['tableenv'])) { $this->config['tableenv'] = self::TALBE_ENV_SUBSCRIBER; } $this->config['requiredgroup'] = 'newsletter'; $this->config['requiredgroupmanager'] = 'newsletter'; $this->config['searchfields'] = array(); $this->config['usefilter'] = 1; $this->config['addcurrentuser'] = 'never'; $this->config['translator'] = 'no'; $this->config['limit'] = 25; switch ($this->config['tableenv']) { case self::TALBE_ENV_GENERICSOURCE: $this->config['table'] = WBDatasource_Newsletter::TABLE_NEWSLETTERGENERICSOURCE; $this->config['searchfields'] = array('title', 'brief'); break; case self::TALBE_ENV_TOPIC: $this->config['table'] = WBDatasource_Newsletter::TABLE_NEWSLETTERTOPIC; $this->config['searchfields'] = array('title', 'brief'); // $this->config['translator'] = 'auto'; break; case self::TALBE_ENV_NEWSLETTERARTICLE: $this->config['table'] = WBDatasource_Newsletter::TABLE_NEWSLETTERARTICLE; $this->config['searchfields'] = array('title', 'brief'); break; case self::TALBE_ENV_SUBSCRIBER: $this->config['table'] = WBDatasource_Newsletter::TABLE_SUBSCRIBER; $this->config['searchfields'] = array('forename', 'surname', 'company', 'position', 'note', 'emaillocal', 'emaildomain'); $this->config['limit'] = 50; break; default: case self::TALBE_ENV_NEWSLETTERISSUE: $this->config['table'] = WBDatasource_Newsletter::TABLE_NEWSLETTERISSUE; $this->config['searchfields'] = array('title', 'brief'); break; } // tweak permissions if (!$this->isUserInGroup($this->config['requiredgroup'])) { $this->config['action'] = 'edit'; } // call parent's init parent::init(); $this->addFiddler4Mandator(); $this->sub = WBClass::create('WBDatasource_Newsletter_Subscriber'); $this->config['languages'] = $this->sub->getAvailableLanguages(); } /** * Run Component * * Display "normal" WBContent_TableEditor, call sub methods * * @return array */ public function run() { $man = $this->mandator->get(); if (1 > $man['newsletter']) { return $this->runDisabled(); } switch ($this->config['tableenv']) { case self::TALBE_ENV_SUBSCRIBER: return $this->runSubscriber(); break; case self::TALBE_ENV_NEWSLETTERISSUE: return $this->runNewsletterIssue(); break; } return parent::run(); } /** * Run Component: Run Disabled * * @return array */ private function runDisabled() { if ('enablenewsletter' == $this->config['action']) { $save = array('newsletter' => 1); $this->table->save(WBDatasource::TABLE_MANDATOR, $this->mandator->getId(), $save); $this->loadTemplates('enabled'); return $this->config; } $this->loadTemplates('disabled'); return $this->config; } /** * Run Component: NewsletterIssue * * @return array */ private function runNewsletterIssue() { switch ($this->config['action']) { case 'display': case 'preview': case 'sendtest': break; default: return parent::run(); break; } $this->setupEnv(); $this->addConfigAsGlobalVars(); $this->initFilter(); $this->issue = WBClass::create('WBDatasource_Newsletter_Issue'); $this->issue->load($this->config['id']); $this->tmpl->addGlobalVars($this->issue->get()); WBParam::set('wb/cache/use', 0); if ('display' == $this->config['action']) { return $this->runNewsletterIssueDisplay(); } $this->nlMailer = WBClass::create('WBMail_Newsletter'); $this->nlMailer->setIssue($this->issue); $sub = $this->getDummySubscriberData($this->req->get('profile', 'use_email')); $sub['email'] = $this->req->get('email', $sub['email']); $this->nlMailer->setSubscriberData($sub); $this->nlMailer->prepare(); if ('sendtest' == $this->config['action']) { $this->runNewsletterIssueSendtest($sub); } return $this->runNewsletterIssuePreview(); } /** * Run Component: NewsletterIssue Send Test E-Mail * * @param array subscriber data with e-mail-address * @return array */ private function runNewsletterIssueSendtest($sub) { /** @var WBMailer */ $mail = WBClass::create('WBMailer'); $mail->setMimeMail($this->nlMailer->getMimeMail()); $mail->addRcpt($sub['email']); $mail->send(); $this->addAlert(WBContent::ALERT_TYPE_SUCCESS, 'Test-E-Mail', 'Die E-Mail wurde an "{EMAIL}" gesendet', $sub); } /** * Run Component: NewsletterIssue Display * * @return array */ private function runNewsletterIssuePreview() { $this->loadTemplates('preview'); $this->tmpl->addGlobalVar('email_subject', $this->nlMailer->getSubject()); $this->tmpl->addGlobalVar('email_html', $this->nlMailer->getHtmlBody(true)); $this->tmpl->addGlobalVar('email_plain', $this->nlMailer->getPlainBody()); return $this->config; } /** * Get Subscriber Data By Profile * * Load dummy subscriber according to named profile * * @param string profile name * @return array */ private function getDummySubscriberData($profile) { if ('self' == $profile) { $this->sub->loadByUser($this->user->getId()); if ($this->sub->isOK()) { return $this->sub->get(true); } } if ('use_email' == $profile) { $uData = $this->user->getData(); $this->sub->loadByEmail($this->req->get('email', $uData['email'])); if ($this->sub->isOK()) { return $this->sub->get(true); } } else if (0 == strncmp('sub:', $profile, 4)) { $id = substr($profile, 4); $this->sub->loadById($id); if ($this->sub->isOK()) { return $this->sub->get(true); } } $sub = array( 'id' => '00000', 'obscure' => 'ENXY', 'emaillocal' => 'tommyatkins', 'emaildomain' => 'example.com', 'gender' => 'male', 'title' => '', 'forename' => 'Tommy', 'surname' => 'Atkins', 'lang' => 'en_GB', 'country' => 'en' ); $sub[$this->sub->getIdentifier()] = $sub['id']; switch ($profile) { case 'en_male': break; case 'en_female': $sub['obscure'] = 'ENXX'; $sub['gender'] = 'female'; $sub['emaillocal'] = 'aliceatkins'; $sub['forename'] = 'Alice'; $sub['surname'] = 'Violet'; break; case 'de_male': $sub['obscure'] = 'DEXY'; $sub['lang'] = 'de_DE'; $sub['country'] = 'de'; $sub['gender'] = 'male'; $sub['emaillocal'] = 'maxmustermann'; $sub['forename'] = 'Max'; $sub['surname'] = 'Mustermann'; break; case 'de_female': $sub['obscure'] = 'DEXX'; $sub['lang'] = 'de_DE'; $sub['country'] = 'de'; $sub['gender'] = 'female'; $sub['emaillocal'] = 'erikamusterfrau'; $sub['forename'] = 'Erika'; $sub['surname'] = 'Musterfrau'; break; default: break; } $sub['email'] = $sub['emaillocal'] . '@' . $sub['emaildomain']; $this->sub->loadByRaw($sub); return $this->sub->get(true); } /** * Run Component: NewsletterIssue Display * * @return array */ private function runNewsletterIssueDisplay() { $this->loadTemplates('display'); // tracking pixel $tpp = sprintf($this->config['trackingpixelpath'], $this->config['id']); $this->tmpl->addGlobalVar('trackingpixel_path', $tpp); $this->tmpl->addGlobalVar('trackingpixel_count', $this->issue->getTrackingPixelCount($tpp)); // URL count if ($this->tmpl->exists('page_list_entry')) { $list = array(); $pURL = $this->table->getIdentifier('url'); $tmp = $this->issue->get(); $date = $tmp['created']; if (!empty($tmp[$pURL])) { $list[] = array( $pURL => $tmp[$pURL] ); } $tmp = $this->issue->getArticle(); foreach ($tmp as $t) { if (empty($t[$pURL])) { continue; } $list[] = array( $pURL => $t[$pURL] ); } /** @var WBDictionary_URL */ $dict = WBClass::create('WBDictionary_URL'); foreach ($list as &$l) { $dict->load($l[$pURL]); $url = $dict->get(); $path = urldecode(substr($url['path'], 16)); $path = explode(' ', $path); $clause = array(); $clause[] = array( 'field' => 'created', 'relation' => 'ge', 'value' => $date ); $clause[] = array( 'field' => 'created', 'relation' => 'lt', 'value' => date('Y-m-d', strtotime($date) + 7 * 24 * 3600) ); $clause[] = array( 'field' => 'path', 'relation' => 'begins', 'value' => $path[0] ); $count = $this->table->count('pageview', null, null, $clause); $l['cnt'] = $count; $l['path'] = implode(' ', $path); } $this->tmpl->addGlobalVar('page_count', count($list)); $this->tmpl->addRows('page_list_entry', $list); } // link tracking if ($this->tmpl->exists('linktracking_list_entry')) { $list = $this->issue->getTracklink(); $this->tmpl->addGlobalVar('linktracking_count', count($list)); $this->tmpl->addRows('linktracking_list_entry', $list); } // most clicked pages if ($this->tmpl->exists('mostclicked_list_entry')) { $list = $this->issue->getMostClickedPages(); $this->tmpl->addGlobalVar('mostclicked_count', count($list)); $this->tmpl->addRows('mostclicked_list_entry', $list); } return $this->config; } /** * Get List of Variables 4 Filter * * @return */ protected function getFilterVars() { switch ($this->config['tableenv']) { case self::TALBE_ENV_SUBSCRIBER: break; default: return parent::getFilterVars(); break; } $vars = array( 'filter_' . $this->mandator->getIdentifier() => $this->mandator->getId() ); return $vars; } /** * Run Component: Subscriber * * @return array */ private function runSubscriber() { $this->setupEnv(); $this->addConfigAsGlobalVars(); $this->initFilter(); switch ($this->config['action']) { case 'setstatus': $this->updateStatus(); $this->config['action'] = 'list'; break; case 'export': $this->export(); $this->config['action'] = 'list'; break; case 'exportfiltered': $this->exportFiltered(); $this->config['action'] = 'list'; break; case 'importtable': $this->processForm('importtable'); return $this->config; break; case 'importlist': $this->processForm('importlist'); return $this->config; break; } $this->initRelationTable(); $rel = $this->relTable->getConfig4Key('newslettertopiclist'); $rel['fixed'] = array(); $this->mandator->inject2Data($rel['fixed']); $this->relTable->setConfig4Key('newslettertopiclist', $rel); return parent::run(); } /** * Tweak Table Filter On Demand * * Implement this method to mange with filter clause and options * * @param array * @param option */ protected function tweakFilter(&$clause, &$option) { switch ($this->config['tableenv']) { case self::TALBE_ENV_SUBSCRIBER: break; default: return parent::tweakFilter($clause, $option); break; } if (!is_array($clause)) { $clause = array(); } if (!is_array($option)) { $option = array(); } if (!isset($option['join']) || !is_array($option['join'])) { $option['join'] = array(); } $this->mandator->inject2Clause($clause, WBDatasource_Newsletter::TABLE_SUBSCRIBERMANDATOR); $option['join'][] = array( 'table' => WBDatasource_Newsletter::TABLE_SUBSCRIBERMANDATOR, 'using' => $this->table->getIdentifier(WBDatasource_Newsletter::TABLE_SUBSCRIBER, true) ); return parent::tweakFilter($clause, $option); } /** * Save Record * * Use parent's method to store record, but add optional source to subscriber * * @param string new * @param array $values * @return string $id */ protected function saveRecord($new, $values) { switch ($this->config['tableenv']) { case self::TALBE_ENV_SUBSCRIBER: if (2 > count($this->config['languages'])) { $values['lang'] = substr($this->config['languages'][0], 0, 2); } break; case self::TALBE_ENV_NEWSLETTERISSUE: if (2 > count($this->config['languages'])) { $values['lang'] = substr($this->config['languages'][0], 0, 2); } $this->injectTrackId4Issue($new, $values); return parent::saveRecord($new, $values); break; default: return parent::saveRecord($new, $values); break; } if ($new) { $id = $this->sub->loadByEmail($values['email']); if (empty($id)) { $values[WBDatasource_Newsletter_Subscriber::OBSCURE_FIELD_NAME] = $this->sub->mkRandomCode(); } else { $org = $this->sub->get(); foreach ($values as $k => &$v) { $v = $org[$k]; } $this->id = $id; $new = false; } } $e = $this->injectStatusTimestamp($values); $values['new'] = 0; $id = parent::saveRecord($new, $values); $this->sub->loadById($id); $this->sub->add4Mandator($this->mandator->getId()); $xid = $this->req->get('genericsource-add', ''); if (empty($xid)) { return $id; } $this->sub->addSource('genericsource', $xid); return $id; } /** * Create TrackLink Id * * @param bool new * @param array newsletter issue values */ private function injectTrackId4Issue($new, &$values) { if (1 > $values['enabled']) { return; } $pTL = $this->table->getIdentifier(WBDatasource_Newsletter::TABLE_TRACKLINK); if (!empty($values[$pTL])) { return; } $save = array( 'title' => 'Newsletter ' . $values['title'], 'blurb' => $values['title'] ); $id = $this->table->save(WBDatasource_Newsletter::TABLE_TRACKLINK, '__new', $save); $values[$pTL] = $id; } /** * Set Status of Subscriber */ private function updateStatus() { if (self::TALBE_ENV_SUBSCRIBER != $this->config['tableenv']) { return; } $id = $this->req->get('id', ''); if (empty($id)) { return; } $status = $this->req->get('status', ''); if (empty($status)) { return; } $value = intval($this->req->get('value', -1)); if (0 > $value) { return; } if (0 < $value) { $value = 1; } $save = array( $status => $value ); $e = $this->injectStatusTimestamp($save); $this->table->save($this->config['table'], $id, $save); $eData = array( 'table' => $this->config['table'], 'id' => $id, 'new' => 0, 'save' => $save ); WBEvent::trigger('newsletter:manager:subscriber:status:' . $this->config['table'], 'Saved record in table', $eData); } /** * Add Status Changed Timestamps * * For a number of flags, add timestamp reccords * * @param array * @return string */ private function injectStatusTimestamp(&$save) { // get old record to see if something has changed $old = array(); if ('__new' != $this->id) { $old = $this->table->get($this->config['table'], $this->id); if (!empty($old)) { $old = $old[0]; } } // check for changes $event = ''; foreach ($save as $k => $v) { // check for changes if (isset($old[$k]) && $v == $old[$k]) { continue; } // add timestamp switch ($k) { case 'enabled': case 'checked': case 'approved': case 'blacklisted': if (0 < $v) { $save[$k . 'timestamp'] = gmdate('Y-m-d H:i:s'); $event = $k; } break; case 'subscribed': if (0 < $v) { $save['subscribedtimestamp'] = gmdate('Y-m-d H:i:s'); $event = 'subscribed'; } else { $save['unsubscribedtimestamp'] = gmdate('Y-m-d H:i:s'); $event = 'unsubscribed'; } default: break; } } return $event; } /** * Export Subscriber List According Filter * * Apply filter and export list * * */ private function exportFiltered() { if (self::TALBE_ENV_SUBSCRIBER != $this->config['tableenv']) { return; } $this->initFilter(); $prop = $this->exportPrepare(true); $prop->setSubject('Filtered newsletter subscriber'); $prop->setDescription('List of newsletter subscribers using filter'); $clause = $this->filter->getClause(); $options = $this->filter->getOptions(); $pager = $this->table->getPager('exportFiltered', $this->config['table'], null, $clause, $options); $this->exportPager($pager); } /** * Export Active Subcribers * * List only subscribers that are: * - checked * - approved * - enabled * - subscribed * - NOT blacklisted */ private function export() { if (self::TALBE_ENV_SUBSCRIBER != $this->config['tableenv']) { return; } $this->initFilter(); $prop = $this->exportPrepare(); $prop->setSubject('Current newsletter subscriber'); $prop->setDescription('List of active newsletter subscribers'); $clause = array(); $clause[] = array( 'field' => 'checked', 'value' => 1 ); $clause[] = array( 'field' => 'approved', 'value' => 1 ); $clause[] = array( 'field' => 'enabled', 'value' => 1 ); $clause[] = array( 'field' => 'subscribed', 'value' => 1 ); $clause[] = array( 'field' => 'blacklisted', 'value' => 0 ); $pager = $this->table->getPager('export', $this->config['table'], null, $clause); $this->exportPager($pager); } /** * Export All Items From Pager * * * @param WBDatasource_Pager */ private function exportPager($pager) { $info = $pager->browse(); for ($i = 0; $i < $info['pages']; ++$i) { $list = $pager->get(); $this->exportList($list); $pager->browse('__next'); } $this->fh->close(); $this->downloadFile($this->fh->realpath(), 'application/vnd.ms-excel', 'newsletterSubriber-' . gmdate('Ymd-His') . '.xls'); } /** * Prepare Spreadsheet * * Create file and mapping * * @return PhpOffice\PhpSpreadsheet\Document\Properties */ private function exportPrepare($withFlags = false) { $this->fh = WBClass::create('WBFile_Xls'); $this->fh->tempnam('subex'); $this->fh->open('w'); $prop = $this->fh->getProperties() ->setCreator('Wombat') ->setTitle('Newsletter Subscriber'); $this->fh->setActiveSheetIndex(0) ->setTitle('Abonnenten'); $map = array( //'company', //'__empty0', //'__empty1', //'country', 'lang', 'gender', 'salutation', 'title', 'forename', 'surname', 'email', 'obscureid' ); $clause = array(); $this->mandator->inject2Clause($clause); $pTop = $this->table->getIdentifier(WBDatasource_Newsletter::TABLE_NEWSLETTERTOPIC, true); $this->topicList = $this->table->get(WBDatasource_Newsletter::TABLE_NEWSLETTERTOPIC, null, null, $clause, array('limit' => 1000)); foreach ($this->topicList as $t) { $map[] = 'topic-' . $t[$pTop]; } if ($withFlags) { $map[] = 'approved'; $map[] = 'checked'; $map[] = 'enabled'; $map[] = 'subscribed'; $map[] = 'blacklisted'; } $map = array_flip($map); $this->fh->setMap($map); $data = array( //'company' => 'Name', //'__empty0' => 'Name-1', //'__empty1' => 'Name-2', //'country' => 'LKZ', 'lang' => 'Sprache', 'gender' => 'Geschlecht', 'salutation'=> 'Ansprache', 'title' => 'Titel', 'forename' => 'Vorname', 'surname' => 'Nachname', 'email' => 'E-Mail', 'obscureid' => 'ObscureId' ); foreach ($this->topicList as $t) { $data['topic-' . $t[$pTop]] = $t['title']; } if ($withFlags) { $data['approved'] = 'Double-Opt-In'; $data['checked'] = 'Geprüft'; $data['enabled'] = 'Freigegeben'; $data['subscribed'] = 'Abo'; $data['blacklisted'] = 'Blacklist'; } $this->fh->write($data); return $prop; } /** * Export List of Subscribers * * @param array $list */ private function exportList($list) { $pTop = $this->table->getIdentifier(WBDatasource_Newsletter::TABLE_NEWSLETTERTOPIC, true); foreach ($list as $l) { $this->sub->loadByRaw($l); $sub = $this->sub->get(true); foreach ($this->topicList as $t) { $topic = $this->sub->getTopic($this->mandator->getId()); $sub['topic-' . $t[$pTop]] = 0; if (in_array($t[$pTop], $topic)) { $sub['topic-' . $t[$pTop]] = 1; } } $this->fh->write($sub); } } /** * Import Data From List * * Extract list from text field * * @param patForms * @param array * @return bool */ public function onImportlistValid($form, $values) { $list = trim($values['list']); if (empty($list)) { $this->addAlert(WBContent::ALERT_TYPE_DANGER, 'Leere Liste', 'In der Liste wurden keinen E-Mail-Adressen gefunden'); return true; } $topic = array(); if (!empty($values['newslettertopiclist'])) { $topic = $values['newslettertopiclist']; } // normalize line breaks $list = str_replace("\r\n", "\n", $list); // comma separated list, make it one line if (strstr($list, ',')) { $list = str_replace("\n", ' ', $list); } $list = str_replace(",", "\n", $list); $list = explode("\n", $list); // don't mark records as new $values['new'] = 0; $count = 0; foreach ($list as $l) { $l = trim($l); if (empty($l)) { continue; } $data = array(); // find e-mail address in angle brackets if (preg_match('/(.*)<(.+)@(.*)>(.*)/', $l, $match)) { $match[1] = trim($match[1]); $match[3] = trim($match[3]); $data['email'] = $match[2] . '@' . $match[3]; if (!empty($match[1]) && !strstr($match[1], '@')) { $name = explode(' ', $match[1]); $data['surname'] = array_pop($name); if (!empty($name)) { $data['forename'] = implode(' ', $name); } } } // try to process non-angle-btacket e-mails else { $match = explode(' ', $l); $name = array(); foreach ($match as $m) { if (strstr($m, '@')) { $data['email'] = $m; continue; } $name[] = $m; } if (1 < count($name)) { $data['surname'] = array_pop($name); } if (!empty($name)) { $data['forename'] = implode(' ', $name); } } if (empty($data['email'])) { continue; } if (empty($data['forename']) && empty($data['surname'])) { // guess name from e-mail address $email = explode('@', $data['email']); if (strstr($email[0], '.')) { $name = explode('.', $email[0]); } if (1 < count($name)) { $data['surname'] = array_pop($name); } if (!empty($name)) { $data['forename'] = implode(' ', $name); } } $this->sub->addOnly($data, $values); $this->sub->add4Mandator($this->mandator->getId()); $this->sub->setTopic($this->mandator->getId(), $topic); ++$count; } if (1 > $count) { $this->addAlert(WBContent::ALERT_TYPE_DANGER, 'Keine E-Mail-Adressen', 'In der Liste wurden keinen E-Mail-Adressen gefunden'); return true; } $this->addAlert(WBContent::ALERT_TYPE_SUCCESS, 'Import abgeschlossen', 'Es wurden {COUNT} Abonnenten importiert.', array('count' => $count)); return true; } /** * Import Data From CSV File * * @param patForms * @param array * @return bool */ public function onImporttableValid($form, $values) { /** @var WBFile_CSV */ $file = WBClass::create('WBFile_CSV'); if (!$file->exists('var/tmp/' . $values['file'])) { $this->addAlert(WBContent::ALERT_TYPE_DANGER, 'Upload fehlgeschlagen', 'Die Datei wurde nicht richtig hochgeladen'); return true; } $file->open(); $file->guessFormat(); $file->guessEncoding(); $line = $file->read(); $file->setMap(array_flip($line)); // don't mark records as new $values['new'] = 0; $topic = array(); if (!empty($values['newslettertopiclist'])) { $topic = $values['newslettertopiclist']; } $count = 0; while (!$file->eof()) { $line = $file->read(); $this->sub->addOnly($line, $values); $this->sub->add4Mandator($this->mandator->getId()); $this->sub->setTopic($this->mandator->getId(), $topic); ++$count; } $file->close(); $this->addAlert(WBContent::ALERT_TYPE_SUCCESS, 'Import abgeschlossen', 'Es wurden {COUNT} Abonnenten importiert.', array('count' => $count)); return true; } /** * Fetch List of Form Elements * * @param string name of the xml- and template-filename * @return array $elements */ protected function getFormElementList($name) { $list = parent::getFormElementList($name); // upload folder: set relative path do basedir foreach ($list as &$e) { if ('File' != $e['type']) { continue; } // absolute path if ('/' == $e['attributes']['uploaddir'][0]) { continue; } $e['attributes']['uploaddir'] = WBParam::get('wb/dir/base') . '/' . $e['attributes']['uploaddir']; } // avoid duplicates if (isset($list['email'])) { $rule = array( 'name' => 'EmailDuplicate', 'params' => array( 'id' => '{NLSID}', 'table' => WBDatasource_Newsletter::TABLE_SUBSCRIBER, 'clause' => array(), 'option' => array() ) ); if (!isset($list['email']['rule']) || !is_array($list['email']['rule'])) { $list['email']['rule'] = array(); } $this->mandator->inject2Clause($rule['params']['clause'], WBDatasource_Newsletter::TABLE_SUBSCRIBERMANDATOR); $rule['params']['option'] = array( 'join' => array( array( 'table' => WBDatasource_Newsletter::TABLE_SUBSCRIBERMANDATOR, 'using' => $this->table->getIdentifier(WBDatasource_Newsletter::TABLE_SUBSCRIBER) ) ) ); $list['email']['rule'][] = $rule; } // language settings if (isset($list['lang'])) { if (2 > count($this->config['languages'])) { unset($list['lang']); } } return $list; } /** * location of form config * * Return sub directory where form element definitions are located * * @return string folder */ protected function getFormConfigDir() { return 'newsletter/manager/' . $this->config['tableenv']; } }