1
0
Fork 0
mirror of https://github.com/sbrl/Pepperminty-Wiki.git synced 2024-11-22 16:33:00 +00:00

Group all the utility functions together

This commit is contained in:
Starbeamrainbowlabs 2015-07-16 10:44:38 +01:00
parent 505eae21d0
commit e553cba9ce
3 changed files with 240 additions and 168 deletions

192
core.php
View file

@ -61,8 +61,49 @@ if($isloggedin)
/////// Login System End /////// /////// Login System End ///////
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////// Security and Consistency Measures //////////////////////////// //////////////////////////////////////// Functions ////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
/*
* @summary Converts a filesize into a human-readable string.
* @source http://php.net/manual/en/function.filesize.php#106569
* @editor Starbeamrainbowlabs
*
* @param $bytes - The number of bytes to convert.
* @param $decimals - The number of decimal places to preserve.
*/
function human_filesize($bytes, $decimals = 2)
{
$sz = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "YB", "ZB"];
$factor = floor((strlen($bytes) - 1) / 3);
return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . @$sz[$factor];
}
/*
* @summary Calculates the time sincce a particular timestamp and returns a
* human-readable result.
* @source http://snippets.pro/snippet/137-php-convert-the-timestamp-to-human-readable-format/
*
* @param $time - The timestamp to convert.
*/
function human_time_since($time)
{
$timediff = time() - $time;
$tokens = array (
31536000 => 'year',
2592000 => 'month',
604800 => 'week',
86400 => 'day',
3600 => 'hour',
60 => 'minute',
1 => 'second'
);
foreach ($tokens as $unit => $text) {
if ($timediff < $unit) continue;
$numberOfUnits = floor($timediff / $unit);
return $numberOfUnits.' '.$text.(($numberOfUnits>1)?'s':'').' ago';
}
}
/* /*
* @summary Gets a list of all the sub pagess of the current page. * @summary Gets a list of all the sub pagess of the current page.
* *
@ -131,6 +172,50 @@ function check_subpage_parents($pagename)
check_subpage_parents($parent_pagename); check_subpage_parents($parent_pagename);
} }
/*
* @summary makes a path safe
*
* @details paths may only contain alphanumeric characters, spaces, underscores, and dashes
*/
function makepathsafe($string)
{
return preg_replace("/[^0-9a-zA-Z\_\-\ \/]/i", "", $string);
}
/*
* @summary Hides an email address from bots by adding random html entities.
*
* @returns The mangled email address.
*/
function hide_email($str)
{
$hidden_email = "";
for($i = 0; $i < strlen($str); $i++)
{
if($str[$i] == "@")
{
$hidden_email .= "&#" . ord("@") . ";";
continue;
}
if(rand(0, 1) == 0)
$hidden_email .= $str[$i];
else
$hidden_email .= "&#" . ord($str[$i]) . ";";
}
return $hidden_email;
}
///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////// Security and Consistency Measures ////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
/*
* Sort out the pageindex. We create it if it doesn't exist, and load and parse
* it if it does.
*/
if(!file_exists("./pageindex.json")) if(!file_exists("./pageindex.json"))
{ {
// From http://in.php.net/manual/en/function.glob.php#106595 // From http://in.php.net/manual/en/function.glob.php#106595
@ -193,39 +278,6 @@ else
{ {
$pageindex = json_decode(file_get_contents("./pageindex.json")); $pageindex = json_decode(file_get_contents("./pageindex.json"));
} }
/*
* @summary makes a path safe
*
* @details paths may only contain alphanumeric characters, spaces, underscores, and dashes
*/
function makepathsafe($string)
{
return preg_replace("/[^0-9a-zA-Z\_\-\ \/]/i", "", $string);
}
/*
* @summary Hides an email address from bots by adding random html entities.
*
* @returns The mangled email address.
*/
function hide_email($str)
{
$hidden_email = "";
for($i = 0; $i < strlen($str); $i++)
{
if($str[$i] == "@")
{
$hidden_email .= "&#" . ord("@") . ";";
continue;
}
if(rand(0, 1) == 0)
$hidden_email .= $str[$i];
else
$hidden_email .= "&#" . ord($str[$i]) . ";";
}
return $hidden_email;
}
// Work around an Opera + Syntastic bug where there is no margin at the left hand side if there isn't a query string when accessing a .php file // Work around an Opera + Syntastic bug where there is no margin at the left hand side if there isn't a query string when accessing a .php file
if(!isset($_GET["action"]) and !isset($_GET["page"])) if(!isset($_GET["action"]) and !isset($_GET["page"]))
@ -288,8 +340,8 @@ class page_renderer
<p>Powered by Pepperminty Wiki, which was built by <a href='//starbeamrainbowlabs.com/'>Starbeamrainbowlabs</a>. Send bugs to 'bugs at starbeamrainbowlabs dot com' or open an issue <a href='//github.com/sbrl/Pepperminty-Wiki'>on github</a>.</p> <p>Powered by Pepperminty Wiki, which was built by <a href='//starbeamrainbowlabs.com/'>Starbeamrainbowlabs</a>. Send bugs to 'bugs at starbeamrainbowlabs dot com' or open an issue <a href='//github.com/sbrl/Pepperminty-Wiki'>on github</a>.</p>
<p>Your local friendly administrators are {admins-name-list}. <p>Your local friendly administrators are {admins-name-list}.
<p>This wiki is managed by <a href='mailto:{admin-details-email}'>{admin-details-name}</a>.</p> <p>This wiki is managed by <a href='mailto:{admin-details-email}'>{admin-details-name}</a>.</p>
{nav-bottom}
</footer> </footer>
{navigation-bar-bottom}
{all-pages-datalist}"; {all-pages-datalist}";
public static $minimal_content_template = "{content} public static $minimal_content_template = "{content}
<hr class='footerdivider' /> <hr class='footerdivider' />
@ -309,6 +361,7 @@ class page_renderer
"{header-html}", "{header-html}",
"{navigation-bar}", "{navigation-bar}",
"{navigation-bar-bottom}",
"{admin-details-name}", "{admin-details-name}",
"{admin-details-email}", "{admin-details-email}",
@ -323,7 +376,8 @@ class page_renderer
$settings->favicon, $settings->favicon,
self::get_css_as_html(), self::get_css_as_html(),
self::render_navigation_bar($settings->navlinks), self::render_navigation_bar($settings->nav_links, $settings->nav_links_extra, "top"),
self::render_navigation_bar($settings->nav_links_bottom, [], "bottom"),
$settings->admindetails["name"], $settings->admindetails["name"],
$settings->admindetails["email"], $settings->admindetails["email"],
@ -368,19 +422,18 @@ class page_renderer
public static $nav_divider = "<span class='nav-divider inflexible'> | </span>"; public static $nav_divider = "<span class='nav-divider inflexible'> | </span>";
public static function render_navigation_bar($nav_links) /*
* @summary Function to render a navigation bar from an array of links. See
* $settings->nav_links for format information.
*
* @param $nav_links - The links to add to the navigation bar.
* @param $nav_links_extra - The extra nav links to add to the "More..."
* menu.
*/
public static function render_navigation_bar($nav_links, $nav_links_extra, $class = "")
{ {
global $settings, $user, $isloggedin, $page; global $settings, $user, $isloggedin, $page;
$result = "<nav>\n"; $result = "<nav class='$class'>\n";
if($isloggedin)
{
$result .= "<span class='inflexible'>Logged in as " . self::render_username($user) . ".</span> "/* . page_renderer::$nav_divider*/;
$result .= "<span><a href='index.php?action=logout'>Logout</a></span>";
$result .= page_renderer::$nav_divider;
}
else
$result .= "<span class='inflexible'>Browsing as Anonymous.</span>" . /*page_renderer::$nav_divider . */"<span><a href='index.php?action=login'>Login</a></span>" . page_renderer::$nav_divider;
// Loop over all the navigation links // Loop over all the navigation links
foreach($nav_links as $item) foreach($nav_links as $item)
@ -391,6 +444,17 @@ class page_renderer
switch($item) switch($item)
{ {
//keywords //keywords
case "user-status":
if($isloggedin)
{
$result .= "<span class='inflexible'>Logged in as " . self::render_username($user) . ".</span> "/* . page_renderer::$nav_divider*/;
$result .= "<span><a href='index.php?action=logout'>Logout</a></span>";
$result .= page_renderer::$nav_divider;
}
else
$result .= "<span class='inflexible'>Browsing as Anonymous.</span>" . /*page_renderer::$nav_divider . */"<span><a href='index.php?action=login'>Login</a></span>" . page_renderer::$nav_divider;
break;
case "search": // Displays a search bar case "search": // Displays a search bar
$result .= "<span class='inflexible'><form method='get' action='index.php' style='display: inline;'><input type='search' name='page' list='allpages' placeholder='Type a page name here and hit enter' /></form></span>"; $result .= "<span class='inflexible'><form method='get' action='index.php' style='display: inline;'><input type='search' name='page' list='allpages' placeholder='Type a page name here and hit enter' /></form></span>";
break; break;
@ -401,7 +465,7 @@ class page_renderer
case "menu": case "menu":
$result .= "<span class='inflexible nav-more'>More...</span>"; $result .= "<span class='inflexible nav-more'>More...</span>";
// todo Add the submenu // todo Add the submenu with $nav_links_extra
break; break;
// It isn't a keyword, so just output it directly // It isn't a keyword, so just output it directly
@ -445,38 +509,6 @@ class page_renderer
} }
} }
///////////////////////////////////////////
//////////////// Functions ////////////////
///////////////////////////////////////////
//from http://php.net/manual/en/function.filesize.php#106569
//edited by Starbeamrainbowlabs
function human_filesize($bytes, $decimals = 2)
{
$sz = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "YB", "ZB"];
$factor = floor((strlen($bytes) - 1) / 3);
return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . @$sz[$factor];
}
//from http://snippets.pro/snippet/137-php-convert-the-timestamp-to-human-readable-format/
function human_time_since($time)
{
$timediff = time() - $time;
$tokens = array (
31536000 => 'year',
2592000 => 'month',
604800 => 'week',
86400 => 'day',
3600 => 'hour',
60 => 'minute',
1 => 'second'
);
foreach ($tokens as $unit => $text) {
if ($timediff < $unit) continue;
$numberOfUnits = floor($timediff / $unit);
return $numberOfUnits.' '.$text.(($numberOfUnits>1)?'s':'').' ago';
}
}
///////////////////////////////////////////
////////////////////////// //////////////////////////
/// Module functions /// /// Module functions ///
////////////////////////// //////////////////////////

214
index.php
View file

@ -91,10 +91,17 @@ $settings->admindetails = [
// [ "Display Text", "Link" ] // [ "Display Text", "Link" ]
// You can also use strings here and they will be printed as-is, except the // You can also use strings here and they will be printed as-is, except the
// following special strings: // following special strings:
// search: Expands to a search box. // user-status Expands to the user's login information
// divider: Expands to a divider to separate links. // e.g. "Logged in as {name}. | Logout".
// more: Expands to the "More..." submenu. // e.g. "Browsing as Anonymous. | Login".
$settings->navlinks = [ //
// search Expands to a search box.
//
// divider Expands to a divider to separate stuff.
//
// more Expands to the "More..." submenu.
$settings->nav_links = [
"user-status",
[ "Home", "index.php" ], [ "Home", "index.php" ],
[ "Login", "index.php?action=login" ], [ "Login", "index.php?action=login" ],
"search", "search",
@ -123,7 +130,9 @@ $settings->nav_links_bottom = [
// inside a <style> tag. This may also be a url - urls will be referenced via a // inside a <style> tag. This may also be a url - urls will be referenced via a
// <link rel='stylesheet' /> tag. // <link rel='stylesheet' /> tag.
$settings->css = "body { margin: 2rem 0; font-family: sans-serif; color: #111111; background: #eee8f2; } $settings->css = "body { margin: 2rem 0; font-family: sans-serif; color: #111111; background: #eee8f2; }
nav { position: absolute; top: 0; left: 0; right: 0; display: flex; background-color: #8a62a7; color: #ffa74d; box-shadow: inset 0 -0.6rem 0.8rem -0.5rem rgba(50, 50, 50, 0.5); } nav.top { position: absolute; top: 0; left: 0; right: 0; box-shadow: inset 0 -0.6rem 0.8rem -0.5rem rgba(50, 50, 50, 0.5); }
nav.bottom { position: absolute; left: 0; right: 0; box-shadow: inset 0 0.8rem 0.8rem -0.5rem rgba(50, 50, 50, 0.5); }
nav { display: flex; background-color: #8a62a7; color: #ffa74d; }
nav > span { flex: 1; text-align: center; line-height: 2; display: inline-block; margin: 0; padding: 0.3rem 0.5rem; transition: all 0.25s; border-left: 3px solid #442772; border-right: 3px solid #442772; } nav > span { flex: 1; text-align: center; line-height: 2; display: inline-block; margin: 0; padding: 0.3rem 0.5rem; transition: all 0.25s; border-left: 3px solid #442772; border-right: 3px solid #442772; }
nav > span:not(.inflexible):hover { transform: scale(1.1); } nav > span:not(.inflexible):hover { transform: scale(1.1); }
nav a { text-decoration: none; font-weight: bolder; color: inherit; } nav a { text-decoration: none; font-weight: bolder; color: inherit; }
@ -239,8 +248,49 @@ if($isloggedin)
/////// Login System End /////// /////// Login System End ///////
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////// Security and Consistency Measures //////////////////////////// //////////////////////////////////////// Functions ////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
/*
* @summary Converts a filesize into a human-readable string.
* @source http://php.net/manual/en/function.filesize.php#106569
* @editor Starbeamrainbowlabs
*
* @param $bytes - The number of bytes to convert.
* @param $decimals - The number of decimal places to preserve.
*/
function human_filesize($bytes, $decimals = 2)
{
$sz = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "YB", "ZB"];
$factor = floor((strlen($bytes) - 1) / 3);
return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . @$sz[$factor];
}
/*
* @summary Calculates the time sincce a particular timestamp and returns a
* human-readable result.
* @source http://snippets.pro/snippet/137-php-convert-the-timestamp-to-human-readable-format/
*
* @param $time - The timestamp to convert.
*/
function human_time_since($time)
{
$timediff = time() - $time;
$tokens = array (
31536000 => 'year',
2592000 => 'month',
604800 => 'week',
86400 => 'day',
3600 => 'hour',
60 => 'minute',
1 => 'second'
);
foreach ($tokens as $unit => $text) {
if ($timediff < $unit) continue;
$numberOfUnits = floor($timediff / $unit);
return $numberOfUnits.' '.$text.(($numberOfUnits>1)?'s':'').' ago';
}
}
/* /*
* @summary Gets a list of all the sub pagess of the current page. * @summary Gets a list of all the sub pagess of the current page.
* *
@ -309,6 +359,50 @@ function check_subpage_parents($pagename)
check_subpage_parents($parent_pagename); check_subpage_parents($parent_pagename);
} }
/*
* @summary makes a path safe
*
* @details paths may only contain alphanumeric characters, spaces, underscores, and dashes
*/
function makepathsafe($string)
{
return preg_replace("/[^0-9a-zA-Z\_\-\ \/]/i", "", $string);
}
/*
* @summary Hides an email address from bots by adding random html entities.
*
* @returns The mangled email address.
*/
function hide_email($str)
{
$hidden_email = "";
for($i = 0; $i < strlen($str); $i++)
{
if($str[$i] == "@")
{
$hidden_email .= "&#" . ord("@") . ";";
continue;
}
if(rand(0, 1) == 0)
$hidden_email .= $str[$i];
else
$hidden_email .= "&#" . ord($str[$i]) . ";";
}
return $hidden_email;
}
///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////// Security and Consistency Measures ////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
/*
* Sort out the pageindex. We create it if it doesn't exist, and load and parse
* it if it does.
*/
if(!file_exists("./pageindex.json")) if(!file_exists("./pageindex.json"))
{ {
// From http://in.php.net/manual/en/function.glob.php#106595 // From http://in.php.net/manual/en/function.glob.php#106595
@ -371,39 +465,6 @@ else
{ {
$pageindex = json_decode(file_get_contents("./pageindex.json")); $pageindex = json_decode(file_get_contents("./pageindex.json"));
} }
/*
* @summary makes a path safe
*
* @details paths may only contain alphanumeric characters, spaces, underscores, and dashes
*/
function makepathsafe($string)
{
return preg_replace("/[^0-9a-zA-Z\_\-\ \/]/i", "", $string);
}
/*
* @summary Hides an email address from bots by adding random html entities.
*
* @returns The mangled email address.
*/
function hide_email($str)
{
$hidden_email = "";
for($i = 0; $i < strlen($str); $i++)
{
if($str[$i] == "@")
{
$hidden_email .= "&#" . ord("@") . ";";
continue;
}
if(rand(0, 1) == 0)
$hidden_email .= $str[$i];
else
$hidden_email .= "&#" . ord($str[$i]) . ";";
}
return $hidden_email;
}
// Work around an Opera + Syntastic bug where there is no margin at the left hand side if there isn't a query string when accessing a .php file // Work around an Opera + Syntastic bug where there is no margin at the left hand side if there isn't a query string when accessing a .php file
if(!isset($_GET["action"]) and !isset($_GET["page"])) if(!isset($_GET["action"]) and !isset($_GET["page"]))
@ -467,6 +528,7 @@ class page_renderer
<p>Your local friendly administrators are {admins-name-list}. <p>Your local friendly administrators are {admins-name-list}.
<p>This wiki is managed by <a href='mailto:{admin-details-email}'>{admin-details-name}</a>.</p> <p>This wiki is managed by <a href='mailto:{admin-details-email}'>{admin-details-name}</a>.</p>
</footer> </footer>
{navigation-bar-bottom}
{all-pages-datalist}"; {all-pages-datalist}";
public static $minimal_content_template = "{content} public static $minimal_content_template = "{content}
<hr class='footerdivider' /> <hr class='footerdivider' />
@ -486,6 +548,7 @@ class page_renderer
"{header-html}", "{header-html}",
"{navigation-bar}", "{navigation-bar}",
"{navigation-bar-bottom}",
"{admin-details-name}", "{admin-details-name}",
"{admin-details-email}", "{admin-details-email}",
@ -500,7 +563,8 @@ class page_renderer
$settings->favicon, $settings->favicon,
self::get_css_as_html(), self::get_css_as_html(),
self::render_navigation_bar(), self::render_navigation_bar($settings->nav_links, $settings->nav_links_extra, "top"),
self::render_navigation_bar($settings->nav_links_bottom, [], "bottom"),
$settings->admindetails["name"], $settings->admindetails["name"],
$settings->admindetails["email"], $settings->admindetails["email"],
@ -545,22 +609,21 @@ class page_renderer
public static $nav_divider = "<span class='nav-divider inflexible'> | </span>"; public static $nav_divider = "<span class='nav-divider inflexible'> | </span>";
public static function render_navigation_bar() /*
* @summary Function to render a navigation bar from an array of links. See
* $settings->nav_links for format information.
*
* @param $nav_links - The links to add to the navigation bar.
* @param $nav_links_extra - The extra nav links to add to the "More..."
* menu.
*/
public static function render_navigation_bar($nav_links, $nav_links_extra, $class = "")
{ {
global $settings, $user, $isloggedin, $page; global $settings, $user, $isloggedin, $page;
$result = "<nav>\n"; $result = "<nav class='$class'>\n";
if($isloggedin)
{
$result .= "<span class='inflexible'>Logged in as " . self::render_username($user) . ".</span> "/* . page_renderer::$nav_divider*/;
$result .= "<span><a href='index.php?action=logout'>Logout</a></span>";
$result .= page_renderer::$nav_divider;
}
else
$result .= "<span class='inflexible'>Browsing as Anonymous.</span>" . /*page_renderer::$nav_divider . */"<span><a href='index.php?action=login'>Login</a></span>" . page_renderer::$nav_divider;
// Loop over all the navigation links // Loop over all the navigation links
foreach($settings->navlinks as $item) foreach($nav_links as $item)
{ {
if(is_string($item)) if(is_string($item))
{ {
@ -568,6 +631,17 @@ class page_renderer
switch($item) switch($item)
{ {
//keywords //keywords
case "user-status":
if($isloggedin)
{
$result .= "<span class='inflexible'>Logged in as " . self::render_username($user) . ".</span> "/* . page_renderer::$nav_divider*/;
$result .= "<span><a href='index.php?action=logout'>Logout</a></span>";
$result .= page_renderer::$nav_divider;
}
else
$result .= "<span class='inflexible'>Browsing as Anonymous.</span>" . /*page_renderer::$nav_divider . */"<span><a href='index.php?action=login'>Login</a></span>" . page_renderer::$nav_divider;
break;
case "search": // Displays a search bar case "search": // Displays a search bar
$result .= "<span class='inflexible'><form method='get' action='index.php' style='display: inline;'><input type='search' name='page' list='allpages' placeholder='Type a page name here and hit enter' /></form></span>"; $result .= "<span class='inflexible'><form method='get' action='index.php' style='display: inline;'><input type='search' name='page' list='allpages' placeholder='Type a page name here and hit enter' /></form></span>";
break; break;
@ -578,7 +652,7 @@ class page_renderer
case "menu": case "menu":
$result .= "<span class='inflexible nav-more'>More...</span>"; $result .= "<span class='inflexible nav-more'>More...</span>";
// todo Add the submenu // todo Add the submenu with $nav_links_extra
break; break;
// It isn't a keyword, so just output it directly // It isn't a keyword, so just output it directly
@ -622,38 +696,6 @@ class page_renderer
} }
} }
///////////////////////////////////////////
//////////////// Functions ////////////////
///////////////////////////////////////////
//from http://php.net/manual/en/function.filesize.php#106569
//edited by Starbeamrainbowlabs
function human_filesize($bytes, $decimals = 2)
{
$sz = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "YB", "ZB"];
$factor = floor((strlen($bytes) - 1) / 3);
return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . @$sz[$factor];
}
//from http://snippets.pro/snippet/137-php-convert-the-timestamp-to-human-readable-format/
function human_time_since($time)
{
$timediff = time() - $time;
$tokens = array (
31536000 => 'year',
2592000 => 'month',
604800 => 'week',
86400 => 'day',
3600 => 'hour',
60 => 'minute',
1 => 'second'
);
foreach ($tokens as $unit => $text) {
if ($timediff < $unit) continue;
$numberOfUnits = floor($timediff / $unit);
return $numberOfUnits.' '.$text.(($numberOfUnits>1)?'s':'').' ago';
}
}
///////////////////////////////////////////
////////////////////////// //////////////////////////
/// Module functions /// /// Module functions ///
////////////////////////// //////////////////////////
@ -941,8 +983,6 @@ register_module([
file_put_contents("./pageindex.json", json_encode($pageindex, JSON_PRETTY_PRINT)); file_put_contents("./pageindex.json", json_encode($pageindex, JSON_PRETTY_PRINT));
// Todo Update the parent page entries in the page index if they exist
if(isset($_GET["newpage"])) if(isset($_GET["newpage"]))
http_response_code(201); http_response_code(201);
else else

View file

@ -29,7 +29,7 @@
"author": "Starbeamrainbowlabs", "author": "Starbeamrainbowlabs",
"description": "Allows you to edit pages by adding the edit and save actions. You should probably include this one.", "description": "Allows you to edit pages by adding the edit and save actions. You should probably include this one.",
"id": "page-edit", "id": "page-edit",
"lastupdate": 1437037943 "lastupdate": 1437037997
}, },
{ {
"name": "Help page", "name": "Help page",