Adrieng
December 13, 2023, 3:30pm
1
Hi,
It seems that I can’t update the site content programmatically. It only work for its title. I’m using Kirby 4.0.1 .
These scripts are working :
$kirby->impersonate('kirby');
try {
$site->update([
'title' => 'My site',
]);
} catch (\Throwable $th) {
throw new Exception($th);
}
$kirby->impersonate('kirby');
try {
$page->update([
'description' => 'New description',
]);
} catch (\Throwable $th) {
throw new Exception($th);
}
But not this one :
$kirby->impersonate('kirby');
try {
$site->update([
'description' => 'New site description',
]);
} catch (\Throwable $th) {
throw new Exception($th);
}
However there is a description field in the site blueprint. It doesn’t work with any other fields than the title.
I don’t understand why. Can you help me ?
Thank you.
EDIT : I’ve made few tests. The update works for fields that doesn’t exists in the site.txt (it creates the data entry) but if it exists I can’t modify it (only for site, not for pages).
texnixe
December 13, 2023, 4:36pm
3
Cannot reproduce this.
What does the site.yml blueprint look like?
Do you have any permissions set, if so, which ones?
Can you reproduce your issue in a fresh Starterkit?
Adrieng
December 13, 2023, 4:43pm
4
Where can I check the permissions ?
Here you are :
title: Site
tabs:
contentTab:
label: Page
columns:
- width: 1/3
sections:
listed:
label: Menu
type: pages
templates:
- free
- repository
- external
status: listed
image:
cover: true
back: white
- width: 1/3
sections:
unlisted:
label: Page non-indexées
type: pages
query: site.index.filterBy('status', 'unlisted').without('error')
help: Ces pages peuvent être pointées par des liens mais ne sont pas affichées dans le menu.
- width: 1/3
sections:
drafts:
label: Brouillon
type: pages
status: draft
create:
- free
- repository
- external
newsletterTab:
label: Newsletter
fields:
newsletterCover:
label: Image de section
type: files
multiple: false
layout: cards
width: 1/2
size: huge
image:
ratio: 16/11
cover: true
newsletterIntro:
label: Texte d'invitation
type: writer
width: 1/2
maxlength: 500
help: Affiché dans la section newsletter pour inviter à s'y abonner.
mapTab: tabs/map
configTab:
label: Contact
fields:
address:
label: Adresse postale
type: text
width: 1/3
icon: pin
mail:
label: Email
type: email
width: 1/3
tel:
label: Téléphone
type: tel
width: 1/3
texnixe
December 13, 2023, 5:04pm
5
So the field you want to update is not defined in site.yml?
Adrieng
December 13, 2023, 5:21pm
6
When it exists it doesn’t work. If I try to update for exemple the address field it doesn’t work while it works if i update the description field that doesn’t exist.
texnixe
December 13, 2023, 5:33pm
7
As I wrote above, works fine for me in both cases. Please download a fresh Starterkit and check if you can reproduce your issue there.
Adrieng
December 13, 2023, 5:50pm
8
Here is a streamable video of the kind of strange behaviors I observe : Enregistrement de l’écran 2023-12-13 à 18.48.05
The mapData field exists in the blueprint, its update doesn’t work.
The description field doesn’t exist, its update works.
(The address field exists, its update doesn’t work neither.)
If it can help you to understand…
I’ll try with a fresh starterkit.
texnixe
December 13, 2023, 6:02pm
9
Well, unfortunately, the mapdata field is missing from your blueprint. Maybe it’s an issue with the field type and what you are trying to store in it.
Adrieng
December 13, 2023, 7:05pm
10
It’s the same for the address field that is shown in the blueprint. No idea ?
Envoyé de mon mobile
Adrieng
December 15, 2023, 8:04am
11
I understand better what’s happening but still can’t fix it.
The function is executed through a hook :
[
'pattern' => '/import-map-data.json',
'action' => function() {
$site = site();
$kirby = kirby();
$csvString = $site->mapDataFile()->toFile()->read();
$mapData = csvStringToYaml($csvString);
$kirby->impersonate('kirby');
try {
$site->update([
'mapdata' => Yaml::encode($mapData),
]);
foreach ($mapData as $item) {
if (!isset($item['street']) && !isset($item['town'])) return;
$parenthesisRegex = '/\s*\([^)]*\)/';
$adress = $item['street'] . ', ' . preg_replace($parenthesisRegex, '', $item['town']);
try {
$item['location'] = getLocationData($adress);
} catch (\Throwable $th) {
return json_encode('Couldn\'t get locations data through open street map. Error : ' . $th);
}
}
F::write('./assets/map-data.json', json_encode($mapData));
} catch (\Throwable $th) {
return json_encode('Couldn\'t write data file. Error : ' . $th);
}
return json_encode('Map data update succeed.');
}
]
The foreach loop takes time because it does multiple fetches to an API. During this time, the “mapdata” field is correctly filled. But right after the loop and the whole script is over, the field is cleared. As if Kirby has automatically performed an update based on the current content of the front-end field (wich is empty). Is it possible ? Does this gives you any idea @texnixe ?
bnomei
December 15, 2023, 8:44am
12
just a guess. if you are logged in the panel and call the endpoint at the same time try not to change the user with impersonating
$kirby->impersonate(kirby()->user()?->id() ?? 'kirby');
Adrieng
December 15, 2023, 8:49am
13
It doesn’t resolve the problem, but thanks !
texnixe
December 15, 2023, 8:57am
14
And if you remove this api stuff, it works?
Adrieng
December 15, 2023, 12:28pm
15
No, it doesn’t work neither.
texnixe
December 15, 2023, 3:53pm
16
Ok, let’s go one step back.
If you try to update that site field from a template instead of from your route, does that work?
Have you ever tested to move your site.yml into a starterkit and test that?
Adrieng:
foreach ($mapData as $item) {
if (!isset($item['street']) && !isset($item['town'])) return;
$parenthesisRegex = '/\s*\([^)]*\)/';
$adress = $item['street'] . ', ' . preg_replace($parenthesisRegex, '', $item['town']);
try {
$item['location'] = getLocationData($adress);
} catch (\Throwable $th) {
return json_encode('Couldn\'t get locations data through open street map. Error : ' . $th);
}
}
Probably unrelated, but in this loop you’re editing a copy of each $item
. This doesn’t mutate the original $mapData
that you then save in './assets/map-data.json'
.
You either have to use a reference pointer to $item
when looping over mapData (foreach ($mapData as &$item) {
) or explicitly put the value back into $mapData
:
foreach ($mapData as $i => $item) {
//...
$mapData[$i]['location'] = getLocationData($adress);
//...
}
1 Like
Adrieng
December 18, 2023, 7:46am
18
I can indeed update the site content through a template !
Following your advice, I did some test through the home template and it works if I call the route from here. As well as if I put the script in the template and execute it without calling the route. So the problem seems to come from the plugin where is located the button that calls the route. Here is the component :
<template>
<k-button
class="import-btn"
variant="filled"
:icon="icon"
@click="importMapData"
>Importer les données</k-button
>
</template>
<script setup>
import { ref } from "vue";
const icon = ref("merge");
function importMapData() {
icon.value = "loader";
fetch("/import-map-data.json")
.then((res) => res.json())
.then((json) => {
icon.value = "check";
console.log(json);
});
fetch("/test");
}
</script>
<style>
.import-btn {
margin-top: 2rem;
}
</style>
And, as a reminder, the route :
[
'pattern' => '/import-map-data.json',
'action' => function() {
$site = site();
$kirby = kirby();
$csvString = $site->mapDataFile()->toFile()->read();
$mapData = csvStringToYaml($csvString);
$kirby->impersonate(kirby()->user()?->id() ?? 'kirby');
try {
$site->update([
'mapData' => Yaml::encode($mapData),
]);
foreach ($mapData as $item) {
if (!isset($item['street']) && !isset($item['town'])) return;
$parenthesisRegex = '/\s*\([^)]*\)/';
$adress = $item['street'] . ', ' . preg_replace($parenthesisRegex, '', $item['town']);
try {
$item['location'] = getLocationData($adress);
} catch (\Throwable $th) {
return json_encode('Couldn\'t get locations data through open street map. Error : ' . $th);
}
}
F::write('./assets/map-data.json', json_encode($mapData));
} catch (\Throwable $th) {
return json_encode('Couldn\'t write data file. Error : ' . $th);
}
return json_encode('Map data update succeed.');
}
],
Adrieng
December 18, 2023, 9:30am
20
I don’t understand why but it finally works.
Here is the final script :
$site = site();
$kirby = kirby();
$csvString = $site->mapDataFile()->toFile()->read();
$mapData = csvStringToYaml($csvString);
$kirby->impersonate(kirby()->user()?->id() ?? 'kirby');
$errors = [];
try {
$site->update(['mapData' => Yaml::encode($mapData)]);
foreach ($mapData as $i => $item) {
try {
if (!isset($item['street']) && !isset($item['town'])) {
throw new Exception("Street or town data is missing for item $i", 400);
}
$parenthesisRegex = '/\s*\([^)]*\)/';
$address = Html::decode($item['street']) . ', ' . preg_replace($parenthesisRegex, '', $item['town']);
$mapData[$i]['location'] = getLocationData($address);
} catch (\Throwable $th) {
$errors[$i] = $th->getMessage();
}
}
F::write('./assets/map-data.json', json_encode($mapData));
} catch (\Throwable $th) {
$errors['general'] = $th->getMessage();
}
if (empty($errors)) {
http_response_code(200);
echo json_encode(['success' => 'Map data update succeeded.']);
exit;
} else {
http_response_code(500);
echo json_encode(['error' => $errors]);
exit;
}
And the final component :
<template>
<div>
<k-button variant="filled" :icon="icon" @click="importMapData"
>Importer les données</k-button
>
<k-info-field
v-if="requestStatus === 'pending'"
:text="requestMessage"
theme="neutral"
/>
<k-info-field
v-if="requestStatus === 'success'"
:text="requestMessage"
theme="positive"
/>
<k-info-field
v-if="requestStatus === 'error'"
:text="requestMessage"
theme="negative"
/>
</div>
</template>
<script setup>
import { ref } from "vue";
const icon = ref("merge");
const requestMessage = ref(
"Attention, l'import de données écrasera les données du tableau ci-dessus."
);
const requestStatus = ref("pending");
function importMapData() {
icon.value = "loader";
fetch("/import-map-data.json")
.then((res) => {
return res.json();
})
.then((json) => {
if (json.error) {
requestStatus.value = "error";
requestMessage.value = `Erreur lors de l'ajout de certaines données. Aucune correspondances trouvées pour les adresses : ${json.error.join(
", "
)}`;
throw new Error(json.error);
}
requestStatus.value = "success";
requestMessage.value = `Données ajoutées. <a href="${window.location.href}" alt="Recharger la page pour rafraîchir les données.">Rechargez la page</a>.`;
icon.value = "check";
console.log(json);
})
.catch((error) => {
requestStatus.value = "error";
requestMessage.value = "Erreur lors des données. Veuillez réessayer.";
console.error("Error:", error);
});
}
</script>
<style scoped>
.k-button {
margin-top: 2rem;
}
.k-info-field {
margin-top: 1rem;
}
</style>