} // // 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);
// 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'));
/** * 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); }
/** * 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); } } } }
/** * 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(); }
/** * 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>'; } }
$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']) {
$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')));
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();