[ Index ]

PHP Cross Reference of GlotPress

title

Body

[close]

/gp-includes/things/ -> translation-set.php (source)

   1  <?php
   2  /**
   3   * Things: GP_Translation_Set class
   4   *
   5   * @package GlotPress
   6   * @subpackage Things
   7   * @since 1.0.0
   8   */
   9  
  10  /**
  11   * Core class used to implement the translation sets.
  12   *
  13   * @since 1.0.0
  14   */
  15  class GP_Translation_Set extends GP_Thing {
  16  
  17      var $table_basename = 'gp_translation_sets';
  18      var $field_names = array( 'id', 'name', 'slug', 'project_id', 'locale' );
  19      var $non_db_field_names = array( 'current_count', 'untranslated_count', 'waiting_count', 'fuzzy_count', 'all_count', 'warnings_count', 'percent_translated', 'wp_locale', 'last_modified' );
  20      var $int_fields = array( 'id', 'project_id' );
  21      var $non_updatable_attributes = array( 'id' );
  22  
  23      /**
  24       * ID of the translation set.
  25       *
  26       * @var int
  27       */
  28      public $id;
  29  
  30      /**
  31       * Name of the translation set.
  32       *
  33       * @var string
  34       */
  35      public $name;
  36  
  37      /**
  38       * Slug of the translation set.
  39       *
  40       * @var string
  41       */
  42      public $slug;
  43  
  44      /**
  45       * Project ID of the translation set.
  46       *
  47       * @var int
  48       */
  49      public $project_id;
  50  
  51      /**
  52       * Locale of the translation set.
  53       *
  54       * @var string
  55       */
  56      public $locale;
  57  
  58      /**
  59       * GP project of the translation set.
  60       *
  61       * @var GP_Project
  62       */
  63      public $project;
  64  
  65      /**
  66       * Number of waiting translations.
  67       *
  68       * @var int
  69       */
  70      public $waiting_count;
  71  
  72      /**
  73       * Number of fuzzy translations.
  74       *
  75       * @var int
  76       */
  77      public $fuzzy_count;
  78  
  79      /**
  80       * Number of untranslated originals.
  81       *
  82       * @var int
  83       */
  84      public $untranslated_count;
  85  
  86      /**
  87       * Number of current translations.
  88       *
  89       * @var int
  90       */
  91      public $current_count;
  92  
  93      /**
  94       * Number of translations with warnings.
  95       *
  96       * @var int
  97       */
  98      public $warnings_count;
  99  
 100      /**
 101       * Number of all originals.
 102       *
 103       * @var int
 104       */
 105      public $all_count;
 106  
 107      /**
 108       * Sets restriction rules for fields.
 109       *
 110       * @since 1.0.0
 111       *
 112       * @param GP_Validation_Rules $rules The validation rules instance.
 113       */
 114  	public function restrict_fields( $rules ) {
 115          $rules->name_should_not_be( 'empty' );
 116          $rules->slug_should_not_be( 'empty' );
 117          $rules->locale_should_not_be( 'empty' );
 118          $rules->project_id_should_not_be( 'empty' );
 119      }
 120  
 121      /**
 122       * Normalizes an array with key-value pairs representing
 123       * a GP_Translation_Set object.
 124       *
 125       * @since 1.0.0
 126       *
 127       * @param array $args Arguments for a GP_Translation_Set object.
 128       * @return array Normalized arguments for a GP_Translation_Set object.
 129       */
 130  	public function normalize_fields( $args ) {
 131          $args = (array) $args;
 132  
 133          if ( isset( $args['name'] ) && empty( $args['name'] ) ) {
 134              if ( isset( $args['locale'] ) && ! empty( $args['locale'] ) ) {
 135                  $locale = GP_locales::by_slug( $args['locale'] );
 136                  $args['name'] = $locale->english_name;
 137              }
 138          }
 139  
 140          if ( isset( $args['slug'] ) && ! $args['slug'] ) {
 141              $args['slug'] = 'default';
 142          }
 143  
 144          if ( ! empty( $args['slug'] ) ) {
 145              $args['slug'] = gp_sanitize_slug( $args['slug'] );
 146          }
 147  
 148          return $args;
 149      }
 150  
 151      /**
 152       * Returns the English name of a locale.
 153       *
 154       * If the slug of the locale is not 'default' then the name of the
 155       * current translation sets gets added as a suffix.
 156       *
 157       * @since 1.0.0
 158       *
 159       * @param  string $separator Separator, in case the slug is not 'default'. Default: '&rarr;'.
 160       * @return string The English name of a locale.
 161       */
 162  	public function name_with_locale( $separator = '&rarr;' ) {
 163          $locale = GP_Locales::by_slug( $this->locale );
 164          $parts = array( $locale->english_name );
 165  
 166          if ( 'default' !== $this->slug ) {
 167              $parts[] = $this->name;
 168          }
 169  
 170          return implode( '&nbsp;' . $separator . '&nbsp;', $parts );
 171      }
 172  
 173  	public function by_project_id_slug_and_locale( $project_id, $slug, $locale_slug ) {
 174          $result = $this->one( "
 175              SELECT * FROM $this->table
 176              WHERE slug = %s AND project_id= %d AND locale = %s", $slug, $project_id, $locale_slug );
 177  
 178          if ( ! $result && 0 === $project_id ) {
 179              $result = $this->create( array( 'project_id' => $project_id, 'name' => GP_Locales::by_slug( $locale_slug )->english_name, 'slug' => $slug, 'locale' => $locale_slug ) );
 180          }
 181  
 182          return $result;
 183      }
 184  
 185  	public function by_locale( $locale_slug ) {
 186          return $this->many( "
 187              SELECT * FROM $this->table
 188              WHERE locale = %s", $locale_slug );
 189      }
 190  
 191  	public function existing_locales() {
 192          global $wpdb;
 193  
 194          return $wpdb->get_col( "SELECT DISTINCT(locale) FROM $this->table" );
 195      }
 196  
 197  	public function existing_slugs() {
 198          global $wpdb;
 199  
 200          return $wpdb->get_col( "SELECT DISTINCT(slug) FROM $this->table" );
 201      }
 202  
 203  	public function by_project_id( $project_id ) {
 204          return $this->many( "
 205              SELECT * FROM $this->table
 206              WHERE project_id = %d ORDER BY name ASC", $project_id );
 207      }
 208  
 209      /**
 210       * Import translations from a Translations object.
 211       *
 212       * @param  Translations $translations   the translations to be imported to this translation-set.
 213       * @param  string       $desired_status 'current', 'waiting' or 'fuzzy'.
 214       * @return boolean or void
 215       */
 216  	public function import( $translations, $desired_status = 'current' ) {
 217          $this->set_memory_limit( '256M' );
 218  
 219          if ( ! isset( $this->project ) || ! $this->project ) {
 220              $this->project = GP::$project->get( $this->project_id );
 221          }
 222  
 223          // Fuzzy is also checked in the flags, but if all strings in an import are fuzzy, fuzzy status can be forced.
 224          if ( ! in_array( $desired_status, array( 'current', 'waiting', 'fuzzy' ), true ) ) {
 225              return false;
 226          }
 227  
 228          $locale = GP_Locales::by_slug( $this->locale );
 229          $user = wp_get_current_user();
 230  
 231          $existing_translations = array();
 232  
 233          $current_translations_list = GP::$translation->for_translation( $this->project, $this, 'no-limit', array( 'status' => 'current', 'translated' => 'yes' ) );
 234          $existing_translations['current'] = new Translations();
 235          foreach ( $current_translations_list as $entry ) {
 236              $existing_translations['current']->add_entry( $entry );
 237          }
 238          unset( $current_translations_list );
 239  
 240          $translations_added = 0;
 241          foreach ( $translations->entries as $entry ) {
 242              if ( empty( $entry->translations ) ) {
 243                  continue;
 244              }
 245  
 246              $is_fuzzy = in_array( 'fuzzy', $entry->flags, true );
 247  
 248              /**
 249               * Filter whether to import fuzzy translations.
 250               *
 251               * @since 1.0.0
 252               *
 253               * @param bool              $import_over  Import fuzzy translation. Default true.
 254               * @param Translation_Entry $entry        Translation entry object to import.
 255               * @param Translations      $translations Translations collection.
 256               */
 257              if ( $is_fuzzy && ! apply_filters( 'gp_translation_set_import_fuzzy_translations', true, $entry, $translations ) ) {
 258                  continue;
 259              }
 260  
 261              /**
 262               * Filters the the status of imported translations of a translation set.
 263               *
 264               * @since 1.0.0
 265               * @since 2.3.0 Added `$new_translation` and `$old_translation` parameters.
 266               *
 267               * @param string              $status          The status of imported translations.
 268               * @param Translation_Entry   $new_translation Translation entry object to import.
 269               * @param GP_Translation|null $old_translation The previous translation.
 270               */
 271              $entry->status = apply_filters( 'gp_translation_set_import_status', $is_fuzzy ? 'fuzzy' : $desired_status, $entry, null );
 272  
 273              $entry->warnings = maybe_unserialize( GP::$translation_warnings->check( $entry->singular, $entry->plural, $entry->translations, $locale ) );
 274              if ( ! empty( $entry->warnings ) && 'current' === $entry->status ) {
 275                  $entry->status = 'waiting';
 276              }
 277  
 278              // Lazy load other entries.
 279              if ( ! isset( $existing_translations[ $entry->status ] ) ) {
 280                  $existing_translations_list = GP::$translation->for_translation( $this->project, $this, 'no-limit', array( 'status' => $entry->status, 'translated' => 'yes' ) );
 281                  $existing_translations[ $entry->status ] = new Translations();
 282                  foreach ( $existing_translations_list as $_entry ) {
 283                      $existing_translations[ $entry->status ]->add_entry( $_entry );
 284                  }
 285                  unset( $existing_translations_list );
 286              }
 287  
 288              $create = false;
 289              $translated = $existing_translations[ $entry->status ]->translate_entry( $entry );
 290              if ( 'current' !== $entry->status && ! $translated ) {
 291                  // Don't create an entry if it already exists as current.
 292                  $translated = $existing_translations['current']->translate_entry( $entry );
 293              }
 294  
 295              if ( $translated ) {
 296                  // We have the same string translated, so create a new one if they don't match.
 297                  $entry->original_id = $translated->original_id;
 298                  $translated_is_different = array_pad( $entry->translations, $locale->nplurals, null ) !== $translated->translations;
 299  
 300                  /**
 301                   * Filter whether to import over an existing translation on a translation set.
 302                   *
 303                   * @since 1.0.0
 304                   *
 305                   * @param bool $import_over Import over an existing translation.
 306                   */
 307                  $create = apply_filters( 'gp_translation_set_import_over_existing', $translated_is_different );
 308              } else {
 309                  // we don't have the string translated, let's see if the original is there
 310                  $original = GP::$original->by_project_id_and_entry( $this->project->id, $entry, '+active' );
 311                  if ( $original ) {
 312                      $entry->original_id = $original->id;
 313                      $create = true;
 314                  }
 315              }
 316              if ( $create ) {
 317                  if ( $user ) {
 318                      $entry->user_id = $user->ID;
 319                  }
 320  
 321                  $entry->translation_set_id = $this->id;
 322  
 323                  $entry->status = apply_filters( 'gp_translation_set_import_status', $entry->status, $entry, $translated );
 324                  // Check for errors.
 325                  $translation = GP::$translation->create( $entry );
 326                  if ( is_object( $translation ) ) {
 327                      $translation->set_status( $entry->status );
 328                      $translations_added += 1;
 329                  }
 330              }
 331          }
 332  
 333          gp_clean_translation_set_cache( $this->id );
 334  
 335          /**
 336           * Fires after translations have been imported to a translation set.
 337           *
 338           * @since 1.0.0
 339           *
 340           * @param int $translation_set The ID of the translation set the import was made into.
 341           */
 342          do_action( 'gp_translations_imported', $this->id );
 343  
 344          return $translations_added;
 345      }
 346  
 347      /**
 348       * Retrieves the number of waiting translations.
 349       *
 350       * @return int Number of waiting translations.
 351       */
 352  	public function waiting_count() {
 353          if ( ! isset( $this->waiting_count ) ) {
 354              $this->update_status_breakdown();
 355          }
 356  
 357          return $this->waiting_count;
 358      }
 359  
 360      /**
 361       * Retrieves the number of untranslated originals.
 362       *
 363       * @return int Number of untranslated originals.
 364       */
 365  	public function untranslated_count() {
 366          if ( ! isset( $this->untranslated_count ) ) {
 367              $this->update_status_breakdown();
 368          }
 369  
 370          return $this->untranslated_count;
 371      }
 372  
 373      /**
 374       * Retrieves the number of fuzzy translations.
 375       *
 376       * @return int Number of fuzzy translations.
 377       */
 378  	public function fuzzy_count() {
 379          if ( ! isset( $this->fuzzy_count ) ) {
 380              $this->update_status_breakdown();
 381          }
 382  
 383          return $this->fuzzy_count;
 384      }
 385  
 386      /**
 387       * Retrieves the number of current translations.
 388       *
 389       * @return int Number of current translations.
 390       */
 391  	public function current_count() {
 392          if ( ! isset( $this->current_count ) ) {
 393              $this->update_status_breakdown();
 394          }
 395  
 396          return $this->current_count;
 397      }
 398  
 399      /**
 400       * Retrieves the number of translations with warnings.
 401       *
 402       * @return int Number of translations with warnings.
 403       */
 404  	public function warnings_count() {
 405          if ( ! isset( $this->warnings_count ) ) {
 406              $this->update_status_breakdown();
 407          }
 408  
 409          return $this->warnings_count;
 410      }
 411  
 412      /**
 413       * Retrieves the number of all originals.
 414       *
 415       * @return int Number of all originals.
 416       */
 417  	public function all_count() {
 418          if ( ! isset( $this->all_count ) ) {
 419              $this->update_status_breakdown();
 420          }
 421  
 422          return $this->all_count;
 423      }
 424  
 425      /**
 426       * Populates the count properties.
 427       */
 428  	public function update_status_breakdown() {
 429          $counts = wp_cache_get( $this->id, 'translation_set_status_breakdown' );
 430  
 431          /*
 432           * The format was changed in 2.1 and 3.0.
 433           *
 434           * In 2.1 the format was changed to an array of objects in the following sequence,
 435           * however not all may exist in the array, so for example, the array may only be 3
 436           * entries long and skip any of the values:
 437           *
 438           *     [0] = Current translations
 439           *     [1] = Fuzzy translations
 440           *     [2] = Rejected translations
 441           *     [3] = Old translations
 442           *     [4] = Translations with warnings
 443           *     [5] = All translations
 444           *
 445           * In 3.0 the untranslated object was added:
 446           *
 447           *     [0] = Current translations
 448           *     [1] = Fuzzy translations
 449           *     [2] = Rejected translations
 450           *     [3] = Old translations
 451           *     [4] = Translations with warnings
 452           *     [5] = Untranslated originals
 453           *     [6] = All translations
 454           *
 455           * Version 3.0 also introduced the cache 'version' array entry to allow for easy
 456           * detection of when the cache should be expired due to changes in the cache.
 457           *
 458           * Note: The _version key is unset after the cache is loaded so that it does not
 459           * get used in the for loops when setting the object properties.
 460           *
 461           */
 462          if ( ! is_array( $counts ) || ! array_key_exists( '_version', $counts ) || GP_CACHE_VERSION !== $counts['_version'] ) {
 463              global $wpdb;
 464              $counts = array();
 465  
 466              $status_counts = $wpdb->get_results( $wpdb->prepare( "
 467                  SELECT
 468                      t.status AS translation_status,
 469                      COUNT(*) AS total,
 470                      COUNT( CASE WHEN o.priority = '-2' THEN o.priority END ) AS `hidden`,
 471                      COUNT( CASE WHEN o.priority <> '-2' THEN o.priority END ) AS `public`
 472                  FROM {$wpdb->gp_translations} AS t
 473                  INNER JOIN {$wpdb->gp_originals} AS o ON t.original_id = o.id
 474                  WHERE
 475                      t.translation_set_id = %d
 476                      AND o.status = '+active'
 477                  GROUP BY t.status
 478              ", $this->id ) );
 479  
 480              if ( $status_counts ) {
 481                  $counts = $status_counts;
 482              }
 483  
 484              $warnings_counts = $wpdb->get_row( $wpdb->prepare( "
 485                  SELECT
 486                      COUNT(*) AS total,
 487                      COUNT( CASE WHEN o.priority = '-2' THEN o.priority END ) AS `hidden`,
 488                      COUNT( CASE WHEN o.priority <> '-2' THEN o.priority END ) AS `public`
 489                  FROM {$wpdb->gp_translations} AS t
 490                  INNER JOIN {$wpdb->gp_originals} AS o ON t.original_id = o.id
 491                  WHERE
 492                      t.translation_set_id = %d AND
 493                      o.status = '+active' AND
 494                      ( t.status = 'current' OR t.status = 'waiting' )
 495                      AND warnings IS NOT NULL
 496                      AND warnings != ''
 497              ", $this->id ) );
 498  
 499              if ( $warnings_counts ) {
 500                  $counts[] = (object) array(
 501                      'translation_status' => 'warnings',
 502                      'total'              => (int) $warnings_counts->total,
 503                      'hidden'             => (int) $warnings_counts->hidden,
 504                      'public'             => (int) $warnings_counts->public,
 505                  );
 506              }
 507  
 508              $untranslated_counts = $wpdb->get_row( $wpdb->prepare( "
 509                  SELECT
 510                      COUNT(*) AS total,
 511                      COUNT( CASE WHEN o.priority = '-2' THEN o.priority END ) AS `hidden`,
 512                      COUNT( CASE WHEN o.priority <> '-2' THEN o.priority END ) AS `public`
 513                  FROM {$wpdb->gp_originals} AS o
 514                  LEFT JOIN {$wpdb->gp_translations} AS t ON o.id = t.original_id AND t.translation_set_id = %d AND t.status != 'rejected' AND t.status != 'old'
 515                  WHERE
 516                      o.project_id = %d AND
 517                      o.status = '+active' AND
 518                      t.translation_0 IS NULL
 519              ", $this->id, $this->project_id ) );
 520  
 521              if ( $untranslated_counts ) {
 522                  $counts[] = (object) array(
 523                      'translation_status' => 'untranslated',
 524                      'total'              => (int) $untranslated_counts->total,
 525                      'public'             => (int) $untranslated_counts->public,
 526                      'hidden'             => (int) $untranslated_counts->hidden,
 527                  );
 528              }
 529  
 530              $counts['_version'] = GP_CACHE_VERSION;
 531  
 532              wp_cache_set( $this->id, $counts, 'translation_set_status_breakdown' );
 533          }
 534  
 535          if ( array_key_exists( '_version', $counts ) ) {
 536              unset( $counts['_version'] );
 537          }
 538  
 539          $all_count = GP::$original->count_by_project_id( $this->project_id, 'all' );
 540          $counts[] = (object) array(
 541              'translation_status' => 'all',
 542              'total'              => $all_count->total,
 543              'hidden'             => $all_count->hidden,
 544              'public'             => $all_count->public,
 545          );
 546  
 547          $statuses = GP::$translation->get_static( 'statuses' );
 548          $statuses[] = 'warnings';
 549          $statuses[] = 'untranslated';
 550          $statuses[] = 'all';
 551          foreach ( $statuses as $status ) {
 552              $this->{$status . '_count'} = 0;
 553          }
 554  
 555          $user_can_view_hidden = GP::$permission->current_user_can( 'write', 'project', $this->project_id );
 556          foreach ( $counts as $count ) {
 557              if ( in_array( $count->translation_status, $statuses, true ) ) {
 558                  $this->{$count->translation_status . '_count'} = $user_can_view_hidden ? (int) $count->total : (int) $count->public;
 559              }
 560          }
 561      }
 562  
 563      /**
 564       * Copies translations from a translation set to the current one
 565       *
 566       * This function doesn't merge then, just copies unconditionally. If a translation already exists, it will be duplicated.
 567       * When copying translations from another project, it will search to find the original first.
 568       */
 569  	public function copy_translations_from( $source_translation_set_id ) {
 570          global $wpdb;
 571          $current_date = $this->now_in_mysql_format();
 572  
 573          $source_set = GP::$translation_set->get( $source_translation_set_id );
 574          if ( $source_set->project_id != $this->project_id ) {
 575              $translations = GP::$translation->find_many_no_map( "translation_set_id = '{$source_set->id}'" );
 576              foreach ( $translations as $entry ) {
 577                  $source_original = GP::$original->get( $entry->original_id );
 578                  $original = GP::$original->by_project_id_and_entry( $this->project_id, $source_original );
 579                  if ( $original ) {
 580                      $entry->original_id = $original->id;
 581                      $entry->translation_set_id = $this->id;
 582                      GP::$translation->create( $entry );
 583                  }
 584              }
 585          } else {
 586              return $this->query( "
 587                  INSERT INTO $wpdb->gp_translations (
 588                      original_id,       translation_set_id, translation_0, translation_1, translation_2, user_id, status, date_added,       date_modified, warnings
 589                  )
 590                  SELECT
 591                      original_id, %s AS translation_set_id, translation_0, translation_1, translation_2, user_id, status, date_added, %s AS date_modified, warnings
 592                  FROM $wpdb->gp_translations WHERE translation_set_id = %s", $this->id, $current_date, $source_translation_set_id
 593              );
 594          }
 595      }
 596  
 597  
 598  	public function percent_translated() {
 599          $original_counts = GP::$original->count_by_project_id( $this->project_id, 'all' );
 600  
 601          if ( GP::$permission->current_user_can( 'write', 'project', $this->project_id ) ) {
 602              $original_count = $original_counts->total;
 603          } else {
 604              $original_count = $original_counts->public;
 605          }
 606  
 607          return $original_count ? floor( $this->current_count() / $original_count * 100 ) : 0;
 608      }
 609  
 610  	public function last_modified() {
 611          return GP::$translation->last_modified( $this );
 612      }
 613  
 614      /**
 615       * Deletes a translation set and all of it's translations and glossaries.
 616       *
 617       * @since 2.0.0
 618       *
 619       * @return bool
 620       */
 621  	public function delete() {
 622          GP::$translation->delete_many( array( 'translation_set_id' => $this->id ) );
 623  
 624          GP::$glossary->delete_many( array( 'translation_set_id', $this->id ) );
 625  
 626          return parent::delete();
 627      }
 628  
 629      /**
 630       * Executes after creating a translation set.
 631       *
 632       * @since 2.4.0
 633       *
 634       * @return bool
 635       */
 636  	public function after_create() {
 637          /**
 638           * Fires after creating a translation set.
 639           *
 640           * @since 2.4.0
 641           *
 642           * @param GP_Translation_Set $translation_set The translation set that was created.
 643           */
 644          do_action( 'gp_translation_set_created', $this );
 645  
 646          return true;
 647      }
 648  
 649      /**
 650       * Executes after saving a translation set.
 651       *
 652       * @since 2.4.0
 653       *
 654       * @param GP_Translation_Set $translation_set_before Translation set before the update.
 655       * @return bool
 656       */
 657  	public function after_save( $translation_set_before ) {
 658          /**
 659           * Fires after saving a translation set.
 660           *
 661           * @since 2.4.0
 662           *
 663           * @param GP_Translation_Set $translation_set        Translation set following the update.
 664           * @param GP_Translation_Set $translation_set_before Translation set before the update.
 665           */
 666          do_action( 'gp_translation_set_saved', $this, $translation_set_before );
 667  
 668          return true;
 669      }
 670  
 671      /**
 672       * Executes after deleting a translation set.
 673       *
 674       * @since 2.4.0
 675       *
 676       * @return bool
 677       */
 678  	public function after_delete() {
 679          /**
 680           * Fires after deleting a translation set.
 681           *
 682           * @since 2.4.0
 683           *
 684           * @param GP_Translation_Set $translation_set The translation set that was deleted.
 685           */
 686          do_action( 'gp_translation_set_deleted', $this );
 687  
 688          return true;
 689      }
 690  }
 691  GP::$translation_set = new GP_Translation_Set();


Generated: Tue Aug 20 01:01:57 2019 Cross-referenced by PHPXref 0.7.1