| | Gabor Hojtsy | | Mark Kronsbein | | Jan Fabry +----------------------------------------------------------------------+ */ if ($argc < 2 || $argc > 3) { ?> Check the revision of translated files against the actual english xml files, and print statistics Usage: [] [>] must be a valid language code used in the repository If you specify , the script only checks the files maintained by the person you add here If you specify >, the output is an html file. Read more about Revision comments and related funcionality in the PHP Documentation Howto. Authors: Thomas Schöfbeck Gabor Hojtsy Mark Kronsbein Jan Fabry "act", REV_NOREV => "norev", REV_CRITICAL => "crit", REV_OLD => "old", REV_NOTAG => "wip", REV_NOTRANS => "wip", REV_CREDIT => "wip", REV_WIP => "wip", ); // Option for the link to cvs.php.net: normal: "&f=h" // long diff: "&f=h&num=10", unified (text): "&f=u" define("CVS_OPT", "&ty=u"); // Initializing variables from parameters $LANG = $argv[1]; if ($argc == 3) { $MAINT = $argv[2]; } else { $MAINT = ""; } // Main directory of the PHP documentation (depends on the // sapi used). We do need the trailing slash! if ("cli" === php_sapi_name()) { if (isset($PHPDOCDIR) && is_dir($PHPDOCDIR)) $DOCDIR = $PHPDOCDIR."/"; else $DOCDIR = "./"; } else $DOCDIR = "../"; // ========================================================================= // Functions to get revision info and credits from a file // ========================================================================= // Grabs the revision tag and stores credits from the file given function get_tags($file, $val = "en-rev") { // Read the first 500 chars. The comment should be at // the begining of the file $fp = @fopen($file, "r") or die ("Unable to read $file."); $line = fread($fp, 500); fclose($fp); // Check for English CVS revision tag (. is for $ in the preg!), // Return if this was needed (it should be there) if ($val == "en-rev") { preg_match("//", $line, $match); return $match[1]; } // Handle credits (only if no maintainer is specified) if ($val == "\\S*") { global $files_by_maint; // Find credits info, let more credits then one, // using commas as list separator if (preg_match("''U", $line, $match_credit)) { // Explode with commas a separators $credits = explode(",", $match_credit[1]); // Store all elements foreach ($credits as $num => $credit) { $files_by_maint[trim($credit)][REV_CREDIT]++; } } } // No match before the preg $match = array(); // Check for the translations "revision tag" preg_match ("//U", $line, $match ); // The tag with revision number is not found so search // for n/a revision comment (comment where revision is not known) if (count($match) == 0) { preg_match ("''U", $line, $match ); } // Return with found revision info (number, maint, status) return $match; } // get_tags() function end // ========================================================================= // Functions to check file status in translated directory, and store info // ========================================================================= // Checks a file, and gather status info function get_file_status($file) { // The information is contained in these global arrays and vars global $DOCDIR, $LANG, $MAINT, $files_by_mark, $files_by_maint; global $file_sizes_by_mark; global $missing_files, $missing_tags, $using_rev; // Transform english file name to translated file name $trans_file = preg_replace("'^".$DOCDIR."en/'", $DOCDIR.$LANG."/", $file); // If we cannot find the file, we push it into the missing files list if (!@file_exists($trans_file)) { $files_by_mark[REV_NOTRANS]++; $trans_name = substr($trans_file, strlen($DOCDIR) + strlen($LANG) + 1); $size = round(filesize($file)/1024, 1); $missing_files[$trans_name] = array( $size ); $file_sizes_by_mark[REV_NOTRANS] += $size; // compute en-tags just if they're needed in the WIP-Table if($using_rev) { $missing_files[$trans_name][] = "1.".get_tags($file); } return FALSE; } // No specific maintainer, check for a revision tag if (empty($MAINT)) { $trans_tag = get_tags($trans_file, "\\S*"); } // If we need to check for a specific translator else { // Get translated files tag, with maintainer $trans_tag = get_tags($trans_file, $MAINT); // If this is a file belonging to another // maintainer, than we would not like to // deal with it anymore if (count($trans_tag) == 0) { $trans_tag = get_tags($trans_file, "\\S*"); // We found a tag for another maintainer if (count($trans_tag) > 0) { return FALSE; } } } // Compute sizes and diffs $en_size = intval(filesize($file) / 1024); $trans_size = intval(filesize($trans_file) / 1024); $size_diff = intval($en_size) - intval($trans_size); // If we found no revision tag, then collect this // file in the missing tags list if (count($trans_tag) == 0) { $files_by_mark[REV_NOTAG]++; $file_sizes_by_mark[REV_NOTAG] += $en_size; $missing_tags[] = array(substr($trans_file, strlen($DOCDIR)), $en_size, $trans_size, $size_diff); return FALSE; } // Distribute values in separate vars for further processing list(, $this_rev, $this_maint, $this_status) = $trans_tag; // Get English file revision $en_rev = get_tags($file); // If we have a numeric revision number (not n/a), compute rev. diff if (is_numeric($this_rev)) { $rev_diff = intval($en_rev) - intval($this_rev); $trans_rev = "1." . $this_rev; $en_rev = "1." . $en_rev; } else { // If we have no numeric revision, make all revision // columns hold the rev from the translated file $rev_diff = $trans_rev = $this_rev; $en_rev = "1." . $en_rev; } // If the file is up-to-date if ($rev_diff === 0) { // Store file by status and maintainer $files_by_mark[REV_UPTODATE]++; $files_by_maint[$this_maint][REV_UPTODATE]++; $file_sizes_by_mark[REV_UPTODATE] += $en_size; return FALSE; } // Compute times and diffs $en_date = intval((time() - filemtime($file)) / 86400); $trans_date = intval((time() - filemtime($trans_file)) / 86400); $date_diff = $en_date - $trans_date; // Make decision on file category by revision, date and size if ($rev_diff >= ALERT_REV || $size_diff >= ALERT_SIZE || $date_diff <= ALERT_DATE) { $status_mark = REV_CRITICAL; } elseif ($rev_diff === "n/a") { $status_mark = REV_NOREV; } else { $status_mark = REV_OLD; } // Store files by status, and by maintainer too $files_by_mark[$status_mark]++; $files_by_maint[$this_maint][$status_mark]++; $file_sizes_by_mark[$status_mark] += $en_size; return array( "full_name" => $file, "short_name" => basename($trans_file), "revision" => array($en_rev, $trans_rev, $rev_diff), "size" => array($en_size, $trans_size, $size_diff), "date" => array($en_date, $trans_date, $date_diff), "maintainer" => $this_maint, "status" => $this_status, "mark" => $status_mark ); } // get_file_status() function end // ========================================================================= // A function to check directory status in translated directory // ========================================================================= // Check the status of files in a diretory of smarty/doc XML files // The English directory is passed to this function to check function get_dir_status($dir) { // Collect files and diretcories in these arrays $directories = array(); $files = array(); // Open the directory $handle = @opendir($dir); // Walk through all names in the directory while ($file = @readdir($handle)) { // If we found a file with one or two point as a name, // or a CVS directory, skip the file if (preg_match("/^\.{1,2}/",$file) || $file == 'CVS') continue; // Collect files and directories if (is_dir($dir.$file)) { $directories[] = $file; } else { $files[] = $file; } } // Close the directory @closedir($handle); // Sort files and directories sort($directories); sort($files); // Go through files first $dir_status = array(); foreach ($files as $file) { // If the file status is OK, append the status info if ($file_status = get_file_status($dir.$file)) { $dir_status[] = $file_status; } } // Then go through subdirectories, merging all the info // coming from subdirs to one array foreach ($directories as $file) { $dir_status = array_merge( $dir_status, get_dir_status($dir.$file.'/') ); } // Return with collected file info in // this dir and subdirectories [if any] return $dir_status; } // get_dir_status() function end // Check for files removed in the EN tree, but still living in the translation function get_old_files($dir) { global $DOCDIR, $LANG; // Collect files and diretcories in these arrays $directories = array(); $files = array(); $special_files = array( // french 'translation.xml' // todo: add all missing languages ); // Open the directory $handle = @opendir($dir); // Walk through all names in the directory while ($file = @readdir($handle)) { // If we found a file with one or two point as a name, // or a CVS directory, skip the file if (preg_match("/^\.{1,2}/", $file) || $file == 'CVS') continue; // skip this files if (in_array($file, $special_files)) { continue; } // Collect files and directories if (is_dir($dir.$file)) { $directories[] = $file; } else { $files[] = $file; } } // Close the directory @closedir($handle); // Sort files and directories sort($directories); sort($files); // Go through files first $old_files_status = array(); foreach ($files as $file) { $en_dir = preg_replace("'^".$DOCDIR.$LANG."/'", $DOCDIR."en/", $dir); if (!@file_exists($en_dir.$file) ) { $old_files_status[$dir.$file] = array(0=>intval(filesize($dir.$file)/1024)); } } // Then go through subdirectories, merging all the info // coming from subdirs to one array foreach ($directories as $file) { $old_files_status = array_merge( $old_files_status, get_old_files($dir.$file.'/') ); } return $old_files_status; } // get_old_files() function end // ========================================================================= // Functions to read in the translation.xml file and process contents // ========================================================================= // Get a multidimensional array with tag attributes function parse_attr_string ($tags_attrs) { $tag_attrs_processed = array(); // Go through the tag attributes foreach($tags_attrs as $attrib_list) { // Get attr name and values preg_match_all("!(.+)=\\s*([\"'])\\s*(.+)\\2!U", $attrib_list, $attribs); // Assign all attributes to one associative array $attrib_array = array(); foreach ($attribs[1] as $num => $attrname) { $attrib_array[trim($attrname)] = trim($attribs[3][$num]); } // Collect in order of tags received $tag_attrs_processed[] = $attrib_array; } // Retrun with collected attributes return $tag_attrs_processed; } // parse_attr_string() end // Parse the translation.xml file for // translation related meta information function parse_translation($DOCDIR, $LANG, $MAINT) { global $files_by_mark; // Path to find translation.xml file, set default values, // in case we can't find the translation file $translation_xml = $DOCDIR.$LANG."/translation.xml"; $output_charset = 'iso-8859-1'; $translation = array( "intro" => "", "persons" => array(), "files" => array(), "allfiles" => array(), ); // Check for file availability, return with default // values, if we cannot find the file if (!@file_exists($translation_xml)) { return array($output_charset, $translation); } // Else go on, and load in the file, replacing all // space type chars with one space $txml = join("", file($translation_xml)); $txml = preg_replace("/\\s+/", " ", $txml); // Get intro text (different for a persons info and // for a whole group info page) if (empty($MAINT)) { preg_match("!(.+)!s", $txml, $match); $translation["intro"] = trim($match[1]); } else { $translation["intro"] = "Personal Statistics for ".$MAINT; } // Get encoding for the output, from the translation.xml // file encoding (should be the same as the used encoding // in HTML) preg_match("!<\?xml(.+)\?>!U", $txml, $match); $xmlinfo = parse_attr_string($match); $output_charset = $xmlinfo[1]["encoding"]; // Get persons list preg pattern, only check for a specific // maintainer, if the users asked for it if (empty($MAINT)) { $pattern = "!!U"; } else { $pattern = "!!U"; } // Find all persons matching the pattern preg_match_all($pattern, $txml, $matches); $translation['persons'] = parse_attr_string($matches[1]); // Get list of work in progress files if (empty($MAINT)) { // Get all wip files preg_match_all("!!U", $txml, $matches); $translation['files'] = parse_attr_string($matches[1]); // Provide info about number of WIP files $files_by_mark[REV_WIP] += count($translation['files']); } else { // Only check for a specific maintainer, if we were asked to preg_match_all("!!U", $txml, $matches); $translation['files'] = parse_attr_string($matches[1]); // Other maintainers wip files need to be cleared from // available files list in the future, so store that info too. preg_match_all("!!U", $txml, $matches); $translation['allfiles'] = parse_attr_string($matches[1]); // Provide info about number of WIP files $files_by_mark[REV_WIP] += count($translation['allfiles']); } // Return with collected info in two vars return array($output_charset, $translation); } // parse_translation() function end() // ========================================================================= // Debug functions for all the functions and code on this page // ========================================================================= // Print preformatted (debug function) function print_pre($var) { print("
");
    print_r($var);
    print("
"); } // print_pre() function end // ========================================================================= // Start of the program execution // ========================================================================= // Check for directory validity if (!@is_dir($DOCDIR . $LANG)) { die("The $LANG language code is not valid"); } // Parse translation.xml file for more information list($charset, $translation) = parse_translation($DOCDIR, $LANG, $MAINT); // Add WIP files to maintainers file count and figure out, // if we need to use optional date and revision columns $using_date = FALSE; $using_rev = FALSE; foreach ($translation["files"] as $num => $fileinfo) { $files_by_maint[$fileinfo["person"]][REV_WIP]++; if (isset($fileinfo["date"])) { $using_date = TRUE; } if (isset($fileinfo["revision"])) { $using_rev = TRUE; } } // Get all files status $files_status = get_dir_status($DOCDIR."en/"); // Get all old files in directory $old_files = get_old_files($DOCDIR.$LANG."/"); $navbar = "

Introduction | " . "Translators | " . "File summary by type | " . "Files | "; if (count($translation["files"]) != 0) $navbar .= "Work in progress | "; $navbar .= "Missing revision numbers | " . "Untranslated files | " . "Old files

\n"; // Figure out generation date $date = date("r"); // ========================================================================= // Start of HTML page // ========================================================================= print << Smarty Manual Revision-check

Status of the translated Smarty Manual

Generated: {$date}   /   Language: $LANG

END_OF_MULTILINE; print ($navbar); // ========================================================================= // Intro block goes here // ========================================================================= // If we have an introduction text, print it out, with an anchor if (!empty($translation["intro"])) { print ''; print '
' . $translation['intro'] . '

'; } // ========================================================================= // Translators table goes here // ========================================================================= // If person list available (valid translation.xml file in lang), print out // the person list, with respect to the maintainer parameter specified if (!empty($translation["persons"])) { print << END_OF_MULTILINE; // ' Please leave this comment here // We will collect the maintainers by nick here $maint_by_nick = array(); // Print out a line for each maintainer (with respect to // maintainer setting provided in command line) foreach($translation["persons"] as $num => $person) { // Do not print out this person, if a // specific maintainer info is asked for if (!empty($MAINT) && $person["nick"] != $MAINT) { continue; } // Put maintaner number into associative array // [Used in further tables for referencing] $maint_by_nick[$person["nick"]] = $num; // Decide on the CVS text and the color of the line if ($person["cvs"] === "yes") { $cvsu = "x"; $col = "old"; } else { $cvsu = " "; $col = "wip"; } // Try to do some antispam actions $person["email"] = str_replace( "@", ":at:", $person["email"] ); // Get file info for this person if (isset($files_by_maint[$person["nick"]])) { $pi = $files_by_maint[$person["nick"]]; } else { $pi = array(); } print("" . "" . "" . "" . "" . "" . "" . "" . "" . "" . "" . "" . "\n"); } print "
Translator's name Contact email Nick C
V
S
Files maintained
cre-
dits
upto-
date
old cri-
tical
no
rev
wip sum
$person[name]$person[email]$person[nick]$cvsu" . $pi[REV_CREDIT] . "" . $pi[REV_UPTODATE] . "" . $pi[REV_OLD] . "" . $pi[REV_CRITICAL] . "" . $pi[REV_NOREV] . "" . $pi[REV_WIP] . "" . array_sum($pi) . "
\n

 

\n"; } // ========================================================================= // Files summary table goes here // ========================================================================= // Do not print out file summary table, if we are printing out a page // for only one maintainer (his personal summary is in the table above) if (empty($MAINT)) { print << END_OF_MULTILINE; $files_sum = array_sum($files_by_mark); $file_sizes_sum = array_sum($file_sizes_by_mark); $file_types = array( array (REV_UPTODATE, "Up to date files"), array (REV_OLD, "Old files"), array (REV_CRITICAL, "Critical files"), array (REV_WIP, "Work in progress"), array (REV_NOREV, "Files without revision number"), array (REV_NOTAG, "Files without revision tag"), array (REV_NOTRANS, "Files available for translation") ); foreach ($file_types as $num => $type) { print "". "". "". "". "". "\n"; } print "\n". "
File status type Number of files Percent of files Size of files (kB) Percent of size
".$type[1]."".intval($files_by_mark[$type[0]])."".number_format($files_by_mark[$type[0]] * 100 / $files_sum, 2 ). "%".intval($file_sizes_by_mark[$type[0]])."".number_format($file_sizes_by_mark[$type[0]] * 100 / $file_sizes_sum, 2). "%
Files total$files_sum100%$file_sizes_sum100%
\n

 

\n"; } print ($navbar."

 

\n"); // ========================================================================= // Files table goes here // ========================================================================= if (count($files_status) != 0) { print << END_OF_MULTILINE; // This was the previous directory [first] $prev_dir = $new_dir = $DOCDIR."en"; // Go through all files collected foreach ($files_status as $num => $file) { // Make the maintainer a link, if we have that maintainer in the list if (isset($maint_by_nick[$file["maintainer"]])) { $file["maintainer"] = '' . $file["maintainer"] . ''; } // If we have a 'numeric' revision diff and it is not zero, // make a link to the CVS repository's diff script if ($file["revision"][2] != "n/a" && $file["revision"][2] !== 0) { $file["short_name"] = "" . $file["short_name"] . ""; } // Guess the new directory from the full name of the file $new_dir = dirname($file["full_name"]); // If this is a new directory, put out dir headline if ($new_dir != $prev_dir) { // Drop out the unneeded parts from the dirname... $display_dir = str_replace($DOCDIR."en/", "", dirname($file["full_name"])); // Print out directory header print "\n"; // Store the new actual directory $prev_dir = $new_dir; } // Write out the line for the current file (get file name shorter) print "". "" . "". "". "". "". "". "". "". "". "". "\n"; } print("
Translated file Revision Size in kB Age in days Maintainer Status
en $LANG diff en $LANG diff en $LANG diff
$display_dir
{$file['short_name']} {$file['revision'][0]} {$file['revision'][1]}{$file['revision'][2]} {$file['size'][0]} {$file['size'][1]} {$file['size'][2]} {$file['date'][0]} {$file['date'][1]} {$file['date'][2]} {$file['maintainer']}".trim($file['status'])."
\n

 

\n$navbar

 

\n"); } // ========================================================================= // Work in progress table goes here // ========================================================================= // If work-in-progress list is available (valid translation.xml file in lang) if (count($translation["files"]) != 0) { // Print out files table header print "\n" . "\n" . "". "". "". ""; // Print out date and revision columns if needed if ($using_date) { print ''; } if ($using_rev) { print '' . ''; } print "\n"; // Go through files, and print out lines for them foreach($translation["files"] as $num => $finfo) { // If we have a valid maintainer, link to the summary if (isset($maint_by_nick[$finfo["person"]])) { $finfo["person"] = '' . $finfo["person"] . ''; } // Print out the line with the first columns print "" . ""; // If we need the date column, print it out if ($using_date) { print ""; } // If we need the revision column, print it out if ($using_rev) { print ""; } // End the line print "\n"; // Collect files in WIP list $wip_files[$finfo["name"]] = TRUE; } print "
Work in progress filesTranslatorTypeDateCO-RevisionEN-Revision
$finfo[name]$finfo[person]$finfo[type]$finfo[date]$finfo[revision]" . $missing_files[$finfo["name"]][1] . "
\n

 

\n$navbar

 

\n"; } // Files translated, but without a revision comment $count = count($missing_tags); if ($count > 0) { print "" . "\n". "". "\n". "\n"; foreach($missing_tags as $val) { // Shorten the filename (we have directory headers) $short_file = basename($val[0]); // Guess the new directory from the full name of the file $new_dir = dirname($val[0]); // If this is a new directory, put out dir headline if ($new_dir != $prev_dir) { // Print out directory header print "\n"; // Store the new actual directory $prev_dir = $new_dir; } print "". "\n"; } print "
Files without Revision-comment ($count files):Sizes in kB
en$LANGdiff
$new_dir
$short_file$val[1]$val[2]$val[3]
\n

 

\n$navbar

 

\n"; } // Merge all work in progress files collected $wip_files = array_merge( $translation["files"], // Files for this translator $translation["allfiles"] // Files for all the translators ); // Delete wip entires from available files list foreach ($wip_files as $file) { if (isset($missing_files[$file['name']])) { unset($missing_files[$file['name']]); } } // Files not translated and not "wip" $count = count($missing_files); if ($count > 0) { print "" . "\n" . "\n"; foreach($missing_files as $file => $info) { // Shorten the filename (we have directory headers) $short_file = basename($file); // Guess the new directory from the full name of the file $new_dir = dirname($file); // If this is a new directory, put out dir headline if ($new_dir != $prev_dir) { // Print out directory header if not "." print "\n"; // Store the new actual directory $prev_dir = $new_dir; } print "" . "\n"; } print "
" . " Available for translation ($count files):kB
$new_dir
$short_file$info[0]
\n

 

\n$navbar

 

\n"; } // Files not in EN tree $count = count($old_files); if ($count > 0) { print "" . "\n" . "\n"; foreach($old_files as $file => $info) { // Shorten the filename (we have directory headers) $short_file = basename($file); // Guess the new directory from the full name of the file $new_dir = dirname($file); // If this is a new directory, put out dir headline if ($new_dir != $prev_dir) { // Print out directory header if not "." print "\n"; // Store the new actual directory $prev_dir = $new_dir; } print "" . "\n"; } print "
" . " Not in EN Tree ($count files):kB
$new_dir
$short_file$info[0]
\n

 

\n$navbar

 

\n"; } // All OK, end the file print "\n\n"; ?>