Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge branch '6' into 7 #856

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
43dc86d
API Standardise deprecations
GuySartorelli Feb 17, 2023
3f2b86b
Merge pull request #783 from creative-commoners/pulls/5/deprecations
tractorcow Feb 22, 2023
f07b036
FIX Don't try to preview unlocalised objects (#781)
GuySartorelli May 23, 2023
034dd15
Update FluentExtension.php
mikey-harveycameron May 31, 2023
53ca9ef
Merge pull request #799 from mikey-harveycameron/patch-2
tractorcow May 31, 2023
336dca2
Implement onAfterDuplicate
RVXD Jun 16, 2023
43b4214
Add onAfterDuplicate to FluentVersionedExtension
RVXD Jun 16, 2023
6313e6d
Merge pull request #804 from RVXD/patch-3
tractorcow Jun 21, 2023
9d04d7a
feat: add absolute base url for locale
tractorcow Aug 31, 2023
904e3f4
chore: fix linting
tractorcow Sep 1, 2023
ba53c83
Merge pull request #809 from tractorcow/feat/base-url
tractorcow Sep 1, 2023
9ab08d1
copy Bugfix from here https://github.com/tractorcow-farm/silverstripe…
MLindenhofer Nov 14, 2023
f18bc71
fix: allow plugins
tractorcow Nov 14, 2023
3ac5a90
Merge pull request #815 from MLindenhofer/ML_CopyFixFromMaster
tractorcow Nov 14, 2023
14ecdb1
FIX allows i18nTextCollector to concatenate keys
davejtoews Feb 8, 2024
2a8e81e
Adds full namespace to string literal
davejtoews Feb 9, 2024
42ecdc3
Merge pull request #824 from davejtoews/bugfix-textcollector-warnings
tractorcow Feb 13, 2024
f79f0f9
fix: don't invoke db for ready check outside of dev urls
tractorcow Mar 20, 2024
05ef577
fix: redundant import
tractorcow Mar 20, 2024
1685a68
Merge pull request #831 from tractorcow-farm/fix/check
tractorcow Mar 24, 2024
182e829
Merge remote-tracking branch 'origin/5.1' into 5
tractorcow Mar 24, 2024
4c1f1e8
Merge remote-tracking branch 'origin/5' into 6
tractorcow Mar 24, 2024
8985d8c
fix: check for request prior to access
tractorcow Mar 24, 2024
9e650e6
Merge pull request #833 from tractorcow-farm/fix/dbready-check-6
tractorcow Mar 24, 2024
9aea31d
NEW Added dev/task to initialise DataObjects for existing datasets
nathanbrauer Mar 27, 2024
07328cf
DOC Updated documentation for new DataObject initialisation task
nathanbrauer Apr 2, 2024
acb1466
Merge pull request #835 from nathanbrauer/feature/initial-task-for-da…
tractorcow Apr 2, 2024
f54103b
Merge branch '6.0' into 6
GuySartorelli May 23, 2024
04c6358
Merge pull request #854 from creative-commoners/pulls/6/merge-conflict
emteknetnz May 24, 2024
a702f41
Merge branch '6' into 7
GuySartorelli May 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 119 additions & 28 deletions docs/en/migrating-from-single-language.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ In case you want to add fluent to an existing site to add multi language functio

## Install fluent

use composer to install fluent, see [installation](installation.md)
Use composer to install fluent, see [installation](installation.md)

## Configure fluent
* add locales

You can either do this in the backend, or for the first setup you can utitlise `default_records` to add the locales to the db.
* Add locales

You can either do this in the backend, or for the first setup you can utitlise `default_records` to add the locales to
the db.
A fluent.yml might look like:

```
Expand All @@ -33,32 +35,121 @@ TractorCow\Fluent\Model\Locale:

When you run `dev/build?flush` again, this adds the records to the database if the locales table is still empty.

## Publish available pages in your default locale
## Populating initial localised content for existing Pages and DataObjects in your default locale

Now your site is broken, cause no pages have been published and added as translated page in your default locale.
You can either publish all pages manually or use [publishall](https://docs.silverstripe.org/en/4/developer_guides/debugging/url_variable_tools/#building-and-publishing-urls) to publish all pages in bulk.
If you run `/admin/pages/publishall` in your browser your site will be fixed again and you can start adding translated content.
Now your site is broken because nothing has been published and added as translated data in your default locale. You can
either manually localise all DataObjects & Pages manually or use one of the automation options below.

### Automated tools for localisation

`InitialPageLocalisation` dev task can be used to either only localise or localise & publish your pages.
This dev task can be run either via CLI or queued as a job if Queued jobs module is installed.

Localise only example

```
dev/tasks/initial-page-localisation-task
```

Localise & publish example

```
dev/tasks/initial-page-localisation-task publish=1
```

Localisation in batches can be done by using the `limit` option.
Example below will localise & publish five pages on each run.

```
dev/tasks/initial-page-localisation-task publish=1&limit=5
```
#### From the CMS (SiteTree only)

Use Silverstripe's
built-in [publishall](https://docs.silverstripe.org/en/4/developer_guides/debugging/url_variable_tools/#building-and-publishing-urls)
tool to publish all Pages in bulk.
Run `/admin/pages/publishall` in your browser and your site will be fixed again and you can start adding translated
content.

_This method will work with Pages only (not localised DataObjects)._

#### Commandline or Queued Jobs (SiteTree and DataObjects)

The `InitialPageLocalisation` and `InitialDataObjectLocalisationTask` dev tasks may be used to localise and, optionally,
publish your `Versioned` data (including Pages) from the commandline or queued as a job (if the Queued Jobs module is installed).

`InitialPageLocalisation` - localise all `SiteTree` objects (Pages)

`InitialDataObjectLocalisationTask` - localise all Fluent-enabled DataObjects (excluding `SiteTree`)

1. Example: Localise all Pages (default, without publishing)

```
dev/tasks/initial-page-localisation-task
```

2. Example: Localise & publish all Pages

```
dev/tasks/initial-page-localisation-task publish=1
```

3. Example: Localising Pages in batches can be done by using the `limit` option.
This will localise & publish five pages on each run.

```
dev/tasks/initial-page-localisation-task publish=1&limit=5
```

4. Example: All the same functionality is available for localising all DataObjects, including `Versioned` and non-Versioned classes

```
dev/tasks/initial-dataobject-localisation-task
```
or

```
dev/tasks/initial-dataobject-localisation-task publish=1&limit=5
```

#### Customize your own initialisation dev task

Perhaps you want to be more selective in how you initialise your localised content.
The `InitialDataObjectLocalisationTask` class can be easily extended to either list exactly which classes you want to
initially localise, or you can exclude specific classes from initialisation.

1. **Initialise specific classes:** The following example will create a task which localises **_ONLY_** `BlogPost`
pages, `Testimonial` objects, _and their subclasses (if any)_.

```php
class CustomLocalisationTask extends InitialDataObjectLocalisationTask
{
/**
* @var string
*/
private static $segment = 'custom-localisation-initialisation-task';

/**
* @var string
*/
protected $title = 'Custom localisation initialisation';

/**
* @var string[]
*/
protected array $include_only_classes = [
\SilverStripe\Blog\Model\BlogPost::class,
\AcmeCo\Model\Testimonial::class
];

}
```

2. **Initialise all DataObjects but exclude some:** The following example will create a task which localises **_ALL_**
DataObjects **_except_** `BlogPost` pages, `Testimonial` objects, _and their subclasses (if any)_.

```php
class CustomLocalisationTask extends InitialDataObjectLocalisationTask
{
/**
* @var string
*/
private static $segment = 'custom-localisation-initialisation-task';

/**
* @var string
*/
protected $title = 'Custom localisation initialisation';

/**
* @var string[]
*/
protected array $exclude_classes = [
\SilverStripe\Blog\Model\BlogPost::class,
\AcmeCo\Model\Testimonial::class
];

}
```

3. **One or the other:** You may specify `$include_only_classes` OR `$exclude_classes` - not both.
If `$include_only_classes` is not an empty array, `$exclude_classes` will be ignored.
46 changes: 44 additions & 2 deletions src/Extension/FluentExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -582,11 +582,11 @@ public function augmentSQL(SQLSelect $query, DataQuery $dataQuery = null)

// Apply substitutions
$localisedPredicate = str_replace($conditionSearch, $conditionReplace, $predicate);

if (empty($localisedPredicate)) {
continue;
}

$where[$index] = [
$localisedPredicate => $parameters
];
Expand Down Expand Up @@ -630,6 +630,32 @@ public function onAfterWrite(): void
$this->handleClassChanged();
}

/**
* If an object is duplicated also duplicate existing localised values from original to new object.
*/
public function onAfterDuplicate($original, $doWrite, $relations): void
{
$localisedTables = $this->owner->getLocalisedTables();
foreach ($localisedTables as $tableName => $fields) {
// Target IDs
$fromID = $original->ID;
$toID = $this->owner->ID;

// Get localised table
$localisedTable = $this->getLocalisedTable($tableName);

// Remove existing translations from duplicated object
DB::prepared_query("DELETE FROM \"$localisedTable\" WHERE \"RecordID\" = ?", [$toID]);

// Copy translations to duplicated object
$fields_str = '"' . implode('","', $fields) . '"';
DB::prepared_query("INSERT INTO \"$localisedTable\" ( \"RecordID\", \"Locale\", $fields_str)
SELECT ? AS \"RecordID\", \"Locale\", $fields_str
FROM \"$localisedTable\"
WHERE \"RecordID\" = ?", [$toID, $fromID]);
}
}

/**
* If an object is changed to another class, we should trigger localised copy
*
Expand Down Expand Up @@ -1115,6 +1141,22 @@ public function updateFluentCMSField(FormField $field)
$field->setTitle($tooltip);
}

/**
* Update preview link to null if the object isn't in the current locale
* and we can't fallback cleanly.
*
* @param ?string $link
*/
public function updatePreviewLink(&$link): void
{
$owner = $this->owner;
$info = $owner->LocaleInformation(FluentState::singleton()->getLocale());

if (!$info->getSourceLocale()) {
$link = null;
}
}

/**
* Require that this record is saved in the given locale for it to be visible
*
Expand Down
45 changes: 45 additions & 0 deletions src/Extension/FluentVersionedExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -1008,4 +1008,49 @@ protected function setVersionCacheItem(string $class, string $stage, string $loc
// Internally store nulls as 0
$this->versionsCache[$class][$stage][$locale][$key] = $value ?: 0;
}

/**
* If an object is duplicated also duplicate existing localised values from original to new object.
*/
public function onAfterDuplicate($original, $doWrite, $relations): void
{
parent::onAfterDuplicate($original, $doWrite, $relations);

$localisedTables = $this->owner->getLocalisedTables();
foreach ($localisedTables as $tableName => $fields) {
// Target IDs
$fromID = $original->ID;
$toID = $this->owner->ID;

// Get localised table
$localisedTable = $this->getLocalisedTable($tableName) . self::SUFFIX_VERSIONS;

// Remove existing translation versions from duplicated object
DB::prepared_query("DELETE FROM \"$localisedTable\" WHERE \"RecordID\" = ?", [$toID]);

// Copy translations to duplicated object
$localisedFields = array_merge(['Locale', 'Version'], $fields);
$fields_str = '"' . implode('","', $localisedFields) . '"';

// Copy all versions of localised object
DB::prepared_query("INSERT INTO \"$localisedTable\" ( \"RecordID\", $fields_str)
SELECT ? AS \"RecordID\", $fields_str
FROM \"$localisedTable\"
WHERE \"RecordID\" = ?", [$toID, $fromID]);

// Also copy versions of base record
$versionsTableName = $tableName . self::SUFFIX_VERSIONS;

// Remove existing versions from duplicated object, created by onBeforeWrite
DB::prepared_query("DELETE FROM \"$versionsTableName\" WHERE \"RecordID\" = ?", [$toID]);

// Copy all versions of base record, todo: optimize to only copy needed versions
$fields = DB::query("SELECT \"COLUMN_NAME\" FROM \"INFORMATION_SCHEMA\".\"COLUMNS\" WHERE \"TABLE_NAME\" = '$versionsTableName' AND \"COLUMN_NAME\" NOT IN('ID','RecordID')");
$fields_str = '"' . implode('","', $fields->column()) . '"';
DB::prepared_query("INSERT INTO \"$versionsTableName\" ( \"RecordID\", $fields_str)
SELECT ? AS \"RecordID\", $fields_str
FROM \"$versionsTableName\"
WHERE \"RecordID\" = ?", [$toID, $fromID]);
}
}
}
Loading
Loading