Showing most recent child excerpt on Parent page
Asked by Daniel Akrong:

Hi,

I am wondering if there is a way to include an excerpt on the parent page, but one that updates based on the excerpt of the most recent child page. I know that with 'Excerpt Include', I could just edit it to the name of the most recent child page, but is there a way to do it autonomously? Maybe there's a macro that allows me to edit as "Date Created" for the child page or something.

It doesn't necessarily have to be an excerpt, that's just a good way I know to have highlights on the parent page. If there's another sort of "Text Box", I could use that as well.

Thanks


Answers:
author: Ken McClean
body: To clarify, you want the excerpt on the parent page to match an excerpt **on** the most recently updated child page? Or you want the parent page to have an excerpt **of**the most recent child page?

comments: author: Daniel Akrong
body: To match the excerpt **on**the most recent child page


author: Ken McClean
body: Hmm okay that's a little more tricky. Let me see if I can put together a macro for you. Might take a day or so.


author: Daniel Akrong
body: That would be great! Thank you


author: Ken McClean
body: I didn't forget about you!

Okay so this macro should go on a page that also has the excerpt-include macro on it, and has child pages with the excerpt-include macro on them  

The macro (running on the parent page) looks at the most recently updated child page, and gets the settings of the excerpt-include macro on that child page.  

It then updates the excerpt-include macro on the parent page to match the child page.  

<br />

If you want to learn more about how I put it together, I wrote a little blog post about it: <https://www.kennethmcclean.com/blog/adventures-in-confluence-macros-1-what-even-is-a-javascript-promise/>

*** ** * ** ***

<br />

<br />

<br />

//Excerpt-Include Copy Macro  
//Copies excerpt-include macro settings from the most recently updated child page, to the parent page  
//Author: Ken McClean

//May 7, 2023  

## @noparams  
\<script type="text/javascript"\>

let pageID = AJS.params.pageId  
//Get the ID of the current (parent) page

<br />

const baseURL = "/rest/api/content/";  
const childrenURL = baseURL + pageID + "/child/page?limit=1000\&expand=history.lastUpdated";  
//Get the API endpoint with which we will fetch the most recently update child pages

fetch(childrenURL)  
.then(response =\> response.json())  

.then(data =\> {  
const sortedChildren = data.results.sort((a, b) =\> {  
const aDate = new Date(a.history.lastUpdated.when);  
const bDate = new Date(b.history.lastUpdated.when);  
return bDate - aDate;  
});  
//Return and sort the most recently update child pages

const mostRecentChildID = sortedChildren\[0\].id;  
console.log("The ID of the most recently updated child page is: " + mostRecentChildID);  
//Turn the ID of the most recently update child page into a variable

//Start second-level loop  
const url = \`/pages/viewpage.action?pageId=\` + mostRecentChildID;  
//Define the URL of the target child page

fetch(url)  
.then(response =\> response.text())

.then(html =\> {  
const div = document.createElement('div');  
div.innerHTML = html;

const macroElements = Array.from(div.querySelectorAll(".conf-macro"));

const matchtestMacros = macroElements.filter(macro =\> macro.getAttribute("data-macro-name") === "excerpt-include");

const macroData = \[\];

matchtestMacros.forEach(macro =\> {  
const divs = macro.querySelectorAll("div");  
const res = divs\[0\].innerHTML.replace(/\<\\/?b\>/g, "");  
macroData.push(res);  
});

const ChildMacroSource = macroData\[0\];  
//Get the first excerpt-include macro from the page  
//We're assuming that we're only interested in the first result

//Start third-level loop

const pageURL = baseURL + pageID + '?expand=body.storage,version';

// Retrieve the page content  
fetch(pageURL)  
.then(response =\> response.json())  
.then(data =\> {  
const pageBody = data.body.storage.value;

// Replace "SourcePage" with "NewPage" in the page body  
const modifiedPageBody = pageBody.replace(/ri:content-title="(\[\^"\]\*)"/g, \`ri:content-title="${ChildMacroSource}"\`);

<br />

//Replace the excerpt-include source with the source from the child page

// Update the page with the modified content  
const updateURL = baseURL + pageID;  
const bodyData = JSON.stringify({  
"id": pageID,  
"type": "page",  
"title": data.title,  
"version": {  
"number": data.version.number + 1,  
"minorEdit": false  
},  
"body": {  
"storage": {  
"value": modifiedPageBody,  
"representation": "storage"  
}  
}  
});

fetch(updateURL, {  
method: 'PUT',  
headers: {  
'Content-Type': 'application/json'  
},  
body: bodyData  
})  
.then(response =\> {  
console.log(response);  
if (!response.ok) {  
throw new Error('Failed to update page content: ' + response);  
}  
alert('Page content updated successfully');  
})  
.catch(error =\> {  
alert('Error: ' + error.message);  
});  
})  
.catch(error =\> alert("Encoutered an error updating the page content: " + error));  
//End third-level loop

}).catch(error =\> console.error("Encoutered an error getting the excerpt-include source from the child page: " + error));  
//Second level "then" loop end

}).catch(error =\> console.log("Error fetching child page ID:", error));  
//First level "then" loop end

\</script\>


author: Daniel Akrong
body: Wow this certainly was a lot of effort! Thanks!


author: Daniel Akrong
body: If i wanted to get into making macros, where would I start? Is there some documentation, videos? Instructionals don't seem to be not as widespread as Python per say


author: Ken McClean
body: You'll find that a lot with Atlassian products. There's not a ton of beginner material out there.  

Macros seem to be 95% JavaScript. If you learn JS, I think you're most of the way there toward writing macros. I only just started, myself; this macro that I wrote for you was my second macro project. But because I'm familiar with Confluence itself, I was able to figure out the rest.


author: Graham Carrick
body: I've just launched a App which might be able to help if you are using Confluence Cloud.

[https://marketplace.atlassian.com/apps/1230774/easyinclude?tab=overview\&hosting=cloud](https://marketplace.atlassian.com/apps/1230774/easyinclude?tab=overview&hosting=cloud)