/** * list files into one directory * * @param string the directory to look at * @return an array of directory entries, or NULL */ public static function list_files($path) { global $context; // we are looking for files $files = array(); // look for directory entries $path_translated = $context['path_to_root'] . $path; if ($handle = Safe::opendir($path_translated)) { // handle files one by one while (($node = Safe::readdir($handle)) !== FALSE) { // skip trivial entries if ($node[0] == '.') { continue; } // skip processed entries if (preg_match('/\\.(done|bak)$/i', $node)) { continue; } // make a real name $target = $path . '/' . $node; $target_translated = $path_translated . '/' . $node; // scan a file if (is_file($target_translated) && is_readable($target_translated)) { $files[] = $target; } } Safe::closedir($handle); } return $files; }
} if ($file == 'index.php') { continue; } if ($file == 'behavior.php') { continue; } if ($file == 'behaviors.php') { continue; } if (!preg_match('/(.*)\\.php$/i', $file, $matches)) { continue; } $behaviors[] = $matches[1]; } Safe::closedir($dir); if (@count($behaviors)) { natsort($behaviors); foreach ($behaviors as $behavior) { $context['text'] .= '<li>' . $behavior . "</li>\n"; } } } $context['text'] .= '</ul>'; // how to use behaviors if (Surfer::is_associate()) { $context['text'] .= '<p>' . sprintf(i18n::s('For example, if you want to apply the behavior <code>foo</code>, go to the %s , and select a target section, or add a new one.'), Skin::build_link('sections/', i18n::s('site map'), 'shortcut')) . '</p>' . '<p>' . i18n::s('In the form used to edit the section, type the keyword <code>foo</code> in the behavior field, then save changes.') . '</p>'; } // referrals, if any $context['components']['referrals'] =& Skin::build_referrals('behaviors/index.php'); // render the skin
/** * delete a directory and all of its content * * @param string the directory to delete */ function delete_all($path) { global $context; $path_translated = str_replace('//', '/', $context['path_to_root'] . '/' . $path); if ($handle = Safe::opendir($path_translated)) { while (($node = Safe::readdir($handle)) !== FALSE) { if ($node[0] == '.') { continue; } // make a real name $target = str_replace('//', '/', $path . '/' . $node); $target_translated = str_replace('//', '/', $path_translated . '/' . $node); // delete a sub directory if (is_dir($target_translated)) { delete_all($path . '/' . $node); Safe::rmdir($target_translated); // delete the node } else { Safe::unlink($target_translated); } // statistics global $deleted_nodes; $deleted_nodes++; } Safe::closedir($handle); } }
/** * delete staging files * * @param string the directory to start with * @see scripts/update.php */ function delete_staging($path) { global $context; $path_translated = str_replace('//', '/', $context['path_to_root'] . '/scripts/staging' . $path); if ($handle = Safe::opendir($path_translated)) { while (($node = Safe::readdir($handle)) !== FALSE) { if ($node == '.' || $node == '..') { continue; } // make a real name $target = str_replace('//', '/', $path . '/' . $node); $target_translated = str_replace('//', '/', $path_translated . '/' . $node); // delete sub directory content if (is_dir($target_translated)) { delete_staging($target); Safe::rmdir($target_translated); // delete all files } else { $context['text'] .= sprintf(i18n::s('Deleting %s'), '/scripts/staging' . $target) . BR . "\n"; Safe::unlink($target_translated); global $deleted_nodes; $deleted_nodes++; } // ensure we have enough time Safe::set_time_limit(30); } Safe::closedir($handle); } }
function include_hook($path) { global $context, $hooks; // animate user screen and take care of time global $scanned_directories; $scanned_directories++; // ensure enough execution time Safe::set_time_limit(30); // open the directory if (!($dir = Safe::opendir($path))) { $context['text'] .= sprintf(i18n::s('Impossible to read %s.'), $path) . BR . "\n"; return; } // browse the directory while (($item = Safe::readdir($dir)) !== FALSE) { // skip some files if ($item[0] == '.') { continue; } // load any 'hook.php', or any file which names ends with 'hook.php' $actual_item = str_replace('//', '/', $path . '/' . $item); if (preg_match('/hook\\.php$/i', $item)) { include_once $actual_item; $context['text'] .= sprintf(i18n::s('Hook %s has been included'), $actual_item) . BR . "\n"; // scan any sub dir except at server root } elseif (is_dir($actual_item) && $path != $context['path_to_root'] && !strpos($path, '/files/') && !strpos($path, '/images/')) { include_hook($actual_item); } } // close the directory Safe::closedir($dir); }
/** * list all files below a certain path * * @param string the path to scan * @return an array of file names */ function list_files_at($root, $path = '') { global $context; // the list of files $files = array(); $path_translated = rtrim(str_replace('//', '/', $context['path_to_root'] . '/' . $root . '/' . $path), '/'); if ($handle = Safe::opendir($path_translated)) { while (($node = Safe::readdir($handle)) !== FALSE) { if ($node[0] == '.') { continue; } // skip transient files if (preg_match('/\\.cache$/i', $node)) { continue; } // make a real name $target = str_replace('//', '/', $path . '/' . $node); $target_translated = str_replace('//', '/', $path_translated . '/' . $node); // extend the list recursively if (is_dir($target_translated)) { $files = array_merge($files, list_files_at($root, $target)); } elseif (is_readable($target_translated)) { $files[] = $path . '/' . $node; } } Safe::closedir($handle); } return $files; }
/** * help to select an image */ function image_helper($name, $sample, $path = 'skins/flexible/logos', $toggle = '') { global $context; $text = ''; // none $checked = ''; if ($context[$name] == 'none') { $checked = ' checked="checked"'; Page::insert_script("\$('#" . $sample . "').css({'display': 'none'});"); } $to_toggle = ''; if ($toggle) { $to_toggle = '$(\'#' . $toggle . '\').css({\'display\': \'inline\'})'; } $text .= '<div style="text-align: center; float:left; width: 150px; margin: 0 10px 20px 0; background-color: #ddd"><div style="width: 150px; height: 70px; background: transparent; position:relative;"><div style="position: absolute; top:50%; margin: 0 auto; width:150px">' . i18n::s('None') . '</div></div>' . '<input type="radio" name="' . $name . '" value="none"' . $checked . ' onclick="$(\'#' . $sample . '\').css({\'display\': \'none\'});' . $to_toggle . '" /></div>'; // scan files if ($dir = Safe::opendir($path)) { // list files in the skin directory $items = array(); while (($item = Safe::readdir($dir)) !== FALSE) { if ($item[0] == '.' || is_dir($context['path_to_root'] . $path . '/' . $item)) { continue; } if (!preg_match('/(\\.gif|\\.jpeg|\\.jpg|\\.png)$/i', $item)) { continue; } $to_toggle = ''; if ($toggle) { $to_toggle = '$(\'#' . $toggle . '\').css({\'display\': \'none\'})'; } $checked = ''; if (strpos($context[$name], $item)) { $checked = ' checked="checked"'; Page::insert_script("\$('#" . $sample . "').src = '" . $context['url_to_root'] . $path . '/' . $item . "';\$('#" . $sample . "').css({'display': 'inline'});" . $to_toggle); } $items[] = '<div style="text-align: center; float:left; width: 150px; margin: 0 10px 20px 0; background-color: #ddd"><div style="width: 150px; height: 70px; background: transparent url(' . $context['url_to_root'] . $path . '/' . $item . ') no-repeat"> </div>' . '<input type="radio" name="' . $name . '" value="' . $context['url_to_root'] . $path . '/' . $item . '"' . $checked . ' onclick="$(\'#' . $sample . '\').src = \'' . $context['url_to_home'] . $context['url_to_root'] . $path . '/' . $item . '\';$(\'#' . $sample . '\').css({\'display\': \'inline\'});' . $to_toggle . '" /></div>'; } Safe::closedir($dir); // list items by alphabetical order if (@count($items)) { natsort($items); foreach ($items as $item) { $text .= $item; } } } return $text; }
// look for css files if ($files = Safe::opendir('../skins/' . $skin)) { while (($file = Safe::readdir($files)) !== FALSE) { if (!preg_match('/\\.css$/i', $file)) { continue; } // change this css file if ($content = Safe::file_get_contents('../skins/' . $skin . '/' . $file)) { $content = str_replace('/yacs/', $context['url_to_root_parameter'], $content); Safe::file_put_contents('skins/' . $skin . '/' . $file, $content); } } Safe::closedir($files); } } Safe::closedir($skins); } // look for software extensions $context['text'] .= Skin::build_block('<form method="get" action="scan.php" id="main_form">' . "\n" . '<p class="assistant_bar">' . Skin::build_submit_button(i18n::s('Look for software extensions'), NULL, 's', 'confirmed') . '</p>' . "\n" . '</form>', 'bottom'); // this may take several minutes $context['text'] .= '<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>\n"; // the followup link, if any } elseif (isset($context['followup_link'])) { // ensure we have a label if (!isset($context['followup_label'])) { $context['followup_label'] = i18n::s('Next step'); } $context['text'] .= Skin::build_block('<form method="get" action="' . $context['url_to_root'] . $context['followup_link'] . '" id="main_form">' . "\n" . '<p class="assistant_bar">' . Skin::build_submit_button($context['followup_label']) . '</p>' . "\n" . '</form>', 'bottom'); // ordinary follow-up commands } else { // follow-up commands
if (!($item =& Images::get_by_anchor_and_name($anchor->get_reference(), $node))) { // create a new image record for this file $item = array(); $item['anchor'] = $anchor->get_reference(); $item['image_name'] = $node; $item['thumbnail_name'] = 'thumbs/' . $node; $item['image_size'] = Safe::filesize($file_path . '/' . $node); $item['use_thumbnail'] = 'A'; // ensure it is always displayed as a clickable small image $item['id'] = Images::post($item); } // ensure that the image is in anchor description field $nodes[$node] = $item['id']; } } Safe::closedir($handle); // embed uploaded images in alphabetical order ksort($nodes); foreach ($nodes as $name => $id) { $anchor->touch('image:create', $id); } } // clear floating thumbnails if ($count) { $anchor->touch('clear'); } // provide feed-back to surfer if ($count) { $context['text'] .= '<p>' . sprintf(i18n::ns('%d image has been processed.', '%d images have been processed.', $count), $count) . '</p>'; } else { $context['text'] .= '<p>' . i18n::s('No image has been processed.') . '</p>';
/** * transform codes to html * * [php] * // build the page * $context['text'] .= ... * * // transform codes * $context['text'] = Codes::render($context['text']); * * // final rendering * render_skin(); * [/php] * * @link http://pureform.wordpress.com/2008/01/04/matching-a-word-characters-outside-of-html-tags/ * * @param string the input string * @return string the transformed string */ public static function render($text) { global $context; // the formatting code interface include_once $context['path_to_root'] . 'codes/code.php'; // streamline newlines, even if this has been done elsewhere $text = str_replace(array("\r\n", "\r"), "\n", $text); // prevent wysiwyg editors to bracket our own tags $text = preg_replace('#^<p>(\\[.+\\])</p>$#m', '$1', $text); // initialize only once static $patterns_map; if (!isset($patterns_map)) { if (Safe::filesize('codes/patterns.auto.php')) { include_once $context['path_to_root'] . 'codes/patterns.auto.php'; } else { // core patterns $patterns_map['|<!-- .* -->|i'] = ''; // remove HTML comments $patterns_map['|</h(\\d)>\\n+|i'] = '</h$1>'; // strip \n after title $patterns_map['/\\n[ \\t]*(From|To|cc|bcc|Subject|Date):(\\s*)/i'] = BR . '$1:$2'; // common message headers $patterns_map['/\\[escape\\](.*?)\\[\\/escape\\]/is'] = 'Codes::render_escaped'; // [escape]...[/escape] (before everything) $patterns_map['/\\[php\\](.*?)\\[\\/php\\]/is'] = 'Codes::render_pre_php'; // [php]...[/php] $patterns_map['/\\[snippet\\](.*?)\\[\\/snippet\\]/is'] = 'Codes::render_pre'; // [snippet]...[/snippet] $patterns_map['/(\\[page\\].*)$/is'] = ''; // [page] (provide only the first one) $patterns_map['/\\[(associate|member|anonymous|hidden|restricted|authenticated)\\](.*?)\\[\\/\\1\\]/is'] = 'Codes::render_hidden'; // [associate]...[/associate] $patterns_map['/\\[redirect=([^\\]]+?)\\]/is'] = 'Codes::render_redirect'; // [redirect=<link>] $patterns_map['/\\[execute=([^\\]]+?)\\]/is'] = 'Codes::render_execute'; // [execute=<name>] $patterns_map['/\\[parameter=([^\\]]+?)\\]/is'] = 'Codes::render_parameter'; // [parameter=<name>] $patterns_map['/\\[lang=([^\\]]+?)\\](.*?)\\[\\/lang\\]/is'] = 'Codes::render_lang'; // [lang=xy]...[/lang] $patterns_map['/\\[(images)=([^\\]]+?)\\]/i'] = 'Codes::render_object'; // [images=<ids>] (before other links) $patterns_map['/\\[(image)=([^\\]]+?)\\]/i'] = 'Codes::render_object'; // [image=<id>] $patterns_map['/##(\\S.*?\\S)##/is'] = '<code>$1</code>'; // ##...## $patterns_map['/\\[code\\](.*?)\\[\\/code\\]/is'] = '<code>$1</code>'; // [code]...[/code] $patterns_map['/\\[indent\\](.*?)\\[\\/(indent)\\]/is'] = 'Skin::build_block'; // [indent]...[indent] $patterns_map['/\\[quote\\](.*?)\\[\\/(quote)\\]/is'] = 'Skin::build_block'; // [quote]...[quote] $patterns_map['/\\[folded(?:=([^\\]]+?))?\\](.*?)\\[\\/(folded)\\]\\s*/is'] = 'Skin::build_box'; // [folded=title]...[/folded],[folded]...[/folded] $patterns_map['/\\[unfolded(?:=([^\\]]+?))?\\](.*?)\\[\\/(unfolded)\\]\\s*/is'] = 'Skin::build_box'; // [unfolded=title]...[/unfolded],[unfolded]...[/unfolded] $patterns_map['/\\[sidebar(?:=([^\\]]+?))?\\](.*?)\\[\\/(sidebar)\\]\\s*/is'] = 'Skin::build_box'; // [sidebar=title]...[/sidebar],[sidebar]...[/sidebar] $patterns_map['/\\[note\\](.*?)\\[\\/(note)\\]\\s*/is'] = 'Skin::build_block'; // [note]...[/note] $patterns_map['/\\[caution\\](.*?)\\[\\/(caution)\\]\\s*/is'] = 'Skin::build_block'; // [caution]...[/caution] $patterns_map['/\\[center\\](.*?)\\[\\/(center)\\]/is'] = 'Skin::build_block'; // [center]...[/center] $patterns_map['/\\[right\\](.*?)\\[\\/(right)\\]/is'] = 'Skin::build_block'; // [right]...[/right] $patterns_map['/\\[(go)=([^\\]]+?)\\]/i'] = 'Codes::render_object'; // [go=<name>] $patterns_map['/\\[(article\\.description)=([^\\]]+?)\\]/i'] = 'Codes::render_object'; // [article.description=<id>] $patterns_map['/\\[(article)=([^\\]]+?)\\]/i'] = 'Codes::render_object'; // [article=<id>] or [article=<id>, title] $patterns_map['/\\[(next)=([^\\]]+?)\\]/i'] = 'Codes::render_object'; // [next=<id>] $patterns_map['/\\[(previous)=([^\\]]+?)\\]/i'] = 'Codes::render_object'; // [previous=<id>] $patterns_map['/\\[random(?:=([^\\]]+?))?\\]/i'] = 'Codes::render_random'; // [random], [random=section:<id>] or [random=category:<id>] $patterns_map['/\\[(section)=([^\\]]+?)\\]/i'] = 'Codes::render_object'; // [section=<id>] or [section=<id>, title] $patterns_map['/\\[(category(?:\\.description)?)=([^\\]]+?)\\]\\n*/is'] = 'Codes::render_object'; // [category=<id>], [category=<id>, title] or [category.description=<id>] $patterns_map['/\\[(user)=([^\\]]+?)\\]/i'] = 'Codes::render_object'; // [user=<id>] $patterns_map['/\\[(users)=([^\\]]+?)\\]/i'] = 'Codes::render_users'; // [users=present] $patterns_map['/\\[(file|download)=([^\\]]+?)\\]/i'] = 'Codes::render_object'; // [file=<id>] or [file=<id>, title] or download=<id>] or [download=<id>, title] $patterns_map['/\\[(comment)=([^\\]]+?)\\]/i'] = 'Codes::render_object'; // [comment=<id>] or [comment=<id>, title] $patterns_map['/\\[(link)(?:=([^\\]]+?))?\\](.*?)\\[\\/link\\]/is'] = 'Codes::render_link'; // [link]url[/link] or [link=label]url[/link] $patterns_map['/\\[(button)=([^\\]]+?)\\](.*?)\\[\\/button\\]/is'] = 'Codes::render_link'; // [button=label]url[/button] $patterns_map['/\\[(button)=([^\\|]+?)\\|([^\\]]+?)]/is'] = 'Codes::render_link'; // [button=label|url] $patterns_map['/\\[(click)=([^\\|]+?)\\|([^\\]]+?)]/is'] = 'Codes::render_link'; // [click=label|url] $patterns_map['/(\\[)([^ ][^\\]\\|]+?[^ ])\\|([^ ][^\\]]+?[^ ])\\]/is'] = 'Codes::render_link'; // [label|url] $patterns_map['#(\\s)([a-z]+?://[a-z0-9_\\-\\.\\~\\/@&;:=%$\\?]+)#'] = 'Codes::render_link'; // make URL clickable $patterns_map['#(\\s)(www\\.[a-z0-9\\-]+\\.[a-z0-9_\\-\\.\\~]+(?:/[^,< \\r\\n\\)]*)?)#i'] = 'Codes::render_link'; // web server url $patterns_map['/http[s]*:\\/\\/www\\.youtube\\.com\\/watch\\?v=([a-zA-Z0-9_\\-]+)[a-zA-Z0-9_\\-&=]*/i'] = '<iframe class="youtube-player" type="text/html" width="445" height="364" src="http://www.youtube.com/embed/$1" frameborder="0"></iframe>'; // YouTube link $patterns_map['/http[s]*:\\/\\/youtu\\.be\\/([a-zA-Z0-9_\\-]+)/i'] = '<iframe class="youtube-player" type="text/html" width="445" height="364" src="http://www.youtube.com/embed/$1" frameborder="0"></iframe>'; // YouTube link too $patterns_map['/\\[clicks=([^\\]]+?)]/is'] = 'Codes::render_clicks'; // [clicks=url] // @TODO: put in extension $patterns_map['/\\[email\\](.*?)\\[\\/email\\]/is'] = 'Codes::render_email'; // [email]url[/email] $patterns_map['/(\\s)([a-z0-9_\\-\\.\\~]+?@[a-z0-9_\\-\\.\\~]+\\.[a-z0-9_\\-\\.\\~]+)/i'] = 'Codes::render_email'; // mail address $patterns_map['/\\[published(?:\\.([^\\]=]+?))?(?:=([^\\]]+?))?\\]\\n*/is'] = 'Codes::render_published'; // [published(.decorated)], [published=section:4029], [published.decorated=section:4029,x] $patterns_map['/\\[updated(?:\\.([^\\]=]+?))?(?:=([^\\]]+?))?\\]\\n*/is'] = 'Codes::render_updated'; // [updated(.decorated)], [updated(.decorated)=section:4029,x] $patterns_map['/\\[sections(?:\\.([^\\]=]+?))?(?:=([^\\]]+?))?\\]\\n*/is'] = 'Codes::render_sections'; // [sections(.decorated)] (site map), [sections(.decorated)=section:4029] (sub-sections), [sections.simple=self] (assigned) $patterns_map['/\\[categories(?:\\.([^\\]=]+?))?(?:=([^\\]]+?))?\\]\\n*/is'] = 'Codes::render_categories'; // [categories(.decorated)] (category tree), [categories(.decorated)=categories:4029] (sub-categories) $patterns_map['/\\[wikipedia=([^\\]]+?)\\]/is'] = 'Codes::render_wikipedia'; // [wikipedia=keyword] or [wikipedia=keyword, title] $patterns_map['/\\[be\\]/i'] = ' <img src="' . $context['url_to_root'] . 'skins/_reference/flags/be.gif" alt="belgian flag" /> '; // [be] belgian flag $patterns_map['/\\[ca\\]/i'] = ' <img src="' . $context['url_to_root'] . 'skins/_reference/flags/ca.gif" alt="canadian flag" /> '; // [ca] canadian flag $patterns_map['/\\[ch\\]/i'] = ' <img src="' . $context['url_to_root'] . 'skins/_reference/flags/ch.gif" alt="swiss flag" /> '; // [ch] swiss flag $patterns_map['/\\[de\\]/i'] = ' <img src="' . $context['url_to_root'] . 'skins/_reference/flags/de.gif" alt="german flag" /> '; // [de] german flag $patterns_map['/\\[en\\]/i'] = ' <img src="' . $context['url_to_root'] . 'skins/_reference/flags/gb.gif" alt="english flag" /> '; // [en] english flag $patterns_map['/\\[es\\]/i'] = ' <img src="' . $context['url_to_root'] . 'skins/_reference/flags/es.gif" alt="spanish flag" /> '; // [es] spanish flag $patterns_map['/\\[fr\\]/i'] = ' <img src="' . $context['url_to_root'] . 'skins/_reference/flags/fr.gif" alt="french flag" /> '; // [fr] french flag $patterns_map['/\\[gr\\]/i'] = ' <img src="' . $context['url_to_root'] . 'skins/_reference/flags/gr.gif" alt="greek flag" /> '; // [gr] greek flag $patterns_map['/\\[it\\]/i'] = ' <img src="' . $context['url_to_root'] . 'skins/_reference/flags/it.gif" alt="italian flag" /> '; // [it] italian flag $patterns_map['/\\[pt\\]/i'] = ' <img src="' . $context['url_to_root'] . 'skins/_reference/flags/pt.gif" alt="portugal flag" /> '; // [pt] portugal flag $patterns_map['/\\[us\\]/i'] = ' <img src="' . $context['url_to_root'] . 'skins/_reference/flags/us.gif" alt="us flag" /> '; // [us] us flag $patterns_map['/\\[clear\\]\\n*/i'] = ' <br style="clear: both;" /> '; // [clear] $patterns_map['/\\[nl\\]\\n*/i'] = BR; // [nl] new line // load formatting codes from files $dir = $context['path_to_root'] . 'codes/'; if ($handle = Safe::opendir($dir)) { while (false !== ($file = Safe::readdir($handle))) { if ($file == '..') { continue; } if ($file == '.') { continue; } //convention : //get file only begining with code_ if (!(substr($file, 0, 5) == 'code_')) { continue; } include_once $dir . $file; //get formatting code patterns from this class $classname = stristr($file, '.', TRUE); $code = new $classname(); $code->get_pattern($patterns_map); unset($code); } Safe::closedir($handle); } // cache all patterns in one unique file for next time Codes::save_patterns($patterns_map); } // end generating patterns from scratch } // end setting $patterns $text = Codes::process($text, $patterns_map); // done return $text; }
/** * walk all files below a certain path * * @param string the absolute path to scan * @param function the function to call back with the file found * * @see scripts/check.php */ public static function walk_files_at($path, $call_back = NULL) { global $context, $script_count; // sanity check $path = rtrim($path, '/'); // list all files at this level $directories = array(); if ($handle = Safe::opendir($path)) { // list all nodes while (($node = Safe::readdir($handle)) !== FALSE) { // special directory names if ($node == '.' || $node == '..') { continue; } // process special nodes if ($node[0] == '.') { continue; } // make a real name $target = $path . '/' . $node; // scan a sub directory if (is_dir($target)) { $directories[] = $target; } elseif (is_readable($target)) { $call_back($target); } } Safe::closedir($handle); } // walk sub-directories as well foreach ($directories as $directory) { Scripts::walk_files_at($directory, $call_back); } }
/** * list all available locales * * @return array of ($locale => $label) */ public static function &list_locales() { global $context, $locales; // list of locales $locales = array(); // one directory per locale if ($dir = Safe::opendir($context['path_to_root'] . 'i18n/locale')) { while (($item = Safe::readdir($dir)) !== FALSE) { if ($item[0] == '.' || !is_dir($context['path_to_root'] . 'i18n/locale/' . $item)) { continue; } // remember locale $locales[$item] = $item; // enhance with manifest file, if any if (is_readable($context['path_to_root'] . 'i18n/locale/' . $item . '/manifest.php')) { include_once $context['path_to_root'] . 'i18n/locale/' . $item . '/manifest.php'; } } Safe::closedir($dir); } else { logger::remember('i18n/i18n.php: Impossible to browse directory i18n/locale'); } // done return $locales; }