Plugin Directory

Changeset 2774075


Ignore:
Timestamp:
08/23/2022 12:20:07 PM (4 years ago)
Author:
memsource
Message:

Version 3.5.0

Location:
memsource-connector/trunk
Files:
12 edited

Legend:

Unmodified
Added
Removed
  • memsource-connector/trunk/memsource.php

    r2746961 r2774075  
    55Plugin URI: https://help.memsource.com/hc/en-us/articles/360012208319-WordPress-Connector
    66Description: Localize WordPress websites with the help of professional translation tools: translation memories, terminology bases and quality checkers.
    7 Version: 3.4.3
     7Version: 3.5.0
    88Text Domain: memsource
    99Domain Path: /locale
     
    1919
    2020define('MEMSOURCE_PLUGIN_PATH', dirname(__FILE__));
    21 define('MEMSOURCE_PLUGIN_VERSION', '3.4.3');
     21define('MEMSOURCE_PLUGIN_VERSION', '3.5.0');
    2222define('MEMSOURCE_PLUGIN_DIR_URL', plugin_dir_url(__FILE__));
    2323
     
    181181function memsource_check_php_version() {
    182182    $php_version = phpversion();
    183     if (!version_compare($php_version, '7.0', '>=')) {
    184         wp_die(__(sprintf('Plugin requires PHP 7.0 or higher. Your version is %s. Please, update the version.', $php_version), 'memsource-connector'));
     183    if (!version_compare($php_version, '7.3', '>=')) {
     184        wp_die(__(sprintf('Plugin requires PHP 7.3 or higher. Your version is %s. Please, update the version.', $php_version), 'memsource-connector'));
    185185    }
    186186}
  • memsource-connector/trunk/readme.txt

    r2746961 r2774075  
    2929== Changelog ==
    3030
     31= 3.5.0 =
     32*Release Date - 23 Aug 2022*
     33
     34* Added SEOPress support
     35* All taxonomies are now transferred to the translation
     36* The lowest supported PHP version is now 7.3
     37* Various bug fixes
     38
    3139= 3.4.3 =
    3240*Release Date - 23 Jun 2022*
  • memsource-connector/trunk/src/Controller/ContentController.php

    r2722771 r2774075  
    1717final class ContentController extends \WP_REST_Controller
    1818{
     19    private const IGNORED_CONTENT_TYPES = [
     20        'acf-field',
     21    ];
     22
    1923    /** @var \wpdb */
    2024    private $wpdb;
     
    99103     * Add a content service for endpoints.
    100104     * @param $content IContentService
    101      * @return IContentService
     105     * @return void
    102106     * @throws \Exception
    103107     */
    104108    public function addContentService(IContentService $content)
    105109    {
     110        if (in_array($content->getType(), self::IGNORED_CONTENT_TYPES)) {
     111            return;
     112        }
     113
    106114        if (!isset($this->contentServices[$content->getType()])) {
    107115            $this->contentServices[$content->getType()] = $content;
    108             return $content;
    109         }
     116            return;
     117        }
     118
    110119        throw new \Exception(sprintf('Content type \'%s\' is registered already.', $content->getType()));
    111120    }
     
    284293
    285294    /**
     295     * @deprecated This endpoint will be present for a while due to backward compatibility.
    286296     * @param $request \WP_REST_Request
    287297     * @param $response \WP_REST_Response
     
    290300    private function storeLastProcessedId(\WP_REST_Request $request, \WP_REST_Response $response)
    291301    {
    292         $contentType = $request->get_param('type') ?: PostService::TYPE;
    293         if ($contentType === PostService::TYPE) {
    294             /** @var $contentService \Memsource\Service\Content\PostService */
    295             $contentService = $this->getContentService(PostService::TYPE);
    296             $id = $contentService->storeLastProcessedId();
    297             $response->set_data($id);
    298             return;
    299         }
    300 
    301         $response->set_status(400);
    302         $response->set_data(['error' => 'Invalid type of content.', 'contentType' => $contentType]);
     302        $response->set_data(['status' => 'OK']);
    303303    }
    304304
  • memsource-connector/trunk/src/Parser/ShortcodeParser.php

    r2746961 r2774075  
    231231                        $translatedText = html_entity_decode($extractedText);
    232232                    }
     233                    $translatedText = str_replace('$', '\$', $translatedText);
    233234                    $shortcodeWithId = $shortcode . ":" . $uniqueId;
    234235                    $storedContent = preg_replace(
     
    258259                    }
    259260                    $translatedText = html_entity_decode($match[3]);
     261                    $translatedText = str_replace('$', '\$', $translatedText);
    260262                    $attributeWithId = $attribute . ":" . $uniqueId;
    261263                    $storedContent = preg_replace(
    262                         "| ${attributeWithId}=[\"'].*?[\"']|sm", " ${attribute}=${delimiter}${translatedText}${delimiter}",
     264                        "| ${attributeWithId}=[\"'].*?[\"']|sm",
     265                        " ${attribute}=${delimiter}${translatedText}${delimiter}",
    263266                        $storedContent
    264267                    );
  • memsource-connector/trunk/src/Service/Content/AbstractPostService.php

    r2722771 r2774075  
    6060        // turn off paging to return all posts
    6161        $queryArgs = ['post_type' => $this->getType(), 'nopaging' => true, 'post_status' => $this->optionsService->getListStatuses()];
    62         $lastId = 0;
    63 
    64         if (isset($_GET['newPosts'])) {
    65             $lastId = $this->optionsService->getLastProcessedId();
    66         }
    67 
    6862        $postsQuery = new \WP_Query();
    6963        $this->filterService->addQueryFilters($postsQuery, true);
     
    7670            $post = $this->getLastRevision($post);
    7771            if (
    78                 $post->ID > $lastId
    79                 &&
    8072                (
    8173                    $post->post_content != ''
     
    161153    }
    162154
    163     public function storeLastProcessedId()
    164     {
    165         // compare with stored ID and save only a higher one
    166         $stored_last_id = $this->optionsService->getLastProcessedId();
    167         $new_last_id = $_POST['lastId'];
    168         // find the last revision ID
    169         $last_revision = $this->getLastRevision(get_post($new_last_id));
    170         if ($last_revision) {
    171             $new_last_id = $last_revision->ID;
    172         }
    173         $overwrite = isset($_POST['overwrite']);
    174         if ($overwrite || $new_last_id > $stored_last_id) {
    175             $this->optionsService->updateLastProcessedId($new_last_id);
    176             return ['id' => $new_last_id];
    177         }
    178         return ['id' => $stored_last_id];
    179     }
    180 
    181155    public function getPost($original_post_id)
    182156    {
     
    249223        update_post_meta($sourcePostId, '_wpml_post_translation_editor_native', 'yes');
    250224
    251         // transfer categories from source to target
    252         $sourceCategories = wp_get_post_categories($sourcePostId);
    253         if (is_array($sourceCategories)) {
    254             $this->translationPlugin->updatePostCategories($sourceCategories, $targetPostId, $language);
    255         }
     225        // transfer all terms from source to target
     226        $this->transferTermsFromSourceToTarget($sourcePostId, $targetPostId, $language);
    256227
    257228        // copy featured image to target
     
    367338        return $json;
    368339    }
     340
     341    /**
     342     * Go through all taxonomies and add already translated terms to the translated post.
     343     *
     344     * @param int $sourcePostId Source post ID
     345     * @param int $targetPostId Target post ID
     346     * @param string $language Translation language
     347     *
     348     * @return void
     349     */
     350    private function transferTermsFromSourceToTarget(int $sourcePostId, int $targetPostId, string $language) {
     351        foreach (get_taxonomies() as $taxonomy) {
     352            $sourceTerms = wp_get_post_terms($sourcePostId, $taxonomy, ['fields' => 'ids']);
     353            if (is_array($sourceTerms)) {
     354                $this->translationPlugin->transferPostTerms($taxonomy, $sourceTerms, $targetPostId, $language);
     355            }
     356        }
     357    }
    369358}
  • memsource-connector/trunk/src/Service/CustomFieldService.php

    r2722771 r2774075  
    1414    const CUSTOMFIELD_META_CONTENT_TYPE = 'customfield_meta';
    1515
    16     const YOAST_SEO_TAG_START = '<!--yoast_wpseo_tag';
    17     const YOAST_SEO_TAG_END = '_yoast_wpseo_tag-->';
     16    const YOAST_SEO_TAG_START = '<yoast_seo_tag_';
     17    const YOAST_SEO_TAG_END = '/>';
    1818    const YOAST_SEO_CUSTOM_FIELD_PREFIX = '_yoast_wpseo_';
     19    const YOAST_SEO_CUSTOM_REPLACEMENT_PREFIX = 'yoast_seo_tag_';
     20
     21    const SEOPRESS_TAG_START = '<seo_press_tag_';
     22    const SEOPRESS_TAG_END = '/>';
     23    const SEOPRESS_CUSTOM_FIELD_PREFIX = '_seopress_';
     24    const SEOPRESS_CUSTOM_REPLACEMENT_PREFIX = 'seo_press_tag_';
    1925
    2026    const RESULT_CUSTOM_FIELDS = 'customFields';
    2127    const RESULT_PLACEHOLDERS = 'placeholders';
    2228
    23     private $translatableSystemCustomFields = [
     29    private $customFieldsWhiteList = [
    2430        '_yoast_wpseo_focuskw',
    2531        '_yoast_wpseo_title',
     
    3036        '_yoast_wpseo_twitter-title',
    3137        '_yoast_wpseo_twitter-description',
     38        '_seopress_titles_title',
     39        '_seopress_titles_desc',
     40        '_seopress_social_fb_title',
     41        '_seopress_social_fb_desc',
     42        '_seopress_social_twitter_title',
     43        '_seopress_social_twitter_desc',
     44        '_seopress_robots_canonical',
     45        '_seopress_analysis_target_kw',
    3246    ];
    3347
     
    4559
    4660    public function __construct(
    47         ElementorPlugin $elementorPlugin,
     61        ElementorPlugin            $elementorPlugin,
    4862        TranslationWorkflowService $translationWorkflowService,
    49         AuthUtils $authUtils,
    50         PlaceholderService $placeholderService
    51     ) {
     63        AuthUtils                  $authUtils,
     64        PlaceholderService         $placeholderService
     65    )
     66    {
    5267        $this->elementorPlugin = $elementorPlugin;
    5368        $this->translationWorkflowService = $translationWorkflowService;
     
    6883            $customFields[$key]['meta_value'] = (string)$row['meta_value'];
    6984
    70             if ($this->elementorPlugin->isElementorDataField($row['meta_key'])) {
     85            if ($this->elementorPlugin->isElementorDataField($row['meta_key']) &&
     86                $this->elementorPlugin->isPostCreatedByElementorPlugin($postId)) {
    7187                $token = $this->authUtils->generateRandomToken();
    7288                $placeholders[$token] = sprintf('<!--_elementor_data_%s-->', $row['meta_value']);
     
    202218
    203219        return
    204             ($firstChar === '_' && !in_array($customFieldName, $this->translatableSystemCustomFields))
     220            ($firstChar === '_' && !in_array($customFieldName, $this->customFieldsWhiteList))
    205221            || in_array($customFieldName, $translationWorkflowFields, true);
    206222    }
     
    305321
    306322        $value = $this->encodeFieldIfSerialized($value);
    307         $value = $this->encodeYoastSeoVariables($key, $value);
     323        $value = $this->encodeYoastAndSeopressVariables($key, $value);
    308324
    309325        return sprintf('<div data-type="custom_field_v2" data-source-id="%d" data-key="%s"><div id="value">%s</div></div>', $sourceId, $key, $value);
     
    311327
    312328    /**
    313      * Convert Yoast SEO variables to HTML comments (and tags in Memsource).
     329     * Convert Yoast and SEOPRESS variables to HTML comments (and tags in Memsource).
    314330     *
    315331     * @param string $customFieldName
     
    318334     * @return string
    319335     */
    320     public function encodeYoastSeoVariables(string $customFieldName, string $customFieldValue): string
    321     {
    322         if (substr($customFieldName, 0, strlen(self::YOAST_SEO_CUSTOM_FIELD_PREFIX)) !== self::YOAST_SEO_CUSTOM_FIELD_PREFIX) {
    323             return $customFieldValue;
    324         }
    325 
    326         return preg_replace(
    327             "|(%%[a-z_]*%%)|sm",
    328             self::YOAST_SEO_TAG_START . '${1}' . self::YOAST_SEO_TAG_END,
    329             $customFieldValue
    330         );
     336    public function encodeYoastAndSeopressVariables(string $customFieldName, string $customFieldValue): string
     337    {
     338        if ((strpos($customFieldName, self::YOAST_SEO_CUSTOM_FIELD_PREFIX) === 0) && $customFieldValue) {
     339            $pattern = '|(%%[a-z_]*%%)|sm';
     340            preg_match_all($pattern, $customFieldValue, $matches);
     341
     342            foreach ($matches[0] ?? [] as $match) {
     343                $replacement = self::YOAST_SEO_TAG_START . str_replace('%%', '', $match) . self::YOAST_SEO_TAG_END;
     344                $customFieldValue = str_replace($match, $replacement, $customFieldValue);
     345
     346            }
     347        }
     348
     349        if ((strpos($customFieldName, self::SEOPRESS_CUSTOM_FIELD_PREFIX) === 0) && $customFieldValue) {
     350            $pattern = '|(%%[a-z_]*%%)|sm';
     351            preg_match_all($pattern, $customFieldValue, $matches);
     352
     353            foreach ($matches[0] ?? [] as $match) {
     354                $replacement = self::SEOPRESS_TAG_START . str_replace('%%', '', $match) . self::SEOPRESS_TAG_END;
     355                $customFieldValue = str_replace($match, $replacement, $customFieldValue);
     356            }
     357        }
     358
     359        return $customFieldValue;
    331360    }
    332361
     
    379408            foreach ($matches ?: [] as $match) {
    380409                $value = $match[3];
    381                 $value = $this->removeYoastSeoTags($value);
     410                $value = $this->removeYoastAndSeoPluginTags($value);
    382411
    383412                if ($this->elementorPlugin->isElementorDataField($match[2])) {
     
    397426
    398427    /**
    399      * Remove HTML comments around Yoast SEO variables.
     428     * Remove HTML comments around Yoast and SEOPRESS variables.
    400429     *
    401430     * @param string $customFieldValue
    402431     * @return string
    403432     */
    404     public function removeYoastSeoTags(string $customFieldValue): string
    405     {
    406         $customFieldValue = str_replace(self::YOAST_SEO_TAG_START, '', $customFieldValue);
    407         $customFieldValue = str_replace(self::YOAST_SEO_TAG_END, '', $customFieldValue);
     433    public function removeYoastAndSeoPluginTags(string $customFieldValue): string
     434    {
     435        if (strpos($customFieldValue, self::YOAST_SEO_CUSTOM_REPLACEMENT_PREFIX) !== false || strpos($customFieldValue, self::SEOPRESS_CUSTOM_REPLACEMENT_PREFIX) !== false) {
     436
     437            $pattern = '|<[^>]*>|sm';
     438            preg_match_all($pattern, $customFieldValue, $matches);
     439
     440            foreach ($matches[0] ?? [] as $match) {
     441                $replacement = '';
     442
     443                if (strpos($match, self::YOAST_SEO_CUSTOM_REPLACEMENT_PREFIX) !== false) {
     444                    $replacement = str_replace(self::YOAST_SEO_CUSTOM_REPLACEMENT_PREFIX, '', $match);
     445                }
     446                if (strpos($match, self::SEOPRESS_CUSTOM_REPLACEMENT_PREFIX) !== false) {
     447                    $replacement = str_replace(self::SEOPRESS_CUSTOM_REPLACEMENT_PREFIX, '', $match);
     448                }
     449
     450                $replacement = '%%' . substr($replacement, 1, -2) . '%%';
     451
     452                $customFieldValue = str_replace($match, $replacement, $customFieldValue);
     453
     454            }
     455        }
    408456
    409457        return $customFieldValue;
     
    499547        global $wpdb;
    500548
    501         $query =  "SELECT post_excerpt as 'field' FROM $wpdb->posts where post_type = 'acf-field'";
     549        $query = "SELECT post_excerpt as 'field' FROM $wpdb->posts where post_type = 'acf-field'";
    502550        $results = $wpdb->get_results($query, ARRAY_A);
    503551
  • memsource-connector/trunk/src/Service/ExternalPlugin/ElementorPlugin.php

    r2730911 r2774075  
    256256    public function isPostCreatedByElementorPlugin($postId): bool
    257257    {
    258         global $wpdb;
    259 
    260         $table = $wpdb->postmeta;
    261         $query = $wpdb->prepare('SELECT * FROM ' . $table . ' WHERE post_id = %d', $postId);
    262         $result = $wpdb->get_results($query, ARRAY_A) ?: [];
    263 
    264         foreach ($result as $row) {
    265             if ($this->isElementorDataField($row['meta_key'])) {
    266                 return true;
    267             }
    268         }
    269 
    270         return false;
     258        $document = Plugin::$instance->documents->get($postId);
     259
     260        return $document && $document->is_built_with_elementor();
    271261    }
    272262
  • memsource-connector/trunk/src/Service/OptionsService.php

    r2643752 r2774075  
    2020    private $OPTION_TOKEN = 'memsource_token';
    2121    private $OPTION_ADMIN_USER = 'memsource_admin_user';
    22     private $OPTION_LAST_PROCESSED_ID = 'memsource_last_processed_id';
    2322    private $OPTION_LIST_STATUS = 'memsource_list_status';
    2423    private $OPTION_INSERT_STATUS = 'memsource_insert_status';
     
    3938        add_option($this->OPTION_TOKEN, AuthUtils::createNewToken());
    4039        add_option($this->OPTION_ADMIN_USER, get_current_user_id());
    41         add_option($this->OPTION_LAST_PROCESSED_ID, 0);
    4240        add_option($this->OPTION_LIST_STATUS, 'publish');
    4341        add_option($this->OPTION_INSERT_STATUS, 'publish');
     
    113111    }
    114112
    115     public function updateLastProcessedId($id) {
    116         return update_option($this->OPTION_LAST_PROCESSED_ID, $id);
    117     }
    118 
    119     public function getLastProcessedId() {
    120         return get_option($this->OPTION_LAST_PROCESSED_ID);
    121     }
    122 
    123113    public function getAdminUser() {
    124114        return get_option($this->OPTION_ADMIN_USER);
  • memsource-connector/trunk/src/Service/TranslationPlugin/ITranslationPlugin.php

    r2611482 r2774075  
    7979     * If possible, assign proper categories to the translation.
    8080     *
    81      * @param int[] $sourceCategories categories of a source post
     81     * @param string $taxonomy Taxonomy name, for example 'category' or 'post_tag'
     82     * @param int[] $sourceTermsIds Categories of a source post
    8283     * @param int $targetPostId Translated post ID
     84     * @param string $targetLanguage Target language
    8385     *
    8486     * @return mixed
    8587     */
    86     public function updatePostCategories(array $sourceCategories, int $targetPostId, string $targetLanguage);
     88    public function transferPostTerms(string $taxonomy, array $sourceTermsIds, int $targetPostId, string $targetLanguage);
    8789
    8890    /**
  • memsource-connector/trunk/src/Service/TranslationPlugin/MultilingualpressPlugin.php

    r2611482 r2774075  
    123123    }
    124124
    125     public function updatePostCategories(array $sourceCategories, int $targetPostId, string $targetLanguage)
     125    public function transferPostTerms(string $taxonomy, array $sourceTermsIds, int $targetPostId, string $targetLanguage)
    126126    {
    127127        $api = $this->api();
     
    129129        $targetSiteId = $this->getSiteIdByLang($targetLanguage);
    130130
    131         $targetCategories = [];
    132 
    133         foreach ($sourceCategories as $sourceCategoryId) {
    134             $translationId = $api->contentIdForSite(get_current_blog_id(), $sourceCategoryId, ContentRelations::CONTENT_TYPE_TERM, $targetSiteId);
     131        $targetTerms = [];
     132
     133        foreach ($sourceTermsIds as $sourceTermId) {
     134            $translationId = $api->contentIdForSite(get_current_blog_id(), $sourceTermId, ContentRelations::CONTENT_TYPE_TERM, $targetSiteId);
    135135
    136136            if ($translationId !== null) {
    137                 $targetCategories[] = $translationId;
    138             }
    139         }
    140 
    141         if (!empty($targetCategories)) {
    142             $mainBlogId = $this->switchToSite($targetSiteId);
    143             wp_set_post_categories($targetPostId, $targetCategories);
     137                $targetTerms[] = $translationId;
     138            }
     139        }
     140
     141        if (!empty($targetTerms)) {
     142            $mainBlogId = $this->switchToSite($targetSiteId);
     143            wp_set_post_terms($targetPostId, $targetTerms, $taxonomy);
    144144            $this->switchToSite($mainBlogId);
    145145        }
  • memsource-connector/trunk/src/Service/TranslationPlugin/NonExistingPlugin.php

    r2611482 r2774075  
    7070    }
    7171
    72     public function updatePostCategories(array $sourceCategories, int $targetPostId, string $targetLanguage)
     72    public function transferPostTerms(string $taxonomy, array $sourceTermsIds, int $targetPostId, string $targetLanguage)
    7373    {
    7474    }
  • memsource-connector/trunk/src/Service/TranslationPlugin/WPMLPlugin.php

    r2643752 r2774075  
    102102    }
    103103
    104     public function updatePostCategories(array $sourceCategories, int $targetPostId, string $targetLanguage)
    105     {
    106         $pluginContentType = $this->convertTermTypeToWpmlType('category');
    107         $defaultSourceCategory = get_option('default_category');
    108         $targetCategories = [];
    109 
    110         foreach ($sourceCategories as $sourceCategoryId) {
    111             $translationId = $this->getTranslationId($pluginContentType, $sourceCategoryId, $targetLanguage);
     104    public function transferPostTerms(string $taxonomy, array $sourceTermsIds, int $targetPostId, string $targetLanguage)
     105    {
     106        $defaultSourceCategory = null;
     107        if ($taxonomy === 'category') {
     108            $defaultSourceCategory = get_option('default_category');
     109        }
     110        $pluginContentType = $this->convertTermTypeToWpmlType($taxonomy);
     111        $targetTerms = [];
     112
     113        foreach ($sourceTermsIds as $sourceTermId) {
     114            $translationId = $this->getTranslationId($pluginContentType, $sourceTermId, $targetLanguage);
    112115
    113116            if ($translationId !== null) {
    114                 $targetCategories[] = $translationId;
    115             }
    116         }
    117 
    118         if (!empty($targetCategories)) {
    119             wp_set_post_categories($targetPostId, $targetCategories);
     117                $targetTerms[] = $translationId;
     118            }
     119        }
     120
     121        if (!empty($targetTerms)) {
     122            wp_set_post_terms($targetPostId, $targetTerms, $taxonomy);
    120123        } elseif (is_numeric($defaultSourceCategory)) {
    121124            $translationId = $this->getTranslationId($pluginContentType, $defaultSourceCategory, $targetLanguage);
     
    257260        }
    258261
    259         return $translation[$targetLanguage] ?? null;
     262        if (!isset($translation[$targetLanguage])) {
     263            return null;
     264        }
     265
     266        return (int) $translation[$targetLanguage];
    260267    }
    261268
Note: See TracChangeset for help on using the changeset viewer.