Monday, November 08, 2010 at 12:05 AM.
system.verbs.builtins.radio.macros.weblogRecentPosts
on weblogRecentPosts (adrdata=radio.weblog.init (), bgColor=radio.data.htmlColors.cellBgColor, hilightBgColor=radio.data.htmlColors.highlightColor, catname=nil) {
<<Changes:
<<11/5/02; 1:23:20 AM by JES
<<Reversed the last change.
<<10/28/02; 7:38:13 PM by JES
<<Call html.processMacros with plainprocessing as true to prevent extra paragraph tags.
<<10/12/02; 2:48:51 AM by JES
<<Localized the header and delete button text.
<<3/11/02; 8:57:40 AM by DW
<<When displaying a recent post, use the title and link info, if it's available.
<<2/13/02; 3:42:48 PM by JES
<<Update relevant category RSS files, when post(s) are deleted. Don't update the main weblog archive pages when a post's flNotOnHomePage is true.
<<2/10/02; 11:09:58 PM by JES
<<Fixed a bug where when deleting a post from an archive page, the post would be deleted, but the public site would not be updated, and a macro error would appear when the browser reloads the page in the desktop website.
<<2/10/02; 10:39:17 PM by JES
<<Fixed a bug where archive pages would limit the number of posts displayed to the same number displayed on the home page, which could prevent editing of some items.
<<2/5/02; 5:04:08 PM by JES
<<Fixed a bug where navigating to previous posts using the calendar on the desktop website home page, would not display previous posts.
<<2/4/02; 2:54:47 PM by JES
<<Added an optional parameter, catname. If specified, only posts in the specified category will be listed.
<<1/25/02; 1:26:05 PM by JES
<<Applied the same optimization to add() that was applied to radio.html.viewNewsItems. Before, this script took 84 ticks. After this change, it takes 29 ticks -- more than twice as fast.
<<1/19/02; 9:11:26 PM by JES
<<Make sure that adrpost^.when is defined. If not, set it to when for a surrounding post if possible, or to clock.now if there are no other posts.
<<1/15/02; 2:08:58 PM by DW
<<Call callbacks in user.radio.callbacks.deleteItem.
<<http://radio.userland.com/stories/storyReader$7740#userradiocallbacksdeleteitem
<<1/9/02; 12:18:21 AM by JES
<<Add a searchArg, uid, whose value is number (clock.now ()) -- to act as a browser cache confuser. This prevents the browser from loading up an old, cached version of a previously edited post, when clicking the Edit button a second time.
<<1/6/02; 4:30:49 AM by JES
<<Added table css classes.
<<11/30/01; 12:32:04 PM by JES
<<Convert line-endings in htmltext to \r, before returning.
<<11/29/01; 5:50:39 PM by JES
<<When a post is deleted, remove it from the storyList in all categories which include it.
<<11/23/01; 9:53:05 PM by DW
<<It's help support was archaic. Modernized.
<<11/23/01; 9:47:05 PM by JES
<<Call html.processMacros on the returned htmltext.
<<11/14/01; 11:08:52 PM by JES
<<Rewrote the post-handling code, which deletes posts and updates the relevant pages. Return the empty string for dynamic pages, which aren't the home page.
<<10/29/01; 4:47:17 PM by JES
<<Added optional parameters: bgColor and hilightBgColor.
<<10/27/01; 9:08:01 AM by DW
<<If static rendering, return the empty string.
<<8/31/01; 8:38:06 PM by JES
<<Factored from radio.weblog.main.
<<Preserve the menu and d getArgs so that we stay on the same archive page, and menu mode when clicking an EDIT button.
local (pta = html.getPageTableAddress ());
<<scratchpad.pagetable = pta^; edit (@scratchpad.pagetable) //debugging code
bundle { //if static rendering, return the empty string
if pta^.radioResponder.flStaticRendering {
return ("")}};
bundle { //if not the weblog editor page, return the empty string
if not pta^.radioResponder.flStaticRendering {
if pta^.path != radio.data.systemUrls.weblogEditor {
if not (string.lower (pta^.path) beginsWith string.lower (radio.data.systemUrls.newCategory)) {
return ("")}}}};
local (htmltext, indentlevel=0);
on add (s) {
htmltext = htmltext + (string.filledstring ("\t", indentlevel) + s + "\r\n");};
local (d = clock.now (), flArchivePage = false, dateArg); //PBS 02/22/01: archive page support
local (editbuttonimg = radio.images.systemImageRef ("icons/editbutton"));
local (globeimg = radio.images.systemImageRef ("qbullet/remote"));
local (xmlimg = radio.images.systemImageRef ("icons/xml"));
local (mediaimg = radio.images.systemImageRef ("qbullet/sound"));
local (checkmarkimg = radio.images.systemImageRef ("icons/checkmark"));
local (helpurl = radio.userinterface.helpurl ("Previous Posts"));
if pta^.method == "POST" {
local (adrargs = @pta^.radioResponder.postArgs);
if defined (adrargs^) {
if defined (adrargs^.deletePosts) {
delete (@adrargs^.deletePosts);
local (postsToDelete); new (tableType, @postsToDelete);
local (adr, adrpost);
for adr in adrargs { //build a table containing addresses of posts to delete
on isPostId (s) {
try {
local (id = number (s));
return (defined (adrdata^.posts.[string.padWithZeros (id, 8)]))};
return (false)};
if isPostId (nameOf (adr^)) {
local (adritem = table.uniqueName ("post", @postsToDelete, 8));
adritem^ = @adrdata^.posts.[string.padwithzeros (nameof (adr^), 8)]}};
local (flUpdateHomePage = false, datesToUpdate = {});
local (catsToUpdate); new (tableType, @catsToUpdate);
for adr in @postsToDelete { //go through all the posts, figure out which pages to update, deleting posts as we go
adrpost = adr^; local (theDate);
bundle { //get theDate
local (day, month, year, hour, minute, second);
date.get (adrpost^.when, @day, @month, @year, @hour, @minute, @second);
theDate = date.set (day, month, year, 0, 0, 0)};
if not flUpdateHomePage { //if flUpdateHome page is true, we already know we need to update
if radio.weblog.isPostOnHomePage (adrpost) {
flUpdateHomePage = true}};
if not (datesToUpdate contains theDate) { //keep a list of archive dates to re-render
if not adrpost^.flNotOnHomePage {
datesToUpdate = datesToUpdate + theDate}};
if defined (adrpost^.categories) { //go through categories and figure out what needs updating
local (ixcat, sizecats = sizeOf (adrpost^.categories));
for ixcat = 1 to sizecats {
if adrpost^.categories[ixcat] { //a hit
local (catname = nameOf (adrpost^.categories[ixcat]));
local (adrcat = @adrdata^.categories.[catname]);
if defined (adrcat^) {
adrcat^.storyList = adrcat^.storyList - {number (nameOf (adrpost^))}};
if not defined (catsToUpdate.[catname]) { //init our temp data
new (tableType, @catsToUpdate.[catname]);
catsToUpdate.[catname].flUpdateIndexPage = false;
catsToUpdate.[catname].datesToUpdate = {}};
if not catsToUpdate.[catname].flUpdateIndexPage { //is this post in the cat's index page?
if radio.weblog.isPostOnHomePage (adrpost, catname) {
catsToUpdate.[catname].flUpdateIndexPage = true}};
if not (catsToUpdate.[catname].datesToUpdate contains theDate) { //the cat's archive page
catsToUpdate.[catname].datesToUpdate = catsToUpdate.[catname].datesToUpdate + theDate}}}};
bundle { //call the callbacks -- 1/15/02; 2:08:29 PM by DW
try {
if not defined (user.radio.callbacks.deleteItem) {
new (tabletype, @user.radio.callbacks.deleteItem)};
local (adrcallback);
for adrcallback in @user.radio.callbacks.deleteItem {
try {
while typeOf (adrcallback^) == addressType {
adrcallback = adrcallback^};
adrcallback^ (adrpost)}}}};
delete (adrpost)};
bundle { //update all the pages
local (dateToUpdate, adrCatToUpdate);
if flUpdateHomePage { //update the weblog home page
radio.weblog.publish (adrdata);
thread.callScript (@radio.weblog.publishRss, {adrdata})};
for dateToUpdate in datesToUpdate { //update weblog archive pages
radio.weblog.publish (adrdata, d:dateToUpdate)};
for adrCatToUpdate in @catsToUpdate { //update all the category pages
local (catname = nameOf (adrCatToUpdate^), flUpdateRss = false);
if adrCatToUpdate^.flUpdateIndexPage {
radio.weblog.publish (adrdata, catname);
flUpdateRss = true};
for dateToUpdate in adrCatToUpdate^.datesToUpdate {
radio.weblog.publish (adrdata, catname, dateToUpdate);
flUpdateRss = true};
if flUpdateRss {
local (adrcat = @adrdata^.categories.[catname]);
adrcat^.flDirty = true}}}}}};
<<bundle //old code
<<local (adr, adrpost, deletedPostDates = {}, deletedPostDate)
<<for adr in adrargs
<<adrpost = @adrdata^.posts.[string.padwithzeros (nameof (adr^), 8)]
<<if defined (adrpost^)
<<local (adrcats = @adrpost^.categories)
<<if defined (adrcats^)
<<local (adr)
<<local (id = number (nameof (adrpost^)))
<<for adr in @adrdata^.categories
<<if adr^.storyList contains id
<<adr^.storyList = adr^.storyList - {id}
<<adr^.flDirty = true
<<delete (adrcats)
<<bundle //add the date for this post to the deletedPostDates list
<<local (day, month, year, hour, minute, second)
<<date.get (adrpost^.when, @day, @month, @year, @hour, @minute, @second)
<<deletedPostDate = date.set (day, month, year, 0, 0, 0)
<<if not (deletedPostDates contains deletedPostDate)
<<deletedPostDates = deletedPostDates + deletedPostDate
<<delete (adrpost)
<<bundle //publish home page and archive pages in their own threads
<<local (flPublishHomePage = false)
<<for deletedPostDate in deletedPostDates
<<if radio.weblog.isDateOnHomePage (deletedPostDate)
<<flPublishHomePage = true
<<if flPublishHomePage
<<radio.weblog.publish (adrdata)
<<for deletedPostDate in deletedPostDates
<<local (day, month, year, hour, minute, second)
<<date.get (deletedPostDate, @day, @month, @year, @hour, @minute, @second)
<<local (maxDate = date.set (day, month, year, 23, 59, 59))
<<thread.callScript (@radio.weblog.publishStaticPage, {adrdata, nil, 1, maxDate, deletedPostDate, false})
local (adrPostToEdit);
bundle { //process GET args
local (adrargs = @pta^.radioResponder.getArgs);
if defined (adrargs^) {
if defined (adrargs^.d) { //PBS 02/22/01: archive page, possibly
flArchivePage = true;
local (year = string.nthField (adrargs^.d, '/', 1));
local (month = string.nthField (adrargs^.d, '/', 2));
local (day = string.nthField (adrargs^.d, '/', 3));
d = date.set (day, month, year, 0, 0, 0);
dateArg = year + "/" + string.padWithZeros (month, 2) + "/" + string.padWithZeros (day, 2);
bundle { //check d against midnight of the day with the last post
local (midnightMostRecentDay);
local (adrLastPost = @adrdata^.posts [sizeOf (adrdata^.posts)]);
local (day, month, year, hour, minute, second);
date.get (adrLastPost^.when, @day, @month, @year, @hour, @minute, @second);
midnightMostRecentDay = date.set (day, month, year, 0, 0, 0);
if d >= midnightMostRecentDay {
flArchivePage = false}}};
if defined (adrargs^.itemToEdit) {
adrPostToEdit = @adrdata^.posts.[string.padWithZeros (adrargs^.itemToEdit, 8)]}}};
bundle { //add the posts
local (ctposts = sizeof (adrdata^.posts));
if ctposts > 0 {
add ("<br>");
add ("<form method=\"POST\" action=\"" + radio.data.systemUrls.weblogEditor + "\">"); indentlevel++;
if flArchivePage {
add ("<input type=\"hidden\" name=\"d\" value=\"" + dateArg + "\">")};
add ("<table class=\"dwsFrameTable\" cellspacing=\"0\" cellpadding=\"0\"><tr bgcolor=\"" + radio.data.htmlColors.framecolor + "\"><td>"); indentlevel++;
add ("<table class=\"dwsTable\" border=\"0\" cellspacing=\"1\" cellpadding=\"5\">"); indentlevel++;
if flArchivePage {
local (t); new (tabletype, @t);
t.day = date.shortString (d);
add (radio.html.prefsElement.separator (radio.string.getLocalizedString ("homePage.postsForDate", @t), false, 4, helpUrl))}
<<"Posts for " + date.shortString (d) + "..."
else {
local (t); new (tabletype, @t);
t.numposts = adrdata^.prefs.viewing.ctPosts;
add (radio.html.prefsElement.separator (radio.string.getLocalizedString ("homePage.previousNPosts", @t), false, 4, helpUrl))};
<<"Previous " + adrdata^.prefs.viewing.ctPosts + " posts..."
local (ctlisted = 0);
case adrdata^.prefs.viewing.mode {
"byPosts" {
local (adrpost, editurl, cellBgColor);
<<local (lastitemnum = ctposts - adrdata^.prefs.viewing.ctPosts + 1)
<<if lastitemnum < 1
<<lastitemnum = 1
<<local (start = ctposts, end = lastitemnum)
local (i = ctposts);
if flArchivePage {
local (day, month, year, hour, minute, second);
date.get (d, @day, @month, @year, @hour, @minute, @second);
<<bundle //obsolete code
<<local (adrFirstPost)
<<local (firstOfDay = date.set (day, month, year, 0, 0, 0))
<<radio.weblog.getNextPostAfterDate (adrdata, firstOfDay, @adrFirstPost)
<<end = indexOf (adrFirstPost^) //reverse chronological order
local (adrLastPost);
local (lastOfDay = date.set (day, month, year, 23, 59, 59));
radio.weblog.getLastPostBeforeDate (adrdata, lastOfDay, @adrLastPost, catname);
i = indexOf (adrLastPost^)};
loop {
if i < 1 {
break};
if not flArchivePage {
if ctlisted >= adrdata^.prefs.viewing.ctPosts {
break}};
adrpost = @adrdata^.posts [i];
if flArchivePage { //the home page -- end is a post table index, if an archive page
if adrpost^.when < d {
break}};
if catname != nil {
if not radio.weblog.isPostInCategory (adrpost, catname) {
i--;
continue}};
if adrpost == adrPostToEdit {
cellBgColor = hilightBgColor}
else {
cellBgColor = bgcolor};
add ("<tr class=\"dwsTableCell\" bgcolor=\"" + cellBgColor + "\">"); indentlevel++;
bundle { //add checkbox
local (name = number (nameof (adrpost^)));
local (checkboxval = "xxx ");
if false {
checkboxval = checkboxval + "checked"};
add ("<td class=\"dwsTableCell\" valign=\"top\"><input type=\"checkbox\" name=\"" + name + "\" value=" + checkboxval + "></td>")};
bundle { //add the post text
local (posttext = string (adrpost^.text));
if defined (adrpost^.title) {
local (titletext = adrpost^.title);
if defined (adrpost^.link) {
titletext = "<a href=\"" + adrpost^.link + "\" class=\"weblogItemTitle\">" + titletext + "</a>"};
posttext = titletext + ". " + posttext};
add ("<td class=\"dwsTableCell\" valign=\"top\">" + posttext + "</td>")};
bundle { //make sure that adrpost^.when is defined
if not defined (adrpost^.when) {
local (ix = indexOf (adrpost^));
if ix > 1 {
adrpost^.when = date (adrdata^.posts[ix - 1].when + 1)}
else {
if sizeOf (adrdata^.posts) > 1 {
adrpost^.when = date (adrdata^.posts[ix + 1].when - 1)}
else {
adrpost^.when = clock.now ()}}}};
add ("<td class=\"dwsTableCell\" valign=\"top\" nowrap><font size=\"-1\">" + radio.string.getDateString (adrpost^.when) + "</font></td>");
bundle { //add little icons
add ("<td class=\"dwsTableCell\" valign=\"top\" nowrap>"); indentlevel++;
bundle { //EDIT button, checkmark, enclosure icons
local (args);
new (tabletype, @args);
args.itemToEdit = number (nameof (adrpost^));
args.uid = number (clock.now ());
<<if defined (pta^.radioResponder.getArgs.menu)
<<args.menu = pta^.radioResponder.getArgs.menu
if flArchivePage {
args.d = dateArg};
<<editurl = pta^.uri + "?" + webserver.encodeargs (@args)
editurl = radio.data.systemUrls.weblogEditor + "?" + webserver.encodeargs (@args);
editurl = string.replaceAll (editurl, "%2F", "/");
local (checkmark = "");
bundle { //an item has a checkmark if it's in at least one category
if defined (adrpost^.categories) {
if sizeOf (adrpost^.categories) > 0 {
local (adr);
for adr in @adrpost^.categories {
if adr^ {
checkmark = " <a title=\"" + radio.data.strings.checkmarktooltip + "\">" + checkmarkimg + "</a>";
break}}}}};
local (encl = "");
bundle { //if it has an enclosure add a media link
if defined (adrpost^.enclosure) {
if not defined (adrpost^.enclosure.error) {
encl = "<br><a href=\"" + adrpost^.enclosure.url + "\">" + mediaimg + "</a>"}}};
add ("<a href=\"" + editurl + "\">" + editbuttonimg + "</a>" + checkmark + encl)};
add ("</td>"); indentlevel--};
add ("</tr>"); indentlevel--;
i--; ctlisted++}}};
if ctlisted == 0 {
return ("")};
add ("</table>"); indentlevel--;
add ("</td></tr></table>"); indentlevel--;
local (deleteButtonText = radio.string.getLocalizedString ("buttons.delete"));
add ("<p><input type=\"submit\" name=\"deletePosts\" value=\"" + deleteButtonText + "\"></p>");
add ("</form>"); indentlevel--}};
bundle { //start the form
local (actionString = "");
if defined (pta^.radioResponder.getArgs) {
if sizeOf (pta^.radioResponder.getArgs) > 0 {
actionString = " action=\"" + pta^.uri + "?" + webserver.encodeArgs (@pta^.radioResponder.getArgs) + "\""}};
add ("<form method=\"POST\"" + actionString + ">"); indentlevel++};
if flArchivePage {
add ("<input type=\"hidden\" name=\"d\" value=\"" + dateArg + "\">")};
add ("</form>"); indentlevel--;
<<return (htmltext)
htmltext = string.replaceAll (htmltext, "\r\n", "\r");
return (html.processMacros (htmltext, false, adrPageTable:pta))}
<<bundle //testing
<<local (params = scratchpad.pagetable)
<<html.setPageTableAddress (@params)
<<radio.macros.weblogRecentPosts ()
<<html.deletePageTableAddress ()
This listing is for code that runs in the OPML Editor environment. I created these listings because I wanted the search engines to index it, so that when I want to look up something in my codebase I don't have to use the much slower search functionality in my object database. Dave Winer.