DEPRECATION WARNING

This documentation is not using the current rendering mechanism and is probably outdated. The extension maintainer should switch to the new system. Details on how to use the rendering mechanism can be found here.

EXT: Mailing List Archive

Author:Kasper Skårhøj
Created:2002-11-01T00:32:00
Changed by:Donald Duck
Changed:2004-01-29T16:43:43
Author:Kasper Skårhøj
Email:kasper@typo3.com
Info 3:
Info 4:

EXT: Mailing List Archive

Extension Key: maillisttofaq

Copyright 2003, Kasper Skårhøj, <kasper@typo3.com>

This document is published under the Open Content License

available from http://www.opencontent.org/opl.shtml

The content of this document is related to TYPO3

- a GNU/GPL CMS/Framework available from www.typo3.com

Table of Contents

EXT: Mailing List Archive 1

Introduction 1

What does it do? 1

Screenshots 1

Features 3

Technical requirements 4

Donations? 4

List management manual 5

Administration 5

Setting up the plugin 5

Where did all the items go? 7

Inserting threads other places in the website 7

Configuration 8

General construction 8

CSS selectors 9

Reference 10

Extending the mailing list archive class 12

Scenario 12

The extension class 12

A perfect framework for site specific extensions 13

Technical information 15

Transfer from the mail server 16

Separate mail- and webserver? 18

Known problems 19

To-Do list 19

Introduction

What does it do?

Its a plugin that creates a continual webbased archive of mailing list mails.

One example is the archive found at http://typo3.org/1422.0.html

The archive includes management features which enables frontend users to clean up the threads, hide messages, categorize threads etc.

The archive is also prepared to be a knowledge base where threads can easily be converted into FAQ or HOWTO items which is basically the knowledge from a thread compiled into a concentrated form.

Screenshots

This screenshot shows the archive on typo3.org. You can see how threads are shown in the overview:

img-1

When you click a thread you will see it listed like this (cropped in the bottom):

img-2

Features

The archive contains the following features

  • Matches fronend user emails with sender emails thus creating references to users on the website.
  • Listing display of threads supports display of mail date, subject, sender name, number of replies, most recent reply, category, states display (answered, unanswered, has FAQ item related, has been rated) and view count
  • Supports searching in threads, possibly limited to category and answered or rated threads
  • Supports display of mails from single or multiple categories
  • Expanded thread mode: lists each thread as headline with all replies indented below
  • Mode for display of of unanswered mails: helps keeping track of which support requests has not yet been taken care of
  • Displays users online
  • Supports frontend users enabled as managers and supervisors: These can clean up the threads, remove unnecessary reply content and Off-Topic (OT) threads to make the archive a tidy information source.
  • Supports rating of messages.
  • Supports online posting and reply (will work only with frontend users and if such users have subscribed to the mailing list with their email)
  • Sticky threads; will stay on top of the list (set by supervisors)
  • Categorization of threads, FAQs and HOWTOs
  • Reminder mails for thread starters to go and create FAQs/HOWTOs
  • Support states for threads: Answered, Unanswered, Don't Care
  • Breaking threads, moving messages to other threads, hiding messages and threads (OT/hide)
  • For frontend users; Listing of "My threads" (Threads you started), "My replies" (Threads where you have replied in), "My selected" (Threads you have selected to be of interest)
  • For managers/supervisors; Listing of "Threads managed by me" and statistics over manager activity, sent requests for FAQ items and search word usage.
  • Full support for localization of labels

Technical requirements

The extension needs some external process which receives mails from the mailing list and inserts them into a MySQL table. The extension includes a script which can do this as long as a mail server which receives list mails to an email address is able to pipe the content into a standalone-PHP script (strictly no webserver is needed) which can insert the content into a MySQL server (on the same or remote machine).

So basically you need a mail server which has at least a PHP binary to pipe mails into and at best a full webserver with MySQL and a TYPO3 installation. The most "simple" solution is to have a mail server running directly on the webserver where the site runs. But there are alternatives as you can see.

Donations?

Since it takes time to develop applications like this one donations are very welcome. You are not required to pay for this extension but we kindly ask you to consider doing it anyways. Such a donation doesn't change whether this extension is available to you or not - it will always be free. But your donation may be the reason why new, powerful extensions can be made for free.

This extension - the mailing list archive - has taken roughly 3 weeks (or 100+ man hours) to develop if you accumulate the time involved over the development and testing period.

If you want to donate, contact Kasper Skårhøj (developer of this extension) . You can get an invoice for consultancy in return.

List management manual

This section of the documentation will describe the features of the archive plugin from the point of view of a list manager.

A list manager is a frontend user who has been assigned the permission to edit threads in the frontend so that the content is cleaned up, OT threads are removed etc.

If anyone thinks that there should be a manual for regular users of the archive then it could be written based on the information found for the managers here - but with all the manager specific information filtered out.

...

[Needs work, ...]

Administration

This section is about how to set up supervisors and managers for the lists - and generally operating the archive from the backend.

There is not much to do for the archive from the backend. Most of the administration is done by managers and supervisors from the fronend.

Setting up the plugin

Categories

Before anything else you might like to create a few categories you can assign to mails and FAQ/HOWTO items. If so, simply create such records in the Storage folder configured for the branch of the website.

img-3

These categories can be assigned to mails and FAQ items during management. It will look like this in the archive, being used in both the category selector box and in the listing:

img-4

Add content element type "Insert plugin"

Simply insert a content element on the page where the archive should be:

img-5

Set the type to this:

img-6

Configure the plugin

Then comes a very important part - configuring the plugin to the mailing list:

img-7

Select which email-header field in the mails from the mailing list server to select on. Here the sender mail is used which is constantly set to "typo3-english-admin@lists.netfielders.de" (#2) when the mails are for the "typo3-english" mailing list.

The value to match the "Select field" (#1) against.It works like this: When mails are moved from the "_inmail" table to the "_ml" table (from the "inbox" to the archive) only mails where the "Select field" value matches the "Select value" are selected - thus its a filter retrieving mails only for the list you want (assuming that the "_inmail" table may receive mails from lots of various mailing lists. On typo3.org that means all the other lists as well.)

The email address to which new messages or replys are sent. Used for the post/reply forms in the archive.

The prefix string used in the subject line. This is used to clean up the subject line. Make sure its correct case and all.

The list "supervisors". These frontend users are super-managers. Basically that means they are allowed to capture/manage threads that other managers actually "own". Supervisors can also make threads "sticky" - managers cannot.

The list of "managers" are frontend users which can manage the threads, thus cleaning up the content, removing reply text, hiding posts, moving posts, categorizing threads and FAQ items.

The starting point of the mailing list archive. Probably you want the FAQ and Mail items to be on the same page as the plugin - so just leave this option. Otherwise you can point out another page where those are stored. But only one page - not many, not recursively.

Where did all the items go?

When messages are moved from the "_inmail" table to the "_ml" table they are available in the backend as records you can edit to some degree. Likewise with FAQ/HOWTO elements. They are stored on the same page as the plugin normally (depending on the "Startingpoint" field, see former page, #7).

img-8

Inserting threads other places in the website

You can actually Insert a thread from the archive on other pages in the system. Example:

img-9

This is a content element of the type "Insert Records". Inside it looks like this:

img-10

As you can see it contains a reference to a message from the archive. No matter if this message is the root message or a reply, the whole thread will be shown:

img-11

Thus, if you have some threads with valuable information you can insert them as regular content on other pages. In the bottom of the page you will find a link pointing to the page where the archive (presumably) is located:

img-12

Configuration

General construction

About how mails ends up in the database from the mail server, please read the chapter "Technical information".

Apart from the issues in that chapter the extension is mainly working like any other plugin in TYPO3 - just add the static template "Mailing List Archive" to the template record:

img-13

... and insert a content element of the type "Insert Plugin" (for configuration of this, see admin part) and thats all.

For the developer you must configure a few TypoScript things for the extension. On typo3.org we are using a TypoScript configuration in the Setup field looking like this:

### COnfiguration of the Mail-To-FAQ plugin
plugin.tx_maillisttofaq_pi1.listView.catSelSize = 1
plugin.tx_maillisttofaq_pi1.listView.catSelNoHeader = 1
plugin.tx_maillisttofaq_pi1.faq_email.admin_name = TYPO3.org List Admin
plugin.tx_maillisttofaq_pi1.faq_email.cc_email = kasper@typo3.com
plugin.tx_maillisttofaq_pi1.faq_email.regards = Regards, The TYPO3.org List Manager Team
plugin.tx_maillisttofaq_pi1.readmail = 1
plugin.tx_maillisttofaq_pi1.readmail.url = http://[...]/1422.0.html?
plugin.tx_maillisttofaq_pi1.listView.results_at_a_time = 35
plugin.tx_maillisttofaq_pi1.messageDividers.123 (
_______________________________________________
Typo3-english mailing list
)
plugin.tx_maillisttofaq_pi1.listView.daysBeforeDirectNotificationsWhenReply = 2
plugin.tx_maillisttofaq_pi1.listView.lgdIndicator.limit_3 = 5000

The plugin is a TypoScript USER_INT content object. This means that the content from the plugin is not cached can cannot be indexed by the Indexed Search extension either. Searching is done by the plugins own search feature.

Also the plugin can select mails/faq items from only one page in the "pidList" - that will be the first integer value that is used as PID.

CSS selectors

The plugin is fully marked up in CSS friendly HTML tags. Almost every element with significance has a class attribute or is contained in a DIV section with one. Thus you can use CSS stylesheets to design the looks of the plugin. This is the same way as most other modern TYPO3 plugins work.

Basically you will have to dig the source code yourself to find which selectors can be used. Notice that the extension "CSS Usage Analyzer" may come in handy to do this analyzing.

On typo3.org you can see in the stylesheet what we did. Looks like this:

/* Styles for the maillist to faq module */
  .tx-maillisttofaq-pi1-listrow TD { background-color:#FAF4F1; padding-left: 3px; padding-right: 3px; }
  .tx-maillisttofaq-pi1-listrow TR.tx-maillisttofaq-pi1-listrow-header TD { background-color:#E4D3CC; white-space: nowrap;}
  .tx-maillisttofaq-pi1-listrow TR.tx-maillisttofaq-pi1-listrow-header TD P { font-weight:bold; font-size: 12px;}
  .tx-maillisttofaq-pi1-listrow TR.tx-maillisttofaq-pi1-listrow-odd TD { background-color:#EEE9E6; }
  .tx-maillisttofaq-pi1-listrow TR.tx-maillisttofaq-pi1-listrow-sticky TD { background-color:#DBE3EB; font-weight: bold; }
  DIV.tx-maillisttofaq-pi1-listrow {width:700px;}

  .tx-maillisttofaq-pi1 .tx-maillisttofaq-pi1-modeSelector TD {padding: 0px 7px 0px 7px; background-color: #E3F3C0; white-space: nowrap;}
  .tx-maillisttofaq-pi1 .tx-maillisttofaq-pi1-modeSelector TD.tx-maillisttofaq-pi1-modeSelector-SCell {font-weight: bold;  background-color: #A1D82F;}
  .tx-maillisttofaq-pi1 DIV.tx-maillisttofaq-pi1-modeSelector {position:relative; left:-2px;}

  .tx-maillisttofaq-pi1 .tx-maillisttofaq-pi1-browsebox TD { background-color:#E3F3C0; padding: 0px 7px 0px 7px;}
  .tx-maillisttofaq-pi1 .tx-maillisttofaq-pi1-browsebox TD.tx-maillisttofaq-pi1-browsebox-SCell { background-color:#A1D82F; }
  .tx-maillisttofaq-pi1 .tx-maillisttofaq-pi1-browsebox TD.tx-maillisttofaq-pi1-browsebox-SCell P { font-weight: bold; }

  .tx-maillisttofaq-pi1 .tx-maillisttofaq-pi1-threads P.tx-maillisttofaq-pi1-root { font-weight: bold; margin-top:10px;}
  .tx-maillisttofaq-pi1 .tx-maillisttofaq-pi1-threads P.tx-maillisttofaq-pi1-lost { color:#666666; font-weight: bold;  margin-top:10px;}
  .tx-maillisttofaq-pi1 .tx-maillisttofaq-pi1-threads P.tx-maillisttofaq-pi1-child { font-size: 11px;}

  .tx-maillisttofaq-pi1 .tx-maillisttofaq-pi1-singleView H3.tx-maillisttofaq-pi1-subject {margin-top: 5px; margin-bottom: 10px;}
  .tx-maillisttofaq-pi1 .tx-maillisttofaq-pi1-singleView P.tx-maillisttofaq-pi1-author { background-color:#E4D3CC;}
  .tx-maillisttofaq-pi1 .tx-maillisttofaq-pi1-singleView P.tx-maillisttofaq-pi1-replyMsg A {padding: 1px 5px 1px 5px;}
  .tx-maillisttofaq-pi1 .tx-maillisttofaq-pi1-singleView P.tx-maillisttofaq-pi1-replyMsg { margin-top:5px; }


  .tx-maillisttofaq-pi1 .tx-maillisttofaq-pi1-singleView P.tx-maillisttofaq-pi1-replyauthor { background-color:#E4D3CC;}
  .tx-maillisttofaq-pi1 .tx-maillisttofaq-pi1-singleView DIV.tx-maillisttofaq-pi1-reply {border: dotted 1px #666666; margin-top: 10px; padding: 5px 5px 5px 5px; background-color:#FAF4F1;}
  .tx-maillisttofaq-pi1 .tx-maillisttofaq-pi1-singleView P.tx-maillisttofaq-pi1-replyauthor-nonMod { background-color:#cccccc;}
  .tx-maillisttofaq-pi1 .tx-maillisttofaq-pi1-singleView DIV.tx-maillisttofaq-pi1-reply-nonMod {border: dotted 1px #666666; margin-top: 10px; padding: 5px 5px 5px 5px; background-color:#eeeeee;}

  .tx-maillisttofaq-pi1 .tx-maillisttofaq-pi1-singleView P A {text-decoration: underline;}
  .tx-maillisttofaq-pi1 .tx-maillisttofaq-pi1-singleView DIV.tx-maillisttofaq-pi1-thrMenu {margin-top: 5px; margin-bottom: 10px;}
  .tx-maillisttofaq-pi1 .tx-maillisttofaq-pi1-singleView P.tx-maillisttofaq-pi1-threadLinks { background-color: #DBE3EB; font-size:13px; font-weight: bold;}
  .tx-maillisttofaq-pi1 .tx-maillisttofaq-pi1-singleView P.tx-maillisttofaq-pi1-child { background-color: #EEF6FF; font-size:11px;}

  .tx-maillisttofaq-pi1 .tx-maillisttofaq-pi1-singleView P.tx-maillisttofaq-pi1-back A {background-color: #F5733A; color: white; font-weight: bold; padding-left: 5px; padding-right: 5px;}
  .tx-maillisttofaq-pi1 .tx-maillisttofaq-pi1-singleView P.tx-maillisttofaq-pi1-back {margin-bottom: 10px;margin-top: 10px;}

  .tx-maillisttofaq-pi1 .tx-maillisttofaq-pi1-singleView P.tx-maillisttofaq-pi1-saveday { text-align: center; font-size:11px; margin-top: 10px; }
  .tx-maillisttofaq-pi1 .tx-maillisttofaq-pi1-singleView P.tx-maillisttofaq-pi1-saveday A {background-color: #E4D3CC; padding: 0px 10px 0px 10px;border: #999999 dotted 1px;}

  .tx-maillisttofaq-pi1 .tx-maillisttofaq-pi1-singleView P.tx-maillisttofaq-pi1-cmd {margin-top: 5px;margin-bottom: 5px; }
  .tx-maillisttofaq-pi1 .tx-maillisttofaq-pi1-singleView P.tx-maillisttofaq-pi1-cmd A {padding: 0px 7px 0px 7px; background-color: #E3F3C0;}

  .tx-maillisttofaq-pi1 .tx-maillisttofaq-pi1-singleView P.tx-maillisttofaq-pi1-modnote {color: #666666; background-color: #DBE3EB;}

  .tx-maillisttofaq-pi1 .tx-maillisttofaq-pi1-singleView DIV.tx-maillisttofaq-pi1-faqitem  {border-bottom: solid 1px #666666; margin-bottom: 20px; padding: 5px 5px 5px 5px; background-color:#EEF6FF;}
  .tx-maillisttofaq-pi1 .tx-maillisttofaq-pi1-singleView P.tx-maillisttofaq-pi1-faqhead { background-color: #DBE3EB; font-size:13px; font-weight: bold;}

  .tx-maillisttofaq-pi1 .tx-maillisttofaq-pi1-singleView DIV.tx-maillisttofaq-pi1-faqitem PRE.tx-maillisttofaq-pi1-faqapre,
          .tx-maillisttofaq-pi1 .tx-maillisttofaq-pi1-singleView DIV.tx-maillisttofaq-pi1-faqitem PRE.tx-maillisttofaq-pi1-faqqpre {margin-top: 5px;margin-bottom: 5px;}

  .tx-maillisttofaq-pi1 .tx-maillisttofaq-pi1-singleView DIV.tx-maillisttofaq-pi1-faqitem P.tx-maillisttofaq-pi1-faqa,
          .tx-maillisttofaq-pi1 .tx-maillisttofaq-pi1-singleView DIV.tx-maillisttofaq-pi1-faqitem P.tx-maillisttofaq-pi1-faqq {margin-top: 15px;}
  .tx-maillisttofaq-pi1 .tx-maillisttofaq-pi1-singleView DIV.tx-maillisttofaq-pi1-faqitem P.tx-maillisttofaq-pi1-faqsbj  {font-size:13px; font-weight: bold;}

  .tx-maillisttofaq-pi1 .tx-maillisttofaq-pi1-singleView DIV.tx-maillisttofaq-pi1-faqitem P.tx-maillisttofaq-pi1-faqedit {background-color: #DBE3EB; padding: 2px 2px 2px 6px; font-size: 11px;}

  .tx-maillisttofaq-pi1 .tx-maillisttofaq-pi1-singleView DIV.tx-maillisttofaq-pi1-faqitem SPAN.tx-maillisttofaq-pi1-faqqhead,
          .tx-maillisttofaq-pi1 .tx-maillisttofaq-pi1-singleView DIV.tx-maillisttofaq-pi1-faqitem SPAN.tx-maillisttofaq-pi1-faqahead {background-color: #DBE3EB; padding: 0px 4px 0px 4px; font-weight: bold; font-size:16px;}

  .tx-maillisttofaq-pi1 .tx-maillisttofaq-pi1-singleView P.tx-maillisttofaq-pi1-managed { background-color: #E4D3CC;font-size:11px; text-align: center;margin-top: 10px;margin-bottom: 10px;}

  .tx-maillisttofaq-pi1 .tx-maillisttofaq-pi1-catbox H3 {margin-top: 10px; margin-bottom: 0px; font-size:13px;}



  .tx-maillisttofaq-pi1 .tx-maillisttofaq-pi1-singleView DIV.tx-maillisttofaq-pi1-replyForm  {border: solid 1px black; margin: 5px 0px 5px 0px; padding: 5px 5px 5px 5px; background-color:#EEF6FF;}
  .tx-maillisttofaq-pi1 .tx-maillisttofaq-pi1-singleView DIV.tx-maillisttofaq-pi1-replyForm P.tx-maillisttofaq-pi1-notice {font-size: 11px; background-color: #DBE3EB; padding: 5px 5px 5px 5px;}
  .tx-maillisttofaq-pi1 .tx-maillisttofaq-pi1-singleView DIV.tx-maillisttofaq-pi1-replyForm P.tx-maillisttofaq-pi1-rpHead {font-weight: bold; font-size: 13px;  background-color: #DBE3EB;}

  .tx-maillisttofaq-pi1 DIV.tx-maillisttofaq-pi1-postForm  {border: solid 1px black; margin: 5px 0px 5px 0px; padding: 5px 5px 5px 5px; background-color:#EEF6FF;}
  .tx-maillisttofaq-pi1 DIV.tx-maillisttofaq-pi1-postForm P.tx-maillisttofaq-pi1-notice {font-size: 11px; background-color: #DBE3EB; padding: 5px 5px 5px 5px;}
  .tx-maillisttofaq-pi1 DIV.tx-maillisttofaq-pi1-postForm P.tx-maillisttofaq-pi1-rpHead {font-weight: bold; font-size: 13px;  background-color: #DBE3EB;}

Reference

This is the TypoScript properties available.

CMD

Property

CMD

Data type

string

Description

Default

pidList

Property

pidList

Data type

integer

Description

Default

tx_newloginbox_pi3-showUidPid

Property

tx_newloginbox_pi3-showUidPid

Data type

integer

Description

Integer, the PID where "fe_users" shown with the Mailing List Archive can be shown details for. Details are supposed to be shown by the extension "newloginbox", referred to by the parameters &tx_newloginbox_pi3[returnUrl] and &tx_newloginbox_pi3[showUid].

Default

storeCompressedOriginalContent

Property

storeCompressedOriginalContent

Data type

boolean

Description

Stored compressed content

Default

enableFinalDesperateTryToLocateThreadBySubject

Property

enableFinalDesperateTryToLocateThreadBySubject

Data type

boolean

Description

If this is set the system will try and locate a proper thread for an answer if "Re:" og "Aw:" is detected in the subject line but the "reference" header not set. On the average 3 replies out of 50 mails to the typo3.org english user list had no reference header.

Default

messageDividers.[0..x]

Property

messageDividers.[0..x]

Data type

string

Description

Strings used to divide message content from reply/footer

Default

faq_email

Property

faq_email

Data type

->plugin.tx_maillisttofaq_pi1.faq_email

Description

Default

readmail

Property

readmail

Data type

boolean / ->plugin.tx_maillisttofaq_pi1.readmail

Description

Default

_CSS_DEFAULT_STYLE

Property

_CSS_DEFAULT_STYLE

Data type

string

Description

Default Style content. Unset this variable if it bugs you...

Default

_LOCAL_LANG.[langkey].[ll-key]

Property

_LOCAL_LANG.[langkey].[ll-key]

Data type

string

Description

Overriding the labels for locallanguage.

Default

_DEFAULT_PI_VARS.[piVar-key]

Property

_DEFAULT_PI_VARS.[piVar-key]

Data type

Description

Default

[tsref: plugin.tx_maillisttofaq_pi1]

results_at_a_time

Property

results_at_a_time

Data type

Description

Default

maxPages

Property

maxPages

Data type

Description

Default

transfer_probability

Property

transfer_probability

Data type

int

Description

Probability that mails are transferred from "inmail" table to archive table. Value can be 1-100

Default

transfer_amount

Property

transfer_amount

Data type

int

Description

Amount of mails to transfer when 'transfer_probability' allows. 1-200.

Default

textarea_style

Property

textarea_style

Data type

style-attribute value

Description

This is the style attribute value used in all textarea and many input fields:

Default

catSelSize

Property

catSelSize

Data type

int+

Description

Category Selector box size

Default

catSelTop

Property

catSelTop

Data type

boolean

Description

Category Selector in top

Default

catSelNoHeader

Property

catSelNoHeader

Data type

boolean

Description

No header for the category selector box.

Default

showHiddenOTmsgHeaders

Property

showHiddenOTmsgHeaders

Data type

boolean

Description

If set, then reply messages marked as OT/Hidden will have their headers shown (so you can see they exist). Otherwise not.

Default

replyIndented_BreakNumChar

Property

replyIndented_BreakNumChar

Data type

int

Description

The number of characters per line in reply text in the reply form.

Default

expireDaysForUnanswered

Property

expireDaysForUnanswered

Data type

int

Description

Days before messages in the "Unanswered" list expires

Default

lgdIndicator

Property

lgdIndicator

Data type

boolean

Description

Enable the display of the length indicator.

Default

lgdIndicator.limit_1

Property

lgdIndicator.limit_1

lgdIndicator.limit_2

lgdIndicator.limit_3

Data type

integer

Description

Limits in characters for states 1,2,3

Default

daysBeforeDirectNotificationsWhenReply

Property

daysBeforeDirectNotificationsWhenReply

Data type

integer

Description

Default

[tsref: plugin.tx_maillisttofaq_pi1.listView]

admin_email

Property

admin_email

Data type

Description

Default

admin_name

Property

admin_name

Data type

Description

Default

cc_email

Property

cc_email

Data type

Description

Default

regards

Property

regards

Data type

Description

Default

[tsref: plugin.tx_maillisttofaq_pi1.faq_email]

probability

Property

probability

Data type

int

Description

Probability that mails will be read from external source. Zero means it's disabled (then a on-server cron-script must to it). Otherwise value can be 1-100

Default

url

Property

url

Data type

string

Description

URL with "?" in end (or parameters). Eg. "http://192.168.230.3/typo3/32/typo3site_dev/1422.0.html?"

Default

password

Property

password

Data type

string

Description

Password, if needed on remote. (Also password for local feed-service.)

Default

number

Property

number

Data type

int

Description

Fetch number of mails:

Default

enableFeed

Property

enableFeed

Data type

boolean

Description

Feed service available?

Default

[tsref: plugin.tx_maillisttofaq_pi1.readmail]

Extending the mailing list archive class

This is a little tutorial on how to add "external" categories to the category selector box. It is also contains some general wisdom about TYPO3 and how to extend the system in a backwards compatible way.

Scenario

On TYPO3.org we have a mailing list archive with categories like this extension provides it by default.

However we needed to be able to categorize threads to specific extensions on typo3.org. The problem is that this is a very typo3.org- specific feature and thus it makes no sense to implement it in the extension as a standard feature.

So what we did was to

  • create an extension class which added all publicly available extensions as categories.
  • Used a negated value of the uid of the extension key table records as category numbers (since the positive numbers were already used by the default category table)

The point would be two things:

  • We would have a dynamic list of categories where new extensions were added automatically.
  • We would be able to select threads for each extension directly from the archive and display them together with the extensions detail pages.
The category selector

In the category selector box you would find the extension categories listed like this:

img-14

... and as a manager you would be able to select any extension as category:

img-15

The extension class

To add these categories we created an extension class looking like this:

<?php class ux\_tx\_maillisttofaq\_pi1 extends tx\_maillisttofaq\_pi1 {function getFAQCategories (){ parent :: getFAQCategories (); $d = $GLOBALS [ 'TSFE' ]-> getStorageSiterootPids (); $storagePID = intval ( $d [ '\_STORAGE\_PID' ]); $query = 'SELECT uid,title,extension\_key,owner\_fe\_user FROM tx\_extrep\_keytable WHERE pid=' . $storagePID . ' AND members\_only=0' . $this -> cObj -> enableFields ( 'tx\_extrep\_keytable' ). ' ORDER BY title' ; $res = mysql ( TYPO3\_db , $query );while( $row = mysql\_fetch\_assoc ( $res )){ $this -> categories [ intval (- $row [ 'uid' ])]=array( 'title' => 'EXT: ' . t3lib\_div :: fixed\_lgd ( $row [ 'title' ], 30 ). ' - ' . $row [ 'extension\_key' ]);}}} ?>

It does this:

  • Calls the original function in the class, parent::getFAQCategories()
  • Then it selects from the extension key table and for each record selected it adds an entry to the internal category array - with negative uid-numbers as keys!

All you need to do now is save this in a file somewhere in typo3conf/* and add this line to a "localconf" file:

$TYPO3_CONF_VARS['FE']['XCLASS']['ext/maillisttofaq/pi1/class.tx_maillisttofaq_pi1.php'] =
        '--the-file-path-of-the-extension-class--';

However this can be done in a more or less cool way. Therefore I would like to suggest a general principle which you can use for every project you make and which would be perfect for this kind of adaption. Read on.

(For general information on the principle of PHP extension classes in TYPO3, look here )

A perfect framework for site specific extensions

A little history: Originally TYPO3 could be extended for local sites by writing "ext_tables.php" files (often referred to by that name). Those files could do all kinds of things to the $TCA array and other configuration variables. They could include libraries as well.

Today extensions has taken over large parts of this role. However extensions are in most case standard applications which you would not change anyways for a local project. So what about making you own little "local" extension just for the site you are working on? This could contain all site specific adaptions!

"user_local"

Step one: Create "user_local" extension.

I rushed to the Extension Manager and used the Kickstarter Wizard to create a new extension. The extension key was selected as "user_local". The "user_" prefix is reserved for site specific extension and is therefore a good choice for an extension which is never going to be shared with anyone - since it is simply a practical container for all the stuff related to this website.

The Kickstarter basically looked like this:

img-16

This creates nothing but a very blank framework for the extension.

Step two: Create the class file

So in the new extension folder I created a PHP file for the extension class (codelisting in prev. chapter):

img-17

Step three: Create a "ext_localconf.php" file

Then I created the "ext_localconf.php" file so that I could configure the system to use my class extension file. This was the code listing:

<?php

$TYPO3_CONF_VARS['FE']['XCLASS']['ext/maillisttofaq/pi1/class.tx_maillisttofaq_pi1.php'] =
        t3lib_extMgm::extPath('user_local').'class.ux_tx_maillisttofaq_pi1.php';


?>

Step four: Set priority

Now, at this point the extension is ready to install. And remember that the idea is that this extension could be used for even more site specific additions.

Since this is the case - that the extension is site specific and probably going to extend other extensions etc. - then we might set the priority flag to "bottom" so that the extension will always be loaded as one of the last extensions. This is done by editing the file "ext_emconf.php" in the extension root directory:

$EM_CONF[$_EXTKEY] = Array (
        'title' => 'LOCAL STUFF',
        'description' => 'Extension which contains local overriding stuff etc.',
        'category' => 'misc',
        'author' => 'Kasper Skårhøj',
        'author_email' => 'kasper@typo3.com',
        'shy' => '',
        'dependencies' => '',
        'conflicts' => '',
  'priority' => 'bottom',
        'module' => '',
        'state' => 'stable',
        'internal' => '',
...

(For more informaton about these flags there is a description of the keys on this page .)

Step five: Install it

Then, install it!

img-18

Or if you changed it while it was installed, clear the cache files:

img-19

Technical information

The mailing list archive requires some kind of connection between a mail server and the database. Please see the introduction chapter regarding this. Therefore its very important to read this information before you get too far with this plugin.

((generated))

Technical overview

Generally the archive works like this:

Getting mails into the database

  • Someone sends a mail to the mailing list. This mail arrives on the list server which forwards it all all the subscribers. This is how mailing lists work.
  • One of the subscribers to the list is your archive email , lets say that is "mailarchive@my_list_archive.com".
  • The mail server which receives messages to "mailarchive@my_list_archive.com" must be configured to pass all mails sent to this address on to a PHP shell script (included with this extension) which will insert the mail into the MySQL database, more precisely the table "tx_maillisttofaq_inmail"

This was the hard part. The rest is pure PHP-only.

Organizing mails in threads

  • You insert the archive plugin on a page on your website. Then depending on the probability configured in TypoScript the archive will look into the _inmail table (where the raw mails are) and see if there are new mails for the archive.
  • If there are new mails the plugin will read the mails, parse them for content and headers, create relations to the existing threads and the archive is thereby updated.
  • During this process the plugin will look into the "fe_users" table and try to find a fe_user (from the current storage folder) with the email address matching the sender - and if found, create a relation to the mail.
Database

The plugin is build on two principles which should make it work efficiently.

All content is stored in a table separate from the meta information of the messages in the archive. In other words the archive listing is done purely by looking up in a "header-table" (tx_maillisttofaq_ml) and when the threads are displayed, the message content is looked up from the content table (tx_maillisttofaq_mlcontent) which is related 1-1 with the header table (referred to as the "_ml" table in the source)

All root-messages (thread starters = tx_maillisttofaq_ml records with the field "parent" set to zero) will contain all content from the reply messages and itself in the field "all_content" of their tx_maillisttofaq_mlcontent-records. This is a cache of the thread content updated each time new messages arrive in the thread and thus making it easy to program the searching of content within a thread since only the root message needs to be searched.

Transfer from the mail server

A prerequisite for the archive to work is that mails from the mailing list arrive to a mail server where they are somehow piped into a MySQL table where the archive can access them.

The extension contains a PHP script configured to run in a unix shell and this script can do this for you.

The "inmail.phpsh" script

This script is not a normal PHP script which is executed by a webserver. It's a shellscript and it requires you to have a PHP- version compiled as a binary available in /usr/bin/php

This is how the first two lines of the script look:

#! /usr/bin/php -q
<?php

This instructs the shell to let the script "/usr/bin/php" execute the further contents of the file (which is plain PHP code).

The script outputs nothing, it just takes input from "stdin" (which is the same as content piped into the script from command line):

// MAIL CONTENT
$filename = "php://stdin";

Further it's very important to notice that the script expects the file "_dir_config_for_php_shell_script.php" to be located in the site root of the website. This script contains a simple line:

<?

$HTTP_ENV_VARS["_"]="/[absolute-path-to-site-document-root]/typo3conf/ext/maillisttofaq/inmail.phpsh";

?>

This script simply defines the absolute path to the inmail.phpsh script. This is needed for the script to perform correctly and this environment variable, $HTTP_ENV_VARS["_"], is not always available, therefore it must be set manually.

The steps you have to take regarding this script is:

Make sure there is a PHP binary, "/usr/bin/php"

Create the file "_dir_config_for_php_shell_script.php" in the TYPO3 website root.That means "/[absolute-path-to-site-document- root]/". In this file set the variable $HTTP_ENV_VARS["_"] to "/[absolute-path-to-site-document- root]/typo3conf/ext/maillisttofaq/inmail.phpsh" (the extension is expected to be locally installed as you can see!)

Make sure permissions allows the 'inmail.phpsh' script to be executed from the shell.

Setting up the mail server

The next thing is to set up the mail server to pipe content into this new script.

I can give an example of how this is done for Postfix and Qmail:

Postfix

  • In the file, "/etc/postfix/virtual" you define that an email address is forwarded to an alias. A line in this file could look like this:

    mailarchive@my_list_archive.com    mailarchive
    
  • In the file, "/etc/postfix/aliases", you configure a mail alias like this:

    mailarchive:     |/[absolute-path-to-site-document-root]/typo3conf/ext/maillisttofaq/inmail.phpsh
    

Qmail

Qmail uses ".qmail-xxx-yyy" files to control the action of mail addresses and with them you can direct content into the script instead of into a mail box. I'm not fully sure (since I tried this long time ago) but if you have a .qmail file for the email address something like this inside should do the trick:

|  /[absolute-path-to-site-document-root]/typo3conf/ext/maillisttofaq/inmail.phpsh

BTW: Qmail actually delivered the env var $HTTP_ENV_VARS["_"] mentioned above so the fix with the file "_dir_config_for_php_shell_script.php" was needed only for Postfix in my tests.

If anyone has comments, confirmations of these methods or additions for other mail servers (windows?) then please let me know.

Debugging that mails are piped into the inmail.phpsh script

You can test the settings by sending a mail to the email address of course. If you don't see a new record appear in the " tx_maillisttofaq_inmail " table before long, then you might want to debug this whole thing.

One thing you could do is to insert a line in the inmail.phpsh script which sends you an email when executed:

mail (

"your_email@email.email",

"INMAIL",

"TEST"

);

If you execute the script from command line you should receive an email from the script right away. Then, try to send a mail to the email address that gets piped into this. Do you get a mail then? If so, the script gets executed and you might want to find out why the mail content is not inserted into the database. Questions like "Is the mail content received by the script?" or "Do I get any errors when inserting the record" are interesting. You can debug it likewise by sending emails from the script to yourself containing error messages. Or write a log file of course.

The _inmail table

When mails are received by the inmail.phpsh script they are inserted in the table "tx_maillisttofaq_inmail "

This is done by the main part of the inmail.phpsh script which looks like this:

$dir = dirname($HTTP_ENV_VARS["_"]);
define("PATH_typo3", dirname(dirname(dirname($dir)))."/typo3/");
define("PATH_site", dirname(PATH_typo3)."/");
define("PATH_t3lib", PATH_typo3."t3lib/");
define("PATH_typo3conf", PATH_site."typo3conf/");   // Typo-configuraton path
define("TYPO3_MODE","BE");


if (substr($dir,strlen(PATH_site))!="typo3conf/ext/maillisttofaq")    {
        die("Wrong path... This '".substr($dir,strlen(PATH_site))."' should be the last part of '".$dir."'");
}
require(PATH_t3lib."class.t3lib_div.php");
require(PATH_t3lib."class.t3lib_extmgm.php");
require(PATH_t3lib."config_default.php");
if (!defined ("TYPO3_db"))    die ("The configuration file was not included.");

require_once (PATH_t3lib."class.t3lib_readmail.php");

// Connect to the database
$result = @mysql_pconnect(TYPO3_db_host, TYPO3_db_username, TYPO3_db_password);
if (!$result)   {
        die("Couldn't connect to database at ".TYPO3_db_host);
}



// MAIL CONTENT
$filename = "php://stdin";

$content = t3lib_div::getUrl($filename);
if (trim($content))     {
        $readMail = t3lib_div::makeInstance("t3lib_readmail");
                // Split mail into head and content
        $mailParts = $readMail->fullParse($content);

        $query="INSERT INTO tx_maillisttofaq_inmail (mailcontent,from_email,to_email,reply_to_email,sender_email,message_id,subject) VALUES (
                '".addslashes($content)."',
                '".addslashes($mailParts["_FROM"]["email"])."',
                '".addslashes($mailParts["_TO"]["email"])."',
                '".addslashes($mailParts["_REPLY_TO"]["email"])."',
                '".addslashes($mailParts["_SENDER"]["email"])."',
                '".addslashes($mailParts["message-id"])."',
                '".addslashes($mailParts["subject"])."'
                )";
        $res = mysql(TYPO3_db,$query);

The table is defined like this:

CREATE TABLE tx_maillisttofaq_inmail (
        uid int(11) unsigned DEFAULT '0' NOT NULL auto_increment,
        mailcontent mediumblob NOT NULL,
        from_email varchar(80) DEFAULT '' NOT NULL,
        to_email varchar(80) DEFAULT '' NOT NULL,
        reply_to_email varchar(80) DEFAULT '' NOT NULL,
        sender_email varchar(80) DEFAULT '' NOT NULL,
        message_id varchar(80) DEFAULT '' NOT NULL,
        subject tinytext NOT NULL,
        deleted tinyint(4) unsigned DEFAULT '0' NOT NULL,
        PRIMARY KEY (uid)
);

When the mail is read from stdin it is parsed by the class "t3lib_readmail" so that header information like from,to,reply-to and sender addresses are known. Further the message id and subject are extracted. All this information is inserted into the table in the fields with corresponding names (see above).

The "deleted" flag

The "deleted" flag is by default zero, but when the frontend plugin reads a mail from the "tx_maillisttofaq_inmail" table (basically the "inbox" of this extension) and inserts it into the "tx_maillisttofaq_ml" table, then the flag is changed to "1".

So all mails in the " tx_maillisttofaq_inmail " table with the deleted flag set to zero has be processed and can be deleted if you like!

Separate mail- and webserver?

The scenario in the former chapter describes a situation where the webserver also contains a mail server. Since this is not always possible you can configure other scenarios. I see two solutions.

The minimum requirements to the mail server is, that PHP exists as a binary so the PHP-script can get executed. But then you have two options:

  • Either you configure the PHP-shell script to insert the mail into a REMOTE MySQL database - the same database as the webserver uses. If this is possible, that is probably the easiest way.
  • Alternatively you set up a "dummy" TYPO3 website on the mail server and installs the archive plugin on that. Just use a standard template or whatever. The idea is that the production webserver is configured to pull content for the inmail table from the mail server via HTTP requests sent to "dummy" website.

The second configuration can be achieved with this configuration:

Production webserver

This server must pull content from the combined mail-/webserver. In TypoScript Setup field you insert this:

plugin.tx_maillisttofaq_pi1.readmail = 1
plugin.tx_maillisttofaq_pi1.readmail.url = http://[mail-web-server-site-url]/1422.0.html?

By default the production server will try and fetch mail content by a probability of 5% (1 out of 20 hits). This is configurable in TypoScript. Likewise you can configure a password and a max number of mails to fetch.

Combined web-/mailserver

The webserver with the dummy TYPO3 site having the Mailing List Archive plugin inserted has the URL "http://[mail-web-server-site- url]/1422.0.html?" in this example.

By setting this TypoScript in the Setup field..:

plugin.tx_maillisttofaq_pi1.readmail.enableFeed = 1

... the plugin will feed a data stream with mail records to the production server pulling it from there (instead of displaying the webpage with the archive). You can configure a password as well.

The parameter that triggers this is "&tx_maillisttofaq_pi1[readMails]=1"

Debugging the pulling action

This can be done on the production server by using the Admin Panel as an admin-user. Normally you would see a message like this:

img-20

Reading mails (pulling) from the combined web-/mailserver was not triggered since the probability for that to happen is 5%

But if it happens (can be configured with "plugin.tx_maillisttofaq_pi1.readmail.probability = 5") then it looks like this:

img-21

You can see the URL which was requested and the result below. In this case 5 messages was received and inserted in the local "_inmail" table on the production server.

When such a transfer happens the local reading of mails from the "_inmail" table is also triggered ("Transferring 50 mails..."). Notice that this query also happens once in a while (prob. 10%) on its own. Further the question selects mails from "_inmail" table based on the sender email address, here "typo3-german- admin@lists.netfielders.de". Thus you can see that the extension can receive mails from many mailing lists and split them out to various plugins on the website(s) if they are just configured to select mails from different lists.

Known problems

Sorting by subject not entirely consistent.The problem is that the subject displayed in the listing has the "subject prefix" (eg. "[Typo3]") removed - thereby the alphabetic sorting may be inconsistent although it generally works fine - if just all posts have the same prefix string.Modifying the subject as a manager doesn't help since this will store the subject in another field in the table.

To-Do list

  • Sorting by category; The problem is that a simple sorting will sort on the UID of the category - not the categorys title.
  • Add more configuration options from TypoScript. Many features could have a enable/disable flag at least.
  • Adding content to the manager manual in this document

img-22 EXT: Mailing List Archive - 19