Factorizes, and improves, our content caching headers.
authorVincent Zanotti <vincent.zanotti@m4x.org>
Sun, 8 Nov 2009 04:15:15 +0000 (05:15 +0100)
committerVincent Zanotti <vincent.zanotti@m4x.org>
Sun, 8 Nov 2009 19:15:00 +0000 (20:15 +0100)
Signed-off-by: Vincent Zanotti <vincent.zanotti@m4x.org>
22 files changed:
ChangeLog
core
htdocs/index.html
include/reminder.inc.php
include/rss.inc.php
include/userset.inc.php
modules/auth.php
modules/bandeau.php
modules/carnet.php
modules/email.php
modules/events.php
modules/gadgets/gadgets.inc.php
modules/lists.php
modules/openid/openid.inc.php
modules/platal.php
modules/profile.php
modules/search.php
modules/stats.php
modules/survey.php
modules/xnet.php
modules/xnetevents.php
modules/xnetgrp.php

index 8beb078..f4d6fe3 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -8,6 +8,7 @@ New:
 
     * Core:
         - Prunes old sessions/events logs after 12 months                  -VZA
+        - Adds proper content caching for static and semi-static content   -VZA
 
 Bug/Wish:
 
diff --git a/core b/core
index 2386dca..a286fc7 160000 (submodule)
--- a/core
+++ b/core
@@ -1 +1 @@
-Subproject commit 2386dca38cb8aea249b3de8cc075a857498b7292
+Subproject commit a286fc7adaddd6aadd16dc69595fe929809d84b3
index 1d34714..7df214a 100644 (file)
@@ -2,7 +2,7 @@
 <html>
   <head>
     <title>Polytechnique.org : le site des élèves et anciens élèves de l'École polytechnique</title>
-    <meta http-equiv="content-type" CONTENT="text/html; charset=utf-8">
+    <meta http-equiv="content-type" content="text/html; charset=utf-8">
   </head>
   <body>
     <table align="center" border=0 width=600>
@@ -28,7 +28,7 @@
           <p>Nous vous remercions de votre compr&eacute;hension.
          </p>
           <p>Avec toutes nos excuses pour le d&eacute;rangement occasionn&eacute; et
-          &agrave; bient&ocirc;t !</p>
+          &agrave; bient&ocirc;t&nbsp;!</p>
           <p align="right"><b>L'&eacute;quipe de Polytechnique.org</b></p>
         </td>
       </tr>
index cede617..a2fedc2 100644 (file)
@@ -105,7 +105,7 @@ abstract class Reminder
     // has been taken into account.
     public function NotifiesAction(&$page)
     {
-        header('Content-Type: text/html; charset=utf-8');
+        pl_content_headers("text/html");
         $page->changeTpl('reminder/notification.tpl', NO_SKIN);
         $page->assign('previous_reminder', $this->title());
     }
@@ -114,7 +114,7 @@ abstract class Reminder
     // when the reminder is the only output of a page.
     public function DisplayStandalone(&$page, $previous_reminder = null)
     {
-        header('Content-Type: text/html; charset=utf-8');
+        pl_content_headers("text/html");
         $page->changeTpl('reminder/base.tpl', NO_SKIN);
         $this->Prepare($page);
         if ($previous_reminder) {
index eb78f63..f292fe8 100644 (file)
@@ -34,7 +34,7 @@ function init_rss($template, $alias, $hash, $require_uid = true)
 
     if ($template) {
         $page->assign('rss_hash', $hash);
-        header('Content-Type: application/rss+xml; charset=utf8');
+        pl_content_headers("application/rss+xml");
     }
     return is_null($user) ? null : $user->id();
 }
index 3a357e4..100be7d 100644 (file)
@@ -362,21 +362,18 @@ class GeolocView implements PlView
 
         switch ($this->type) {
           case 'icon.swf':
-            header("Content-type: application/x-shockwave-flash");
-            header("Pragma:");
+            pl_cached_content_headers("application/x-shockwave-flash");
             readfile(dirname(__FILE__).'/../modules/geoloc/icon.swf');
             exit;
 
           case 'dynamap.swf':
-            header("Content-type: application/x-shockwave-flash");
-            header("Pragma:");
+            pl_cached_content_headers("application/x-shockwave-flash");
             readfile(dirname(__FILE__).'/../modules/geoloc/dynamap.swf');
             exit;
 
           case 'init':
             $page->changeTpl('geoloc/init.tpl', NO_SKIN);
-            header('Content-Type: text/xml');
-            header('Pragma:');
+            pl_cached_content_headers("text/xml", "utf-8");
             if (!empty($GLOBALS['IS_XNET_SITE'])) {
                 $page->assign('background', 0xF2E9D0);
             }
@@ -384,8 +381,7 @@ class GeolocView implements PlView
 
           case 'city':
             $page->changeTpl('geoloc/city.tpl', NO_SKIN);
-            header('Content-Type: text/xml');
-            header('Pragma:');
+            pl_cached_content_headers("text/xml", "utf-8");
             $only_current = Env::v('only_current', false)? ' AND FIND_IN_SET(\'active\', adrf.statut)' : '';
             $it =& $this->set->get('u.user_id AS id, u.prenom, u.nom, u.promo, al.alias',
                                    "INNER JOIN  adresses AS adrf  ON (adrf.uid = u.user_id $only_current)
@@ -401,8 +397,7 @@ class GeolocView implements PlView
                 $page->changeTpl('geoloc/country.tpl', SIMPLE);
             } else {
                 $page->changeTpl('geoloc/country.tpl', NO_SKIN);
-                header('Content-Type: text/xml');
-                header('Pragma:');
+                pl_cached_content_headers("text/xml", "utf-8");
             }
             $mapid = Env::has('mapid') ? Env::i('mapid', -2) : false;
             list($countries, $cities) = geoloc_getData_subcountries($mapid, $this->set, 10);
index 424361c..99bce24 100644 (file)
@@ -88,7 +88,7 @@ class AuthModule extends PLModule
 
             $res .= "</membres>\n\n";
 
-            header('Content-Type: text/xml; charset="UTF-8"');
+            pl_content_headers("text/xml");
             echo $res;
         }
         exit;
@@ -109,7 +109,7 @@ class AuthModule extends PLModule
 
             $request  = @$GLOBALS['HTTP_RAW_POST_DATA'];
             $response = xmlrpc_server_call_method($server, $request, null);
-            header('Content-Type: text/xml');
+            pl_content_headers("text/xml");
             print $response;
             xmlrpc_server_destroy($server);
         }
index 4a226ac..012281e 100644 (file)
@@ -32,13 +32,14 @@ class BandeauModule extends PLModule
 
     function handler_icone(&$page)
     {
-        header("Content-Type: image/png");
+        pl_cached_content_headers("image/png");
         readfile('../htdocs/images/x.png');
         exit();
     }
 
     function handler_html(&$page, $login = '')
     {
+        pl_cached_content_headers("text/html");
         $page->changeTpl('skin/common.bandeau.tpl', NO_SKIN);
         $page->assign('login', $login == 'login');
         $page->assign('seed', $login);
@@ -46,7 +47,7 @@ class BandeauModule extends PLModule
 
     function handler_css(&$page)
     {
-        header("Content-Type: text/css");
+        pl_cached_content_headers("text/css");
         readfile('../htdocs/css/bandeau.css');
         exit();
     }
index 54a118d..e6ca6a6 100644 (file)
@@ -307,7 +307,7 @@ class CarnetModule extends PLModule
         }
         $page->assign('events', $annivs);
 
-        header('Content-Type: text/calendar; charset=utf-8');
+        pl_content_headers("text/calendar");
     }
 
     function handler_vcard(&$page, $photos = null)
index 41c5e8a..fc76f01 100644 (file)
@@ -913,8 +913,7 @@ L'équipe d'administration <support@" . $globals->mail->domain . '>';
 
                 // Output the list of users with recently broken addresses,
                 // along with the count of valid redirections.
-                header('Content-Type: text/x-csv; charset=utf-8;');
-                header('Cache-Control: no-cache');
+                pl_content_headers("text/x-csv");
 
                 $csv = fopen('php://output', 'w');
                 fputcsv($csv, array('nom', 'prenom', 'promo', 'alias', 'bounce', 'nbmails', 'url'), ';');
index 5ee7b81..d9c3a0b 100644 (file)
@@ -192,7 +192,7 @@ class EventsModule extends PLModule
             $res = XDB::query("SELECT * FROM evenements_photo WHERE eid = {?}", $eid);
             if ($res->numRows()) {
                 $photo = $res->fetchOneAssoc();
-                header('Content-Type: image/' . $photo['attachmime']);
+                pl_cached_dynamic_content_headers("image/" . $photo['attachmime']);
                 echo $photo['attach'];
                 exit;
             }
@@ -200,20 +200,20 @@ class EventsModule extends PLModule
             require_once 'validations.inc.php';
             $valid = Validate::get_request_by_id($valid);
             if ($valid && $valid->img) {
-                header('Content-Type: image/' . $valid->imgtype);
+                pl_cached_dynamic_content_headers("image/" . $valid->imgtype);
                 echo $valid->img;
                 exit;
             }
         } else {
             $upload = new PlUpload(S::user()->login(), 'event');
             if ($upload->exists() && $upload->isType('image')) {
-                header('Content-Type: ' . $upload->contentType());
+                pl_cached_dynamic_content_headers($upload->contentType());
                 echo $upload->getContents();
                 exit;
             }
         }
         global $globals;
-        header('Content-Type: image/png');
+        pl_cached_dynamic_content_headers("image/png");
         echo file_get_contents($globals->spoolroot . '/htdocs/images/logo.png');
         exit;
     }
@@ -238,7 +238,7 @@ class EventsModule extends PLModule
         }
         $page->assign('texte', $texte);
         $page->assign('titre', $titre);
-        header('Content-Type: text/html; charset=utf-8');
+        pl_content_headers("text/html");
     }
 
     function handler_ev_submit(&$page)
@@ -296,7 +296,7 @@ class EventsModule extends PLModule
 
     function handler_tips(&$page, $tips = null)
     {
-        header('Content-Type: text/html; charset="UTF-8"');
+        pl_content_headers("text/html");
         $page->changeTpl('include/tips.tpl', NO_SKIN);
         $page->assign('tips', $this->get_tips($tips));
     }
index f668fb4..81a8bd4 100644 (file)
@@ -23,8 +23,7 @@
 function init_igoogle_xml($template)
 {
     Platal::page()->changeTpl($template, NO_SKIN);
-
-    header('Content-Type: application/xml; charset=utf-8');
+    pl_cached_content_headers("application/xml", "utf-8");
 }
 
 function init_igoogle_html($template, $auth = AUTH_PUBLIC)
index dcce23c..fe87382 100644 (file)
@@ -131,7 +131,7 @@ class ListsModule extends PLModule
 
     function handler_ajax(&$page, $list = null)
     {
-        header('Content-Type: text/html; charset="UTF-8"');
+        pl_content_headers("text/html");
         $domain = $this->prepare_client($page);
         $page->changeTpl('lists/liste.inc.tpl', NO_SKIN);
         S::assert_xsrf_token();
@@ -346,9 +346,7 @@ class ListsModule extends PLModule
         $this->prepare_client($page);
         $members = $this->client->get_members($liste);
         $list = list_fetch_names(list_extract_members($members[1]));
-        header('Content-Type: text/x-csv; charset=utf-8;');
-        header('Pragma: ');
-        header('Cache-Control: ');
+        pl_content_headers("text/x-csv");
 
         echo "email,nom,prenom,promo\n";
         foreach ($list as $member) {
index c8c5bf7..5075084 100644 (file)
@@ -177,7 +177,7 @@ class OpenId
     // Renders the main XRDS page.
     public function RenderMainXrdsPage(&$page)
     {
-        header('Content-type: application/xrds+xml');
+        pl_content_headers("application/xrds+xml");
         $page->changeTpl('openid/idp_xrds.tpl', NO_SKIN);
         $page->assign('type2', Auth_OpenID_TYPE_2_0_IDP);
         $page->assign('sreg', Auth_OpenID_SREG_URI);
@@ -187,7 +187,7 @@ class OpenId
     // Renders the XRDS page of |user|.
     public function RenderUserXrdsPage(&$page, User &$user)
     {
-        header('Content-type: application/xrds+xml');
+        pl_content_headers("application/xrds+xml");
         $page->changeTpl('openid/user_xrds.tpl', NO_SKIN);
         $page->assign('type2', Auth_OpenID_TYPE_2_0);
         $page->assign('type1', Auth_OpenID_TYPE_1_1);
index 1279260..068ff05 100644 (file)
@@ -75,14 +75,8 @@ class PlatalModule extends PLModule
 
     function handler_cacert(&$page)
     {
-        $data = file_get_contents("/etc/ssl/xorgCA/cacert.pem","r");
-        header("Pragma:");
-        header("Set-Cookie:");
-        header("Cache-Control:");
-        header("Expires:");
-        header("Content-Type: application/x-x509-ca-cert");
-        header("Content-Length: ".strlen($data));
-        echo $data;
+        pl_cached_content_headers("application/x-x509-ca-cert");
+        readfile("/etc/ssl/xorgCA/cacert.pem");
         exit;
     }
 
index 58a485f..5c985cc 100644 (file)
@@ -102,10 +102,10 @@ class ProfileModule extends PLModule
 
         // Display the photo, or a default one when not available.
         if ($photo_type && $photo_data != null) {
-            header('Content-type: image/' . $photo_type);
+            pl_cached_dynamic_content_headers("image/$photo_type");
             echo $photo_data;
         } else {
-            header('Content-type: image/png');
+            pl_cached_dynamic_content_headers("image/png");
             echo file_get_contents(dirname(__FILE__).'/../htdocs/images/none.png');
         }
         exit;
@@ -123,8 +123,7 @@ class ProfileModule extends PLModule
         $img  = $thumb ?
             dirname(__FILE__).'/../htdocs/images/medals/thumb/' . $res->fetchOneCell() :
             dirname(__FILE__).'/../htdocs/images/medals/' . $res->fetchOneCell();
-        $type = mime_content_type($img);
-        header("Content-Type: $type");
+        pl_cached_content_headers(mime_content_type($img));
         echo file_get_contents($img);
         exit;
     }
@@ -363,22 +362,14 @@ class ProfileModule extends PLModule
 
     function handler_applis_js(&$page)
     {
-        header('Content-Type: text/javascript; charset=utf-8');
-        header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
-        header('Last-Modified:' . gmdate('D, d M Y H:i:s') . ' GMT');
-        header('Cache-Control: no-cache, must-revalidate');
-        header('Pragma: no-cache');
+        pl_cached_content_headers("text/javascript", "utf-8");
         $page->changeTpl('profile/applis.js.tpl', NO_SKIN);
         require_once "applis.func.inc.php";
     }
 
     function handler_grades_js(&$page)
     {
-        header('Content-Type: text/javascript; charset=utf-8');
-        header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
-        header('Last-Modified:' . gmdate('D, d M Y H:i:s') . ' GMT');
-        header('Cache-Control: no-cache, must-revalidate');
-        header('Pragma: no-cache');
+        pl_cached_content_headers("text/javascript", "utf-8");
         $page->changeTpl('profile/grades.js.tpl', NO_SKIN);
         $res    = XDB::iterator("SELECT  *
                                    FROM  profile_medals_grades
@@ -401,7 +392,7 @@ class ProfileModule extends PLModule
 
     function handler_ajax_address(&$page, $adid)
     {
-        header('Content-Type: text/html; charset=utf-8');
+        pl_content_headers("text/html");
         $page->changeTpl('profile/adresses.address.tpl', NO_SKIN);
         $page->assign('i', $adid);
         $page->assign('adr', array());
@@ -409,7 +400,7 @@ class ProfileModule extends PLModule
 
     function handler_ajax_tel(&$page, $adid, $telid)
     {
-        header('Content-Type: text/html; charset=utf-8');
+        pl_content_headers("text/html");
         $page->changeTpl('profile/adresses.tel.tpl', NO_SKIN);
         $page->assign('i', $adid);
         $page->assign('adid', "addresses_$adid");
@@ -420,7 +411,7 @@ class ProfileModule extends PLModule
 
     function handler_ajax_medal(&$page, $id)
     {
-        header('Content-Type: text/html; charset=utf-8');
+        pl_content_headers("text/html");
         $page->changeTpl('profile/deco.medal.tpl', NO_SKIN);
         $page->assign('id', $id);
         $page->assign('medal', array('valid' => 0, 'grade' => 0));
@@ -428,7 +419,7 @@ class ProfileModule extends PLModule
 
     function handler_ajax_job(&$page, $id)
     {
-        header('Content-Type: text/html; charset=utf-8');
+        pl_content_headers("text/html");
         $page->changeTpl('profile/jobs.job.tpl', NO_SKIN);
         $page->assign('i', $id);
         $page->assign('job', array());
@@ -444,7 +435,7 @@ class ProfileModule extends PLModule
 
     function handler_ajax_secteur(&$page, $id, $sect, $ssect = -1)
     {
-        header('Content-Type: text/html; charset=utf-8');
+        pl_content_headers("text/html");
         $res = XDB::iterator("SELECT  id, label
                                 FROM  emploi_ss_secteur
                                WHERE  secteur = {?}", $sect);
@@ -456,7 +447,7 @@ class ProfileModule extends PLModule
 
     function handler_ajax_skill(&$page, $cat, $id)
     {
-        header('Content-Type: text/html; charset=utf-8');
+        pl_content_headers("text/html");
         $page->changeTpl('profile/skill.skill.tpl', NO_SKIN);
         $page->assign('cat', $cat);
         $page->assign('id', $id);
@@ -630,7 +621,7 @@ class ProfileModule extends PLModule
 
     function handler_ref_sect(&$page, $sect)
     {
-        header('Content-Type: text/html; charset=utf-8');
+        pl_content_headers("text/html");
         $page->changeTpl('include/field.select.tpl', NO_SKIN);
         $page->assign('onchange', 'setSSecteurs()');
         $page->assign('id', 'ssect_field');
@@ -643,7 +634,7 @@ class ProfileModule extends PLModule
 
     function handler_ref_country(&$page, $sect, $ssect = '')
     {
-        header('Content-Type: text/html; charset=utf-8');
+        pl_content_headers("text/html");
         $page->changeTpl('include/field.select.tpl', NO_SKIN);
         $page->assign('name', 'pays_sel');
         $where = ($ssect ? ' AND ms.ss_secteur = {?}' : '');
@@ -726,10 +717,10 @@ class ProfileModule extends PLModule
         list($logo, $logo_mime) = $res->fetchOneRow();
 
         if (!empty($logo)) {
-            header("Content-type: $mime");
+            pl_cached_dynamic_content_headers($logo_mime);
             echo $logo;
         } else {
-            header('Content-type: image/jpeg');
+            pl_cached_dynamic_content_headers("image/jpeg");
             readfile(dirname(__FILE__) . '/../htdocs/images/dflt_carre.jpg');
         }
 
@@ -765,10 +756,9 @@ class ProfileModule extends PLModule
 
         switch ($action) {
             case "original":
-                header("Content-type: image/jpeg");
+                pl_cached_content_headers("image/jpeg");
                readfile("/home/web/trombino/photos" . $user->promo() . "/" . $user->login() . ".jpg");
                 exit;
-               break;
 
             case "new":
                 S::assert_xsrf_token();
index 2912a49..5520042 100644 (file)
@@ -217,7 +217,7 @@ class SearchModule extends PLModule
         //   result1|nb1
         //   result2|nb2
         //   ...
-        header('Content-Type: text/plain; charset="UTF-8"');
+        pl_content_headers("text/plain");
         $q = preg_replace(array('/\*+$/', // always look for $q*
                                 '/([\^\$\[\]])/', // escape special regexp char
                                 '/\*/'), // replace joker by regexp joker
@@ -419,7 +419,7 @@ class SearchModule extends PLModule
             $field = '`fonction_fr`';
             break;
           case 'diploma':
-            header('Content-Type: text/xml; charset="UTF-8"');
+            pl_content_headers("text/xml");
             $this->get_diplomas();
             $page->changeTpl('search/adv.grade.form.tpl', NO_SKIN);
             return;
@@ -456,12 +456,12 @@ class SearchModule extends PLModule
           default: exit();
         }
         if (isset($idVal)) {
-            header('Content-Type: text/plain; charset="UTF-8"');
+            pl_content_headers("text/plain");
             $result = XDB::query('SELECT '.$field.' AS field FROM '.$db.' WHERE '.$id.' = {?} LIMIT 1',$idVal);
             echo $result->fetchOneCell();
             exit();
         }
-        header('Content-Type: text/xml; charset="UTF-8"');
+        pl_content_headers("text/xml");
         $page->changeTpl('include/field.select.tpl', NO_SKIN);
         $page->assign('name', $type);
         $page->assign('list', XDB::iterator('SELECT  '.$field.' AS field,
index 2a74184..85ee054 100644 (file)
@@ -91,7 +91,7 @@ class StatsModule extends PLModule
         }
 
         //Genere le graphique à la volée avec GNUPLOT
-        header( "Content-type: image/png");
+        pl_cached_dynamic_content_headers("image/png");
 
         $delt = ($total - $init_nb)/10;
         $delt = $delt ? $delt : 5;
@@ -236,7 +236,7 @@ EOF
 EOF2;
         }
 
-        header('Content-type: image/png');
+        pl_cached_dynamic_content_headers("image/png");
         passthru($gnuplot);
         exit;
     }
index 3ce8541..199d96d 100644 (file)
@@ -109,7 +109,7 @@ class SurveyModule extends PLModule
             return PL_DO_AUTH;
         }
         if ($show == 'csv') {
-            header('Content-Type: text/csv; charset="UTF-8"');
+            pl_content_headers("text/csv");
             echo $survey->toCSV();
             exit;
         } else {
@@ -364,7 +364,7 @@ class SurveyModule extends PLModule
     function handler_ajax(&$page, $type)
     {
         $this->load('survey.inc.php');
-        header('Content-Type: text/html; charset="UTF-8"');
+        pl_content_headers("text/html");
         if (Survey::isType($type)) { // when type has been chosen, the form is updated to fit exactly the type of question chosen
             $page->changeTpl('survey/edit_new.tpl', NO_SKIN);
             $page->assign('survey_types', Survey::getTypes());
index 87be73a..0152bdb 100644 (file)
@@ -48,10 +48,10 @@ class XnetModule extends PLModule
                             WHERE alias = {?}", $x);
 
         if ((list($type, $data) = $res->fetchOneRow())) {
-            Header("Content-type: image/$type");
+            pl_cached_dynamic_content_headers("image/$type");
             echo $data;
         } else {
-            Header('Content-type: image/png');
+            pl_cached_dynamic_content_headers("image/png");
             echo file_get_contents(dirname(__FILE__).'/../htdocs/images/none.png');
         }
         exit;
@@ -219,7 +219,7 @@ class XnetModule extends PLModule
         $allkeys = func_get_args();
         unset($allkeys[0]);
         $url = join('/',$allkeys);
-        header("Content-type: text/javascript; charset=utf-8");
+        pl_content_headers("text/javascript");
         echo '$.ajax({ url: "'.$url.'?forceXml=1", dataType: "xml", success: function(xml) { $("body",xml).insertBefore("body"); $("body:eq(1)").remove(); }});';
         exit;
     }
index ebde459..3d21fe4 100644 (file)
@@ -290,10 +290,7 @@ class XnetEventsModule extends PLModule
             return PL_NOT_FOUND;
         }
 
-        header('Content-type: text/x-csv; encoding=UTF-8');
-        header('Pragma: ');
-        header('Cache-Control: ');
-
+        pl_content_headers("text/x-csv");
         $page->changeTpl('xnetevents/csv.tpl', NO_SKIN);
 
         $admin = may_update();
@@ -339,7 +336,7 @@ class XnetEventsModule extends PLModule
         $page->register_function('display_ical', 'display_ical');
         $page->assign_by_ref('e', $evt);
 
-        header('Content-Type: text/calendar; charset=utf-8');
+        pl_content_headers("text/calendar");
     }
 
     function handler_edit(&$page, $eid = null)
index 4454a63..187151d 100644 (file)
@@ -186,22 +186,12 @@ class XnetGrpModule extends PLModule
                           $globals->asso('id'));
         list($logo, $logo_mime) = $res->fetchOneRow();
 
+        pl_cached_dynamic_content_headers(empty($logo) ? "image/jpeg" : $logo_mime);
         if (!empty($logo)) {
-            header("Content-type: $mime");
-            header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
-            header('Last-Modified:' . gmdate('D, d M Y H:i:s') . ' GMT');
-            header('Cache-Control: no-cache, must-revalidate');
-            header('Pragma: no-cache');
             echo $logo;
         } else {
-            header('Content-type: image/jpeg');
-            header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
-            header('Last-Modified:' . gmdate('D, d M Y H:i:s') . ' GMT');
-            header('Cache-Control: no-cache, must-revalidate');
-            header('Pragma: no-cache');
-            readfile(dirname(__FILE__).'/../htdocs/images/dflt_carre.jpg');
+            readfile(dirname(__FILE__) . '/../htdocs/images/dflt_carre.jpg');
         }
-
         exit;
     }
 
@@ -507,9 +497,7 @@ class XnetGrpModule extends PLModule
                  GROUP BY  m.uid
                  ORDER BY  nom, prenom",
                  $globals->mail->domain, $globals->asso('id'));
-        header('Content-Type: text/x-csv; charset=utf-8;');
-        header('Pragma: ');
-        header('Cache-Control: ');
+        pl_content_headers("text/x-csv");
         $page->changeTpl('xnetgrp/annuaire-csv.tpl', NO_SKIN);
         $page->assign('ann', $ann);
     }
@@ -831,7 +819,7 @@ class XnetGrpModule extends PLModule
 
     function handler_admin_member_new_ajax(&$page)
     {
-        header('Content-Type: text/html; charset="UTF-8"');
+        pl_content_headers("text/html");
         $page->changeTpl('xnetgrp/membres-new-search.tpl', NO_SKIN);
         $res = null;
         if (Env::has('login')) {
@@ -1195,20 +1183,20 @@ class XnetGrpModule extends PLModule
             $res = XDB::query("SELECT * FROM groupex.announces_photo WHERE eid = {?}", $eid);
             if ($res->numRows()) {
                 $photo = $res->fetchOneAssoc();
-                header('Content-Type: image/' . $photo['attachmime']);
+                pl_cached_dynamic_content_headers("image/" . $photo['attachmime']);
                 echo $photo['attach'];
                 exit;
             }
         } else {
             $upload = new PlUpload(S::user()->login(), 'xnetannounce');
             if ($upload->exists() && $upload->isType('image')) {
-                header('Content-Type: ' . $upload->contentType());
+                pl_cached_dynamic_content_headers($upload->contentType());
                 echo $upload->getContents();
                 exit;
             }
         }
         global $globals;
-        header('Content-Type: image/png');
+        pl_cached_dynamic_content_headers("image/png");
         echo file_get_contents($globals->spoolroot . '/htdocs/images/logo.png');
         exit;
     }