plugins/admin/LangManager.php
author Dan
Thu, 17 Dec 2009 04:27:50 -0500
changeset 1168 277a9cdead3e
parent 1081 745200a9cc2a
child 1227 bdac73ed481e
permissions -rw-r--r--
Namespace_Default: added a workaround for an inconsistency in SQL. Basically, if you join the same table multiple times under multiple aliases, COUNT() always uses the first instance. Was affecting the comment counter in the "discussion" button.

<?php

/*
 * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between
 * Copyright (C) 2006-2009 Dan Fuhry
 *
 * This program is Free Software; you can redistribute and/or modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
 */

function page_Admin_LangManager()
{
  global $db, $session, $paths, $template, $plugins; // Common objects
  global $lang;
  global $cache;
  if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
  {
    $login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
    echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
    echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
    return;
  }
  if ( isset($_POST['action']) )
  {
    $action =& $_POST['action'];
    // Parse parameters
    if ( strpos($action, ';') )
    {
      // Parameter section
      $parms = substr($action, strpos($action, ';') + 1);
      
      // Action name section
      $action = substr($action, 0, strpos($action, ';'));
      
      // Match all parameters
      preg_match_all('/([a-z0-9_]+)=(.+?)(;|$)/', $parms, $matches);
      $parms = array();
      
      // For each full parameter, assign $parms an associative value
      foreach ( $matches[0] as $i => $_ )
      {
        $parm = $matches[2][$i];
        
        // Is this parameter in the form of an integer?
        // (designed to ease validation later)
        if ( ctype_digit($parm) )
          // Yes, run intval(), this enabling is_int()-ish checks
          $parm = intval($parm);
        
        $parms[$matches[1][$i]] = $parm;
      }
    }
    switch ( $action )
    {
      case 'install_language':
        
        if ( defined('ENANO_DEMO_MODE') )
        {
          echo '<div class="error-box">' . $lang->get('acplm_err_lang_install_demo') . '</div>';
          break;
        }
        
        $lang_list = list_available_languages();
        // Verify that we have this language's metadata
        if ( isset($lang_list[@$parms['iso639']]) )
        {
          // From here it's all downhill :-)
          $lang_code =& $parms['iso639'];
          $lang_data =& $lang_list[$lang_code];
          
          $result = install_language($lang_code, $lang_data['name_eng'], $lang_data['name']);
          if ( $result )
          {
            // Language installed. Import the language files.
            $lang_local = new Language($lang_code);
            if ( file_exists(ENANO_ROOT . "/language/{$lang_data['dir']}/backup.json") )
            {
              $lang_local->import(ENANO_ROOT . "/language/{$lang_data['dir']}/backup.json");
            }
            else
            {
              foreach ( array('core', 'admin', 'tools', 'user') as $file )
              {
                $lang_local->import(ENANO_ROOT . "/language/{$lang_data['dir']}/$file.json");
              }
            }
            unset($lang_local);
            
            echo '<div class="info-box">' . $lang->get('acplm_msg_lang_install_success', array('lang_name' => htmlspecialchars($lang_data['name_eng']))) . '</div>';
          }
        }
        break;
      case 'modify_language':
        
        $lang_id =& $parms['lang_id'];
        if ( !is_int($lang_id) )
        {
          echo 'Hacking attempt';
          break;
        }
        
        if ( isset($parms['finish']) && !empty($_POST['lang_name_native']) && !empty($_POST['lang_name_english']) && !defined('ENANO_DEMO_MODE') )
        {
          // We just did validation above, it's safe to save.
          $name_native = $db->escape($_POST['lang_name_native']);
          $name_english = $db->escape($_POST['lang_name_english']);
          
          $q = $db->sql_query('UPDATE ' . table_prefix . "language SET lang_name_native = '$name_native', lang_name_default = '$name_english' WHERE lang_id = $lang_id;");
          if ( !$q )
            $db->_die();
          
          echo '<div class="info-box">' . $lang->get('acplm_msg_basic_save_success') . '</div>';
        }
        else if ( isset($parms['finish']) && defined('ENANO_DEMO_MODE') )
        {
          echo '<div class="error-box">' . $lang->get('acplm_err_lang_install_demo') . '</div>';
        }
        
        // Select language data
        $q = $db->sql_query('SELECT lang_name_native, lang_name_default, lang_code FROM ' . table_prefix . "language WHERE lang_id = $lang_id;");
        if ( !$q )
          $db->_die();
        
        list($name_native, $name_english, $lang_code) = $db->fetchrow_num();
        
        // Output properties table
        echo '<h3>' . $lang->get('acplm_heading_modify') . '</h3>';
        
        acp_start_form();
        
        ?>
          <div class="tblholder">
            <table border="0" cellspacing="1" cellpadding="4">
              <tr>
                <th colspan="2">
                  <?php
                    echo $lang->get('acplm_th_lang_basic');
                  ?>
                </th>
              </tr>
              <tr>
              <td class="row2" style="width: 50%;">
                  <?php
                    echo str_replace('"', '', $lang->get('acplm_field_lang_name_native'));
                  ?>
                </td>
                <td class="row1">
                  <input type="text" name="lang_name_native" value="<?php echo htmlspecialchars($name_native); ?>" />
                </td>
              </tr>
              <tr>
                <td class="row2">
                  <?php
                    echo $lang->get('acplm_field_lang_name_english');
                  ?>
                </td>
                <td class="row1">
                  <input type="text" name="lang_name_english" value="<?php echo htmlspecialchars($name_english); ?>" />
                </td>
              </tr>
              <tr>
                <td class="row2">
                  <?php
                    echo $lang->get('acplm_field_lang_code') . '<br />'
                       . '<small>' . $lang->get('acplm_field_lang_code_hint') . '</small>';
                  ?>
                </td>
                <td class="row1">
                  <?php
                    echo $lang_code;
                  ?>
                </td>
              </tr>
              <tr>
                <th class="subhead" colspan="2">
                  <button name="action" value="modify_language;finish=1;lang_id=<?php echo $lang_id; ?>"><?php echo $lang->get('etc_save_changes'); ?></button>
                </th>
              </tr>
            </table>
          </div>
        </form>
        
        <?php
        acp_start_form();
        ?>
        
        <h3><?php echo $lang->get('acplm_heading_edit_strings_portal'); ?></h3>
        <p><?php echo $lang->get('acplm_msg_edit_strings_portal_intro'); ?></p>
        
        <p>
        
        <?php
        
        // Grab a Language object
        if ( $lang->lang_id == $lang_id )
        {
          $lang_local =& $lang;
        }
        else
        {
          $lang_local = new Language($lang_id);
          $lang_local->fetch();
        }
        
        $categories_loc = array();
        
        // Using the & here ensures that a reference is created, thus avoiding wasting memory
        foreach ( $lang_local->strings as $cat => &$_ )
        {
          unset($_);
          $categories_loc[$cat] = htmlspecialchars($lang->get("meta_$cat"));
        }
        
        asort($categories_loc);
        
        echo '<select name="cat_id">';
        foreach ( $categories_loc as $cat_id => $cat_name)
        {
          echo "<option value=\"$cat_id\">$cat_name</option>";
        }
        echo '</select>';
        
        ?>
        <button name="action" value="edit_strings;lang_id=<?php echo $lang_id; ?>">
          <?php echo $lang->get('acplm_btn_edit_strings_portal'); ?>
        </button>
        </p>
        
        <h3><?php echo $lang->get('acplm_heading_reimport_portal'); ?></h3>
        <p><?php echo $lang->get('acplm_msg_reimport_portal_intro'); ?></p>
        
        <p>
          <button name="action" value="reimport;iso639=<?php echo $lang_code; ?>;lang_id=<?php echo $lang_id; ?>">
            <?php echo $lang->get('acplm_btn_reimport'); ?>
          </button>
        </p>
        
        </form>
        
        <?php
        
        echo '<h3>' . $lang->get('acplm_heading_backup') . '</h3>';
        echo '<p>' . $lang->get('acplm_backup_intro') . '</p>';
        
        echo '<form action="' . makeUrlNS('Admin', 'LangManager') . '" method="post">';
        echo '<button name="action" value="backup_language;lang_id=' . $lang_id . '">' . $lang->get('acplm_btn_create_backup') . '</button>';
        echo '</form>';
        
        return true;
      case 'edit_strings':
        
        $cat_id = @$_POST['cat_id'];
        if ( !preg_match('/^[a-z0-9]+$/', $cat_id) || !is_int(@$parms['lang_id']) )
          break;
        
        $lang_id =& $parms['lang_id'];
        
        if ( isset($parms['save']) && !defined('ENANO_DEMO_MODE') )
        {
          // Grab a Language object
          if ( $lang->lang_id == $lang_id )
          {
            $lang_local =& $lang;
          }
          else
          {
            $lang_local = new Language($lang_id);
          }
          // Main save loop
          // Trying to minimize queries as much as possible here, but you know how that goes.
          $count_upd = 0;
          foreach ( $_POST['string'] as $string_id => $user_content )
          {
            $curr_content = $lang_local->get_uncensored("{$cat_id}_{$string_id}");
            if ( $curr_content != $user_content )
            {
              $count_upd++;
              $user_content = $db->escape($user_content);
              $string_id = $db->escape($string_id);
              $q = $db->sql_query('UPDATE ' . table_prefix . "language_strings SET string_content = '$user_content' WHERE lang_id = $lang_id AND string_category = '$cat_id' AND string_name = '$string_id';");
              if ( !$q )
                $db->_die();
            }
          }
          if ( $count_upd > 0 )
          {
            // Update the cache
            $lang_local->regen_caches();
            
            // Update modification time
            $q = $db->sql_query('UPDATE ' . table_prefix . "language SET last_changed = " . time() . " WHERE lang_id = $lang_id;");
            if ( !$q )
              $db->_die();
          }
          
          echo '<div class="info-box">' . $lang->get('acplm_msg_string_save_success') . '</div>';
        }
        else if ( isset($parms['save']) && defined('ENANO_DEMO_MODE') )
        {
          echo '<div class="error-box">' . $lang->get('acplm_err_lang_install_demo') . '</div>';
        }
        
        acp_start_form();
        
        $cat_name = $lang->get("meta_$cat_id");
        echo '<h3>' . $lang->get('acplm_editor_heading', array('cat_name' => $cat_name)) . '</h3>';
        
        // Fetch all strings
        // This is more efficient than iterating through $lang->strings, I think.
        $q = $db->sql_query('SELECT string_id, string_name, string_content FROM ' . table_prefix . "language_strings WHERE string_category = '$cat_id' AND lang_id = $lang_id;");
        if ( !$q )
          $db->_die();
        
        ?>
        <div class="tblholder">
          <table border="0" cellspacing="1" cellpadding="4">
            <tr>
              <th style="width: 3%;"><?php echo $lang->get('acplm_editor_col_string_name'); ?></th>
              <th><?php echo $lang->get('acplm_editor_col_string_content'); ?></th>
            </tr>
        <?php
        
        while ( $row = $db->fetchrow_num() )
        {
          list($string_id, $string_name, $string_content) = $row;
          unset($row);
          
          echo '<tr>';
          
          if ( strpos($string_content, "\n") )
          {
            $editor = '<textarea rows="' . get_line_count($string_content) . '" cols="50" style="width: 99%;" ';
            $editor .= 'name="string[' . htmlspecialchars($string_name) . ']" ';
            $editor .= '>' . htmlspecialchars($string_content);
            $editor .= '</textarea>';
          }
          else
          {
            $editor = '<input type="text" size="50" style="width: 99%;" ';
            $editor .= 'name="string[' . htmlspecialchars($string_name) . ']" ';
            $editor .= 'value="' . htmlspecialchars($string_content) . '" ';
            $editor .= '/>';
          }
          
          echo '<td class="row2">' . htmlspecialchars($string_name) . '</td>';
          echo '<td class="row1">' . $editor . '</td>';
          
          
          echo '</tr>';
          echo "\n";
        }
        
        echo '<tr>
                <th class="subhead" colspan="2">';
                
        echo '<input type="hidden" name="cat_id" value="' . $cat_id . '" />';
                
        // Button: save
        echo '<button name="action" value="edit_strings;lang_id=' . $lang_id . ';save=1" style="font-weight: bold;">' . $lang->get('etc_save_changes') . '</button> ';
        // Button: revert
        echo '<button name="action" value="edit_strings;lang_id=' . $lang_id . '" style="font-weight: normal;">' . $lang->get('acplm_editor_btn_revert') . '</button> ';
        // Button: cancel
        echo '<button name="action" value="modify_language;lang_id=' . $lang_id . '" style="font-weight: normal;">' . $lang->get('acplm_editor_btn_cancel') . '</button>';
                
        echo '  </th>
              </tr>';
        
        ?>
          </table>
        </div>
        <?php
        echo '</form>';
        
        return true;
      case 'reimport':
        if ( !isset($parms['iso639']) || !is_int(@$parms['lang_id']) )
          break;
        
        if ( defined('ENANO_DEMO_MODE') )
        {
          echo '<div class="error-box">' . $lang->get('acplm_err_lang_install_demo') . '</div>';
          break;
        }
        
        $lang_code =& $parms['iso639'];
        $lang_id =& $parms['lang_id'];
        
        $lang_list = list_available_languages();
        
        if ( !isset($lang_list[$lang_code]) )
          break;
        
        // Grab a Language object
        if ( $lang->lang_id == $lang_id )
        {
          $lang_local =& $lang;
        }
        else
        {
          $lang_local = new Language($lang_id);
        }
        
        $lang_data =& $lang_list[$lang_code];
        
        // This is the big re-import loop
        if ( file_exists(ENANO_ROOT . "/language/{$lang_data['dir']}/backup.json") )
        {
          $lang_local->import(ENANO_ROOT . "/language/{$lang_data['dir']}/backup.json");
        }
        else
        {
          foreach ( array('core', 'admin', 'tools', 'user') as $file )
          {
            $lang_local->import(ENANO_ROOT . "/language/{$lang_data['dir']}/$file.json");
          }
        }
        
        echo '<div class="info-box">' . $lang->get('acplm_msg_reimport_success') . '</div>';
        
        break;
      case 'backup_language':
        if ( !is_int(@$parms['lang_id']) )
          break;
        
        $lang_id =& $parms['lang_id'];
        
        // Grab a Language object
        if ( $lang->lang_id == $lang_id )
        {
          $lang_local =& $lang;
        }
        else
        {
          $lang_local = new Language($lang_id);
        }
        
        $filename = 'enano_lang_' . $lang_local->lang_code . '_' . enano_date('ymd') . '.json';
        
        // Free as much memory as possible
        $db->close();
        unset($GLOBALS['session'], $GLOBALS['paths'], $GLOBALS['template'], $GLOBALS['plugins']);
        
        // HTTP headers
        header('Content-type: application/json');
        header('Content-disposition: attachment; filename=' . $filename);
        
        // Export to JSON
        $lang_struct = array(
            'categories' => array_keys($lang_local->strings),
            'strings' => $lang_local->strings
          );
        
        $lang_struct = enano_json_encode($lang_struct);
        
        header('Content-length: ' . strlen($lang_struct));
        echo $lang_struct;
        
        exit;
        
      case 'uninstall_language':
        if ( !is_int(@$parms['lang_id']) )
          break;
        
        if ( defined('ENANO_DEMO_MODE') )
        {
          echo '<div class="error-box">' . $lang->get('acplm_err_lang_install_demo') . '</div>';
          break;
        }
        
        $lang_id =& $parms['lang_id'];
        
        if ( isset($parms['confirm']) )
        {
          $lang_default = intval(getConfig('default_language'));
          if ( $lang_default == $lang_id )
          {
            echo '<div class="error-box">' . $lang->get('acplm_err_cant_uninstall_default') . '</div>';
            break;
          }
          if ( $lang_id == $lang->lang_id )
          {
            // Unload the current language since it's about to be uninstalled
            unset($lang, $GLOBALS['lang']);
            $GLOBALS['lang'] = new Language($lang_default);
            global $lang;
          }
          // We're clear
          
          // Remove cache files
          $cache_file = ENANO_ROOT . "/cache/lang_{$lang_id}.php";
          if ( file_exists($cache_file) )
            @unlink($cache_file);
          
          $cache->purge("lang_{$lang_id}");
          
          // Remove strings
          $q = $db->sql_query('DELETE FROM ' . table_prefix . "language_strings WHERE lang_id = $lang_id;");
          if ( !$q )
            $db->_die();
          
          // Delete the language
          $q = $db->sql_query('DELETE FROM ' . table_prefix . "language WHERE lang_id = $lang_id;");
          if ( !$q )
            $db->_die();
          
          echo '<div class="info-box">' . $lang->get('acplm_msg_uninstall_success') . '</div>';
          break;
        }
        
        acp_start_form();
        
        echo '<h3>' . $lang->get('acplm_uninstall_confirm_title') . '</h3>';
        echo '<p>' . $lang->get('acplm_uninstall_confirm_body') . '</p>';
        
        echo '<p><button name="action" style="font-weight: bold;" value="uninstall_language;lang_id=' . $lang_id . ';confirm=1">' . $lang->get('acplm_btn_uninstall_confirm') . '</button> ';
        echo '<button name="action" value="home">' . $lang->get('acplm_btn_uninstall_cancel') . '</button></p>';
        
        echo '</form>';
        return true;
    }
  }
  
  acp_start_form();
  
  // Select current languages
  $q = $db->sql_query('SELECT lang_code, lang_name_native, lang_name_default, lang_id FROM ' . table_prefix . "language ORDER BY lang_id ASC;");
  if ( !$q )
    $db->_die();
  
  // Language properties/edit/delete portal table
  echo '<h3>' . $lang->get('acplm_heading_editor_portal') . '</h3>';
  
  echo '<div class="tblholder">';
  echo '<table border="0" cellspacing="1" cellpadding="4">';
  echo '<tr>
          <th>' . $lang->get('acplm_col_lang_id') . '</th>
          <th>' . $lang->get('acplm_col_lang_code') . '</th>
          <th>' . $lang->get('acplm_col_lang_name') . '</th>
          <th>' . $lang->get('acplm_col_lang_name_eng') . '</th>
          <th></th>
        </tr>';
  
  $cls = 'row2';
  
  $btn_edit = $lang->get('acplm_portal_btn_edit');
  $btn_unin = $lang->get('acplm_portal_btn_unin');
  
  while ( $row = $db->fetchrow($q) )
  {
    $cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
    
    echo '<tr>';
    
    $lang_code = htmlspecialchars($row['lang_code']);
    $lang_name_native  = htmlspecialchars($row['lang_name_native']);
    $lang_name_english = htmlspecialchars($row['lang_name_default']);
    
    echo "<td class=\"$cls\" style=\"text-align: center;\">{$row['lang_id']}</td>";
    echo "<td class=\"$cls\" style=\"text-align: center;\">{$lang_code}</td>";
    echo "<td class=\"$cls\" style=\"text-align: center;\">{$lang_name_native}</td>";
    echo "<td class=\"$cls\" style=\"text-align: center;\">{$lang_name_english}</td>";
    echo "<td class=\"$cls\" style=\"text-align: center;\"><button name=\"action\" value=\"modify_language;lang_id={$row['lang_id']}\">$btn_edit</button> <button name=\"action\" value=\"uninstall_language;lang_id={$row['lang_id']}\">$btn_unin</button></td>";
    
    echo '</tr>';
  }
  
  echo '</table></div>';
  
  // Reset the result pointer to zero so we can fetch that list of languages again
  if ( !$db->sql_data_seek(0, $q) )
  {
    $db->_die('LangManager doing seek back to zero for installation blacklist');
  }
  
  // $lang_list is fetched by the posthandler sometimes
  if ( !isset($lang_list) )
  {
    // Build a list of languages in the languages/ directory, then
    // eliminate the ones that are already installed.
    $lang_list = list_available_languages();
  }
  
  while ( $row = $db->fetchrow($q) )
  {
    $lang_code =& $row['lang_code'];
    if ( isset($lang_list[$lang_code]) )
    {
      unset($lang_list[$lang_code]);
      unset($lang_list[$lang_code]); // PHP <5.1.4 Zend bug
    }
  }
  
  if ( count($lang_list) > 0 )
  {
    echo '<h3>' . $lang->get('acplm_heading_install') . '</h3>';
    echo '<div class="tblholder">
            <table border="0" cellspacing="1" cellpadding="4">
              <tr>
                <th>' . $lang->get('acplm_col_lang_code') . '</th>
                <th>' . $lang->get('acplm_col_lang_name') . '</th>
                <th>' . $lang->get('acplm_col_lang_name_eng') . '</th>
                <th></th>
              </tr>';
              
    $cls = 'row2';
    foreach ( $lang_list as $lang_code => $lang_data )
    {
      $cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
      
      echo '<tr>';
      
      $lang_code = htmlspecialchars($lang_code);
      $lang_data['name'] = htmlspecialchars($lang_data['name']);
      $lang_data['name_eng'] = htmlspecialchars($lang_data['name_eng']);
      
      echo "<td class=\"$cls\" style=\"text-align: center;\">$lang_code</td>";
      echo "<td class=\"$cls\" style=\"text-align: center;\">{$lang_data['name']}</td>";
      echo "<td class=\"$cls\" style=\"text-align: center;\">{$lang_data['name_eng']}</td>";
      echo "<td class=\"$cls\" style=\"text-align: center;\"><button name=\"action\" value=\"install_language;iso639=$lang_code\">" . $lang->get('acplm_btn_install_language') . "</button></td>";
      
      echo '</tr>';
    }
    echo '    </tr>
            </table>
          </div>';
  }
  echo '</form>';
}