/    Sign up×
Community /Pin to ProfileBookmark

doesn´t verify blank fields in form

Hi! I have a problem! The system I work with is a Fedora with MySql and Php 4. In the application there is a FORM to introduce data in the DDBB, and there are some obligatory fields, so there is a typical JS function that, on submit and before saving the information with other php script, it checks if there are any blank fields and shows a typical error message. This was working correctly until some collateral efects appeared after changing some SQL querys. The problem now is that anyone can save the information with all the mandatory fields left blank. And this is not the expected behaviour… I’ve been checking all the code and making some changes during the last month but I can´t get the solution. It is not neccesary to say that I am not an experienced developer … So I am asking for some help please, because I am desperated for a solution… Maybe this has happened to someone before? … or is a typical error? Or maybe someone know other solutions or debuging methods to focus the problem? Thanks in advance!

to post a comment
PHP

10 Comments(s)

Copy linkTweet thisAlerts:
@Phill_PaffordAug 21.2006 — Can you post your code?
Copy linkTweet thisAlerts:
@pcthugAug 22.2006 — You should be using PHP to validate your form, not Javascript. To eliminate empty fields, employ the following PHP blank field checking:
[code=php]
$error = '';
foreach($_POST as $key) {
if(empty($_POST[$key])) {
$error .="<strong>$key</strong> is a mandatory field and therefore must be completed.<br />n";
}
}
if(!empty($error))
{
exit($error);
}[/code]

However for the most accurate and beneficial response, please post your current code.
Copy linkTweet thisAlerts:
@gorkongrooveauthorAug 22.2006 — Thanks! Well, the code is (a bit large, so it´s going to be sliced in posts...):


*******************************************************************

#######

editar.php

#######


*******************************************************************

[FONT=Courier New][code=php]<?
session_start();

$__usuario = $_SESSION["__usuario"];

require_once("Smarty.class.php");

include_once("alioli.ini");
include_once("ez_sql.php");
include_once("pagina.inc.php");

$id_lom = $HTTP_GET_VARS["id_lom"];
$pantalla = $HTTP_GET_VARS["pantalla"];

$smarty = new Smarty;
$p = new pagina();
$p->menu();

if (!isset($__usuario)) { $p->Redireccionar(); exit;}


$sql6 = "select * from lom_educational_learning_resource_types";
if ($datos_lom_educational_learning_resource_types = $db->get_results($sql6)){
foreach ($datos_lom_educational_learning_resource_types as $fila6 ){
$smarty->append('edu_learn_id', $fila6->id);
$smarty->append('edu_learn_description', $fila6->description);
}
}


$sql2 = "select * from avl_languages";
if ($datos_avl_languages = $db->get_results($sql2)){
foreach ($datos_avl_languages as $fila2 ){
$smarty->append('id_language', $fila2->id_language);
$smarty->append('caption', $fila2->caption);
$smarty->append('code', $fila2->code);
}
}

$sql2 = "select * from lom_rights_types ";
if ($datos_rights_types = $db->get_results($sql2)){
foreach ($datos_rights_types as $fila2 ){
$smarty->append('id_rights', $fila2->id);
$smarty->append('caption_rights', $fila2->caption);
$smarty->append('default_rights', $fila2->Iddefault);
}
}


$sql1 = "select * from lom_educational_interactivity_type";
if ($datos_lom_educational_interactivity_type= $db->get_results($sql1)){
foreach ($datos_lom_educational_interactivity_type as $fila1 ){
$smarty->append('interactivity_type_id', $fila1->id);
$smarty->append('interactivity_type_description', $fila1->description);
}
}


$sql2 = "select * from lom_educational_intended_end_user_role";
if ($datos_lom_educational_intended_end_user_role = $db->get_results($sql2)){
foreach ($datos_lom_educational_intended_end_user_role as $fila2 ){
$smarty->append('intended_end_user_id', $fila2->id);
$smarty->append('intended_end_user_description', $fila2->description);
}
}


$sql3 = "select * from lom_educational_context";
if ($datos_lom_educational_context = $db->get_results($sql3)){
foreach ($datos_lom_educational_context as $fila3 ){
$smarty->append('context_id', $fila3->id);
$smarty->append('context_description', $fila3->description);
}
}


$sql4 = "select * from lom_educational_difficulty";
if ($datos_lom_educational_difficulty = $db->get_results($sql4)){
foreach ($datos_lom_educational_difficulty as $fila4 ){
$smarty->append('difficulty_id', $fila4->id);
$smarty->append('difficulty_description', $fila4->description);
}
}


$sql4 = "select * from lom_general_structure";
if ($datos_lom_general_structure = $db->get_results($sql4)){
foreach ($datos_lom_general_structure as $fila4 ){
$smarty->append('general_structure_id', $fila4->id_structure);
$smarty->append('general_structure_description', $fila4->description);
}
}

$sql5 = "select * from lom_general_aggregation_level";
if ($datos_lom_general_aggregation_level= $db->get_results($sql5)){
foreach ($datos_lom_general_aggregation_level as $fila5 ){
$smarty->append('aggregation_level_id', $fila5->id_aggregation_level);
$smarty->append('aggregation_level_description', $fila5->description);
}
}


$sql5 = "select id, extension, mime, ltf_order from lom_technical_format ORDER BY ltf_order";
if ($datos_lom_technical_format = $db->get_results($sql5)){
foreach ($datos_lom_technical_format as $fila5){
$smarty->append('technical_id', $fila5->id);
$smarty->append('technical_datos', $fila5->mime." (".$fila5->extension.") ");
}
}


if ($pantalla=='Modify'){


$sqlg3 = "SELECT g.loID, g.title, g.description, g.catalog, g.entity, g.keyword, g.coverage, g.id_structure, g.id_aggregation_level, g.id_language, g.copyrightcom
FROM lom_general g
WHERE g.loID='$id_lom'";
if ($datos_lom_general = $db->get_results($sqlg3)){
foreach ($datos_lom_general as $fila ){
$smarty->assign('id_lom', $fila->loID);
$smarty->assign('title', $fila->title);
$smarty->assign('description', $fila->description);
$smarty->assign('entity', $fila->entity);
$smarty->assign('keyword', $fila->keyword);
$smarty->assign('coverage', $fila->coverage);
$smarty->assign('catalog', $fila->catalog);

$smarty->assign('id_structure1', $fila->id_structure);
$smarty->assign('id_aggregation_levell', $fila->id_aggregation_level);
$smarty->assign('id_language11', $fila->id_language);
$smarty->assign('copyrightcom', $fila->copyrightcom);
}
}

$sqlg1 = "SELECT id, interactivity_type, learning_resource_type, interactivity_level,
semantic_density, intended_end_user_role, context, typical_age_range, difficulty, typical_learning_time,
description, language
FROM lom_educational
WHERE loID='$id_lom'";

if ($datos_lom_educational = $db->get_results($sqlg1)){
foreach ($datos_lom_educational as $fila ){
$smarty->assign('id_educational', $fila->id);
$smarty->assign('id_interactivity_type1', $fila->interactivity_type);
$smarty->assign('id_learning_resource_type1', $fila->learning_resource_type);
$smarty->assign('id_interactivity_level1', $fila->interactivity_level);
$smarty->assign('id_intended_end_user_role1', $fila->intended_end_user_role);
$smarty->assign('id_context1', $fila->context);
$smarty->assign('id_semantic_density1', $fila->semantic_density);
$smarty->assign('id_difficulty1', $fila->difficulty);
$smarty->assign('typical_learning_time',$fila->typical_learning_time);
$smarty->assign('typical_age_range',$fila->typical_age_range);
$smarty->assign('educational_description',$fila->description);
$smarty->assign('id_language21',$fila->language);
$smarty->assign('id_edu',$fila->id);
}
}

$sqlg2 = "SELECT distinct id_technical, id_format, size, location, requirement
FROM lom_technical
WHERE loID='$id_lom'";

if ($datos_lom_general = $db->get_results($sqlg2)){
foreach ($datos_lom_general as $fila ){
$smarty->assign('id_tec', $fila->id_technical);
$smarty->assign('id_format1', $fila->id_format);
$smarty->assign('size', $fila->size);
$smarty->assign('location', $fila->location);
$smarty->assign('requirement', $fila->requirement);
}
}
}


$smarty->assign('app_dir', $app_dir);
$smarty->assign('pantalla', $pantalla);
$smarty->assign('id_lom', $id_lom);
$smarty->display('editar.tpl');
$p->pie();
?>[/code]


[/FONT]
Copy linkTweet thisAlerts:
@gorkongrooveauthorAug 22.2006 — second part:



*******************************************************************

#####

forms.js

#####


*******************************************************************
[FONT=Courier New]
[code=php]/* form functions */

function submit_add_content_form ()
{
var funcion = new function () {}
alert_message="";

first_unfilled_field = "";

campo = document.forma.learning_resource_type;

if ( campo.value == 0 ){
alert_message += "* Learning Resource Type n";
}

campo = document.forma.title;
if ( campo.value == "" )
{
alert_message += "* Titlen";
x = first_unfilled_field;
first_unfilled_field = (x ? x : campo );
}

campo = document.forma.id_language1;
if ( campo.value == 0 ){
alert_message += "* Languagen";
}

campo = document.forma.lom_description;
if ( campo.value == "" )
{
alert_message += "* Descriptionn";
x = first_unfilled_field;
first_unfilled_field = (x ? x : campo );
}

campo = document.forma.copyrightcom; //gor
if ( campo.value == "")
{
alert_message += "* Copyright Commentsn";
}

campo = document.forma.life_entity; //gor
if ( campo.value == "")
{
alert_message += "* Entityn";
}

campo = document.forma.interactivity_type_id;
if ( campo.value == 0 ){
alert_message += "* Interactivity Typen";
}

campo = document.forma.intended_end_user_id;
if ( campo.value == 0 ){
alert_message += "* Intended end usern";
}

campo = document.forma.context_id;
if ( campo.value == 0 ){
alert_message += "* Contextn";
}

campo = document.forma.difficulty_id;
if ( campo.value == 0 )
{
alert_message += "* Difficultyn";
}

campo = document.forma.typical_learning_time;
if ( campo.value == "" )
{
alert_message += "* Typical Learning Timen";
x = first_unfilled_field;
first_unfilled_field = (x ? x : campo );
}

campo = document.forma.learning_outcomes;
if ( campo.value == "" )
{
alert_message += "* Learning Outcomesn";
x = first_unfilled_field;
first_unfilled_field = (x ? x : campo );
}


campo = document.forma.id_language;
if ( campo.value == 0 )
{
alert_message += "* Languagen";
}

campo = document.forma.aggregation_level_id;
if ( campo.value == 0 ){
alert_message += "* Aggregation Leveln";
}

campo = document.forma.id_structure;
if ( campo.value == 0 ){
alert_message += "* Structuren";
}


if ( alert_message ) {
alert ( "Sorry, you must fill in the mandatory fields (*):n" + alert_message );
return false;
} else {
return true;
}
}

[/code]






[/FONT]
Copy linkTweet thisAlerts:
@gorkongrooveauthorAug 22.2006 — The third part ...


[FONT=Courier New]






*******************************************************************

######

editar.tpl

######


*******************************************************************




[code=php]<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">

<script language="JavaScript" type="text/javascript" src="{$app_dir}/js/tabs.js"></script>
<script language="JavaScript" type="text/javascript" src="{$app_dir}/js/forms.js"></script>

<form action='grabar.php' method='post' name=forma onSubmit="return submit_add_content_form(this);">
<table width="100%" cellspacing="0" cellpadding="5">
<tr>
<td class="tab-hi" id="tab_group_mandatory" onClick="section_tabs.toggle('group_mandatory')">
<span class="tab-text">General info&nbsp;</span><span class="littlered">*</span>
</td>
<td class="tabspacer">&nbsp;</td>
<td class="tab" id="tab_group_educational" onClick="section_tabs.toggle('group_educational')">
<span class="tab-text">Educational&nbsp;</span><span class="littlered">*</span></td>
<td class="tabspacer">&nbsp;</td>
<td class="tab" id="tab_group_technical" onClick="section_tabs.toggle('group_technical')">
<span class="tab-text">Technical&nbsp;</span><span class="littlered">*</span></td>
<td class="tabspacer">&nbsp;</td>
</tr>
</table>
<!-- tab 1: General Info -->
<div id="group_mandatory" style="display: inline">
<!-- div class="inner">
<div class="separator">General Info</div>
</div -->

<p>Please fill in the following fields.&nbsp;&nbsp; * obligatory fields.</p>

<table width="100%" border="0" cellspacing="1" cellpadding="5" class="inner">
<tr>
<th class="shortdesc" Valign="Top">
Learning Resource Type:<span class="littlered">*</span>
</th>
<td class="shortdesc">
<select name="learning_resource_type">
<option value="0"> Select </option>
{section name=res loop=$edu_learn_description}
<option value="{$edu_learn_id[res]}"
{if $id_learning_resource_type1==$edu_learn_id[res]}
selected
{/if }
>{$edu_learn_description[res]}</option>
{/section}
</select>

&nbsp;
</td>
</tr>
<tr>
<th class="shortdesc_odd">
Title :<span class="littlered">*</span>
</th>
<td class="shortdesc_odd">
<input type=text name=title value='{$title}' size=60>
</td>
</tr>
<tr>
<th class="shortdesc">
Language:<span class="littlered">*</span>
</th>
<td class="shortdesc">
<select name=id_language1>
<option value=0> Select </option>
{html_options values=$id_language output=$caption selected=$id_language11}
</select>
</td>
</tr>
<tr>
<th class="shortdesc_odd">
Description/Summary:<span class="littlered">*</span>
</th>
<td class="shortdesc_odd">
<textarea cols="40" rows="3" maxlength="2000" name="lom_description">{$description}</textarea><div class="form_field_desc">(Learning outcomes, target)</div>
</td>
</tr>

<tr>
<th></th>
<td></td>
</tr>


<tr>
<th class="shortdesc">
Copyright Comments:
</th>
<td class="shortdesc">

<input type=text name=copyrightcom value='{$copyrightcom}' size=60>
<div class="form_field_desc">(Year, Author, comments on the conditions of using this learning object, ...)</div>
</td>
</tr>

<tr>

<th class="shortdesc_odd">
Entity:
</th>
<td class="shortdesc_odd">
<textarea cols="40" rows="3" maxlength="1000" name="life_entity">{$entity}</textarea>
<div class="form_field_desc">(Please, refer to the copyright institution that supports the authoring.)</div>
</td>
</tr>

</table>
<h3><font color="#9F3C99">Add content to Repository (step 1/3):</font></h3>
</div>[/code]




[/FONT]
Copy linkTweet thisAlerts:
@gorkongrooveauthorAug 22.2006 — And the last part (continues editar.tpl...). Thanks.


[FONT=Courier New]

[code=php] <!-- tab 2: Educational -->
<div id="group_educational" style="display: none">
<!-- div class="inner">
<div class="separator">Educational</div>
</div -->
<table width="100%" class="inner">
<tr>
<th class="shortdesc" valign="top">Interactivity type&nbsp;<span class="littlered">*</span></th>
<td class="shortdesc" width="90%" valign="top">
<select name=interactivity_type_id>

<option value=0> Select </option>
{html_options values=$interactivity_type_id output=$interactivity_type_description selected=$id_interactivity_type1}
</select>
</td>
</tr>
<tr>
<th class="shortdesc_odd" valign="top">Intended End User&nbsp;<span class="littlered">*</span></th>
<td class="shortdesc_odd" width="90%" valign="top">
<select name=intended_end_user_id>

<option value=0> Select </option>
{html_options values=$intended_end_user_id output=$intended_end_user_description selected=$id_intended_end_user_role1}
</select>
</td>
</tr>
<tr>
<th class="shortdesc" valign="top">Context&nbsp;<span class="littlered">*</span></th>
<td class="shortdesc" width="90%" valign="top">
<select name= context_id>

<option value=0> Select </option>
{html_options values=$context_id output=$context_description selected=$id_context1}
</select>
<div class="form_field_desc">(The principal environment within which the learning and use of this <br>
learning object is intended to take place)
</td>
</tr>
<tr>
<th class="shortdesc_odd" valign="top">Difficulty&nbsp;<span class="littlered">*</span></th>
<td class="shortdesc_odd" width="90%" valign="top">
<select name= difficulty_id>

<option value=0> Select </option>
{html_options values=$difficulty_id output=$difficulty_description selected=$id_difficulty1}
</select>
</td>
</tr>
<tr>
<th class="shortdesc" valign="top">Typical Learning Time&nbsp;<span class="littlered">*</span></th>
<td class="shortdesc" width="90%" valign="top"> <input type=text name=typical_learning_time value='{$typical_learning_time}' size=60>
</td>
</tr>
<tr>
<th class="shortdesc_odd" valign="top">Learning outcomes&nbsp;<span class="littlered">*</span></th>
<td class="shortdesc_odd" width="90%" valign="top"><textarea cols="50" rows="3" name="learning_outcomes">{$educational_description}</textarea>
</td>
</tr>
<tr>
<th class="shortdesc" valign="top">Language&nbsp;<span class="littlered">*</span></th>
<td class="shortdesc" width="90%" valign="top">
<select name=id_language>

<option value=0> Select </option>
{html_options values=$id_language output=$caption selected=$id_language21}
</select>
</td>
</tr>
</table>
<h3><font color="#9F3C99">Add content to Repository (step 2/3):</font></h3>
</div>

<!-- tab 3: Technical -->
<div id="group_technical" style="display: none">
<!-- div class="inner">
<div class="separator">Technical</div>
</div -->
<table width="100%" class="inner">
<tr>
<th class="shortdesc_odd" valign="top">Aggregation level&nbsp;<span class="littlered">*</span></th>
<td class="shortdesc_odd" colspan="3"width="90%" valign="top">
<select name=aggregation_level_id>

<option value=0> Select </option>
{html_options values=$aggregation_level_id output=$aggregation_level_description selected=$id_aggregation_levell}
</select>
<div class="form_field_desc">(The functional granularity of this learning object.<br>
1: the smallest level of aggregation, e.g., raw media data or fragments.<br>
2: a collection of level 1 learning objects, e.g., a lesson.<br>
3: a collection of level 2 learning objects, e.g., a couse.<br>
4: the largest level of granularity, e.g., a ser of course that lead toa<br>
certificate.<br>
NOTE 1:--Level 4 objects can contain level3 objects, or can recursively contain <br>
other level 4 objcts.) </div>
</td>
</tr>
<tr>
<th class="shortdesc" valign="top">Structure&nbsp;<span class="littlered">*</span></th>
<td class="shortdesc" colspan="3"width="90%" valign="top">
<select name=id_structure>

<option value=0> Select </option>
{html_options values=$general_structure_id output=$general_structure_description selected=$id_structure1}
</select>
<div class="form_field_desc">(Underlying organizational structure of this learning object.<br>
<b>atomic:</b> an object that is indivisible (in this context).<br>
<b>collection:</b> a set of objects with no specified relationship between them. <br>
<b>networked:</b> a set of objects with relationrelationship between them.<br>
<b>hierarchical:</b>a set of objects whose relationships can be represented by a <br>
tree structure.<br>
<b>linear:</b>a set of objects that are fully ordered <br>
Ex.: A set of objects that are connected by "previous" and "next" <br>
relationships.) </div>
</td>
</tr>

<tr>

<th class="shortdesc_odd" valign="top">Format&nbsp;</th>
<td class="shortdesc_odd" width="90%" valign="top">
<select name=technical_id >
<option value=0> Select </option>
{html_options values=$technical_id output=$technical_datos}
</select>
</td>
<th class="shortdesc_odd" valign="top">Size&nbsp;</th>
<td class="shortdesc_odd" width="90%" valign="top">
<input type="hidden" name="size" />
<i>(automatic)</i></td>
</tr>

<tr>
<th class="shortdesc" valign="top">Requirements&nbsp;</th>
<td class="shortdesc" colspan="3" width="90%" valign="top">
<input type=text name=requirement value='{$requirement}' size=60>
<div class="form_field_desc">(The technical capabilities necessary for using this learning object)</div>
</td>
</tr>
</table>
<h3><font color="#9F3C99">Add content to Repository (step 3/3):</font></h3>
</div>
<input type=hidden name=pantalla value={$pantalla}>
<input type=hidden name=id_lom value={$id_lom}>
<!-- nav buttons next and previous -->
<table width="90%" border="0" align="center" cellpadding="5">
<tr>
<td width="40%">&nbsp;</td>
<td align="left"><input type="button" id="prevbutton" value="&lt; &lt; Previous" title="go next tab" onClick="section_tabs.prevTab()" disabled></td>
<td align="left"><input type="button" id="nextbutton" value="Next &gt;&gt;" title="go previous tab" onClick="section_tabs.nextTab()"></td>
<td width="30%" align="center"><input type="Submit" id="submit_button" value="Save" ></td>
<td width="10%">&nbsp;</td>
<!-- td align="right"><input type="button" id="search_button" value="Search"></td> -->
</tr>
</table>
</form>[/code]
[/FONT]
Copy linkTweet thisAlerts:
@gorkongrooveauthorAug 24.2006 — I don´t know why, but it is solved. Like it appeared it disappeared .... I realized some changes in the php files. I didn´t liked the results so I commented the new lines typed, and after that all was working in the desired way. Thanks Phill and pcthug. I am working know trying to make some server-side validation, and it works. The main problem now is, after showing the error message (with a new Smarty .tpl file), how to redirect the user to the form and keep the filled fields with the information, so he or she doesn't need to fill ALL the fields another time. I am now reading some literature to do it. Bye!
Copy linkTweet thisAlerts:
@NogDogAug 24.2006 — Just a note, be careful about using empty() to validate fields if 0 (zero) is a valid entry, as empty() will return TRUE for a variable with a value of 0. In that case, you should do something like:
[code=php]
if(!isset($_POST['fieldname']) or trim($_POST['fieldname']) === "")
{
// field is empty (note use of "===" operator instead of "=="
}
[/code]
Copy linkTweet thisAlerts:
@netbuddyAug 24.2006 — You should be using PHP to validate your form, not Javascript...[/QUOTE]

You really believe that theirs no benefit to validating a form before it is presented to the server? what if your a busy server, that will create an overhead that is nothing more tha ntime / money waster, yes validate Client-Side, it saves on resources, why waste valuable resources, bandwidth as not all hiosts give unlimited bandwidth, so in some situations where traffic is a consideration and part of the projects design spec, you would benefit from freeing up bandwidth by doing checks client side.

WHEN the form is posted, sure validate further to ensure that the attempt to post is not a hijack or code injection to thwart any server hijack / break in.

Simply to dismiss client-side is shooting yourself in the foot, most good site designs will employ client-side validation to ensure that data meets a minimum criteria, server-side simply re-enforvces your policy with allot more control for you.

The two go hand in hand m8ty.
Copy linkTweet thisAlerts:
@NogDogAug 24.2006 — You really believe that theirs no benefit to validating a form before it is presented to the server? what if your a busy server, that will create an overhead that is nothing more tha ntime / money waster, yes validate Client-Side, it saves on resources, why waste valuable resources, bandwidth as not all hiosts give unlimited bandwidth, so in some situations where traffic is a consideration and part of the projects design spec, you would benefit from freeing up bandwidth by doing checks client side.

WHEN the form is posted, sure validate further to ensure that the attempt to post is not a hijack or code injection to thwart any server hijack / break in.

Simply to dismiss client-side is shooting yourself in the foot, most good site designs will employ client-side validation to ensure that data meets a minimum criteria, server-side simply re-enforvces your policy with allot more control for you.

The two go hand in hand m8ty.[/QUOTE]

I think we are in agreement here, I just want to clarify:

The only problem with using client-side validation is if you depend on it and do not re-check on the server. There is a small percentage of users who have Javascript disabled on their browsers. Whether this is due to security concerns or ignorance (they don't even realize they disabled it), we as developers cannot simply dismiss such users as not being worth our time. Therefore, your form processing needs to work irregardless of whether the form inputs have been validated by the client or not.

Using client-side validation [i]will[/i] give the majority of users faster feedback and reduce some of the network/server load, so it's fine to use it for that reason. Just do not assume on the server side that such validation has occurred, or you may "shoot yourself in the foot" by processing invalid form inputs if you don't re-check them there. (Or for that matter, malicious users could submit their own post data, completely bypassing your form and any input validation it does.)
×

Success!

Help @gorkongroove spread the word by sharing this article on Twitter...

Tweet This
Sign in
Forgot password?
Sign in with TwitchSign in with GithubCreate Account
about: ({
version: 0.1.9 BETA 6.16,
whats_new: community page,
up_next: more Davinci•003 tasks,
coming_soon: events calendar,
social: @webDeveloperHQ
});

legal: ({
terms: of use,
privacy: policy
});
changelog: (
version: 0.1.9,
notes: added community page

version: 0.1.8,
notes: added Davinci•003

version: 0.1.7,
notes: upvote answers to bounties

version: 0.1.6,
notes: article editor refresh
)...
recent_tips: (
tipper: @nearjob,
tipped: article
amount: 1000 SATS,

tipper: @meenaratha,
tipped: article
amount: 1000 SATS,

tipper: @meenaratha,
tipped: article
amount: 1000 SATS,
)...