[ Index ]

PHP Cross Reference of GlotPress

title

Body

[close]

/gp-templates/ -> translations.php (source)

   1  <?php
   2  /**
   3   * Template for the translations table.
   4   *
   5   * @package    GlotPress
   6   * @subpackage Templates
   7   */
   8  
   9  /** @var GP_Translation_Set $translation_set */
  10  
  11  gp_title(
  12      sprintf(
  13          /* translators: 1: Translation set name. 2: Project name. */
  14          __( 'Translations &lt; %1$s &lt; %2$s &lt; GlotPress', 'glotpress' ),
  15          $translation_set->name,
  16          $project->name
  17      )
  18  );
  19  gp_breadcrumb(
  20      array(
  21          gp_project_links_from_root( $project ),
  22          gp_link_get( $url, $translation_set->name ),
  23      )
  24  );
  25  gp_enqueue_scripts( array( 'gp-editor', 'gp-translations-page' ) );
  26  wp_localize_script(
  27      'gp-translations-page',
  28      '$gp_translations_options',
  29      array(
  30          'sort'   => __( 'Sort', 'glotpress' ),
  31          'filter' => __( 'Filter', 'glotpress' ),
  32      )
  33  );
  34  
  35  // localizer adds var in front of the variable name, so we can't use $gp.editor.options
  36  $editor_options = compact( 'can_approve', 'can_write', 'url', 'discard_warning_url', 'set_priority_url', 'set_status_url' );
  37  
  38  wp_localize_script( 'gp-editor', '$gp_editor_options', $editor_options );
  39  
  40  gp_tmpl_header();
  41  $i = 0;
  42  ?>
  43  <h2>
  44      <?php
  45      printf(
  46          /* translators: 1: Project name. 2: Translation set name. */
  47          __( 'Translation of %1$s: %2$s', 'glotpress' ),
  48          esc_html( $project->name ),
  49          esc_html( $translation_set->name )
  50      );
  51      ?>
  52      <?php gp_link_set_edit( $translation_set, $project, __( '(edit)', 'glotpress' ) ); ?>
  53      <?php gp_link_set_delete( $translation_set, $project, __( '(delete)', 'glotpress' ) ); ?>
  54      <?php if ( $glossary && $glossary->translation_set_id === $translation_set->id ) : ?>
  55      <?php echo gp_link( $glossary->path(), __( 'Glossary', 'glotpress' ), array( 'class' => 'glossary-link' ) ); ?>
  56      <?php elseif ( $can_approve ) : ?>
  57          <?php echo gp_link_get( gp_url( '/glossaries/-new', array( 'translation_set_id' => $translation_set->id ) ), __( 'Create Glossary', 'glotpress' ), array( 'class' => 'glossary-link' ) ); ?>
  58      <?php endif; ?>
  59  </h2>
  60  
  61  <div class="filter-toolbar">
  62      <form id="upper-filters-toolbar" class="filters-toolbar" action="" method="get" accept-charset="utf-8">
  63          <div>
  64          <a href="#" class="revealing filter"><?php _e( 'Filter &darr;', 'glotpress' ); ?></a> <span class="separator">&bull;</span>
  65          <a href="#" class="revealing sort"><?php _e( 'Sort &darr;', 'glotpress' ); ?></a> <strong class="separator">&bull;</strong>
  66          <?php
  67          $current_filter = '';
  68          $filter_links   = array();
  69  
  70          // Use array_filter() to remove empty values, store them for use later if a custom filter has been applied.
  71          $filters_values_only = array_filter( $filters );
  72          $sort_values_only    = array_filter( $sort );
  73          $filters_and_sort    = array_merge( $filters_values_only, $sort_values_only );
  74  
  75          /**
  76           * Check to see if a term or user login has been added to the filter or one of the other filter options, if so,
  77           * we don't want to match the standard filter links.
  78           *
  79           * Note: Don't check for the warnings filter here otherwise we won't be able to use this value during the check
  80           * to see if the warnings filter link entry is the currently selected filter.
  81           */
  82          $additional_filters = array_key_exists( 'term', $filters_and_sort ) ||
  83                                  array_key_exists( 'user_login', $filters_and_sort ) ||
  84                                  array_key_exists( 'with_comment', $filters_and_sort ) ||
  85                                  array_key_exists( 'case_sensitive', $filters_and_sort ) ||
  86                                  array_key_exists( 'with_plural', $filters_and_sort ) ||
  87                                  array_key_exists( 'with_context', $filters_and_sort );
  88  
  89          // Because 'warnings' is not a translation status we need to know if we're filtering on it before we check
  90          // for what filter links to add.
  91          $warnings_filter = array_key_exists( 'warnings', $filters_and_sort );
  92  
  93          $all_filters = array(
  94              'status' => 'current_or_waiting_or_fuzzy_or_untranslated',
  95          );
  96  
  97          $current_filter_class = array(
  98              'class' => 'filter-current',
  99          );
 100  
 101          $is_current_filter = ( array() === array_diff( $all_filters, $filters_and_sort ) || array() === $filters_and_sort ) && ! $additional_filters && ! $warnings_filter;
 102          $current_filter    = $is_current_filter ? 'all' : $current_filter;
 103  
 104          $filter_links[] = gp_link_get(
 105              $url,
 106              // Translators: %s is the total strings count for the current translation set.
 107              sprintf( __( 'All&nbsp;(%s)', 'glotpress' ), number_format_i18n( $translation_set->all_count() ) ),
 108              $is_current_filter ? $current_filter_class : array()
 109          );
 110  
 111          $translated_filters = array(
 112              'filters[translated]' => 'yes',
 113              'filters[status]'     => 'current',
 114          );
 115  
 116          $is_current_filter = array() === array_diff( $translated_filters, $filters_and_sort ) && false === $additional_filters && ! $warnings_filter;
 117          $current_filter    = $is_current_filter ? 'translated' : $current_filter;
 118  
 119          $filter_links[] = gp_link_get(
 120              add_query_arg( $translated_filters, $url ),
 121              // Translators: %s is the translated strings count for the current translation set.
 122              sprintf( __( 'Translated&nbsp;(%s)', 'glotpress' ), number_format_i18n( $translation_set->current_count() ) ),
 123              $is_current_filter ? $current_filter_class : array()
 124          );
 125  
 126          $untranslated_filters = array(
 127              'filters[status]' => 'untranslated',
 128          );
 129  
 130          $is_current_filter = array() === array_diff( $untranslated_filters, $filters_and_sort ) && false === $additional_filters && ! $warnings_filter;
 131          $current_filter    = $is_current_filter ? 'untranslated' : $current_filter;
 132  
 133          $filter_links[] = gp_link_get(
 134              add_query_arg( $untranslated_filters, $url ),
 135              // Translators: %s is the untranslated strings count for the current translation set.
 136              sprintf( __( 'Untranslated&nbsp;(%s)', 'glotpress' ), number_format_i18n( $translation_set->untranslated_count() ) ),
 137              $is_current_filter ? $current_filter_class : array()
 138          );
 139  
 140          $waiting_filters = array(
 141              'filters[translated]' => 'yes',
 142              'filters[status]'     => 'waiting',
 143          );
 144  
 145          $is_current_filter = array() === array_diff( $waiting_filters, $filters_and_sort ) && ! $additional_filters && ! $warnings_filter;
 146          $current_filter    = $is_current_filter ? 'waiting' : $current_filter;
 147  
 148          $filter_links[] = gp_link_get(
 149              add_query_arg( $waiting_filters, $url ),
 150              // Translators: %s is the waiting strings count for the current translation set.
 151              sprintf( __( 'Waiting&nbsp;(%s)', 'glotpress' ), number_format_i18n( $translation_set->waiting_count() ) ),
 152              $is_current_filter ? $current_filter_class : array()
 153          );
 154  
 155          $fuzzy_filters = array(
 156              'filters[translated]' => 'yes',
 157              'filters[status]'     => 'fuzzy',
 158          );
 159  
 160          $is_current_filter = array() === array_diff( $fuzzy_filters, $filters_and_sort ) && ! $additional_filters && ! $warnings_filter;
 161          $current_filter    = $is_current_filter ? 'fuzzy' : $current_filter;
 162  
 163          $filter_links[] = gp_link_get(
 164              add_query_arg( $fuzzy_filters, $url ),
 165              // Translators: %s is the fuzzy strings count for the current translation set.
 166              sprintf( __( 'Fuzzy&nbsp;(%s)', 'glotpress' ), number_format_i18n( $translation_set->fuzzy_count() ) ),
 167              $is_current_filter ? $current_filter_class : array()
 168          );
 169  
 170          $warning_filters = array(
 171              'filters[warnings]' => 'yes',
 172          );
 173  
 174          $is_current_filter = array() === array_diff( $warning_filters, $filters_and_sort ) && ! $additional_filters && ! array_key_exists( 'status', $filters_and_sort );
 175          $current_filter    = $is_current_filter ? 'warning' : $current_filter;
 176  
 177          $filter_links[] = gp_link_get(
 178              add_query_arg( $warning_filters, $url ),
 179              // Translators: %s is the strings with warnings count for the current translation set.
 180              sprintf( __( 'Warnings&nbsp;(%s)', 'glotpress' ), number_format_i18n( $translation_set->warnings_count() ) ),
 181              $is_current_filter ? $current_filter_class : array()
 182          );
 183  
 184          // If no filter has been selected yet, then add the current filter count to the end of the filter links array.
 185          if ( '' === $current_filter ) {
 186              // Build an array or query args to add to the link using the current sort/filter options.
 187              $custom_filter = array();
 188  
 189              foreach ( $filters_values_only as $key => $value ) {
 190                  $custom_filter[ 'filters[' . $key . ']' ] = $value;
 191              }
 192  
 193              foreach ( $sort_values_only as $key => $value ) {
 194                  $custom_filter[ 'sort[' . $key . ']' ] = $value;
 195              }
 196  
 197              $filter_links[] = gp_link_get(
 198                  add_query_arg( $custom_filter, $url ),
 199                  // Translators: %s is the strings with the current filter count for the current translation set.
 200                  sprintf( __( 'Current&nbsp;Filter&nbsp;(%s)', 'glotpress' ), number_format_i18n( $total_translations_count ) ),
 201                  $current_filter_class
 202              );
 203          }
 204  
 205          // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
 206          echo implode( ' <span class="separator">&bull;</span> ', $filter_links );
 207          ?>
 208          </div>
 209          <div class="filters-expanded filters hidden">
 210              <div class="filters-expanded-section">
 211                  <label for="filters[term]" class="filter-title"><?php _e( 'Term:', 'glotpress' ); ?></label><br />
 212                  <input type="text" value="<?php echo gp_esc_attr_with_entities( gp_array_get( $filters, 'term' ) ); ?>" name="filters[term]" id="filters[term]" /><br />
 213  
 214                  <fieldset>
 215                      <legend class="filter-title"><?php _e( 'Term Scope:', 'glotpress' ); ?></legend>
 216                      <?php
 217                      echo gp_radio_buttons(
 218                          'filters[term_scope]',
 219                          array(
 220                              'scope_originals'    => __( 'Originals only', 'glotpress' ),
 221                              'scope_translations' => __( 'Translations only', 'glotpress' ),
 222                              'scope_context'      => __( 'Context only', 'glotpress' ),
 223                              'scope_references'   => __( 'References only', 'glotpress' ),
 224                              'scope_both'         => __( 'Both Originals and Translations', 'glotpress' ),
 225                              'scope_any'          => __( 'Any', 'glotpress' ),
 226                          ),
 227                          gp_array_get( $filters, 'term_scope', 'scope_any' )
 228                      );
 229                      ?>
 230                  </fieldset>
 231              </div>
 232  
 233              <div class="filters-expanded-section">
 234                  <fieldset>
 235                      <legend class="filter-title"><?php _e( 'Status:', 'glotpress' ); ?></legend>
 236                      <?php
 237                      echo gp_radio_buttons(
 238                          'filters[status]', // TODO: show only these, which user is allowed to see afterwards.
 239                          array(
 240                              'current_or_waiting_or_fuzzy_or_untranslated' => __( 'Current/waiting/fuzzy + untranslated (All)', 'glotpress' ),
 241                              'current'                                     => __( 'Current only', 'glotpress' ),
 242                              'old'                                         => __( 'Approved, but obsoleted by another translation', 'glotpress' ),
 243                              'waiting'                                     => __( 'Waiting approval', 'glotpress' ),
 244                              'rejected'                                    => __( 'Rejected', 'glotpress' ),
 245                              'untranslated'                                => __( 'Without current translation', 'glotpress' ),
 246                              'either'                                      => __( 'Any', 'glotpress' ),
 247                          ),
 248                          gp_array_get( $filters, 'status', 'current_or_waiting_or_fuzzy_or_untranslated' )
 249                      );
 250                      ?>
 251                  </fieldset>
 252              </div>
 253  
 254              <div class="filters-expanded-section">
 255                  <fieldset>
 256                      <legend class="filter-title"><?php _e( 'Options:', 'glotpress' ); ?></legend>
 257                      <input type="checkbox" name="filters[with_comment]" value="yes" id="filters[with_comment][yes]" <?php gp_checked( 'yes' === gp_array_get( $filters, 'with_comment' ) ); ?>>&nbsp;<label for='filters[with_comment][yes]'><?php _e( 'With comment', 'glotpress' ); ?></label><br />
 258                      <input type="checkbox" name="filters[with_context]" value="yes" id="filters[with_context][yes]" <?php gp_checked( 'yes' === gp_array_get( $filters, 'with_context' ) ); ?>>&nbsp;<label for='filters[with_context][yes]'><?php _e( 'With context', 'glotpress' ); ?></label><br />
 259                      <input type="checkbox" name="filters[warnings]" value="yes" id="filters[warnings][yes]" <?php gp_checked( 'yes' === gp_array_get( $filters, 'warnings' ) ); ?>>&nbsp;<label for='filters[warnings][yes]'><?php _e( 'With warnings', 'glotpress' ); ?></label><br />
 260                      <input type="checkbox" name="filters[with_plural]" value="yes" id="filters[with_plural][yes]" <?php gp_checked( 'yes' === gp_array_get( $filters, 'with_plural' ) ); ?>>&nbsp;<label for='filters[with_plural][yes]'><?php _e( 'With plural', 'glotpress' ); ?></label><br />
 261                      <input type="checkbox" name="filters[case_sensitive]" value="yes" id="filters[case_sensitive][yes]" <?php gp_checked( 'yes' === gp_array_get( $filters, 'case_sensitive' ) ); ?>>&nbsp;<label for='filters[case_sensitive][yes]'><?php _e( 'Case sensitive', 'glotpress' ); ?></label>
 262                  </fieldset>
 263              </div>
 264  
 265              <div class="filters-expanded-section">
 266                  <label for="filters[user_login]" class="filter-title"><?php _e( 'User:', 'glotpress' ); ?></label><br />
 267                  <input type="text" value="<?php echo gp_esc_attr_with_entities( gp_array_get( $filters, 'user_login' ) ); ?>" name="filters[user_login]" id="filters[user_login]" /><br />
 268              </div>
 269  
 270              <?php
 271              /**
 272               * Fires after the translation set filters options.
 273               *
 274               * This action is inside a DL element.
 275               *
 276               * @since 2.1.0
 277               */
 278              do_action( 'gp_translation_set_filters_form' );
 279              ?>
 280  
 281              <div class="filters-expanded-actions">
 282                  <input type="submit" class="button" value="<?php esc_attr_e( 'Apply Filters', 'glotpress' ); ?>" name="filter" />
 283              </div>
 284          </div>
 285          <div class="filters-expanded sort hidden">
 286              <div class="filters-expanded-section">
 287                  <fieldset>
 288                      <legend class="filter-title"><?php _ex( 'By:', 'sort by', 'glotpress' ); ?></legend>
 289                      <?php
 290                      $default_sort = get_user_option( 'gp_default_sort' );
 291                      if ( ! is_array( $default_sort ) ) {
 292                          $default_sort = array(
 293                              'by'  => 'priority',
 294                              'how' => 'desc',
 295                          );
 296                      }
 297  
 298                      $sort_bys = wp_list_pluck( gp_get_sort_by_fields(), 'title' );
 299                      echo gp_radio_buttons( 'sort[by]', $sort_bys, gp_array_get( $sort, 'by', $default_sort['by'] ) );
 300                      ?>
 301                  </fieldset>
 302              </div>
 303  
 304              <div class="filters-expanded-section">
 305                  <fieldset>
 306                      <legend class="filter-title"><?php _e( 'Order:', 'glotpress' ); ?></legend>
 307                      <?php
 308                      echo gp_radio_buttons(
 309                          'sort[how]',
 310                          array(
 311                              'asc'  => __( 'Ascending', 'glotpress' ),
 312                              'desc' => __( 'Descending', 'glotpress' ),
 313                          ),
 314                          gp_array_get( $sort, 'how', $default_sort['how'] )
 315                      );
 316                      ?>
 317                  </fieldset>
 318              </div>
 319  
 320              <?php
 321              /**
 322               * Fires after the translation set sort options.
 323               *
 324               * This action is inside a DL element.
 325               *
 326               * @deprecated 2.1.0 Call gp_translation_set_sort_form instead
 327               * @since 1.0.0
 328               */
 329              do_action( 'gp_translation_set_filters' );
 330  
 331              /**
 332               * Fires after the translation set sort options.
 333               *
 334               * This action is inside a DL element.
 335               *
 336               * @since 2.1.0
 337               */
 338              do_action( 'gp_translation_set_sort_form' );
 339              ?>
 340  
 341              <div class="filters-expanded-actions">
 342                  <input type="submit" class="button" value="<?php esc_attr_e( 'Apply Sorting', 'glotpress' ); ?>" name="sorts" />
 343              </div>
 344          </div>
 345      </form>
 346  </div>
 347  
 348  <?php
 349  if ( $can_approve ) {
 350      gp_translations_bulk_actions_toolbar( $bulk_action, $can_write, $translation_set, 'top' );
 351  }
 352  
 353  echo gp_pagination( $page, $per_page, $total_translations_count );
 354  
 355  $class_rtl = 'rtl' === $locale->text_direction ? ' translation-sets-rtl' : '';
 356  ?>
 357  <table id="translations" class="translations clear<?php echo esc_attr( $class_rtl ); ?>">
 358      <thead>
 359      <tr>
 360          <?php
 361          if ( $can_approve ) :
 362              ?>
 363              <th class="checkbox"><input type="checkbox" /></th>
 364              <?php
 365          endif;
 366          ?>
 367          <th class="priority"><?php /* Translators: Priority */ _e( 'Prio', 'glotpress' ); ?></th>
 368          <th class="original"><?php _e( 'Original string', 'glotpress' ); ?></th>
 369          <th class="translation"><?php _e( 'Translation', 'glotpress' ); ?></th>
 370          <th class="actions">&mdash;</th>
 371      </tr>
 372      </thead>
 373  <?php
 374      if ( $glossary ) {
 375          $glossary_entries       = $glossary->get_entries();
 376          $glossary_entries_terms = gp_sort_glossary_entries_terms( $glossary_entries );
 377      }
 378  
 379      $root_locale          = null;
 380      $root_translation_set = null;
 381      $has_root             = null;
 382  
 383      if ( null !== $locale->variant_root ) {
 384          $root_locale          = GP_Locales::by_slug( $locale->variant_root );
 385          $root_translation_set = GP::$translation_set->by_project_id_slug_and_locale( $project->id, $translation_set->slug, $locale->variant_root );
 386  
 387          // Only set the root translation flag if we have a valid root translation set, otherwise there's no point in querying it later.
 388          if ( null !== $root_translation_set ) {
 389              $has_root = true;
 390          }
 391      }
 392  
 393      foreach ( $translations as $translation ) {
 394          if ( ! $translation->translation_set_id ) {
 395              $translation->translation_set_id = $translation_set->id;
 396          }
 397  
 398      $can_approve_translation = GP::$permission->current_user_can( 'approve', 'translation', $translation->id, array( 'translation' => $translation ) );
 399      gp_tmpl_load( 'translation-row', get_defined_vars() );
 400  }
 401  ?>
 402  <?php
 403      if ( ! $translations ) :
 404  ?>
 405      <tr><td colspan="<?php echo $can_approve ? 5 : 4; ?>"><?php _e( 'No translations were found!', 'glotpress' ); ?></td></tr>
 406  <?php
 407      endif;
 408  ?>
 409  </table>
 410  <?php
 411  if ( $can_approve ) {
 412      gp_translations_bulk_actions_toolbar( $bulk_action, $can_write, $translation_set, 'bottom' );
 413  }
 414  
 415  echo gp_pagination( $page, $per_page, $total_translations_count );
 416  ?>
 417  <div id="legend" class="secondary clearfix">
 418      <div><strong><?php _e( 'Legend:', 'glotpress' ); ?></strong></div>
 419  <?php
 420      foreach ( GP::$translation->get_static( 'statuses' ) as $legend_status ) :
 421  ?>
 422      <div class="box status-<?php echo esc_attr( $legend_status ); ?>"></div>
 423      <div>
 424  <?php
 425          switch ( $legend_status ) {
 426              case 'current':
 427                  _e( 'Current', 'glotpress' );
 428                  break;
 429              case 'waiting':
 430                  _e( 'Waiting', 'glotpress' );
 431                  break;
 432              case 'fuzzy':
 433                  _e( 'Fuzzy', 'glotpress' );
 434                  break;
 435              case 'old':
 436                  _e( 'Old', 'glotpress' );
 437                  break;
 438              case 'rejected':
 439                  _e( 'Rejected', 'glotpress' );
 440                  break;
 441              default:
 442                  echo esc_html( $legend_status );
 443          }
 444  ?>
 445      </div><?php endforeach; ?>
 446      <div class="box has-warnings"></div>
 447      <div><?php _e( 'With warnings', 'glotpress' ); ?></div>
 448  <?php
 449      if ( $locale->variant_root ) :
 450  ?>
 451      <div class="box root-translation"></div>
 452      <div><?php _e( 'Root translation', 'glotpress' ); // phpcs:ignore WordPress.Security.EscapeOutput. ?></div>
 453  <?php
 454      endif
 455  ?>
 456  </div>
 457  <p class="clear actionlist secondary">
 458      <?php
 459          $footer_links = array();
 460          if ( ( isset( $can_import_current ) && $can_import_current ) || ( isset( $can_import_waiting ) && $can_import_waiting ) ) {
 461              $footer_links[] = gp_link_get( gp_url_project( $project, array( $locale->slug, $translation_set->slug, 'import-translations' ) ), __( 'Import Translations', 'glotpress' ) );
 462          }
 463  
 464          /**
 465           * The 'default' filter is 'Current/waiting/fuzzy + untranslated (All)', however that is not
 466           * the default action when exporting so make sure to set it on the export link if no filter
 467           * has been activated by the user.
 468           */
 469          if ( ! array_key_exists( 'status', $filters ) ) {
 470              $filters['status'] = 'current_or_waiting_or_fuzzy_or_untranslated';
 471          }
 472  
 473          $export_url     = gp_url_project( $project, array( $locale->slug, $translation_set->slug, 'export-translations' ) );
 474          $export_link    = gp_link_get(
 475              $export_url,
 476              __( 'Export', 'glotpress' ),
 477              array(
 478                  'id'      => 'export',
 479                  'filters' => add_query_arg( array( 'filters' => $filters ), $export_url ),
 480              )
 481          );
 482          $format_options = array();
 483          foreach ( GP::$formats as $slug => $format ) {
 484              $format_options[ $slug ] = $format->name;
 485          }
 486          $what_dropdown   = gp_select(
 487              'what-to-export',
 488              array(
 489                  'all'      => _x( 'all current', 'export choice', 'glotpress' ),
 490                  'filtered' => _x( 'only matching the filter', 'export choice', 'glotpress' ),
 491              ),
 492              'all'
 493          );
 494          $format_dropdown = gp_select( 'export-format', $format_options, 'po' );
 495          /* translators: 1: export 2: what to export dropdown (all/filtered) 3: export format */
 496          $footer_links[] = sprintf( __( '%1$s %2$s as %3$s', 'glotpress' ), $export_link, $what_dropdown, $format_dropdown );
 497  
 498          /**
 499           * Filter footer links in translations.
 500           *
 501           * @since 1.0.0
 502           *
 503           * @param array              $footer_links    Default links.
 504           * @param GP_Project         $project         The current project.
 505           * @param GP_Locale          $locale          The current locale.
 506           * @param GP_Translation_Set $translation_set The current translation set.
 507           */
 508          $footer_links = apply_filters( 'gp_translations_footer_links', $footer_links, $project, $locale, $translation_set );
 509  
 510          // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
 511          echo implode( ' &bull; ', $footer_links );
 512      ?>
 513  </p>
 514  <?php
 515  gp_tmpl_footer();


Generated: Fri Sep 25 01:01:51 2020 Cross-referenced by PHPXref 0.7.1