[ Index ]

PHP Cross Reference of GlotPress

title

Body

[close]

/gp-includes/things/ -> project.php (source)

   1  <?php
   2  /**
   3   * Things: GP_Project class
   4   *
   5   * @package GlotPress
   6   * @subpackage Things
   7   * @since 1.0.0
   8   */
   9  
  10  /**
  11   * Core class used to implement the projects.
  12   *
  13   * @since 1.0.0
  14   */
  15  class GP_Project extends GP_Thing {
  16  
  17      var $table_basename           = 'gp_projects';
  18      var $field_names              = array( 'id', 'name', 'slug', 'path', 'description', 'parent_project_id', 'source_url_template', 'active' );
  19      var $int_fields               = array( 'id', 'parent_project_id', 'active' );
  20      var $non_updatable_attributes = array( 'id' );
  21  
  22      public $id;
  23      public $name;
  24      public $slug;
  25      public $path;
  26      public $description;
  27      public $parent_project_id;
  28      public $source_url_template;
  29      public $active;
  30      public $user_source_url_template;
  31  
  32      /**
  33       * Sets restriction rules for fields.
  34       *
  35       * @since 1.0.0
  36       *
  37       * @param GP_Validation_Rules $rules The validation rules instance.
  38       */
  39  	public function restrict_fields( $rules ) {
  40          $rules->name_should_not_be( 'empty' );
  41          $rules->slug_should_not_be( 'empty' );
  42      }
  43  
  44      // Additional queries
  45  
  46  	public function by_path( $path ) {
  47          /**
  48           * Filters the prefix for the locale glossary path.
  49           *
  50           * @since 2.3.1
  51           *
  52           * @param string $$locale_glossary_path_prefix Prefix for the locale glossary path.
  53           */
  54          $locale_glossary_path_prefix = apply_filters( 'gp_locale_glossary_path_prefix', '/languages' );
  55  
  56          if ( $locale_glossary_path_prefix === $path ) {
  57              return GP::$glossary->get_locale_glossary_project();
  58          }
  59  
  60          $path = rawurlencode( urldecode( $path ) );
  61          $path = str_replace( '%2F', '/', $path );
  62          $path = str_replace( '%20', ' ', $path );
  63          $path = trim( $path, '/' );
  64  
  65          return $this->one( "SELECT * FROM $this->table WHERE path = %s", $path );
  66      }
  67  
  68      /**
  69       * Fetches the project by ID or object.
  70       *
  71       * @since 2.3.0
  72       *
  73       * @param int|object $thing_or_id A project or the ID.
  74       * @return GP_Project|false The project on success or false on failure.
  75       */
  76  	public function get( $thing_or_id ) {
  77          if ( is_numeric( $thing_or_id ) && 0 === (int) $thing_or_id ) {
  78              return GP::$glossary->get_locale_glossary_project();
  79          }
  80  
  81          return parent::get( $thing_or_id );
  82      }
  83  
  84      /**
  85       * Retrieves the sub projects
  86       *
  87       * @return array Array of GP_Project
  88       */
  89  	public function sub_projects() {
  90          $sub_projects = $this->many( "SELECT * FROM $this->table WHERE parent_project_id = %d ORDER BY active DESC, id ASC", $this->id );
  91  
  92          /**
  93           * Filter the list of sub-projects of a project.
  94           *
  95           * @since 1.0.0
  96           *
  97           * @param array  $sub_projects An array of sub projects as GP_Project.
  98           * @param string $project_id   ID of the current project. Can be zero at the top level.
  99           */
 100          $sub_projects = apply_filters( 'gp_projects', $sub_projects, $this->id );
 101  
 102          return $sub_projects;
 103      }
 104  
 105  	public function top_level() {
 106          $projects = $this->many( "SELECT * FROM $this->table WHERE parent_project_id IS NULL OR parent_project_id < 1 ORDER BY name ASC" );
 107  
 108          /** This filter is documented in gp-includes/things/project.php */
 109          $projects = apply_filters( 'gp_projects', $projects, 0 );
 110  
 111          return $projects;
 112      }
 113  
 114      // Triggers
 115  
 116      /**
 117       * Executes after creating a project.
 118       *
 119       * @since 1.0.0
 120       *
 121       * @return bool
 122       */
 123  	public function after_create() {
 124          /**
 125           * Fires after creating a project.
 126           *
 127           * @since 1.0.0
 128           *
 129           * @param GP_Project $project The project that was created.
 130           */
 131          do_action( 'gp_project_created', $this );
 132  
 133          // TODO: pass some args to pre/after_create?
 134          if ( is_null( $this->update_path() ) ) {
 135              return false;
 136          }
 137  
 138          return true;
 139      }
 140  
 141      /**
 142       * Executes after saving a project.
 143       *
 144       * @since 1.0.0
 145       * @since 3.0.0 Added the `$project_before` parameter.
 146       *
 147       * @param GP_Project $project_before Project before the update.
 148       * @return bool
 149       */
 150  	public function after_save( $project_before ) {
 151          /**
 152           * Fires after saving a project.
 153           *
 154           * @since 1.0.0
 155           * @since 3.0.0 Added the `$project_before` parameter.
 156           *
 157           * @param GP_Project $project        Project following the update.
 158           * @param GP_Project $project_before Project before the update.
 159           */
 160          do_action( 'gp_project_saved', $this, $project_before );
 161  
 162          // TODO: pass the update args to after/pre_save?
 163          // TODO: only call it if the slug or parent project were changed
 164          return ! is_null( $this->update_path() );
 165      }
 166  
 167      /**
 168       * Executes after deleting a project.
 169       *
 170       * @since 2.0.0
 171       *
 172       * @return bool
 173       */
 174  	public function after_delete() {
 175          /**
 176           * Fires after deleting a project.
 177           *
 178           * @since 2.0.0
 179           *
 180           * @param GP_Project $project The project that was deleted.
 181           */
 182          do_action( 'gp_project_deleted', $this );
 183  
 184          return true;
 185      }
 186  
 187      /**
 188       * Normalizes an array with key-value pairs representing
 189       * a GP_Project object.
 190       *
 191       * @since 1.0.0
 192       *
 193       * @param array $args Arguments for a GP_Project object.
 194       * @return array Normalized arguments for a GP_Project object.
 195       */
 196  	public function normalize_fields( $args ) {
 197          if ( isset( $args['parent_project_id'] ) ) {
 198              $args['parent_project_id'] = $this->force_false_to_null( $args['parent_project_id'] );
 199          }
 200  
 201          if ( isset( $args['slug'] ) && ! $args['slug'] ) {
 202              $args['slug'] = $args['name'];
 203          }
 204  
 205          if ( ! empty( $args['slug'] ) ) {
 206              $args['slug'] = gp_sanitize_slug( $args['slug'] );
 207          }
 208  
 209          if ( ( isset( $args['path'] ) && ! $args['path'] ) || ! isset( $args['path'] ) || is_null( $args['path'] ) ) {
 210              unset( $args['path'] );
 211          }
 212  
 213          if ( isset( $args['active'] ) ) {
 214              if ( 'on' === $args['active'] ) {
 215                  $args['active'] = 1;
 216              }
 217  
 218              if ( ! $args['active'] ) {
 219                  $args['active'] = 0;
 220              }
 221          }
 222  
 223          $args = parent::normalize_fields( $args );
 224  
 225          return $args;
 226      }
 227  
 228      // Helpers
 229  
 230      /**
 231       * Updates this project's and its children's paths, according to its current slug.
 232       */
 233  	public function update_path() {
 234          global $wpdb;
 235          $old_path       = isset( $this->path ) ? $this->path : '';
 236          $parent_project = $this->get( $this->parent_project_id );
 237          if ( $parent_project ) {
 238              $path = gp_url_join( $parent_project->path, $this->slug );
 239          } elseif ( ! $wpdb->last_error ) {
 240              $path = $this->slug;
 241          } else {
 242              return null;
 243          }
 244  
 245          $path = trim( $path, '/' );
 246  
 247          $this->path = $path;
 248          $res_self   = $this->update( array( 'path' => $path ) );
 249          if ( is_null( $res_self ) ) {
 250              return $res_self;
 251          }
 252          // Update children's paths, too.
 253          if ( $old_path ) {
 254              $query = "UPDATE $this->table SET path = CONCAT(%s, SUBSTRING(path, %d)) WHERE path LIKE %s";
 255              return $this->query( $query, $path, strlen( $old_path ) + 1, $wpdb->esc_like( $old_path ) . '%' );
 256          } else {
 257              return $res_self;
 258          }
 259      }
 260  
 261      /**
 262       * Regenerate the paths of all projects from its parents slugs
 263       */
 264  	public function regenerate_paths( $parent_project_id = null ) {
 265          // TODO: do it with one query. Use the tree generation code from GP_Route_Main::_options_from_projects()
 266          if ( $parent_project_id ) {
 267              $parent_project = $this->get( $parent_project_id );
 268              $path           = $parent_project->path;
 269          } else {
 270              $path              = '';
 271              $parent_project_id = null;
 272          }
 273  
 274          $projects = $this->find( array( 'parent_project_id' => $parent_project_id ) );
 275          foreach ( (array) $projects as $project ) {
 276              $project->update( array( 'path' => trim( gp_url_join( $path, $project->slug ), '/' ) ) );
 277              $this->regenerate_paths( $project->id );
 278          }
 279      }
 280  
 281  	public function source_url( $file, $line ) {
 282          $source_url = false;
 283          if ( $source_url_template = $this->source_url_template() ) {
 284              $source_url = str_replace( array( '%file%', '%line%' ), array( $file, $line ), $source_url_template );
 285          }
 286  
 287          /**
 288           * Allows per-reference overriding of the source URL defined as project setting.
 289           *
 290           * @since 2.2.0
 291           *
 292           * @param string|false $source_url The originally generated source URL, or false if no URL is available.
 293           * @param GP_Project $project The current project.
 294           * @param string $file The referenced file name.
 295           * @param string $line The line number in the referenced file.
 296           */
 297          return apply_filters( 'gp_reference_source_url', $source_url, $this, $file, $line );
 298      }
 299  
 300  	public function source_url_template() {
 301          if ( isset( $this->user_source_url_template ) ) {
 302              return $this->user_source_url_template;
 303          } else {
 304              if ( $this->id && is_user_logged_in() && ( $templates = get_user_meta( get_current_user_id(), 'gp_source_url_templates', true ) )
 305                       && isset( $templates[ $this->id ] ) ) {
 306                  $this->user_source_url_template = $templates[ $this->id ];
 307                  return $this->user_source_url_template;
 308              } else {
 309                  return $this->source_url_template;
 310              }
 311          }
 312      }
 313  
 314      /**
 315       * Gives an array of project objects starting from the current project
 316       * then its parent, its parent and up to the root.
 317       *
 318       * @todo Cache the results. Invalidation is tricky, because on each project update we need to invalidate the cache
 319       * for all of its children.
 320       *
 321       * @return array
 322       */
 323  	public function path_to_root() {
 324          $path = array();
 325          if ( $this->parent_project_id ) {
 326              $parent_project = $this->get( $this->parent_project_id );
 327  
 328              if ( $parent_project ) {
 329                  $path = $parent_project->path_to_root();
 330              }
 331          }
 332          return array_merge( array( &$this ), $path );
 333      }
 334  
 335  	public function set_difference_from( $other_project ) {
 336          $this_sets  = (array) GP::$translation_set->by_project_id( $this->id );
 337          $other_sets = (array) GP::$translation_set->by_project_id( $other_project->id );
 338          $added      = array();
 339          $removed    = array();
 340  
 341          foreach ( $other_sets as $other_set ) {
 342              if ( ! gp_array_any( array( $this, '_compare_set_item' ), $this_sets, $other_set ) ) {
 343                  $added[] = $other_set;
 344              }
 345          }
 346  
 347          foreach ( $this_sets as $this_set ) {
 348              if ( ! gp_array_any( array( $this, '_compare_set_item' ), $other_sets, $this_set ) ) {
 349                  $removed[] = $this_set;
 350              }
 351          }
 352  
 353          return array(
 354              'added'   => $added,
 355              'removed' => $removed,
 356          );
 357      }
 358  
 359  	public function _compare_set_item( $set, $this_set ) {
 360          return ( $set->locale == $this_set->locale && $set->slug = $this_set->slug );
 361      }
 362  
 363  	public function copy_sets_and_translations_from( $source_project_id ) {
 364          $sets = GP::$translation_set->by_project_id( $source_project_id );
 365  
 366          foreach ( $sets as $to_add ) {
 367              $new_set = GP::$translation_set->create(
 368                  array(
 369                      'project_id' => $this->id,
 370                      'name'       => $to_add->name,
 371                      'locale'     => $to_add->locale,
 372                      'slug'       => $to_add->slug,
 373                  )
 374              );
 375              if ( ! $new_set ) {
 376                  $this->errors[] = sprintf(
 377                      /* translators: %s: Translation set. */
 378                      __( 'Couldn&#8217;t add translation set named %s', 'glotpress' ),
 379                      esc_html( $to_add->name )
 380                  );
 381              } else {
 382                  // Duplicate translations.
 383                  $new_set->copy_translations_from( $to_add->id );
 384              }
 385          }
 386      }
 387  
 388  	public function copy_originals_from( $source_project_id ) {
 389          global $wpdb;
 390          return $this->query(
 391              "INSERT INTO $wpdb->gp_originals (
 392                  `project_id`, `context`, `singular`, `plural`, `references`, `comment`, `status`, `priority`, `date_added`
 393              )
 394              SELECT
 395                  %s AS `project_id`, `context`, `singular`, `plural`, `references`, `comment`, `status`, `priority`, `date_added`
 396              FROM $wpdb->gp_originals WHERE project_id = %s",
 397              $this->id,
 398              $source_project_id
 399          );
 400      }
 401  
 402      /**
 403       * Gives an array of project objects starting from the current project children
 404       * then its grand children etc.
 405       *
 406       * @return array
 407       */
 408  	public function inclusive_sub_projects() {
 409          $sub_projects = $this->sub_projects();
 410          foreach ( $sub_projects as $sub ) {
 411              $sub_projects = array_merge( $sub_projects, $sub->inclusive_sub_projects() );
 412          }
 413  
 414          return $sub_projects;
 415      }
 416  
 417  	public function duplicate_project_contents_from( $source_project ) {
 418          $source_sub_projects = $source_project->inclusive_sub_projects();
 419  
 420          // Duplicate originals, translations sets and translations for the root project.
 421          $this->copy_originals_from( $source_project->id );
 422          $this->copy_sets_and_translations_from( $source_project->id );
 423  
 424          // Keep a list of parents to preserve hierarchy.
 425          $parents                        = array();
 426          $parents[ $source_project->id ] = $this->id;
 427  
 428          // Duplicate originals, translations sets and translations for the child projects.
 429          foreach ( $source_sub_projects as $sub ) {
 430              $copy_project                    = new GP_Project( $sub->fields() );
 431              $copy_project->parent_project_id = $parents[ $sub->parent_project_id ];
 432              $parent_project                  = $copy_project->get( $copy_project->parent_project_id );
 433  
 434              $copy_project->path  = gp_url_join( $parent_project->path, $copy_project->slug );
 435              $copy                = GP::$project->create( $copy_project );
 436              $parents[ $sub->id ] = $copy->id;
 437  
 438              $copy->copy_originals_from( $sub->id );
 439              $copy->copy_sets_and_translations_from( $sub->id );
 440          }
 441      }
 442  
 443      /**
 444       * Deletes a project and all of sub projects, translations, translation sets, originals and glossaries.
 445       *
 446       * @since 2.0.0
 447       *
 448       * @return bool
 449       */
 450  	public function delete() {
 451          GP::$project->delete_many( array( 'parent_project_id' => $this->id ) );
 452  
 453          GP::$translation_set->delete_many( array( 'project_id' => $this->id ) );
 454  
 455          GP::$original->delete_many( array( 'project_id' => $this->id ) );
 456  
 457          return parent::delete();
 458      }
 459  }
 460  GP::$project = new GP_Project();


Generated: Fri Mar 29 01:01:13 2024 Cross-referenced by PHPXref 0.7.1