Exemple #1
0
 /**
  * list images
  *
  * @param resource the SQL result
  * @return string the rendered text
  *
  * @see layouts/layout.php
  **/
 function layout($result)
 {
     global $context;
     // empty list
     if (!SQL::count($result)) {
         $output = array();
         return $output;
     }
     // we return an array of ($url => $attributes)
     $items = array();
     // process all items in the list
     while ($item = SQL::fetch($result)) {
         // get the anchor for this image
         if ($item['anchor']) {
             $anchor = Anchors::get($item['anchor']);
         }
         // url to view the image
         $url = $context['url_to_home'] . $context['url_to_root'] . Images::get_url($item['id']);
         // time of last update
         $time = SQL::strtotime($item['edit_date']);
         // the title as the label
         if ($item['title']) {
             $label = ucfirst($item['title']) . ' (' . $item['image_name'] . ')';
         } else {
             $label = $item['image_name'];
         }
         // the section
         $section = '';
         if (is_object($anchor)) {
             $section = ucfirst($anchor->get_title());
         }
         // the author(s) is an e-mail address, according to rss 2.0 spec
         $author = $item['create_address'] . ' (' . $item['create_name'] . ')';
         if ($item['create_address'] != $item['edit_address']) {
             if ($author) {
                 $author .= ', ';
             }
             $author .= $item['edit_address'] . ' (' . $item['edit_name'] . ')';
         }
         // the description
         $description = Codes::beautify($item['description']);
         // cap the number of words
         $description = Skin::cap($description, 300);
         // fix image references
         $description = preg_replace('#"/([^">]+?)"#', '"' . $context['url_to_home'] . '/$1"', $description);
         $introduction = $description;
         // other rss fields
         $extensions = array();
         // url for enclosure
         $type = Files::get_mime_type($item['image_name']);
         $extensions[] = '<enclosure url="' . $context['url_to_home'] . $context['url_to_root'] . Files::get_path($item['anchor'], 'images') . '/' . $item['image_name'] . '"' . ' length="' . $item['image_size'] . '"' . ' type="' . $type . '" />';
         // list all components for this item
         $items[$url] = array($time, $label, $author, $section, NULL, $introduction, $description, $extensions);
     }
     // end of processing
     SQL::free($result);
     return $items;
 }
Exemple #2
0
 /**
  * embed an interactive object
  *
  * The id designates the target file.
  * It can also include width and height of the target canvas, as in: '12, 100%, 250px'
  *
  * @param string id of the target file
  * @return string the rendered string
  **/
 public static function render_embed($id)
 {
     global $context;
     // split parameters
     $attributes = preg_split("/\\s*,\\s*/", $id, 4);
     $id = $attributes[0];
     // get the file
     if (!($item = Files::get($id))) {
         $output = '[embed=' . $id . ']';
         return $output;
     }
     // stream in a separate page
     if (isset($attributes[1]) && preg_match('/window/i', $attributes[1])) {
         if (!isset($attributes[2])) {
             $attributes[2] = i18n::s('Play in a separate window');
         }
         $output = '<a href="' . $context['url_to_home'] . $context['url_to_root'] . Files::get_url($item['id'], 'stream', $item['file_name']) . '" onclick="window.open(this.href); return false;" class="button"><span>' . $attributes[2] . '</span></a>';
         return $output;
     }
     // file extension
     $extension = strtolower(substr($item['file_name'], -3));
     // set a default size
     if (!isset($attributes[1])) {
         if (!strcmp($extension, 'gan')) {
             $attributes[1] = '98%';
         } elseif (!strcmp($extension, 'mm') && isset($context['skins_freemind_canvas_width'])) {
             $attributes[1] = $context['skins_freemind_canvas_width'];
         } else {
             $attributes[1] = 480;
         }
     }
     if (!isset($attributes[2])) {
         if (!strcmp($extension, 'gan')) {
             $attributes[2] = '300px';
         } elseif (!strcmp($extension, 'mm') && isset($context['skins_freemind_canvas_height'])) {
             $attributes[2] = $context['skins_freemind_canvas_height'];
         } else {
             $attributes[2] = 360;
         }
     }
     // object attributes
     $width = $attributes[1];
     $height = $attributes[2];
     $flashvars = '';
     if (isset($attributes[3])) {
         $flashvars = $attributes[3];
     }
     // rendering depends on file extension
     switch ($extension) {
         // stream a video
         case '3gp':
         case 'flv':
         case 'm4v':
         case 'mov':
         case 'mp4':
             // a flash player to stream a flash video
             $flvplayer_url = $context['url_to_home'] . $context['url_to_root'] . 'included/browser/player_flv_maxi.swf';
             // file is elsewhere
             if (isset($item['file_href']) && $item['file_href']) {
                 $url = $item['file_href'];
             } else {
                 $url = $context['url_to_home'] . $context['url_to_root'] . Files::get_url($item['id'], 'fetch', $item['file_name']);
             }
             // pass parameters to the player
             if ($flashvars) {
                 $flashvars = str_replace('autostart=true', 'autoplay=1', $flashvars) . '&';
             }
             $flashvars .= 'width=' . $width . '&height=' . $height;
             // if there is a static image for this video, use it
             if (isset($item['icon_url']) && $item['icon_url']) {
                 $flashvars .= '&startimage=' . urlencode($item['icon_url']);
             }
             // if there is a subtitle file for this video, use it
             if (isset($item['file_name']) && ($srt = 'files/' . str_replace(':', '/', $item['anchor']) . '/' . str_replace('.' . $extension, '.srt', $item['file_name'])) && file_exists($context['path_to_root'] . $srt)) {
                 $flashvars .= '&srt=1&srturl=' . urlencode($context['url_to_home'] . $context['url_to_root'] . $srt);
             }
             // if there is a logo file in the skin, use it
             Skin::define_img_href('FLV_IMG_HREF', 'codes/flvplayer_logo.png', '');
             if (FLV_IMG_HREF) {
                 $flashvars .= '&top1=' . urlencode(FLV_IMG_HREF . '|10|10');
             }
             // rely on Flash
             if (Surfer::has_flash()) {
                 // the full object is built in Javascript --see parameters at http://flv-player.net/players/maxi/documentation/
                 $output = '<div id="flv_' . $item['id'] . '" class="no_print">Flash plugin or Javascript are turned off. Activate both and reload to view the object</div>' . "\n";
                 Page::insert_script('var flashvars = { flv:"' . $url . '", ' . str_replace(array('&', '='), array('", ', ':"'), $flashvars) . '", autoload:0, margin:1, showiconplay:1, playeralpha:50, iconplaybgalpha:30, showfullscreen:1, showloading:"always", ondoubleclick:"fullscreen" }' . "\n" . 'var params = { allowfullscreen: "true", allowscriptaccess: "always" }' . "\n" . 'var attributes = { id: "file_' . $item['id'] . '", name: "file_' . $item['id'] . '"}' . "\n" . 'swfobject.embedSWF("' . $flvplayer_url . '", "flv_' . $item['id'] . '", "' . $width . '", "' . $height . '", "9", "' . $context['url_to_home'] . $context['url_to_root'] . 'included/browser/expressinstall.swf", flashvars, params);' . "\n");
                 // native support
             } else {
                 // <video> is HTML5, <object> is legacy
                 $output = '<video width="' . $width . '" height="' . $height . '" autoplay="" controls="" src="' . $url . '" >' . "\n" . '	<object width="' . $width . '" height="' . $height . '" data="' . $url . '" type="' . Files::get_mime_type($item['file_name']) . '">' . "\n" . '		<param value="' . $url . '" name="movie" />' . "\n" . '		<param value="true" name="allowFullScreen" />' . "\n" . '		<param value="always" name="allowscriptaccess" />' . "\n" . '		<a href="' . $url . '">No video playback capabilities, please download the file</a>' . "\n" . '	</object>' . "\n" . '</video>' . "\n";
             }
             // job done
             return $output;
             // a ganttproject timeline
         // a ganttproject timeline
         case 'gan':
             // where the file is
             $path = Files::get_path($item['anchor']) . '/' . rawurlencode($item['file_name']);
             // we actually use a transformed version of the file
             $cache_id = Cache::hash($path) . '.xml';
             // apply the transformation
             if (!file_exists($context['path_to_root'] . $cache_id) || filemtime($context['path_to_root'] . $cache_id) < filemtime($context['path_to_root'] . $path) || !($text = Safe::file_get_contents($context['path_to_root'] . $cache_id))) {
                 // transform from GanttProject to SIMILE Timeline
                 $text = Files::transform_gan_to_simile($path);
                 // put in cache
                 Safe::file_put_contents($cache_id, $text);
             }
             // load the SIMILE Timeline javascript library in shared/global.php
             $context['javascript']['timeline'] = TRUE;
             // cache would kill the loading of the library
             cache::poison();
             // 1 week ago
             $now = gmdate('M d Y H:i:s', time() - 7 * 24 * 60 * 60);
             // load the right file
             $output = '<div id="gantt" style="height: ' . $height . '; width: ' . $width . '; border: 1px solid #aaa; font-family: Trebuchet MS, Helvetica, Arial, sans serif; font-size: 8pt"></div>' . "\n";
             Page::insert_script('var simile_handle;' . "\n" . 'function onLoad() {' . "\n" . '  var eventSource = new Timeline.DefaultEventSource();' . "\n" . '	var theme = Timeline.ClassicTheme.create();' . "\n" . '            theme.event.bubble.width = 350;' . "\n" . '            theme.event.bubble.height = 300;' . "\n" . '  var bandInfos = [' . "\n" . '    Timeline.createBandInfo({' . "\n" . '        eventSource:    eventSource,' . "\n" . '        date:           "' . $now . '",' . "\n" . '        width:          "80%",' . "\n" . '        intervalUnit:   Timeline.DateTime.WEEK,' . "\n" . '        intervalPixels: 200,' . "\n" . '		  theme:          theme,' . "\n" . '        layout:         "original"  // original, overview, detailed' . "\n" . '    }),' . "\n" . '    Timeline.createBandInfo({' . "\n" . '        showEventText: false,' . "\n" . '        trackHeight: 0.5,' . "\n" . '        trackGap: 0.2,' . "\n" . '        eventSource:    eventSource,' . "\n" . '        date:           "' . $now . '",' . "\n" . '        width:          "20%",' . "\n" . '        intervalUnit:   Timeline.DateTime.MONTH,' . "\n" . '        intervalPixels: 50' . "\n" . '    })' . "\n" . '  ];' . "\n" . '  bandInfos[1].syncWith = 0;' . "\n" . '  bandInfos[1].highlight = true;' . "\n" . '  bandInfos[1].eventPainter.setLayout(bandInfos[0].eventPainter.getLayout());' . "\n" . '  simile_handle = Timeline.create(document.getElementById("gantt"), bandInfos, Timeline.HORIZONTAL);' . "\n" . '	simile_handle.showLoadingMessage();' . "\n" . '  Timeline.loadXML("' . $context['url_to_home'] . $context['url_to_root'] . $cache_id . '", function(xml, url) { eventSource.loadXML(xml, url); });' . "\n" . '	simile_handle.hideLoadingMessage();' . "\n" . '}' . "\n" . "\n" . 'var resizeTimerID = null;' . "\n" . 'function onResize() {' . "\n" . '    if (resizeTimerID == null) {' . "\n" . '        resizeTimerID = window.setTimeout(function() {' . "\n" . '            resizeTimerID = null;' . "\n" . '            simile_handle.layout();' . "\n" . '        }, 500);' . "\n" . '    }' . "\n" . '}' . "\n" . "\n" . '// observe page major events' . "\n" . '$(document).ready( onLoad);' . "\n" . '$(window).resize(onResize);' . "\n");
             // job done
             return $output;
             // a Freemind map
         // a Freemind map
         case 'mm':
             // if we have an external reference, use it
             if (isset($item['file_href']) && $item['file_href']) {
                 $target_href = $item['file_href'];
                 // else redirect to ourself
             } else {
                 // ensure a valid file name
                 $file_name = utf8::to_ascii($item['file_name']);
                 // where the file is
                 $path = Files::get_path($item['anchor']) . '/' . rawurlencode($item['file_name']);
                 // map the file on the regular web space
                 $url_prefix = $context['url_to_home'] . $context['url_to_root'];
                 // redirect to the actual file
                 $target_href = $url_prefix . $path;
             }
             // allow several viewers to co-exist in the same page
             static $freemind_viewer_index;
             if (!isset($freemind_viewer_index)) {
                 $freemind_viewer_index = 1;
             } else {
                 $freemind_viewer_index++;
             }
             // load flash player
             $url = $context['url_to_home'] . $context['url_to_root'] . 'included/browser/visorFreemind.swf';
             // variables
             $flashvars = 'initLoadFile=' . $target_href . '&openUrl=_self';
             $output = '<div id="freemind_viewer_' . $freemind_viewer_index . '">Flash plugin or Javascript are turned off. Activate both and reload to view the object</div>' . "\n";
             Page::insert_script('var params = {};' . "\n" . 'params.base = "' . dirname($url) . '/";' . "\n" . 'params.quality = "high";' . "\n" . 'params.wmode = "transparent";' . "\n" . 'params.menu = "false";' . "\n" . 'params.flashvars = "' . $flashvars . '";' . "\n" . 'swfobject.embedSWF("' . $url . '", "freemind_viewer_' . $freemind_viewer_index . '", "' . $width . '", "' . $height . '", "6", "' . $context['url_to_home'] . $context['url_to_root'] . 'included/browser/expressinstall.swf", false, params);' . "\n");
             // offer to download a copy of the map
             $menu = array($target_href => i18n::s('Browse this map with Freemind'));
             // display menu commands below the viewer
             $output .= Skin::build_list($menu, 'menu_bar');
             // job done
             return $output;
             // native flash
         // native flash
         case 'swf':
             // where to get the file
             if (isset($item['file_href']) && $item['file_href']) {
                 $url = $item['file_href'];
             } else {
                 $url = $context['url_to_home'] . $context['url_to_root'] . 'files/' . str_replace(':', '/', $item['anchor']) . '/' . rawurlencode($item['file_name']);
             }
             $output = '<div id="swf_' . $item['id'] . '" class="no_print">Flash plugin or Javascript are turned off. Activate both and reload to view the object</div>' . "\n";
             Page::insert_script('var params = {};' . "\n" . 'params.base = "' . dirname($url) . '/";' . "\n" . 'params.quality = "high";' . "\n" . 'params.wmode = "transparent";' . "\n" . 'params.allowfullscreen = "true";' . "\n" . 'params.allowscriptaccess = "always";' . "\n" . 'params.flashvars = "' . $flashvars . '";' . "\n" . 'swfobject.embedSWF("' . $url . '", "swf_' . $item['id'] . '", "' . $width . '", "' . $height . '", "6", "' . $context['url_to_home'] . $context['url_to_root'] . 'included/browser/expressinstall.swf", false, params);' . "\n");
             return $output;
             // link to file page
         // link to file page
         default:
             // link label
             $text = Skin::strip($item['title'] ? $item['title'] : str_replace('_', ' ', $item['file_name']));
             // make a link to the target page
             $url = Files::get_permalink($item);
             // return a complete anchor
             $output =& Skin::build_link($url, $text);
             return $output;
     }
 }
Exemple #3
0
     if ($name = implode(' - ', $name)) {
         Safe::header("icy-name: " . utf8::to_iso8859(utf8::transcode($name)));
     }
     // genre
     if ($value = implode(', ', @$data['comments_html']['genre'])) {
         Safe::header("icy-genre: " . utf8::to_iso8859(utf8::transcode($value)));
     }
     // audio bitrate
     if ($value = @$data['audio']['bitrate']) {
         Safe::header("icy-br: " . substr($value, 0, -3));
     }
     // this server
     Safe::header("icy-url: " . $context['url_to_home'] . $context['url_to_root']);
 }
 // serve the right type
 Safe::header('Content-Type: ' . Files::get_mime_type($item['file_name']));
 // suggest a name for the saved file
 $file_name = utf8::to_ascii($item['file_name']);
 Safe::header('Content-Disposition: attachment; filename="' . str_replace('"', '', $file_name) . '"');
 // we accepted (limited) range requests
 Safe::header('Accept-Ranges: bytes');
 // provide only a slice of the file
 if (isset($_SERVER['HTTP_RANGE']) && !strncmp('bytes=', $_SERVER['HTTP_RANGE'], 6)) {
     // maybe several ranges
     $range = substr($_SERVER['HTTP_RANGE'], 6);
     // process only the first range, if several are specified
     if ($position = strpos($range, ',')) {
         $range = substr($range, 0, $position);
     }
     // beginning and end of the range
     list($offset, $end) = explode('-', $range);
Exemple #4
0
 /**
  * integrate some player for the file, if any
  *
  * @param array the file to look at
  * @param int width for video player
  * @param int height of video player
  * @return string tags to be put in the HTML flow, or an empty string
  */
 public static function interact($item, $width = 320, $height = 240, $flashvars = '', $with_icon = TRUE)
 {
     global $context;
     static $counter;
     if (!isset($counter)) {
         $counter = 1;
     } else {
         $counter++;
     }
     // display explicit title, if any
     $title = '';
     if ($item['title']) {
         $title = '<p>' . Skin::strip($item['title']) . '</p>';
     }
     // several ways to play flash
     switch (strtolower(substr($item['file_name'], -3))) {
         // audio file handled by dewplayer
         case 'mp3':
             // only if the player is available
             if (file_exists($context['path_to_root'] . 'included/browser/dewplayer.swf')) {
                 // the player
                 $dewplayer_url = $context['url_to_root'] . 'included/browser/dewplayer.swf';
                 // the mp3 file
                 if (isset($item['file_href']) && $item['file_href']) {
                     $mp3_url = $item['file_href'];
                 } else {
                     $mp3_url = $context['url_to_root'] . Files::get_url($item['id'], 'fetch', $item['file_name']);
                 }
                 $flashvars = 'son=' . $mp3_url;
                 // combine the two in a single object
                 $output = '<div id="interact_' . $counter . '" class="no_print">Flash plugin or Javascript are turned off. Activate both and reload to view the object</div>' . "\n" . $title;
                 Page::insert_script('var params = {};' . "\n" . 'params.base = "' . dirname($mp3_url) . '/";' . "\n" . 'params.quality = "high";' . "\n" . 'params.wmode = "transparent";' . "\n" . 'params.menu = "false";' . "\n" . 'params.flashvars = "' . $flashvars . '";' . "\n" . 'swfobject.embedSWF("' . $dewplayer_url . '", "interact_' . $counter . '", "200", "20", "6", "' . $context['url_to_home'] . $context['url_to_root'] . 'included/browser/expressinstall.swf", false, params);' . "\n");
                 return $output;
             }
             // native flash
         // native flash
         case 'swf':
             // where to get the file
             if (isset($item['file_href']) && $item['file_href']) {
                 $url = $item['file_href'];
             } else {
                 $url = $context['url_to_home'] . $context['url_to_root'] . 'files/' . str_replace(':', '/', $item['anchor']) . '/' . rawurlencode($item['file_name']);
             }
             $output = '<div id="interact_' . $counter . '" class="no_print">Flash plugin or Javascript are turned off. Activate both and reload to view the object</div><br />' . "\n" . $title;
             Page::insert_script('var params = {};' . "\n" . 'params.base = "' . dirname($url) . '/";' . "\n" . 'params.quality = "high";' . "\n" . 'params.wmode = "transparent";' . "\n" . 'params.allowfullscreen = "true";' . "\n" . 'params.allowscriptaccess = "always";' . "\n" . 'params.flashvars = "' . $flashvars . '";' . "\n" . 'swfobject.embedSWF("' . $url . '", "interact_' . $counter . '", "' . $width . '", "' . $height . '", "6", "' . $context['url_to_home'] . $context['url_to_root'] . 'included/browser/expressinstall.swf", false, params);' . "\n");
             return $output;
             // stream a video
         // stream a video
         case '3gp':
         case 'flv':
         case 'm4v':
         case 'mov':
         case 'mp4':
             // a flash player to stream a flash video
             $flvplayer_url = $context['url_to_home'] . $context['url_to_root'] . 'included/browser/player_flv_maxi.swf';
             // file is elsewhere
             if (isset($item['file_href']) && $item['file_href']) {
                 $url = $item['file_href'];
             } else {
                 $url = $context['url_to_home'] . $context['url_to_root'] . Files::get_url($item['id'], 'fetch', $item['file_name']);
             }
             // pass parameters to the player
             if ($flashvars) {
                 $flashvars = str_replace('autostart=true', 'autoplay=1', $flashvars) . '&';
             }
             $flashvars .= 'width=' . $width . '&height=' . $height;
             // rely on Flash
             if (Surfer::has_flash()) {
                 // the full object is built in Javascript --see parameters at http://flv-player.net/players/maxi/documentation/
                 $output = '<div id="interact_' . $counter . '" class="no_print">Flash plugin or Javascript are turned off. Activate both and reload to view the object</div>' . "\n" . $title;
                 Page::insert_script('var flashvars = { flv:"' . $url . '", ' . str_replace(array('&', '='), array('", ', ':"'), $flashvars) . '", autoload:0, margin:1, showiconplay:1, playeralpha:50, iconplaybgalpha:30, showloading:"always", ondoubleclick:"fullscreen" }' . "\n" . 'var params = { allowfullscreen: "true", allowscriptaccess: "always" }' . "\n" . 'var attributes = { id: "interact_' . $counter . '", name: "file_' . $item['id'] . '"}' . "\n" . 'swfobject.embedSWF("' . $flvplayer_url . '", "interact_' . $counter . '", "' . $width . '", "' . $height . '", "9", "' . $context['url_to_home'] . $context['url_to_root'] . 'included/browser/expressinstall.swf", flashvars, params);' . "\n");
                 // native support
             } else {
                 $output = '<object width="' . $width . '" height="' . $height . '" data="' . $url . '" type="' . Files::get_mime_type($item['file_name']) . '">' . "\n" . '	<param value="' . $url . '" name="movie" />' . "\n" . '	<param value="true" name="allowFullScreen" />' . "\n" . '	<param value="always" name="allowscriptaccess" />' . "\n" . '	<a href="' . $url . '">No video playback capabilities, please download the file</a>' . "\n" . '</object>' . "\n" . $title;
             }
             return $output;
     }
     if (!$with_icon) {
         return '';
     }
     // this is a reasonably large image
     if (Files::is_image($item['file_name']) && ($image_information = Safe::GetImageSize($context['path_to_root'] . 'files/' . str_replace(':', '/', $item['anchor']) . '/' . $item['file_name'])) && $image_information[0] <= 600) {
         // provide a direct link to it!
         $src = $context['url_to_home'] . $context['url_to_root'] . 'files/' . str_replace(':', '/', $item['anchor']) . '/' . rawurlencode($item['file_name']);
         $icon = '<img src="' . $src . '" width="' . $image_information[0] . '" height="' . $image_information[1] . '" alt="" style="padding: 3px"/>' . BR;
         return Skin::build_link(Files::get_download_url($item), $icon, 'basic') . $title;
     }
     // explicit icon
     if ($item['thumbnail_url']) {
         $icon = $item['thumbnail_url'];
     } else {
         $icon = $context['url_to_root'] . Files::get_icon_url($item['file_name']);
     }
     // a clickable image to access the file
     if ($icon) {
         $icon = '<img src="' . $icon . '" alt="" style="padding: 3px"/>';
         // label for this file
         $text = '';
         // signal restricted and private files
         if ($item['active'] == 'N') {
             $text .= PRIVATE_FLAG;
         } elseif ($item['active'] == 'R') {
             $text .= RESTRICTED_FLAG;
         }
         // use file name, or regular title
         $text .= Skin::strip($item['title'] ? $item['title'] : str_replace('_', ' ', $item['file_name']));
         // flag files uploaded recently
         if ($item['create_date'] >= $context['fresh']) {
             $text .= NEW_FLAG;
         } elseif ($item['edit_date'] >= $context['fresh']) {
             $text .= UPDATED_FLAG;
         }
         // make a link to the target page
         $url = Files::get_download_url($item);
         return Skin::build_link($url, $icon, 'basic') . BR . Skin::build_link($url, $text, 'basic');
     }
     // nothing special
     return '';
 }
Exemple #5
0
 /**
  * build and transmit a complex e-mail messages
  *
  * This function allows for individual posts, textual and HTML messages, and attached files.
  *
  * For this to work, e-mail service has to be explicitly activated in the
  * main configuration panel, at [script]control/configure.php[/script].
  *
  * You can refer to local images in HTML parts, and the function will automatically attach these
  * to the message, else mail clients would not display them correctly.
  *
  * The message actually sent has a complex structure, with several parts assembled together,
  * as [explained at altepeter.net|http://altepeter.net/tech/articles/html-emails].
  *
  * @link http://altepeter.net/tech/articles/html-emails
  *
  * Several recipients can be provided as a list of addresses separated by
  * commas. For bulk posts, recipients can be transmitted as an array of strings.
  * In all cases, this function sends one separate message per recipient.
  *
  * This function will ensure that only one mail message is send to a recipient,
  * by maintaining an internal list of addresses that have been processed.
  * Therefore, if this function is called several times, with some repeated recipients,
  * those will receive only the first message, and other messages to the same address
  * will be dropped.
  *
  * Bracketed recipients, such as ##Foo Bar <*****@*****.**>##, are handled properly,
  * meaning ##foo@bar.com## is transmitted to the mailing function, while
  * the string ##To: Foo Bar <*****@*****.**>## is added to headers.
  *
  * If an array of messages is provided to the function, it is turned to a multi-part
  * message, as in the following example:
  *
  * [php]
  * $message = array();
  * $message['text/plain; charset=utf-8'] = 'This is a plain message';
  * $message['text/html'] = '<html><head><body>This is an HTML message</body></html>';
  * Mailer::post($from, $to, $subject, $message);
  * [/php]
  *
  * It is recommended to begin with the bare text, and to have the rich format part coming
  * after, as in the example. Also, if you don't provide a charset, then UTF-8 is used.
  *
  * Long lines of text/plain parts are wrapped according to
  * [link=Dan's suggestion]http://mailformat.dan.info/body/linelength.html[/link].
  *
  * @link http://mailformat.dan.info/body/linelength.html Dan's Mail Format Site: Body: Line Length
  *
  * Message parts are encoded either as quoted-printable (textual entities) or as base-64 (others).
  *
  * A list of files to be attached to the message can be provided as in the following example:
  *
  * [php]
  * $attachments = array();
  * $attachments[] = 'special/report.pdf';
  * $attachments[] = 'skins/my_skin/newsletters/image.png';
  * Mailer::post($from, $to, $subject, $message, $attachments);
  * [/php]
  *
  * Files are named from the installation directory of yacs, as visible in the examples.
  *
  * This function returns the number of successful posts,
  * and populates the error context, where applicable.
  *
  * @param string sender address
  * @param mixed recipient address(es)
  * @param string subject
  * @param mixed actual message, either a string, or an array of message parts
  * @param array attachments, if any
  * @param mixed additional headers, if any
  * @return the number of actual posts, or 0
  *
  * @see articles/mail.php
  * @see letters/new.php
  * @see users/mail.php
  */
 public static function post($from, $to, $subject, $message, $attachments = NULL, $headers = '')
 {
     global $context;
     // ensure that we have a sender
     if (!$from) {
         $from = Mailer::get_from_recipient();
     }
     // email services have to be activated
     if (!isset($context['with_email']) || $context['with_email'] != 'Y') {
         Logger::error(i18n::s('E-mail has not been enabled on this system.'));
         return 0;
         // check sender address
     } elseif (!$from) {
         Logger::error(i18n::s('Empty sender address'));
         return 0;
         // check recipient address
     } elseif (!$to) {
         Logger::error(i18n::s('Empty recipient address'));
         return 0;
         // check mail subject
     } elseif (!$subject) {
         Logger::error(i18n::s('No subject'));
         return 0;
         // check mail content
     } elseif (!$message) {
         Logger::error(i18n::s('No message'));
         return 0;
     }
     // the end of line string for mail messages
     if (!defined('M_EOL')) {
         define('M_EOL', "\n");
     }
     // encode the subject line
     $subject = Mailer::encode_subject($subject);
     // make some text out of an array
     if (is_array($headers)) {
         $headers = implode(M_EOL, $headers);
     }
     // From: header
     if (!preg_match('/^From: /im', $headers)) {
         $headers .= M_EOL . 'From: ' . $from;
     }
     // Reply-To: header
     if (!preg_match('/^Reply-To: /im', $headers)) {
         $headers .= M_EOL . 'Reply-To: ' . $from;
     }
     // Return-Path: header --to process errors
     if (!preg_match('/^Return-Path: /im', $headers)) {
         $headers .= M_EOL . 'Return-Path: ' . $from;
     }
     // Message-ID: header --helps to avoid spam filters
     if (!preg_match('/^Message-ID: /im', $headers)) {
         $headers .= M_EOL . 'Message-ID: <uniqid.' . uniqid() . '@' . $context['host_name'] . '>';
     }
     // MIME-Version: header
     if (!preg_match('/^MIME-Version: /im', $headers)) {
         $headers .= M_EOL . 'MIME-Version: 1.0';
     }
     // arrays are easier to manage
     if (is_string($message)) {
         // turn HTML entities to UTF-8
         $message = Safe::html_entity_decode($message, ENT_QUOTES, 'UTF-8');
         $copy = $message;
         $message = array();
         $message['text/plain; charset=utf-8'] = $copy;
         unset($copy);
     }
     // turn attachments to some array too
     if (is_string($attachments)) {
         $attachments = array($attachments);
     } elseif (!is_array($attachments)) {
         $attachments = array();
     }
     // we only consider objects from this server
     $my_prefix = $context['url_to_home'] . $context['url_to_root'];
     // transcode objects that will be transmitted along the message (i.e., images)
     foreach ($message as $type => $part) {
         // search throughout the full text
         $head = 0;
         while ($head = strpos($part, ' src="', $head)) {
             $head += strlen(' src="');
             // a link has been found
             if ($tail = strpos($part, '"', $head + 1)) {
                 $reference = substr($part, $head, $tail - $head);
                 // remember local links only
                 if (!strncmp($reference, $my_prefix, strlen($my_prefix))) {
                     // local name
                     $name = urldecode(substr($reference, strlen($my_prefix)));
                     // content-id to be used instead of the link
                     $cid = sprintf('%u@%s', crc32($name), $context['host_name']);
                     // transcode the link in this part
                     $part = substr($part, 0, $head) . 'cid:' . $cid . substr($part, $tail);
                     // remember to put content in attachments of this message, if not done yet
                     if (!in_array($name, $attachments)) {
                         $attachments[] = $name;
                     }
                 }
             }
         }
         // remember the transcoded part
         $message[$type] = $part;
     }
     // we need some boundary string
     if (count($message) + count($attachments) > 1) {
         $boundary = md5(time());
     }
     // wrapping threshold
     if (!defined('WRAPPING_LENGTH')) {
         define('WRAPPING_LENGTH', 70);
     }
     // combine message parts
     $content_type = '';
     $body = '';
     foreach ($message as $type => $part) {
         // quote textual entities
         if (!strncmp($type, 'text/', 5)) {
             $content_encoding = 'quoted-printable';
             $part = quoted_printable_encode($part);
             // encode everything else
         } else {
             $content_encoding = 'base64';
             $part = chunk_split(base64_encode($content), 76, M_EOL);
         }
         // only one part
         if (count($message) == 1) {
             $content_type = $type;
             $body = $part;
             // one part among several
         } else {
             // let user agent select among various alternatives
             if (!$content_type) {
                 $content_type = 'multipart/alternative; boundary="' . $boundary . '-internal"';
             }
             // introduction to assembled parts
             if (!$body) {
                 $body = 'This is a multi-part message in MIME format.';
             }
             // this part only --second EOL is part of the boundary chain
             $body .= M_EOL . M_EOL . '--' . $boundary . '-internal' . M_EOL . 'Content-Type: ' . $type . M_EOL . 'Content-Transfer-Encoding: ' . $content_encoding . M_EOL . M_EOL . $part;
         }
     }
     // finalize the body
     if (count($message) > 1) {
         $body .= M_EOL . M_EOL . '--' . $boundary . '-internal--';
     }
     // a mix of things
     if (count($attachments)) {
         // encoding is irrelevant if there are multiple parts
         if (!strncmp($content_type, 'multipart/', 10)) {
             $content_encoding = '';
         } else {
             $content_encoding = M_EOL . 'Content-Transfer-Encoding: ' . $content_encoding;
         }
         // identify the main part of the overall message
         $content_start = 'mainpart';
         // the current body becomes the first part of a larger message
         $body = 'This is a multi-part message in MIME format.' . M_EOL . M_EOL . '--' . $boundary . '-external' . M_EOL . 'Content-Type: ' . $content_type . $content_encoding . M_EOL . 'Content-ID: <' . $content_start . '>' . M_EOL . M_EOL . $body;
         // message parts should be considered as an aggregate whole --see RFC 2387
         $content_type = 'multipart/related; type="multipart/alternative"; boundary="' . $boundary . '-external"';
         $content_encoding = '';
         // process every file
         foreach ($attachments as $name => $content) {
             // read external file content
             if (preg_match('/^[0-9]+$/', $name)) {
                 // only a file name has been provided
                 $name = $content;
                 // read file content from the file system
                 if (!($content = Safe::file_get_contents($name))) {
                     continue;
                 }
             }
             // file name is the file type
             if (preg_match('/name="(.+)?"/', $name, $matches)) {
                 $type = $name;
                 $name = $matches[1];
             } else {
                 $type = Files::get_mime_type($name);
             }
             // a unique id for for this file
             $cid = sprintf('%u@%s', crc32($name), $context['host_name']);
             // set a name that avoids problems
             $basename = utf8::to_ascii(basename($name));
             // headers for one file
             $body .= M_EOL . M_EOL . '--' . $boundary . '-external' . M_EOL . 'Content-Type: ' . $type . M_EOL . 'Content-Disposition: inline; filename="' . str_replace('"', '', $basename) . '"' . M_EOL . 'Content-ID: <' . $cid . '>';
             // transfer textual entities as they are
             if (!strncmp($type, 'text/', 5)) {
                 $body .= M_EOL . 'Content-Transfer-Encoding: quoted-printable' . M_EOL . M_EOL . quoted_printable_encode($content);
                 // encode everything else
             } else {
                 $body .= M_EOL . 'Content-Transfer-Encoding: base64' . M_EOL . M_EOL . chunk_split(base64_encode($content), 76, M_EOL);
             }
         }
         // the closing boundary
         $body .= M_EOL . M_EOL . '--' . $boundary . '-external--';
     }
     // Content-Type: header
     if ($content_type && !preg_match('/^Content-Type: /im', $headers)) {
         $headers .= M_EOL . 'Content-Type: ' . $content_type;
     }
     // Content-Transfer-Encoding: header
     if (!isset($boundary) && $content_encoding && !preg_match('/^Content-Transfer-Encoding: /im', $headers)) {
         $headers .= M_EOL . 'Content-Transfer-Encoding: ' . $content_encoding;
     }
     // Start: header
     if (isset($boundary) && isset($content_start) && $content_start && !preg_match('/^Start: /im', $headers)) {
         $headers .= M_EOL . 'Start: ' . $content_start;
     }
     // X-Mailer: header --helps to avoid spam filters
     if (!preg_match('/^X-Mailer: /im', $headers)) {
         $headers .= M_EOL . 'X-Mailer: yacs';
     }
     // strip leading spaces and newlines
     $headers = trim($headers);
     // make an array of recipients
     if (!is_array($to)) {
         $to = Mailer::explode_recipients($to);
     }
     // the list of recipients contacted during overall script execution
     if (!isset($context['mailer_recipients'])) {
         $context['mailer_recipients'] = array();
     }
     // process every recipient
     $posts = 0;
     foreach ($to as $recipient) {
         // clean the provided string
         $recipient = trim(str_replace(array("\r\n", "\r", "\n", "\t"), ' ', $recipient));
         // this e-mail address has already been processed
         if (in_array($recipient, $context['mailer_recipients'])) {
             if (isset($context['debug_mail']) && $context['debug_mail'] == 'Y') {
                 Logger::remember('shared/mailer.php: Skipping recipient already processed', $recipient, 'debug');
             }
             continue;
             // remember this recipient
         } else {
             $context['mailer_recipients'][] = $recipient;
         }
         // queue the message
         Mailer::queue($recipient, $subject, $body, $headers);
         $posts++;
     }
     // track last submission
     include_once $context['path_to_root'] . 'shared/values.php';
     Values::set('mailer.last.queued', $subject . ' (' . $posts . ' recipients)');
     // return the number of actual posts
     return $posts;
 }