//
// File: hdsResource.cpp
//
// Legal Notice: This file is part of the HadronZoo C++ Class Library. Copyright 2025 HadronZoo Project (http://www.hadronzoo.com)
//
// The HadronZoo C++ Class Library is free software: You can redistribute it, and/or modify it under the terms of the GNU Lesser General Public License, as published by the Free
// Software Foundation, either version 3 of the License, or any later version.
//
// The HadronZoo C++ Class Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along with the HadronZoo C++ Class Library. If not, see http://www.gnu.org/licenses.
//
//
// Dissemino Resource Management
//
// Resources such as pages, articles and include blocks have inherently unique and human readable identifiers such as pathname and title but internal operation
// is enhanced by allocation of a unique numeric id. This is best done internally as it is a nuicence to have to allocate ids to these entities in the configs.
// Instead, the human operator only needs to set the version number of each resource. This will be ver="1" in all cases to begin with but will be increased by
// 1 if editied.
//
// It is important to understand that Dissemino makes a distinction between resorces that are explicitly defined in the configs and those that are derived from
// other sources such as uploads from users. While the latter may amount to whole pages or articles to Dissemino they are meaningless binaries to be stored in
// the database and given addresses accordingly. The regime herein described applies only to config defined resources.
//
// To this end, Dissemino manages a series of files in the document root as follows:-
//
// 1) website.res.xml: This lists all config defined resources and adds the internally assigned id, the date and time stamp and the version to the pathname and
// title. The
//
// 2) website.$$.txt: Where $$ in this case is the default language code. This file will contain a series of strings extracted from the currently applicable
// resources.
//
// If ANY of the above files do not exist in the document root, Dissemino will take the configs as autorative and create a complete set of new files missing files from the configs
// a veriety of reasons, it is these have practical limitations. In
// particular, change over time
#include <iostream>
#include <fstream>
#include <unistd.h>
#include <stdarg.h>
#include <dirent.h>
#include <netdb.h>
#include <signal.h>
#include "hzErrcode.h"
#include "hzTokens.h"
#include "hzTextproc.h"
#include "hzDissemino.h"
using namespace std ;
/*
** Variables
*/
//static hdsNavtree s_treeDataModel ; // Tree for object model display
extern hzString _dsmScript_tog ; // Navtree toggle script
extern hzString _dsmScript_loadArticle ; // Navtree load article script
extern hzString _dsmScript_navtree ; // Navtree script
/*
** Functions
*/
hzEcode hdsApp::IndexPages (void)
{
// Go through all pages found in the config files and index them
//
// Arguments: None
//
// Returns: E_FORMAT If any page could not be tokenized
// E_OK If the pages were indexed
_hzfunc("hdsApp::IndexPages") ;
hzVect<hzToken> toks ; // Token list
hzChain pageVal ; // Extract content from tags into chain, then tokenize to get words to index
hzToken T ; // Tokens
hdsResource* pRes ; // Resource under consideration
hdsPage* pPage ; // Current page
uint32_t nD ; // Document number
uint32_t nCount ; // Loop counter
uint32_t nDone ; // Count of actual inserts
hzEcode rc = E_OK ; // Return code
/*
** Allocate working buffers and load HTML page
*/
for (nD = 0 ; nD < m_ResourcesName.Count() ; nD++)
{
pRes = m_ResourcesName.GetObj(nD) ;
pPage = dynamic_cast<hdsPage*>(pRes) ;
if (!pPage)
continue ;
if (!pPage->m_Bodytext.Size())
continue ;
// Pass thru page tags looking for indexable content. This will include the page title, description metatags and the content of paragraphs.
// Note tha paragraph content must be assumed to be complex and so is comprised of the pretext of the subtags and only lastly the content.
pageVal.Clear() ;
pageVal << pPage->m_Title ;
pageVal.AddByte(CHAR_NL) ;
pageVal << pPage->m_Desc ;
pageVal.AddByte(CHAR_NL) ;
pageVal << pPage->m_Bodytext ;
rc = TokenizeChain(toks, pageVal, TOK_MO_WHITE) ;
if (rc != E_OK)
{
m_pLog->Out("Abandoning indexation of page %s (%s)\n", *pPage->m_Url, *pPage->m_Title) ;
break ;
}
for (nDone = nCount = 0 ; rc == E_OK && nCount < toks.Count() ; nCount++)
{
T = toks[nCount] ;
if (!T.Value())
continue ;
nDone++ ;
rc = m_PageIndex.Insert(T.Value(), nD) ;
}
m_pLog->Out("Indexing page %s (%s), %d of %d tokens\n", *pPage->m_Url, *pPage->m_Title, nDone, toks.Count()) ;
}
return rc ;
}
/*
** Language Support, Text String Import/Export
*/
bool _strhasalphas (const char* str)
{
// Does string contain alpha chars?
//
// Arguments: 1) str Test string
//
// Returns: True If the supplied cstr contains alpha chars
// False If it does not
const char* i ; // Input string iterator
uint32_t uVal ; // Entity value
uint32_t nLen ; // Entity length
uint32_t nAlphas = 0 ; // Count of alpha characters
if (!str || !str[0])
return false ;
for (i = str ; *i ; i++)
{
if (IsEntity(uVal, nLen, i))
{ i += (nLen - 1) ; continue ; }
if (IsAlpha(*i))
nAlphas++ ;
}
return nAlphas > 1 ? true : false ;
}
//void hdsApp::_exportStr (hzChain& Z, hdsVE* pVE, const hdsUSL& rid)
void hdsApp::_exportStr (hzChain& Z, hdsVE* pVE, uint32_t rid)
{
// Now for each subtag, output first the pretext (part of this tag's content) and then call recursively on the subtag
//
// Arguments: 1) Z Export output chain
// 2) pVE Visual entity
//
// Returns: None
hdsVE* pX ; // Visual entity pointer
uint32_t nLen ; // Lenght of visual entity strings
if (!pVE)
Fatal("hdsApp::_exportStr: No visible entity poiter\n") ;
if (pVE->m_Tag == "pre") return ;
if (pVE->m_Tag == "script") return ;
for (pX = pVE->Children() ; pX ; pX = pX->Sibling())
{
if (pX->m_strPretext)
{
// Test string for alpha chars first, if none, don't export the string
if (_strhasalphas(*pX->m_strPretext))
{
//Z.Printf("%s.%u.0) %s\n", *usl, pVE->m_VID, *pX->m_strPretext) ;
Z.Printf("%u.%u.0) %s\n", rid, pVE->m_VID, *pX->m_strPretext) ;
nLen = pX->m_strPretext.Length() ;
if (pX->m_strPretext[nLen-1] != CHAR_NL)
Z.AddByte(CHAR_NL) ;
}
}
//_exportStr(Z, pX, usl) ;
_exportStr(Z, pX, rid) ;
}
if (pVE->m_strContent)
{
if (_strhasalphas(*pVE->m_strContent))
{
//Z.Printf("%s.%u.1) %s\n", *usl, pVE->m_VID, *pVE->m_strContent) ;
Z.Printf("%u.%u.1) %s\n", rid, pVE->m_VID, *pVE->m_strContent) ;
nLen = pVE->m_strContent.Length() ;
if (pVE->m_strContent[nLen-1] != CHAR_NL)
Z.AddByte(CHAR_NL) ;
}
}
}
hzEcode hdsApp::ExportStrings (void)
{
// Export all strings for external language translation
//
// Arguments: None
//
// Returns: E_OPENFAIL If the string export output stream cannot be opened
// E_OK If the strings are exported
_hzfunc("hdsApp::ExportStrings") ;
hzList<hdsVE*>::Iter ih ; // Html entity iterator
hdsArticle* pArt ; // Generic article pointer
hdsArticleStd* pArtStd ; // Standar article pointer
ofstream os ; // Output stream
hzChain Z ; // Output chain
hdsNavtree* pAG ; // Article group
hdsPage* pPage ; // Page pointer
hdsBlock* pBlok ; // Include Block
//hdsSubject* pSubj ; // Subject (navbar heading)
hdsVE* pVE ; // Visible entity
hzString filepath ; // File path
hzString S ; // Temp string
uint32_t nG ; // Article group iterator
uint32_t n ; // Page iterator
uint32_t nV ; // Visual entity iterator
// Do strings by page
Z.Printf("%s/website.%s.txt", *m_Docroot, *m_DefaultLang) ;
filepath = Z ;
Z.Clear() ;
os.open(*filepath) ;
if (os.fail())
return hzerr(E_OPENFAIL, "Cannot open file [%s]\n", *filepath) ;
for (n = 0 ; n < m_setPgSubjects.Count() ; n++)
{
S = m_setPgSubjects.GetObj(n) ;
Z.Printf("s%d) %s\n\n", n, *S) ;
}
for (n = 0 ; n < m_Includes.Count() ; n++)
{
pBlok = m_Includes.GetObj(n) ;
for (pVE = pBlok->Children() ; pVE ; pVE = pVE->Sibling())
_exportStr(Z, pVE, n+1) ; //pBlok->m_USL) ;
}
//for (n = 0 ; n < m_PagesUid.Count() ; n++)
for (n = 0 ; n < m_vecPages.Count() ; n++)
{
//pPage = m_PagesUid.GetObj(n) ;
pPage = m_vecPages[n] ;
if (!(pPage->m_flagVE & VE_LANG))
{ m_pLog->Log("Skipping page %s\n", *pPage->m_Url) ; continue ; }
m_pLog->Log("Doing Page %s\n", *pPage->m_Url) ;
// for (ih = pPage->m_VEs ; ih.Valid() ; ih++)
// {
// pVE = ih.Element() ;
// _exportStr(Z, pVE) ;
// }
for (nV = 0 ; nV < pPage->m_VEs.Count() ; nV++)
{
pVE = pPage->m_VEs[nV] ;
//_exportStr(Z, pVE, pPage->m_USL) ;
_exportStr(Z, pVE, pPage->m_RID) ;
}
}
os << Z ;
os.close() ;
os.clear() ;
Z.Clear() ;
m_pLog->Log("File %s Total %d pages\n", *filepath, n) ;
// Do article groups
for (nG = 0 ; nG < m_ArticleGroups.Count() ; nG++)
{
pAG = m_ArticleGroups.GetObj(nG) ;
//m_pLog->Out("Doing article group %s (total %d articles)\n", *pAG->m_Groupname, pAG->m_Articles.Count()) ;
m_pLog->Out("Doing article group %s (total %d articles)\n", *pAG->m_Groupname, pAG->Count()) ;
Z.Printf("%s/%s.%s.txt", *m_Docroot, *pAG->m_Groupname, *m_DefaultLang) ;
filepath = Z ;
Z.Clear() ;
os.open(*filepath) ;
if (os.fail())
{
m_pLog->Out("%s. Cannot open file [%s]\n", *filepath) ;
return E_OPENFAIL ;
}
for (n = 0 ; n < pAG->Count() ; n++)
{
pArt = (hdsArticle*) pAG->GetItem(n) ;
if (!pArt)
continue ;
pArtStd = dynamic_cast<hdsArticleStd*>(pArt) ;
if (pArtStd)
{
if (!(pArtStd->m_flagVE & VE_LANG))
{ m_pLog->Out("Skipping article %s\n", *pArtStd->m_Title) ; continue ; }
// for (pVE = pArt->Root() ; pVE ; pVE = pVE->Sibling())
// _exportStr(Z, pVE) ;
for (nV = 0 ; nV < pArtStd->m_VEs.Count() ; nV++)
{
pVE = pArtStd->m_VEs[nV] ;
//_exportStr(Z, pVE, pArtStd->m_USL) ;
_exportStr(Z, pVE, pArtStd->m_RID) ;
}
}
}
os << Z ;
os.close() ;
os.clear() ;
Z.Clear() ;
m_pLog->Log("File %s Total %d articles (size now %d)\n", *filepath, n, Z.Size()) ;
}
return E_OK ;
}
#if 0
void hdsApp::ImportStrings (void)
{
// For each supported language other than the default, import translated text. The export produces files of the form 'projectname.lang' for pages declared
// with an <xpage> and 'projectname.articlegroup.lang' for all articles groups. The import process expects these to have been manually converted offline to
// files of the form 'projectname.lc' and 'projectname.articlegroup.lc' where 'lc' is the non-default language code. The supported languages are indicated
// in the <siteLanguages> tag.
//
// Arguments: None
// Returns: None
_hzfunc("hdsApp::ImportStrings") ;
hzList<hdsVE*>::Iter ih ; // Html entity iterator
ifstream is ; // Output stream
chIter zi ; // File iterator
hzChain Z ; // Output chain
hzChain Para ; // Construction chain
hdsLang* pLang ; // Langauge
hdsNavtree* pAG ; // Article group
hzString filepath ; // File path
hzString tmpStr ; // Temp string for value
hzString S ; // Temp string for value
hdsUSL U ; // Temp string for uid
uint32_t nL ; // Language iterator
uint32_t n ; // Article group iterator
for (nL = 0 ; nL < m_Languages.Count() ; nL++)
{
pLang = m_Languages.GetObj(nL) ;
if (pLang->m_name == m_DefaultLang)
continue ;
// Do strings by page
Z.Printf("%s/website.%s.txt", *m_Docroot, *pLang->m_code) ;
filepath = Z ;
Z.Clear() ;
is.open(*filepath) ;
if (is.fail())
{ m_pLog->Log("Cannot open file [%s]\n", *filepath) ; continue ; }
Z << is ;
is.close() ;
is.clear() ;
m_pLog->Log("Opened language file [%s] (total %d bytes)\n", *filepath, Z.Size()) ;
// Scan the file content and load strings into the string table for the language. Pages begin with a http:// type URL and are identified from this. The
// strings for the page will begin with the form 'page_uid.string_id)' and end with a double newline.
U.Clear() ;
S = (char*) 0 ;
for (zi = Z ; !zi.eof() && *zi <= CHAR_SPACE ; zi++) ;
for (; !zi.eof() ;)
{
// First part is the id string followed by the ')'
for (; !zi.eof() && *zi > CHAR_SPACE ; zi++)
{
if (*zi == CHAR_PARCLOSE)
break ;
Para.AddByte(*zi) ;
}
for (zi++ ; !zi.eof() && *zi < CHAR_SPACE ; zi++) ;
tmpStr = Para ;
U.SetByText(*tmpStr) ;
Para.Clear() ;
// Second part is the content terminated by double newline
for (; !zi.eof() ; zi++)
{
if (zi == "\n\n")
{
S = Para ;
Para.Clear() ;
if (!m_pDfltLang->m_LangStrings.Exists(U))
m_pLog->Log("WARNING No match on string veId (%s=[%s])\n", *U, *S) ;
else
pLang->m_LangStrings.Insert(U, S) ;
U.Clear() ;
S = (char*) 0 ;
for (; !zi.eof() && *zi <= CHAR_SPACE ; zi++) ;
break ;
}
else
Para.AddByte(*zi) ;
}
}
Z.Clear() ;
m_pLog->Log("File %s Total %d strings\n", *filepath, pLang->m_LangStrings.Count()) ;
// Do article groups
for (n = 0 ; n < m_ArticleGroups.Count() ; n++)
{
pAG = m_ArticleGroups.GetObj(n) ;
//m_pLog->Out("Doing article group %s (total %d articles)\n", *pAG->m_Groupname, pAG->m_Articles.Count()) ;
m_pLog->Out("Doing article group %s (total %d articles)\n", *pAG->m_Groupname, pAG->Count()) ;
Z.Printf("%s/%s.%s.txt", *m_Docroot, *pAG->m_Groupname, *pLang->m_code) ;
filepath = Z ;
Z.Clear() ;
is.open(*filepath) ;
if (is.fail())
{ m_pLog->Log("Cannot open file [%s]\n", *filepath) ; continue ; }
Z << is ;
is.close() ;
is.clear() ;
m_pLog->Log("Opened language file [%s] (total %d bytes)\n", *filepath, Z.Size()) ;
// Scan the file content and load strings into the string table for the language. Articles begin with a http:// type URL and are identified from this. The
// strings for the page will begin with the form 'page_uid.string_id)' and end with a double newline.
U.Clear() ;
S = (char*) 0 ;
for (zi = Z ; !zi.eof() && *zi <= CHAR_SPACE ; zi++) ;
for (; !zi.eof() ;)
{
// First part is the id string followed by the ')'
for (; !zi.eof() && *zi > CHAR_SPACE ; zi++)
{
if (*zi == CHAR_PARCLOSE)
break ;
Para.AddByte(*zi) ;
}
for (zi++ ; !zi.eof() && *zi < CHAR_SPACE ; zi++) ;
//U = Para ;
tmpStr = Para ;
U.SetByText(*tmpStr) ;
//stRef = U ;
Para.Clear() ;
// Second part is the content terminated by double newline
for (; !zi.eof() ; zi++)
{
if (zi == "\n\n")
{
S = Para ;
Para.Clear() ;
if (!m_pDfltLang->m_LangStrings.Exists(U))
m_pLog->Log("WARNING No match on string veId (%s=[%s])\n", *U, *S) ;
else
pLang->m_LangStrings.Insert(U, S) ;
U.Clear() ;
S = (char*) 0 ;
for (; !zi.eof() && *zi <= CHAR_SPACE ; zi++) ;
break ;
}
else
Para.AddByte(*zi) ;
}
}
Z.Clear() ;
m_pLog->Log("File %s Total %d strings\n", *filepath, pLang->m_LangStrings.Count()) ;
}
}
}
#endif
hzEcode hdsApp::CreateDefaultForm (const hzString& cname)
{
// Create a form definition and form handler for the named data class.
_hzfunc("hdsApp::CreateDefaultForm") ;
const hdbClass* pClass ; // Data class pointer
const hdbMember* pMbr ; // Data class member
hdsFormdef* pFormdef ; // Form definition
hdsFormhdl* pFormhdl ; // Form handler
hdsFormref* pFormref ; // Form reference
hdsField* pFld ; // Field being processed
hzString S ; // Temp string
uint32_t mbrNo ; // Member number
hzEcode rc = E_OK ; // Return code
// Establish the class exists and does not already have a default form and from-hamdler (it can have other forms and form-handlers in the configs).
if (cname == "subscriber")
{
// Special case. Both success and failure lead to the re-displaying the current page
m_pLog->Log("Subscriber class is a special case. No default form is required\n") ;
return E_OK ;
}
pClass = m_ADP.GetPureClass(cname) ;
if (!pClass)
{ m_pLog->Out("No such class as %s\n", *cname) ; return E_NOTFOUND ; }
m_pLog->Log("Doing class %s with %d members\n", *cname, pClass->MbrCount()) ;
// Create the form definition and add the fields
pFormdef = new hdsFormdef() ;
pFormdef->m_Formname = "dflt_form_" + cname ;
pFormdef->m_DfltAct = "/dflt_fhdl_" + cname ;
pFormdef->m_pClass = pClass ;
m_FormDefs.Insert(pFormdef->m_Formname, pFormdef) ;
for (mbrNo = 0 ; mbrNo < pClass->MbrCount() ; mbrNo++)
{
pMbr = pClass->GetMember(mbrNo) ;
// Create feild for each atomic member
pFld = new hdsField(this) ;
pFld->InitVE(this) ;
pFld->m_strPretext = pMbr->strName() ;
pFld->m_Line = mbrNo ;
pFld->m_Indent = 0 ;
// pFld->m_Fldspec.m_pType = m_ADP.GetDatatype(dtS) ;
// if (!pFld->m_Fldspec.m_pType)
// { m_pLog->Log("File %s Line %d: xfield %s: Illegal fld type %s\n", *cfg, pN->Line(), *vnam, *dtS) ; goto failed ; }
// pFld->m_Fldspec.m_Refname = pFormdef->m_Formname + "->" + vnam ;
// pFld->m_Varname = vnam ;
// pFld->m_flagVE |= flags ;
// pFld->m_Fldspec.nRows = nRows ;
// pFld->m_Fldspec.nCols = nCols ;
// pFld->m_Fldspec.nSize = nSize ;
// if (!pFormdef->m_mapFlds.Exists(pFld->m_Varname))
// pFormdef->m_mapFlds.Insert(pFld->m_Varname, pFld) ;
pFormdef->m_vecFlds.Add(pFld) ;
}
pFormhdl = new hdsFormhdl() ;
pFormhdl->m_Refname = "dflt_fhdl_" + cname ;
pFormhdl->m_pFormdef = pFormdef ;
pFormhdl->m_flgFH |= VE_COOKIES ;
m_FormHdls.Insert(pFormhdl->m_Refname, pFormhdl) ;
m_FormUrl2Hdl.Insert(pFormdef->m_DfltAct, pFormhdl->m_Refname) ;
pFormref = new hdsFormref(this) ;
pFormref->m_Formname = pFormdef->m_Formname ;
//pFormref->m_flagVE |= VE_LOGINFORM ;
m_FormUrl2Ref.Insert(pFormdef->m_DfltAct, pFormref) ;
m_FormRef2Url.Insert(pFormref, pFormdef->m_DfltAct) ;
return rc ;
}
hzEcode hdsApp::ExportDefaultForm (const hzString& cname)
{
// Export data classes found in the configs as XML fragments that define default forms and form-handlers. This facility is provided as a development aid in
// order to save time. The output is to file name "classforms.def" in the current directory which should always be the development config directory for the
// website. The facility is invoked by calling Dissemino with "-forms" supplied as an argument. Dissemino will load the configs, write the classforms.def
// file and then exit.
//
// There will be one form and one form-handler generated for each class found in the configs, including those for which form(s) currently exists. Forms in
// HTML cannot be nested so in cases where class members are composite (have a data type which is another class), the form produced will contain an extra
// button for each composite member. The class which a member has as its data type will have to have been declare beforehand (otherwise the configs cannot
// be read in), and this will have a form and form handler. The ...
_hzfunc("hdsApp::ExportDefaultForm") ;
ofstream os ; // Output file
hzChain Z ; // Output chain
const hdsFldspec* pSpec ; // Field spec
const hdbMember* pMem ; // Data class member pointer
const hdbClass* pClass ; // Data class pointer
hdsBlock* pIncl ; // Pointer to default include block
hzString dfltInclude ; // Use a <xinclude> block in pages?
const char* mname ; // Member name
uint32_t nM ; // Data class member iterator
bool bUser ; // True is class is a user class
char fname[80] ; // Filename
char pebuf[4] ; // Percent entity form
hzEcode rc = E_OK ; // Return code
m_pLog->Log("Have %d classes and %d repositories\n", m_ADP.CountDataClass(), m_ADP.CountObjRepos()) ;
pebuf[0] = CHAR_PERCENT ;
pebuf[1] = 's' ;
pebuf[2] = ':' ;
pebuf[3] = 0 ;
if (m_Includes.Count())
{
pIncl = m_Includes.GetObj(0) ;
dfltInclude = pIncl->m_Refname ;
}
pClass = m_ADP.GetPureClass(cname) ;
if (!pClass)
return hzerr(E_NOTFOUND, "No such class as %s\n", *cname) ;
m_pLog->Log("Doing class %s with %d members\n", *cname, pClass->MbrCount()) ;
bUser = false ;
if (m_UserTypes.Exists(cname))
bUser = true ;
// Start the XML output
Z << "<disseminoInclude>\n" ;
/*
** Write out a basic list page for the class
*/
Z.Printf("<xpage path=\"/list_%s\" subject=\"%s\" title=\"List %s\" access=\"any\" bgcolor=\"eeeeee\" ops=\"noindex\">\n",
*cname, *pClass->m_Category, *cname) ;
Z << "\t<desc>None</desc>\n" ;
if (dfltInclude)
Z.Printf("\t<xblock name=\"%s\"/>\n", *dfltInclude) ;
Z.AddByte(CHAR_NL) ;
Z.Printf("\t<xtable repos=\"%s\" criteria=\"all\" rows=\"10000\" width=\"90\" height=\"500\" css=\"main\">\n", *cname) ;
Z <<
"\t\t<ifnone><p>Sorry no records ...</p></ifnone>\n"
"\t\t<header>Table header ...</header>\n"
"\t\t<footer>Table footer ...</footer>\n" ;
for (nM = 0 ; nM < pClass->MbrCount() ; nM++)
{
pMem = pClass->GetMember(nM) ;
mname = pMem->txtName() ;
Z.Printf("\t\t<xcol member=\"%s\" title=\"%s\"/>\n", mname, mname) ;
}
Z << "\t</xtable>\n" ;
Z << "</xpage>\n" ;
/*
** Write out a basic host page for the form for the class
*/
Z.Printf("<xpage path=\"/add_%s\" subject=\"%s\" title=\"Add %s\" access=\"any\" bgcolor=\"eeeeee\" ops=\"noindex\">\n", *cname, *pClass->m_Category, *cname) ;
Z << "\t<desc>None</desc>\n" ;
if (dfltInclude)
Z.Printf("\t<xblock name=\"%s\"/>\n", *dfltInclude) ;
Z.AddByte(CHAR_NL) ;
Z <<
"<style>\n"
".tooltip { position: relative; display: inline-block; border-bottom: 1px dotted black; }\n"
".tooltip .tooltiptext { visibility: hidden; width: 120px; background-color: black; color: #fff; text-align: center; padding: 5px 0; border-radius: 6px; position: absolute; z-index: 1; }\n"
".tooltip:hover .tooltiptext { visibility: visible; }\n"
"</style>\n\n" ;
Z <<
"\t<table width=\"96%\" align=\"center\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n"
"\t\t<tr><td height=\"20\"> </td></tr>\n"
"\t\t<tr><td class=\"title\" align=\"center\">Title: Add a " << cname << "</td></tr>\n"
"\t\t<tr>\n"
"\t\t<td valign=\"top\" align=\"center\" class=\"main\">\n"
"\t\t<p>\n"
"\t\tUse this form to create an instance of " << cname << "\n"
"\t\t</p>\n"
"\t\t</td>\n"
"\t</tr>\n"
"\t<xdiv user=\"public\">\n"
"\t<tr><td height=\"20\"> </td></tr>\n"
"\t<tr>\n"
"\t\t<td height=\"20\" align=\"center\" class=\"main\">\n"
"\t\tPlease be aware that by submitting this form, you are consenting to the use of cookies. Please read our cookie policy to <a href=\"/cookies\">find out more</a>\n"
"\t\t</td>\n"
"\t</tr>\n"
"\t</xdiv>\n"
"\t<tr><td height=\"40\"> </td></tr>\n"
"\t</table>\n\n" ;
// Now write out the form itself
if (bUser)
Z.Printf("\t<xform name=\"form_reg_%s\" action=\"fhdl_phase1_%s\" class=\"%s\">\n", *cname, *cname, *cname) ;
else
Z.Printf("\t<xform name=\"form_add_%s\" action=\"fhdl_add_%s\" class=\"%s\">\n", *cname, *cname, *cname) ;
Z << "\t<xhide name=\"lastpage\" value=\"%x:referer;\"/>\n\n" ;
Z << "\t<table width=\"56%\" align=\"center\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"main\">\n" ;
for (nM = 0 ; nM < pClass->MbrCount() ; nM++)
{
pMem = pClass->GetMember(nM) ;
mname = pMem->txtName() ;
pSpec = pMem->GetSpec() ;
if (!pSpec)
{ m_pLog->Log("Warning No Field specification for member %s\n", mname) ; continue ; }
if (pSpec->m_Desc)
Z.Printf("\t\t<tr height=\"18\"><td class=\"fld\"><div class=\"tooltip\">%s<span class=\"tooltiptext\">%s</span></div>:</td>\t<td><xfield member=\"%s\"",
*pSpec->m_Title, *pSpec->m_Desc, mname) ;
else
Z.Printf("\t\t<tr height=\"18\"><td class=\"fld\">%s:</td>\t<td><xfield member=\"%s\"", *pSpec->m_Title, mname) ;
Z << "\tdata=\"" ; Z << pebuf ; Z << mname << "\"" ;
Z << "\tflags=\"req\"/></td></tr>\n" ;
}
Z << "\t</table>\n" ;
// Write out a rudimentry page footer
Z <<
"\t<table width=\"96%\" align=\"center\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"fld\">\n"
"\t\t<tr height=\"50\"><td align=\"center\"><xbutton show=\"Abort\"/> <xbutton show=\"Submit\"/></td></tr>\n"
"\t\t<tr height=\"50\" bgcolor=\"#CCCCCC\"><td class=\"vb10blue\"><center>Site Name</center></td></tr>\n"
"\t</table>\n" ;
Z << "\t</xform>\n" ;
Z << "</xpage>\n\n" ;
// Write form-handler for class
if (bUser)
{
// Write out user registration form-handler. This will be the first form-handler (with name of the form fhdl_classname), with a sendemail step to
// conduct email verification and reference to a second form-handler. This second form handler will also be produced and will commit a new user.
Z.Printf("<formhdl name=\"fhdl_phase1_%s\" form=\"form_reg_%s\" ops=\"cookie\">\n", *cname, *cname) ;
Z << "\t<procdata>\n" ;
// Do the <setvar tags for all the members
Z << "\t\t<setvar name=\"iniCode\" type=\"uint32_t\" value=\"%x:usecs;\"/>\n" ;
for (nM = 0 ; nM < pClass->MbrCount() ; nM++)
{
pMem = pClass->GetMember(nM) ;
mname = pMem->txtName() ;
Z.Printf("\t\t<setvar name=\"%s\" type=\"%s\" value=\"%ce:%s;\"/>\n", mname, pSpec->m_pType->txtType() , CHAR_PERCENT, mname) ;
}
// Do the send email code thing
Z.Printf("\t\t<sendemail from=\"%s\" to=\"%ce:email;\" smtpuser=\"%s\" smtppass=\"%s\" subject=\"Membership Application\">\n",
*m_SmtpAddr, CHAR_PERCENT, *m_SmtpUser, *m_SmtpPass) ;
Z.Printf("Thank you for registering with %s. Your initial login code is %cs:iniCode;i\n\n", *m_Domain, CHAR_PERCENT) ;
Z << "The code is good for 1 hour. Enter this code instead of your selected password in the login screen.\n\n" ;
Z << "This email has been generated by a submission to the Registration form at " ;
Z << m_Domain ;
Z << ". If you have recived this in error, please ignore\n" ;
Z << "\t\t</sendemail>\n" ;
Z << "\t</procdata>\n" ;
Z << "\t<error goto=\"/register\"/>\n\n" ;
Z.Printf("\t<response name=\"form_reg_%s\" bgcolor=\"eeeeee\">\n", *cname) ;
Z << "\t\t<xblock name=\"gdInclude\"/>\n" ;
Z.Printf("<xform name=\"formCkCorp1\" action=\"hdlCkCorp\">\n") ;
Z <<
"\t<table width=\"96%\" align=\"center\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n"
"\t\t<tr><td width=5 height=25> </td></tr>\n"
"\t\t<tr><td align=center class=title>Email Verification</td></tr>\n"
"\t\t<tr height=\"250\">\n"
"\t\t<td class=\"main\">\n"
"\t\tThank you %s:fname; %s:lname; for your registration on belhalf of %s:orgname; to the gdpr360 XLS document processing service.\n"
"\t\t</td>\n"
"\t\t</tr>\n"
"\t\t<tr height=\"50\"><td align=\"center\" class=\"fld\">Code:</td><td><xfield fldspec=\"fs_Usec\" var=\"testCode\" flags=\"req\"/></td></tr>\n"
"\t\t<tr height=\"50\"><td align=\"center\"><xbutton show=\"Complete Registration\"/></td></tr>\n"
"\t\t</table>\n"
"\t</xform>\n"
"\t</response>\n"
"</formhdl>\n\n" ;
Z.Printf("<formhdl name=\"fhdl_phase2_%s\" form=\"form_reg_%s\" ops=\"cookie\">\n", *cname, *cname) ;
Z <<
"\t<procdata>\n"
"\t\t<testeq param1=\"%e:testCode;\" param2=\"%s:iniCode;\">\n"
"\t\t\t<error name=\"InvalidCode\" bgcolor=\"eeeeee\">\n"
"\t\t\t\t<xblock name=\"gdInclude\"/>\n"
"\t\t\t\t<xform name=\"formCkCorp2\" action=\"hdlCkCorp\">\n"
"\t\t\t\t\t<table width=\"96%\" align=\"center\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n"
"\t\t\t\t\t\t<tr><td width=5 height=25> </td></tr>\n"
"\t\t\t\t\t\t<tr><td align=center class=title>Oops! Wrong Code!</td></tr>\n"
"\t\t\t\t\t\t<tr height=\"250\"><td class=\"main\">Please check the email again and type in or cut and paste the code.</td></tr>\n"
"\t\t\t\t\t\t<tr height=\"50\"><td align=\"center\" class=\"fld\">Code:</td><td><xfield fldspec=\"fs_Usec\" var=\"testCode\" flags=\"req\"/></td></tr>\n"
"\t\t\t\t\t\t<tr height=\"50\"><td align=\"center\"><xbutton show=\"Complete Registration\"/></td></tr>\n"
"\t\t\t\t\t</table>\n"
"\t\t\t\t</xform>\n"
"\t\t\t</error>\n"
"\t\t</testeq>\n" ;
Z.Printf("<addSubscriber class=\"%s\" userName=\"%css:orgname;\" userPass=\"%css:password;\" userEmail=\"%css:email;\">\n",
*cname, CHAR_PERCENT, CHAR_PERCENT, CHAR_PERCENT) ;
for (nM = 0 ; nM < pClass->MbrCount() ; nM++)
{
pMem = pClass->GetMember(nM) ;
mname = pMem->txtName() ;
Z.Printf("\t\t\t<seteq member=\"%s\" input=\"%cs%s\"/>\n", mname, CHAR_PERCENT, mname) ;
}
Z <<
"\t\t</addSubscriber>\n"
"\t\t<logon user=\"%s:orgname;\"/>\n"
"\t</procdata>\n" ;
Z <<
"\t<error name=\"RegistrationError\" bgcolor=\"eeeeee\">\n"
"\t\t<xblock name=\"gdInclude\"/>\n"
"\t\t<div class=\"stdpage\">\n"
"\t\t<table bgcolor=\"#FFDDDD\" width=\"100%\" align=\"center\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n"
"\t\t<tr><td width=5 height=25> </td></tr>\n"
"\t\t<tr><td align=center class=title>Internal Error</td></tr>\n"
"\t\t<tr height=\"160\"><td> </td></tr>\n"
"\t\t<tr>\n"
"\t\t<td>An Internal error has occured and system administration has been notified. Please try again later.</td>\n"
"\t\t</tr>\n"
"\t\t<tr height=\"160\"><td> </td></tr>\n"
"\t\t<tr height=\"50\"><td align=\"center\"><xbutton show=\"Return to Site\" goto=\"/index.html\"/></td></tr>\n"
"\t\t</table>\n"
"\t\t</div>\n"
"\t</error>\n" ;
Z <<
"\t<response name=\"CorpCodeOK\" bgcolor=\"eeeeee\">\n"
"\t\t<xblock name=\"gdInclude\"/>\n"
"\t\t<table width=\"96%\" align=\"center\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n"
"\t\t\t<tr><td width=5 height=25> </td></tr>\n"
"\t\t\t<tr><td align=center class=title>Registration Complete</td></tr>\n"
"\t\t\t<tr height=\"250\">\n"
"\t\t\t\t<td class=\"main\">\n"
"\t\t\t\tYour registration as %u:orgname; is now complete. You are logged in and may now use the document processing facility.\n"
"\t\t\t\t</td>\n"
"\t\t\t</tr>\n"
"\t\t\t<tr height=\"50\"><td align=\"center\"><xbutton show=\"Continue\" goto=\"/menu\"/></td></tr>\n"
"\t\t</table>\n"
"\t</response>\n"
"</formhdl>\n" ;
}
else
{
// Write out standard commit form-handler. (fhdl_classname)
Z.Printf("<formhdl name=\"fhdl_add_%s\" form=\"form_add_%s\">\n", *cname, *cname) ;
Z << "\t<procdata>\n" ;
Z.Printf("<commit class=\"%s\">\n", *cname) ;
for (nM = 0 ; nM < pClass->MbrCount() ; nM++)
{
pMem = pClass->GetMember(nM) ;
mname = pMem->txtName() ;
Z.Printf("<seteq member=\"%s\" input=\"%ce:%s;\"/>\n", mname, CHAR_PERCENT, mname) ;
}
Z <<
"\t\t</commit>\n"
"\t</procdata>\n\n" ;
Z.Printf("<error goto=\"/add_%s\"/>\n", *cname) ;
Z.Printf("<response name=\"%s\" bgcolor=\"eeeeee\">\n", *cname) ;
Z <<
"\t</response>\n"
"</formhdl>\n" ;
}
Z << "</disseminoInclude>\n" ;
// Commit the pro-forma page, form and form-handlers to a file called 'classname.forms' This can then be adapted and included in the main XML file for
// the site
if (Z.Size())
{
sprintf(fname, "%s/%s.forms", *m_Configdir, *cname) ;
m_pLog->Log("Writing form of %d bytes to %s\n", Z.Size(), fname) ;
os.open(fname) ;
if (os.fail())
threadLog("Could not open export forms file (classforms.dat)\n") ;
else
{
os << Z ;
if (os.fail())
threadLog("Could not write export forms file (classforms.dat)\n") ;
os.close() ;
}
os.clear() ;
}
Z.Clear() ;
return rc ;
}