/** * add one activity record * * @param array e.g., array('anchor' => 'article:123', 'action' => 'edit') * @return mixed some error array, or 'OK' */ function post($parameters) { global $context; // look for an anchor if (empty($parameters['anchor'])) { return array('code' => -32602, 'message' => 'Invalid param "anchor"'); } // look for an action if (empty($parameters['action'])) { return array('code' => -32602, 'message' => 'Invalid param "action"'); } // save this in the database Activities::post($parameters['anchor'], $parameters['action']); // done return 'OK'; }
/** * record a click * * @param string the external url that is targeted * */ public static function click($url) { global $context; // we record only GET requests if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] != 'GET') { return; } // do not count crawling if (isset($_SERVER['HTTP_USER_AGENT']) && preg_match('/(blo\\.gs|\\bblog|bot\\b|crawler\\b|frontier\\b|slurp\\b|spider\\b)/i', $_SERVER['HTTP_USER_AGENT'])) { return; } // record the activity Activities::post($url, 'click'); // do not record clicks driving to search engines if (preg_match('/\\b(google|yahoo)\\b/i', $url)) { return; } // if this url is known $query = "SELECT * FROM " . SQL::table_name('links') . " AS links" . " WHERE links.link_url LIKE '" . SQL::escape($url) . "'"; if ($item = SQL::query_first($query)) { // increment the number of clicks $query = "UPDATE " . SQL::table_name('links') . " SET hits=hits+1 WHERE id = " . SQL::escape($item['id']); SQL::query($query); // else create a new record with a count of one click } else { // get the section for clicks $anchor = Sections::lookup('clicks'); // no section yet, create one if (!$anchor) { $fields['nick_name'] = 'clicks'; $fields['title'] = i18n::c('Clicks'); $fields['introduction'] = i18n::c('Clicked links are referenced here.'); $fields['description'] = i18n::c('YACS ties automatically external links to this section on use. Therefore, you will have below a global picture of external sites that are referenced through your site.'); $fields['active_set'] = 'N'; // for associates only $fields['locked'] = 'Y'; // no direct contributions $fields['index_map'] = 'N'; // listd only to associates $fields['rank'] = 20000; // towards the end of the list // reference the new section if ($fields['id'] = Sections::post($fields)) { $anchor = 'section:' . $fields['id']; } } // create a new link in the database $fields = array(); $fields['anchor'] = $anchor; $fields['link_url'] = $url; $fields['hits'] = 1; Surfer::check_default_editor($fields); if ($fields['id'] = Links::post($fields)) { Links::clear($fields); } } }
$menu[] = Skin::build_submit_button(i18n::s('Download this file'), NULL, NULL, 'confirmed', 'no_spin_on_click'); $menu[] = Skin::build_link($anchor->get_url('files'), i18n::s('Cancel'), 'span'); // to get the actual file $target_href = $context['url_to_home'] . $context['url_to_root'] . Files::get_url($item['id'], 'fetch', $item['file_name']); // render commands $context['text'] .= '<form method="post" action="' . $context['script_url'] . '" id="main_form"><div>' . "\n" . Skin::finalize_list($menu, 'assistant_bar') . '<input type="hidden" name="id" value="' . $item['id'] . '" />' . "\n" . '<input type="hidden" name="action" value="confirm" />' . "\n" . '</div></form>' . "\n"; // set the focus Page::insert_script('$("#confirmed").focus();'); //actual transfer } elseif ($item['id'] && $item['anchor']) { // increment the count of downloads if (!Surfer::is_crawler()) { Files::increment_hits($item['id']); } // record surfer activity Activities::post('file:' . $item['id'], 'fetch'); $anchor->touch('file:fetch', 'file:' . $item['id'], true); // if we have an external reference, use it if (isset($item['file_href']) && $item['file_href']) { $target_href = $item['file_href']; // we have direct access to the file } else { // ensure a valid file name $file_name = utf8::to_ascii($item['file_name']); // where the file is located $path = Files::get_path($item['anchor']) . '/' . $item['file_name']; // file attributes $attributes = array(); // transmit file content if (!headers_sent() && ($handle = Safe::fopen($context['path_to_root'] . $path, "rb")) && ($stat = Safe::fstat($handle))) { // stream FLV files if required to do so
} if (is_object($anchor) && Surfer::may_upload()) { $menu = array_merge($menu, array('files/edit.php?anchor=' . $anchor->get_reference() => i18n::s('Upload another file'))); } $follow_up .= Skin::build_list($menu, 'menu_bar'); $context['text'] .= Skin::build_block($follow_up, 'bottom'); // forward to the updated page } else { // touch the related anchor $anchor->touch('file:update', $_REQUEST['id'], isset($_REQUEST['silent']) && $_REQUEST['silent'] == 'Y'); // clear cache Files::clear($_REQUEST); // increment the post counter of the surfer Users::increment_posts(Surfer::get_id()); // record surfer activity Activities::post('file:' . $_REQUEST['id'], 'upload'); if ($render_overlaid) { echo 'post done'; die; } // forward to the anchor page Safe::redirect($anchor->get_url('files')); } // display the form on GET } else { $with_form = TRUE; } // display the form if ($with_form) { // prevent updates from section owner or associate if (isset($item['assign_id']) && $item['assign_id'] && !Surfer::is($item['assign_id'])) {
/** * process uploaded file * * This function processes files from the temporary directory, and put them at their definitive * place. * * It returns FALSE if there is a disk error, or if some virus has been detected, or if * the operation fails for some other reason (e.g., file size). * * @param array usually, $_FILES['upload'] * @param string target location for the file * @param mixed reference to the target anchor, of a function to parse every file individually * @return mixed file name or array of file names or FALSE if an error has occured */ public static function upload($input, $file_path, $target = NULL, $overlay = NULL) { global $context, $_REQUEST; // size exceeds php.ini settings -- UPLOAD_ERR_INI_SIZE if (isset($input['error']) && $input['error'] == 1) { Logger::error(i18n::s('The size of this file is over limit.')); } elseif (isset($input['error']) && $input['error'] == 2) { Logger::error(i18n::s('The size of this file is over limit.')); } elseif (isset($input['error']) && $input['error'] == 3) { Logger::error(i18n::s('No file has been transmitted.')); } elseif (isset($input['error']) && $input['error'] == 4) { Logger::error(i18n::s('No file has been transmitted.')); } elseif (!$input['size']) { Logger::error(i18n::s('No file has been transmitted.')); } // do we have a file? if (!isset($input['name']) || !$input['name'] || $input['name'] == 'none') { return FALSE; } // access the temporary uploaded file $file_upload = $input['tmp_name']; // $_FILES transcoding to utf8 is not automatic $input['name'] = utf8::encode($input['name']); // enhance file name $file_name = $input['name']; $file_extension = ''; $position = strrpos($input['name'], '.'); if ($position !== FALSE) { $file_name = substr($input['name'], 0, $position); $file_extension = strtolower(substr($input['name'], $position + 1)); } $input['name'] = $file_name; if ($file_extension) { $input['name'] .= '.' . $file_extension; } // ensure we have a file name $file_name = utf8::to_ascii($input['name']); // uploads are not allowed if (!Surfer::may_upload()) { Logger::error(i18n::s('You are not allowed to perform this operation.')); } elseif (!Files::is_authorized($input['name'])) { Logger::error(i18n::s('This type of file is not allowed.')); } elseif ($file_path && !Safe::is_uploaded_file($file_upload)) { Logger::error(i18n::s('Possible file attack.')); } else { // create folders if ($file_path) { Safe::make_path($file_path); } // sanity check if ($file_path && $file_path[strlen($file_path) - 1] != '/') { $file_path .= '/'; } // move the uploaded file if ($file_path && !Safe::move_uploaded_file($file_upload, $context['path_to_root'] . $file_path . $file_name)) { Logger::error(sprintf(i18n::s('Impossible to move the upload file to %s.'), $file_path . $file_name)); } else { // process the file where it is if (!$file_path) { $file_path = str_replace($context['path_to_root'], '', dirname($file_upload)); $file_name = basename($file_upload); } // check against viruses $result = Files::has_virus($context['path_to_root'] . $file_path . '/' . $file_name); // no virus has been found in this file if ($result == 'N') { $context['text'] .= Skin::build_block(i18n::s('No virus has been found.'), 'note'); } // this file has been infected! if ($result == 'Y') { // delete this file immediately Safe::unlink($file_path . '/' . $file_name); Logger::error(i18n::s('This file has been infected by a virus and has been rejected!')); return FALSE; } // explode a .zip file include_once $context['path_to_root'] . 'shared/zipfile.php'; if (preg_match('/\\.zip$/i', $file_name) && isset($_REQUEST['explode_files'])) { $zipfile = new zipfile(); // check files extracted from the archive file function explode_callback($name) { global $context; // reject all files put in sub-folders if (($path = substr($name, strlen($context['uploaded_path'] . '/'))) && strpos($path, '/') !== FALSE) { Safe::unlink($name); } elseif (!Files::is_authorized($name)) { Safe::unlink($name); } else { // make it easy to download $ascii = utf8::to_ascii(basename($name)); Safe::rename($name, $context['uploaded_path'] . '/' . $ascii); // remember this name $context['uploaded_files'][] = $ascii; } } // extract archive components and save them in mentioned directory $context['uploaded_files'] = array(); $context['uploaded_path'] = $file_path; if (!($count = $zipfile->explode($context['path_to_root'] . $file_path . '/' . $file_name, $file_path, '', 'explode_callback'))) { Logger::error(sprintf('Nothing has been extracted from %s.', $file_name)); return FALSE; } // one single file has been uploaded } else { $context['uploaded_files'] = array($file_name); } // ensure we know the surfer Surfer::check_default_editor($_REQUEST); // post-process all uploaded files foreach ($context['uploaded_files'] as $file_name) { // this will be filtered by umask anyway Safe::chmod($context['path_to_root'] . $file_path . $file_name, $context['file_mask']); // invoke post-processing function if ($target && is_callable($target)) { call_user_func($target, $file_name, $context['path_to_root'] . $file_path); // we have to update an anchor page } elseif ($target && is_string($target)) { $fields = array(); // update a file with the same name for this anchor if ($matching =& Files::get_by_anchor_and_name($target, $file_name)) { $fields['id'] = $matching['id']; } elseif (isset($input['id']) && ($matching = Files::get($input['id']))) { $fields['id'] = $matching['id']; // silently delete the previous version of the file if (isset($matching['file_name'])) { Safe::unlink($file_path . '/' . $matching['file_name']); } } // prepare file record $fields['file_name'] = $file_name; $fields['file_size'] = filesize($context['path_to_root'] . $file_path . $file_name); $fields['file_href'] = ''; $fields['anchor'] = $target; // change title if (isset($_REQUEST['title'])) { $fields['title'] = $_REQUEST['title']; } // change has been documented if (!isset($_REQUEST['version']) || !$_REQUEST['version']) { $_REQUEST['version'] = ''; } else { $_REQUEST['version'] = ' - ' . $_REQUEST['version']; } // always remember file uploads, for traceability $_REQUEST['version'] = $fields['file_name'] . ' (' . Skin::build_number($fields['file_size'], i18n::s('bytes')) . ')' . $_REQUEST['version']; // add to file history $fields['description'] = Files::add_to_history($matching, $_REQUEST['version']); // if this is an image, maybe we can derive a thumbnail for it? if (Files::is_image($file_name)) { include_once $context['path_to_root'] . 'images/image.php'; Image::shrink($context['path_to_root'] . $file_path . $file_name, $context['path_to_root'] . $file_path . 'thumbs/' . $file_name); if (file_exists($context['path_to_root'] . $file_path . 'thumbs/' . $file_name)) { $fields['thumbnail_url'] = $context['url_to_home'] . $context['url_to_root'] . $file_path . 'thumbs/' . rawurlencode($file_name); } } // change active_set if (isset($_REQUEST['active_set'])) { $fields['active_set'] = $_REQUEST['active_set']; } // change source if (isset($_REQUEST['source'])) { $fields['source'] = $_REQUEST['source']; } // change keywords if (isset($_REQUEST['keywords'])) { $fields['keywords'] = $_REQUEST['keywords']; } // change alternate_href if (isset($_REQUEST['alternate_href'])) { $fields['alternate_href'] = $_REQUEST['alternate_href']; } // overlay, if any if (is_object($overlay)) { // allow for change detection $overlay->snapshot(); // update the overlay from form content $overlay->parse_fields($_REQUEST); // save content of the overlay in this item $fields['overlay'] = $overlay->save(); $fields['overlay_id'] = $overlay->get_id(); } // create the record in the database if (!($fields['id'] = Files::post($fields))) { return FALSE; } // record surfer activity Activities::post('file:' . $fields['id'], 'upload'); } } // so far so good if (count($context['uploaded_files']) == 1) { return $context['uploaded_files'][0]; } else { return $context['uploaded_files']; } } } // some error has occured return FALSE; }
/** * retrieve recipients of last post * * This is useful to list all persons notified after a post for example. * * @param string the reference of the notifying item, if any * @return mixed text to be integrated into the page */ public static function build_recipients($reference = '') { global $context; // nothing to show if (!isset($context['mailer_recipients'])) { return ''; } // title mentions number of recipients $count = count($context['mailer_recipients']); $title = sprintf(i18n::ns('%d person has been notified', '%d persons have been notified', $count), $count); // remember the number of notifications sent from this anchor if ($reference) { Activities::post($reference, 'notify', $count); } // return the bare list if (!$title) { return $context['mailer_recipients']; } // build a nice list $list = array(); if ($count > 50) { $count = 30; } else { $count = 100; } //never reached foreach ($context['mailer_recipients'] as $recipient) { $list[] = htmlspecialchars($recipient); if ($count-- == 1) { $list[] = sprintf(i18n::s('and %d other persons'), count($context['mailer_recipients']) - 30); break; } } return '<hr align="left" size="1" width="200" />' . Skin::build_box($title, Skin::finalize_list($list, 'compact'), 'folded'); }