Beispiel #1
0
}
//
// daily jobs
//
echo 'Checking daily jobs...' . BR;
// get date of last run
$record = Values::get_record('cron.daily', NULL_DATE);
// wait at least 1 day = 86400 seconds between runs
if (isset($record['edit_date'])) {
    $target = SQL::strtotime($record['edit_date']) + 86400;
} else {
    $target = time();
}
// request to be delayed
if ($target > time()) {
    echo 'Wait until ' . gmdate('r', $target) . ' GMT' . BR;
} else {
    Values::set('cron.daily', 'running...');
    // do the job and provide feed-back to user
    $context['text'] = Hooks::include_scripts('daily');
    echo $context['text'];
    // remember tick date and resulting text
    Values::set('cron.daily', $context['text']);
    // log outcome of script execution in debug mode
    if ($context['with_debug'] == 'Y') {
        Logger::remember('cron.php: daily processing', $context['text'], 'debug');
    }
}
// all done
$time = round(get_micro_time() - $context['start_time'], 2);
exit(sprintf('Script terminated in %.2f seconds.', $time) . BR);
Beispiel #2
0
                // we have an array to format
                if (is_array($items)) {
                    $items = Skin::build_list($items, '2-columns');
                }
                // displayed as another page section
                $text .= Skin::build_box(i18n::s('Other sections'), $items, 'header1', 'other_sections');
            }
        }
        // cache this to speed subsequent queries
        Cache::put($cache_id, $text, 'sections');
    }
    $context['text'] .= $text;
}
// the suffix hook for the site map page
if (is_callable(array('Hooks', 'include_scripts'))) {
    $context['text'] .= Hooks::include_scripts('sections/index.php#suffix');
}
// page tools
if (Surfer::is_associate()) {
    $context['page_tools'][] = Skin::build_link('sections/edit.php', i18n::s('Add a section'));
    $context['page_tools'][] = Skin::build_link('help/populate.php', i18n::s('Content Assistant'));
    $context['page_tools'][] = Skin::build_link('sections/check.php', i18n::s('Maintenance'));
}
// display extra information
$cache_id = 'sections/index.php#extra';
if (!($text = Cache::get($cache_id))) {
    // see also
    $lines = array();
    $lines[] = Skin::build_link('categories/', i18n::s('Categories'));
    $lines[] = Skin::build_link('search.php', i18n::s('Search'));
    $lines[] = Skin::build_link('help/', i18n::s('Help index'));
Beispiel #3
0
 /**
  * do whatever is necessary when a page has been updated
  *
  * This function:
  * - logs the update
  * - sends notification to watchers and to followers
  * - "touches" the container of the page,
  * - ping referred pages remotely (via the pingback protocol)
  * - ping selected servers, if any
  * - and triggers the hook 'update'.
  *
  * The first parameter provides the watching context to consider. If call is related
  * to the creation of a published page, the context is the section that hosts the new
  * page. If call is related to a draft page that has been published, then the context
  * is the page itself.
  *
  * This function is also able to notify followers of the surfer who has initiated the
  * action.
  *
  * @param object the watching context
  * @param array attributes of the published page
  * @param object page overlay, if any
  * @param boolean TRUE if dates should be left unchanged, FALSE otherwise
  * @param boolean TRUE if watchers should be notified, FALSE otherwise
  * @param boolean TRUE if followers should be notified, FALSE otherwise
  */
 public static function finalize_update($anchor, $item, $overlay = NULL, $silently = FALSE, $with_watchers = TRUE, $with_followers = FALSE)
 {
     global $context;
     // proceed only if the page has been published
     if (isset($item['publish_date']) && $item['publish_date'] > NULL_DATE) {
         // notification to send by e-mail
         $mail = array();
         $mail['subject'] = sprintf(i18n::c('%s: %s'), i18n::c('Update'), strip_tags($item['title']));
         $mail['notification'] = Articles::build_notification('update', $item);
         $mail['headers'] = Mailer::set_thread('article:' . $item['id']);
         // allow the overlay to prevent notifications of watcherss
         if (is_object($overlay) && !$overlay->should_notify_watchers($mail)) {
             $with_watchers = FALSE;
         }
         // send to watchers of this page, and to watchers upwards
         if ($with_watchers && ($handle = new Article())) {
             $handle->load_by_content($item, $anchor);
             $handle->alert_watchers($mail, 'article:update', $item['active'] == 'N');
         }
         // never notify followers on private pages
         if (isset($item['active']) && $item['active'] == 'N') {
             $with_followers = FALSE;
         }
         // allow the overlay to prevent notifications of followers
         if (is_object($overlay) && !$overlay->should_notify_followers()) {
             $with_followers = FALSE;
         }
         // send to followers of this user
         if ($with_followers && Surfer::get_id()) {
             $mail['message'] = Mailer::build_notification($mail['notification'], 2);
             Users::alert_watchers('user:'******'article:update', $item['id'], $silently);
         // advertise public pages
         if (isset($item['active']) && $item['active'] == 'Y') {
             // expose links within the page
             $raw = '';
             if (isset($item['introduction'])) {
                 $raw .= $item['introduction'];
             }
             if (isset($item['source'])) {
                 $raw .= ' ' . $item['source'];
             }
             if (isset($item['description'])) {
                 $raw .= ' ' . $item['description'];
             }
             // pingback to referred links, if any
             include_once $context['path_to_root'] . 'links/links.php';
             Links::ping($raw, 'article:' . $item['id']);
             // ping servers, if any
             Servers::notify($anchor->get_url());
         }
     }
     // 'update' hook
     if (is_callable(array('Hooks', 'include_scripts'))) {
         Hooks::include_scripts('update', $item['id']);
     }
     // log page update
     $label = sprintf(i18n::c('Update: %s'), strip_tags($item['title']));
     $poster = Users::get_link($item['edit_name'], $item['edit_address'], $item['edit_id']);
     $description = sprintf(i18n::c('Updated by %s in %s'), $poster, $anchor->get_title());
     $description .= "\n\n" . '<a href="' . Articles::get_permalink($item) . '">' . $item['title'] . '</a>';
     Logger::notify('articles/articles.php: ' . $label, $description);
 }
Beispiel #4
0
 /**
  * process one message
  *
  * This function looks for a target anchor in message title. It also performs some security
  * checks before accepting actual submission.
  *
  * The subject line may indicate the container for the post, as per following patterns:
  * - [#1234] - a comment to be appended to article 1234
  * - [#s1234] - a page to be created in section 1234
  *
  * If no anchor is defined in message title, then the section id defined in queue parameters is
  * used. If no section has been defined for the queue, then a new page is created in the default
  * section.
  *
  * @param string message raw content
  */
 public static function process_message($entity)
 {
     global $context;
     // sanity check
     if (!trim($entity)) {
         return NULL;
     }
     // retrieve queue parameters
     list($server, $account, $password, $allowed, $match, $section, $options, $hooks, $prefix, $suffix) = $context['mail_queue'];
     // split headers and body
     if (!($position = strpos($entity, "\n\n"))) {
         Logger::remember('agents/messages.php: Can not split header and body', $entity);
         return NULL;
     }
     $message_headers = substr($entity, 0, $position);
     // security match
     if ($match && !preg_match('/' . preg_quote($match, '/') . '/i', $message_headers)) {
         Logger::remember('agents/messages.php: Message does not match /' . preg_quote($match, '/') . '/');
         return NULL;
     }
     // parse and decode all headers
     $message_headers = Messages::parse_headers($message_headers);
     // identify message sender
     $post_sender = NULL;
     foreach ($message_headers as $header) {
         if (preg_match('/From/i', $header['name'])) {
             $post_sender = $header['value'];
             break;
         }
     }
     // use only the mail address
     if (preg_match('/^[^<>]*<([^<>]+)>/', $post_sender, $matches)) {
         $post_sender = $matches[1];
     }
     // no poster
     if (!$post_sender) {
         Logger::remember('agents/messages.php: No poster address');
         return NULL;
     }
     // ensure poster is allowed to move forward
     $granted = FALSE;
     // maybe the sender has been recorded
     $user = Users::get($post_sender);
     // the address is in the list of allowed addresses, including anyone
     if ($allowed && preg_match('/\\b(' . preg_quote($post_sender, '/') . '|anyone)\\b/i', $allowed)) {
         $granted = TRUE;
         // email addresses not present in the database are allowed
         if (!$user['id']) {
             list($user['nick_name'], $domain) = explode('@', $post_sender);
             $user['id'] = 0;
             $user['email'] = $post_sender;
             $user['capability'] = 'M';
         }
         // the poster has to be recorded in the database
     } elseif (!$user['id']) {
         Logger::remember('agents/messages.php: Unknown poster address ' . $post_sender);
         return NULL;
         // maybe subscribers are allowed to post here
     } elseif ($user['capability'] == 'S' && $allowed && preg_match('/\\bany_subscriber\\b/i', $allowed)) {
         $granted = TRUE;
     } elseif ($user['capability'] == 'M' && $allowed && preg_match('/\\bany_member\\b/i', $allowed)) {
         $granted = TRUE;
     } elseif ($user['capability'] != 'A') {
         Logger::remember('agents/messages.php: Poster ' . $post_sender . ' is not allowed to contribute by e-mail');
         return NULL;
     }
     // message post date
     $context['mail_date'] = gmdate('D, j M Y G:i:s') . ' GMT';
     foreach ($message_headers as $header) {
         if (preg_match('/Date/i', $header['name'])) {
             $context['mail_date'] = $header['value'];
             break;
         }
     }
     // process message subject line --set in $context['mail_subject']
     $context['mail_subject'] = i18n::c('Item sent by e-mail');
     $anchor = NULL;
     foreach ($message_headers as $header) {
         if (preg_match('/Subject/i', $header['name'])) {
             $context['mail_subject'] = $header['value'];
             if (preg_match('/\\[#(a|s)*([0-9]+)\\]/', $header['value'], $matches)) {
                 $context['mail_subject'] = preg_replace('/' . preg_quote($matches[0], '/') . '/', '', $header['value']);
                 if (!$matches[1] || $matches[1] == 'a') {
                     $anchor = 'article:' . $matches[2];
                 } else {
                     $anchor = 'section:' . $matches[2];
                 }
             }
             break;
         }
     }
     // anchor is defined in queue parameters
     if (!$anchor && $section) {
         $anchor = 'section:' . $section;
     }
     // use default section
     if (!$anchor && ($section = Sections::get_default())) {
         $anchor = 'section:' . $section;
     }
     // no anchor to use
     if (!$anchor) {
         Logger::remember('agents/messages.php: No anchor has been found');
         return NULL;
     }
     // do the job
     include_once $context['path_to_root'] . 'comments/comments.php';
     if ($reference = Messages::process_entity($entity, $user, $anchor, NULL)) {
         // trigger hooks
         if (is_callable(array('Hooks', 'include_scripts'))) {
             // set hook parameters -- $context['mail_queue'] has queue attributes
             $context['mail_headers'] = $message_headers;
             $context['mail_body'] = substr($entity, $position + 2);
             $context['mail_poster'] = $user;
             $context['mail_anchor'] = $anchor;
             $context['mail_reference'] = $reference;
             // insert 'inbound-mail' in hooks to call
             $hooks = trim('inbound-mail ' . $hooks);
             // trigger each hook one by one
             $hooks = preg_split('/[\\s,]+/', $hooks, -1, PREG_SPLIT_NO_EMPTY);
             foreach ($hooks as $hook) {
                 Hooks::include_scripts($hook);
             }
         }
     }
 }
Beispiel #5
0
 /**
  * duplicate items related to one anchor
  *
  * This function is invoked  when some anchor is duplicated.
  *
  * Note: do not refer here to objects that will be duplicated through
  * overlays, such as Dates.
  *
  * The duplicate_related_to hook is used to invoke any software extension bound as follows:
  * - id: 'shared/anchors.php#duplicate_related_to'
  * - type: 'include'
  * - parameters: array of strings referencing origin and target anchors
  *
  * @param string reference of the source anchor (e.g., 'article:12')
  * @param string reference of the target anchor (e.g., 'article:12')
  */
 public static function duplicate_related_to($from_anchor, $to_anchor)
 {
     global $context;
     // duplicate related articles
     Articles::duplicate_for_anchor($from_anchor, $to_anchor);
     // duplicate related categories
     Categories::duplicate_for_anchor($from_anchor, $to_anchor);
     // duplicate related comments
     include_once $context['path_to_root'] . 'comments/comments.php';
     Comments::duplicate_for_anchor($from_anchor, $to_anchor);
     // do not duplicate related dates -- this will be done through overlays
     // duplicate related files
     Files::duplicate_for_anchor($from_anchor, $to_anchor);
     // duplicate related images
     include_once $context['path_to_root'] . 'images/images.php';
     Images::duplicate_for_anchor($from_anchor, $to_anchor);
     // duplicate related links
     include_once $context['path_to_root'] . 'links/links.php';
     Links::duplicate_for_anchor($from_anchor, $to_anchor);
     // duplicate related locations
     include_once $context['path_to_root'] . 'locations/locations.php';
     Locations::duplicate_for_anchor($from_anchor, $to_anchor);
     // duplicate related sections
     Sections::duplicate_for_anchor($from_anchor, $to_anchor);
     // duplicate related tables
     include_once $context['path_to_root'] . 'tables/tables.php';
     Tables::duplicate_for_anchor($from_anchor, $to_anchor);
     // duplicate memberships for this anchor
     Members::duplicate_for($from_anchor, $to_anchor);
     // the duplicate_related_to hook
     if (is_callable(array('Hooks', 'include_scripts'))) {
         $context['text'] .= Hooks::include_scripts('shared/anchors.php#duplicate_related_to', array($from_anchor, $to_anchor));
     }
     // clear the cache as well
     Cache::clear();
 }
Beispiel #6
0
/**
 * dynamically generate the page
 *
 * @see skins/index.php
 */
function send_body()
{
    global $context, $action;
    // check that the user is an admin, but only if there is at least one user record
    $query = "SELECT count(*) FROM " . SQL::table_name('users');
    if (!Surfer::is_associate() && SQL::query($query) !== FALSE) {
        Safe::header('Status: 401 Unauthorized', TRUE, 401);
        echo '<p>' . i18n::s('You are not allowed to perform this operation.') . "</p>\n";
        return;
    }
    // log the current surfer as an associate if not yet the case
    if (!Surfer::is_associate()) {
        $fields = array();
        $fields['id'] = 1;
        $fields['nick_name'] = 'admin';
        $fields['email'] = '';
        $fields['capability'] = 'A';
        Surfer::set($fields);
        echo '<p>' . i18n::s('You have associate privilege') . '</p>';
    }
    // check every table of the database
    if ($action == 'build') {
        // maybe we will have to switch the server off
        $temporary_off = FALSE;
        // ensure nobody else will access the database during the operation
        if (file_exists('../parameters/switch.on')) {
            if (Safe::rename($context['path_to_root'] . 'parameters/switch.on', $context['path_to_root'] . 'parameters/switch.off')) {
                echo BR . i18n::s('The server has been switched off.');
                $temporary_off = TRUE;
            }
            // let concurrent on-going transactions finish properly
            Safe::sleep(3);
            // first installation
        } elseif (!file_exists('../parameters/switch.off')) {
            echo '<p>' . i18n::s('Review provided information and go to the bottom of the page to move forward.') . "</a></p>\n";
        }
        // ensure utf8 character set for this database
        $query = "ALTER DATABASE `" . $context['database'] . "`  DEFAULT CHARACTER SET utf8";
        SQL::query($query);
        // create tables for users
        echo Users::setup();
        // create tables for activities
        echo Activities::setup();
        // create tables for notifications
        include_once '../users/notifications.php';
        echo Notifications::setup();
        // create tables for messages
        echo Mailer::setup();
        // create tables for visits
        include_once '../users/visits.php';
        echo Visits::setup();
        // create tables for sections
        echo Sections::setup();
        // create tables for articles
        echo Articles::setup();
        // create tables for images
        include_once '../images/images.php';
        echo Images::setup();
        // create tables for tables
        include_once '../tables/tables.php';
        echo Tables::setup();
        // create tables for files
        echo Files::setup();
        // create tables for links
        include_once '../links/links.php';
        echo Links::setup();
        // create tables for locations
        include_once '../locations/locations.php';
        echo Locations::setup();
        // create tables for comments
        include_once '../comments/comments.php';
        echo Comments::setup();
        // create tables for categories
        echo Categories::setup();
        // create tables for members
        include_once '../shared/members.php';
        echo Members::setup();
        // create tables for dates
        include_once '../dates/dates.php';
        echo Dates::setup();
        // create tables for servers
        include_once '../servers/servers.php';
        echo Servers::setup();
        // create tables for versions
        include_once '../versions/versions.php';
        echo Versions::setup();
        // create tables for enrolments
        include_once '../shared/enrolments.php';
        echo Enrolments::setup();
        // create tables for values
        include_once '../shared/values.php';
        echo Values::setup();
        // create tables for the cache
        echo Cache::setup();
        // create tables for the php documentation
        include_once '../scripts/phpdoc.php';
        echo PhpDoc::setup();
        // the setup hook
        if (is_callable(array('Hooks', 'include_scripts'))) {
            echo Hooks::include_scripts('control/setup.php');
        }
        // reopen the server for others
        if ($temporary_off && Safe::rename($context['path_to_root'] . 'parameters/switch.off', $context['path_to_root'] . 'parameters/switch.on')) {
            echo '<p>' . i18n::s('The server has been switched on.') . '</p>';
        }
        // in the middle of an update
        if (file_exists('../parameters/switch.off')) {
            echo Skin::build_block('<form method="get" action="../scripts/run_once.php">' . "\n" . '<p class="assistant_bar">' . Skin::build_submit_button(i18n::s('Run one-time scripts and go to the Control Panel')) . '</p>' . "\n" . '</form>', 'bottom');
            // this may take several minutes
            echo '<p>' . i18n::s('When you will click on the button the server will be immediately requested to proceed. However, because of the so many things to do on the back-end, you may have to wait for minutes before getting a response displayed. Thank you for your patience.') . '</p>';
            // populate the database on first installation
        } elseif (!file_exists('../parameters/switch.on')) {
            echo Skin::build_block('<form method="get" action="populate.php">' . "\n" . '<p class="assistant_bar">' . Skin::build_submit_button(i18n::s('Initialize the database')) . '</p>' . "\n" . '</form>', 'bottom');
            // or back to the control panel
        } else {
            $menu = array('control/' => i18n::s('Control Panel'));
            echo Skin::build_list($menu, 'menu_bar');
        }
        // clear the cache
        Cache::clear();
        // remember the change
        $label = i18n::c('The database has been optimised');
        Logger::remember('control/setup.php: ' . $label);
        // ask for confirmation
    } else {
        // the splash message
        echo '<p>' . i18n::s('This script will check the structure of the database and optimize data storage:') . '</p>' . "\n" . '<ul>' . "\n" . '<li>' . i18n::s('Missing tables will be created, if necessary.') . '</li>' . "\n" . '<li>' . i18n::s('Some columns may be created or converted if their type has evolved.') . '</li>' . "\n" . '<li>' . i18n::s('All indexes will be (re)built.') . '</li>' . "\n" . '<li>' . i18n::s('Data files will be optimized as well.') . '</li>' . "\n" . '</ul>' . "\n";
        // the submit button
        echo '<form method="post" action="' . $context['script_url'] . '" id="main_form"><p>' . Skin::build_submit_button(i18n::s('Ensure the database structure is accurate'), NULL, NULL, 'confirmed') . '<input type="hidden" name="action" value="build" />' . '</p></form>';
        // the script used for form handling at the browser
        Page::insert_script('$("#confirmed").focus();');
        // this may take several minutes
        echo '<p>' . i18n::s('When you will click on the button the server will be immediately requested to proceed. However, because of the so many things to do on the back-end, you may have to wait for minutes before getting a response displayed. Thank you for your patience.') . '</p>';
    }
}
Beispiel #7
0
     $with_form = TRUE;
     $item = $_REQUEST;
     $item['password'] = $item['confirm'];
     // password has been md5 in Users::post()
     unset($item['id']);
     // successful post
 } else {
     // post an overlay, with the new user id
     if (is_object($overlay) && !$overlay->remember('insert', $_REQUEST, 'user:'******'id'])) {
         $item = $_REQUEST;
         $with_form = TRUE;
         // thanks
     } else {
         // 'users/edit.php#post' hook
         if (is_callable(array('Hooks', 'include_scripts'))) {
             Hooks::include_scripts('users/edit.php#post', $_REQUEST['id']);
         }
         // associates are redirected to the new user page
         if (Surfer::is_associate()) {
             Safe::redirect(Users::get_permalink($_REQUEST));
         } else {
             // get the new record
             $item = Users::get($_REQUEST['id'], TRUE);
             // the welcome page
             $context['page_title'] = i18n::s('Welcome!');
             // the welcome message
             $context['text'] .= '<p>' . ucfirst($item['nick_name']) . ',</p>' . '<p>' . i18n::s('You are now a registered user of this community. Each time you will visit this site, please provide your nick name and password to authenticate.') . '</p>';
             // follow-up commands
             $follow_up = i18n::s('What do you want to do now?');
             // just proceed
             if (isset($_REQUEST['forward']) && $_REQUEST['forward']) {
Beispiel #8
0
     $fields['publish_date'] = gmstrftime('%Y-%m-%d %H:%M:%S');
     $fields['thumbnail_url'] = $context['url_to_home'] . $context['url_to_root'] . 'skins/_reference/thumbnails/page.gif';
     if (Articles::post($fields)) {
         $text .= sprintf(i18n::s('A page "%s" has been created.'), $fields['title']) . BR . "\n";
     } else {
         $text .= Logger::error_pop() . BR . "\n";
     }
 }
 // nothing added
 if (!$text) {
     $text = i18n::s('No item has been added');
 }
 // report to surfer
 $context['text'] .= Skin::build_box(i18n::s('Pages'), $text);
 // the populate hook
 if (is_callable(array('Hooks', 'include_scripts')) && ($text = Hooks::include_scripts('control/populate.php'))) {
     $context['text'] .= Skin::build_box(i18n::s('Extensions'), $text);
 }
 // script has ben included
 if (isset($context['populate_follow_up']) && $context['populate_follow_up'] == 'none') {
 } elseif (!file_exists('../parameters/switch.on') && !file_exists('../parameters/switch.off')) {
     $context['text'] .= Skin::build_block('<form method="get" action="../skins/configure.php">' . "\n" . '<p class="assistant_bar">' . Skin::build_submit_button(i18n::s('Configure the page factory')) . '</p>' . "\n" . '</form>', 'bottom');
     // or back to the control panel
 } else {
     // follow-up commands
     $context['text'] .= '<h3>' . i18n::s('What do you want to do now?') . '</h3>';
     // follow-up commands
     $menu = array();
     $menu = array_merge($menu, array('sections/' => i18n::s('Check the updated Site Map')));
     $menu = array_merge($menu, array('help/populate.php' => i18n::s('Launch the Content Assistant again')));
     $menu = array_merge($menu, array('control/' => i18n::s('Control Panel')));
Beispiel #9
0
if (Surfer::is_associate()) {
    $context['page_details'] .= '<p class="details">' . sprintf(i18n::s('Edited %s'), Skin::build_date(getlastmod())) . '</p>';
}
// the prefix hook for the help page
if (is_callable(array('Hooks', 'include_scripts'))) {
    $context['text'] .= Hooks::include_scripts('help/index.php#prefix');
}
// where to look for information
$context['text'] .= Skin::build_block(i18n::s('Where to look for information?'), 'title') . '<ul>' . '<li>' . sprintf(i18n::s('%s and change it if you like.'), Skin::build_link('users/view.php', i18n::s('Review your profile'))) . '</li>' . '<li>' . sprintf(i18n::s('%s of this site.'), Skin::build_link($context['url_to_root'], i18n::s('Go to the front page'))) . '</li>' . '<li>' . Skin::build_link('sections/', i18n::s('Site map')) . '</li>' . '<li>' . Skin::build_link('categories/', i18n::s('Categories')) . '</li>' . '<li>' . sprintf(i18n::s('Index of most recent %1$s, %2$s, %3$s and %4$s'), Skin::build_link('articles/', i18n::s('pages')), Skin::build_link('files/', i18n::s('files')), Skin::build_link('comments/', i18n::s('threads')), Skin::build_link('users/', i18n::s('people'))) . '</li>' . '<li>' . Skin::build_link('search.php', i18n::s('Full-text search')) . '</li>';
if (!Surfer::is_logged() && (!isset($context['users_without_registration']) || $context['users_without_registration'] != 'Y')) {
    $context['text'] .= '<li> ' . sprintf(i18n::s('%s to access more material, and to receive our newsletters'), Skin::build_link('users/edit.php', i18n::s('Register'))) . '</li>';
}
$context['text'] .= '</ul>';
// everybody, but subscriptors, may contribute
if (!Surfer::is_logged() || Surfer::is_member()) {
    // how to format text in pages
    $context['text'] .= Skin::build_block(i18n::s('How to format text in pages?'), 'title') . i18n::s('<p>To ease the production of text YACS has a little code interpreter. You will find in pages listed below descriptions of these codes, but also examples of their visual rendering on your system.</p>') . '<ul>' . '<li>' . Skin::build_link('smileys/', i18n::s('Smileys available at this system')) . '</li>' . '<li>' . sprintf(i18n::s('%s (bold, underline, ...)'), Skin::build_link('codes/basic.php', i18n::s('In-line'))) . '</li>' . '<li>' . sprintf(i18n::s('%s (and shortcuts, buttons, ...)'), Skin::build_link('codes/links.php', i18n::s('Links'))) . '</li>' . '<li>' . sprintf(i18n::s('%s (with bullets, numbered, ...)'), Skin::build_link('codes/lists.php', i18n::s('Lists'))) . '</li>' . '<li>' . sprintf(i18n::s('%s (indentation, script, quote, ...)'), Skin::build_link('codes/blocks.php', i18n::s('Blocks'))) . '</li>' . '<li>' . sprintf(i18n::s('%s (with headers, with grids, use CSV data, ...)'), Skin::build_link('codes/tables.php', i18n::s('Tables'))) . '</li>' . '<li>' . sprintf(i18n::s('%s (and table of content)'), Skin::build_link('codes/titles.php', i18n::s('Titles and questions'))) . '</li>' . '<li>' . sprintf(i18n::s('%s (updates, content, ...)'), Skin::build_link('codes/live.php', i18n::s('Dynamic queries'))) . '</li>' . '<li>' . sprintf(i18n::s('%s (twitter, calendar, ...)'), Skin::build_link('codes/widgets.php', i18n::s('Widgets'))) . '</li>' . '<li>' . sprintf(i18n::s('%s (charts, ...)'), Skin::build_link('codes/misc.php', i18n::s('Miscellaneous codes'))) . '</li>' . '</ul>';
}
// locate a reference server
Safe::load('parameters/scripts.include.php');
if (isset($context['reference_server']) && $context['reference_server']) {
    $target = 'http://' . $context['reference_server'] . '/';
} else {
    $target = 'http://www.yacs.fr/';
}
// the suffix hook for the help page
if (is_callable(array('Hooks', 'include_scripts'))) {
    $context['text'] .= Hooks::include_scripts('help/index.php#suffix');
}
// render the skin
render_skin();