allegro Home
acon : Console and WebService Utility for allegro Databases
with
 FLEX : Scripting Language for acon
and

Introduction to a35 Web Interface
               

Contents
Part 1 : What is it all about?
Part 2 : The most important things about FLEX
Part 3 : Some useful patterns
Part 4
: The secret behind a35
Part 5 : Forms in a35  (extended in Oct. 2018)
FLEX Compact Command Table
Basic requirements for a35
Appendix : Special variables in FLEX
Part 1 : What is it all about?

WikiPedia: "A console application is a computer program designed to be used via a text-only computer interface, such as a text terminal, the command line interface of some operating systems (Unix, DOS, etc.) or the text-based interface included with most Graphical User Interface (GUI) operating systems, such as the Win32 console in Microsoft Windows, the Terminal in Mac OS X, and xterm in Unix."

Typically, a console application does not conduct a dialog with its user. Instead, the user calls it up from the console, typing in its name and mostly adding appropriate arguments to let the program know what exactly it is expected to do. There are innumerable programs of this kind in every computing environment. Mostly, they are specific for the operating system they are used with.

Basics about acon 
Now what is it that you can make  acon  do for you? Basically, it can access any allegro database and extract all information you may need from it, and in any format you want. It can also update records, delete them or save new ones.

To do anything useful, acon will always need at least four arguments:

dbdir     = The name of the database directory  (default: demo2, works only if acon is
               started in c:\allegro and there is a folder .\demo2 there)
dbname = The name of the database definition file (a.k.a. "index parameter file",
               default=cat for the file cat.api)
cfg        = The name of the configuration file (defaulting to  a  for a.cfg)
job        = The name of the jobfile - and this is where FLEX comes in: that file contains the
                FLEX script to be executed

That makes it three things telling acon which database to access, and one thing that tells it what to do.

The syntax of an acon call is therefore this:

acon -d dbdir -b dbname -k cfg
-j job    [spaces after -d, -b etc. are optional]

This may be simplified by wrapping it up in a batchfile or shellscript  abc  (or whatever) that would then be activated by some command like

abc job

If you have only one allegro database, the first three arguments will always be the same, so this makes lots of sense.

Example: If you have the demo database attached to your program directory in a subdirectory /var/db/demo2, then the call to execute  xyz.job  would look like this:

acon -d /var/db/demo2 -b cat -k a -j xyz    [spaces after -d, -b, -k etc. are optional]


Basics about FLEX   (see also FLEX Compact Command Table)
acon is no use unless it is told what to do, in its own language, written down in a text file called a "jobfile". Any job must be formulated in a language that was developed specifically for allegro databases, and that language is called FLEX. (Not to be mixed up with "Flex", the Adobe language for their "Flash" program. FLEX originated earlier than Adobe Flex which meanwhile was abandoned anyhow.) The language FLEX has almost 100 command words but many of them are only for very special tasks. And most command words come with some or many different options. By human standards, still not exactly a large vocabulary. And like with any programming language, there are very strict rules to obey. FLEX combines database functions with string manipulation, calculations, file access, Internet access, program calls, and formatted data output. An extended version for the Windows graphical client program a99 has a range of user interaction functions not available to acon and therefore not to the browser client a35 based on it.

The "Hello World" example
Any programming language is usually introduced by the simplest possible example, consisting of the output of the message "Hello World". Easy enough to do in FLEX: Write a small file, named  hello.job, containing just this line:

write "Hello World!"

Then, call acon with this command:

acon -j hello   (Under UNIX/Linux :  ./acon -j hello )

Actually, this will work only if you have the small demo database installed, in a subdirectory  demo2 under your program directory where acon resides and where you make the call.
This job involves no database access at all, but acon needs a database context before it can do anything - so you have to provide one. This makes sense because, practically, you will hardly ever want to use acon without a database context.

Now, on to a more meaningful example, needing more than 1 line:

find #1
write "This is record nr. 1:" n kr

Put this into a file  nr1.job, then enter
  acon -j nr1

Just try it. To use it on your own database, you would have to enter
  
acon -d dbdir -b dbname -k cfg -j nr1

The first line uses the find command to retrieve the record with the internal number 1.
The second line writes text to the console:
   "This is record nr. 1:"   is literal text (use single quotes alternatively)
   n    means "new line"
   kr   is one of many special variables you have access to; this one contains just the text of the current record. (See the list of special variables in the Appendix)

Now, without knowing anything else, you may already start trying variations of this little script, including adding more lines of output.
Do try it, to get some feeling for this basic stuff.


Two high-end examples
for a glimpse of FLEX's potential:
  • al.job   A utility for every allegro database.
    Start it on the demo base with  ./acon -j al
    Apply it to your own with  ./acon -d dbdir -b dbname -k cfg -j al

  • a35 : The Web interface
    On the  allegro website, the demo database is accessible under a35. Whatever you see there is the result of a FLEX job, channelled through two standardized JavaScript functions in the browser and one PHP function on the Webserver, the FLEX jobs themselves being processed by acon, possibly on another server, mediated by the avanti server program. For new functions or modifications, only some FLEX code needs to be written, nothing else - no JavaScript, no PHP.


Part 2 : The most important things about FLEX


1. The one most important advice
  1. FLEX is quite unlike any other programming or scripting language. It is far less powerful than C or Java or Perl for general computing, but on the other hand, it is much more powerful for the things one needs to do with allegro databases. Therefore, do not use intuition derived from conventions in other languages, but strictly stick to the rules as provided here. 
     
2. The two most important principles
  1. The purpose of FLEX is to keep as many functions as possible out of the core software (written in C and C++) and put them into open scripts (jobfiles) which any user might modify and improve. 
    This covers all the administrative functions (circulation, acquisitions, serials, statistics) in a99 (the Windows client) as well as the entire spectrum of Web functionality in a35 (the browser client, based on acon at the server's end) where it reduces the need for JavaScript and PHP scripting to a few universal scripts that are part of the package. 

  2. The chief goal is utmost compactness and efficiency, thereby to achieve short response times in Internet services - as FLEX also serves as the engine behind a35 and all other web interfaces for allegro.

3. The three most important rules

  1. One line - one command; and unlike with C or PHP, there's no terminating character.
    Exception: an  if  command may be followed by a string of commands
    , separated by ;

  2. Command words are case-sensitive: Find or FIND instead of  find  will not work

  3. Comments have to be on separate lines, beginning with a double slash //

4. The four most important concepts
  1. All commands in FLEX may be shortened to the first three letters. Only these are significant, and you can actually add any other letters to the first three and it will work the same way.  
    (Exceptions: The commands  if  (obviously) and  erase  (deletes the current record))

  2. All variables in FLEX are textual. There are no typed variables for numbers, dates, etc., but everything is text. That doesn't mean FLEX can't do calculations or handle dates - it can even calculate the date of Easter.
    Yet there are different storage containers for variables, distinguished by the form of their names:
    #nnn :
    data fields of the current record - these behave like variables in FLEX; use #nnn$x  for subfield $x of #100
    #uxy :  background variables as known from the export language (xy = two alphanum. characters)
    $name : they exist as long as the FLEX job lasts (name is alphanumeric, of variable length)
    &name : these are "persistent" - they can last much longer, outliving the job, but they belong to the database the job works on, stored in a special index file. (Not in use yet for a35)

  3. Variables need no 'declare' before they can be used. To create a variable, you just assign a value by the command  insert vname. (vname being any name of the four types above; we'll show how that's done in a minute.) Because of Concept 1, you guess it, you may also write  ins  or  inser  or indeed insect - the program will understand insert
    Important: Names of variables may only be used in the commands var, write, and insert. All other commands take their input from the string created by a var command in the line preceding the command; see the example in 4.:

  4. Two most important entities are not named: The "current database record" (= the one last loaded) and the "internal Variable". The latter is always referred to as  iV = "internal Variable", but only in descriptions, not in a script itself. Almost every command changes the content of this iV, and many commands use its content when given without a value. In other words: commands like find can get their arguments indirectly. If, e.g., we have a variable #uxy = PER shakespeare? , then we may write
    var #uxy
    find
    but not:
    find #uxy
    and the same works for other commands. More examples follow.

5. The five most important commands

Many commands are very important - and it always depends on context and requirements, of course.
These five are those that will be used most often, and you cannot do much without any one of them: 
  1. find
    To retrieve records or result sets from the database by using a familiar syntax, like  
    find per shakespeare? and tit hamlet.
    If you know the internal number of a record, say 123, the command 
    find #123  will retrieve it, to become the "current record"
    You are likely to ask now: how do I get the first, or second ... member record of a result set?
    Well,  find r1  will get you the first, find r2  or simply  next  the second, and so on.
    Wait until Part 3 with your next questions.

  2. variable
    This serves to put stuff into the iV (not mentioning it by name). "Stuff" can be literal text, content from the current record, or values of variables, + numerous special variables. Examples:
    var "Sorry, an error has occured"
    var #100
    var #uxy
    var "The author's name is  " #100$a " and the title: " #245$a ", publ. in " #260$c

    var "your Database is named " B " and it resides in the folder " D
     
    (B and D are "special" variables, see Appendix)
    var "The sum total is " $sumTotal
    var "The last session was on " &lastSessionDate

    What happens here is just that the the stuff that follows  var  goes to the internal variable, to wait there for the next command. "The stuff that follows" is called a cstring and is a mixture of literal text and all sorts of variables, including a range of internal special variables like  t  for the total size of the database. That makes this command a powerful device to compose strings of any length for every conceivable purpose.
    So, if you see a var command, always look at the next line to see what's done with it.
    Quite frequently, an insert command follows, to save the iV content somewhere:

  3. insert
    to put the iV's content (not mentioning it by name) into a variable or a record.
    You might write
    ins #100      Field #100 of the current record
    ins $a-#100   Subfield $a of field #100 of the current record (not  ins #100$a !)
    ins #uxy      User variable #uxy (available in export parameters!). Same syntax for subfields
    ins $message  The $ variables live as long as the job lasts. Same syntax for subfields
    ins &xyz      The & variables outlive the job to be available in subsequent jobs. No subfield. 
    Corollary: To delete a variable, you put nothing into it
    var ""     This puts
    nothing into the iV 
    ins #uxy
      The variable  #uxy  is annihilated, not just emptied - it ceases to exist.
    var ""     This puts nothing into the iV again
    ins #260$d
    The subfield $d of field  #260  is annihilated, not just emptied - it ceases to exist.

  4. if   /  if not
    to check a condition and, if it holds / or doesn't, execute another command, or chain of commands, following it. (Keep 1.1 in mind here: There's no  then  and no  else. So don't try.)
    There are lots of conditions an  if  command can check. With many commands, success or failure can be checked with an immediately following  if yes  or  if no.
    if not  checks the opposite of the condition, which is useful when it's only that you want to react to.
    Important case:  With  if vname ...  and  if not vname ...  you check the existence or nonexistence of the variable  vname. (see the Corollary in 3.)

  5. write / Write
    to write stuff either to the console or, if an export file has been opened, to that file. 
    write  has the same syntax as  var. The difference is that what follows the command goes directly into the output, not into the iV.  
    The capitalized variant  Write, however, always writes to the console.
And now, try it all out: Make a jobfile and run it, then modify it, as described in Part 1.


Part 3 : Some useful patterns

To do anything of substance almost always calls for more than one or two lines of code.
Before we go on to look at many more commands, here's an example that can serve as a pattern in many real-life situations:
EXAMPLE: How do we build a result set and then produce a listing of the records containing just a few select data fields?
In the course of this, some other very useful commands creep in to enrich our vocabulary.

To be more concrete, and using the demo database, let's make a list of titles (#20) with publishers' names (#75) and dates (#76) of all records that contain Shakespeare as author and the words "Hamlet" or "Othello" in the title. Well, actually, something like this can be achieved in two lines:

find per shakespeare, william and (tit othello or tit hamlet)
list

Try it out now, to see what happens:
Call the script  soh.job  and start it with  acon -jsoh.

That this works, however, is more of an accident due to the characteristics of the demo database and its short title structure, which is what the  list  command extracts. In practice, there will be many cases where the task is different. And even in this case, we didn't get exactly what was asked for - the short title line contains the author (#40) and call number (#90) as well, but not the publisher.
To be more flexible, we can follow a common pattern, and it may rightfully be called

The most important pattern

find per shakespeare, william and (tit othello or tit hamlet)
first
:loop
wri #20 ". - " #75 " (" #76 ")" n
next
if yes jump loop

For our little task, we already have to go beyond our five most important commands.
We walk through it line by line, with the four "new" commands marked in blue:

//   A find command builds the result set:
find per shakespeare, william and (tit othello or tit hamlet)
//   Now load the first member record of the set
first
//   Here comes a jump mark that helps us structure a loop [the : is blue.]
:loop
//   Compose the required line of output 
wri #20 ". - " #75 " (" #76 ")" n
//   There can be any number of command lines here to create the output
//   Then load the next member
next
//   If this was possible (if we weren't on the last one already) jump back to :loop
if yes jump loop

The word "loop" is not a command word. That's to say, the jump mark at the beginning of a loop can be any other word. And quite obviously, when you need more than one loop in a script, you'll need a different mark for each of them. Only the colon is part of the FLEX language. It just says: This line can be jumped to from anywhere in the script using the  jump command.

Fine, you'll say, but what if I wanted a listing of my entire database, not just a result set?
That's no more difficult, and there's not even the need then for a  find:

first #
:loop
if not deleted wri #20 ". - " #75 " (" #76 ")" n
next #
if yes jump loop

The # after  first  and  next  changes the meaning of the commands:
first #  loads the record with the internal number 1, and
next #   loads the one with the next internal number, following after the one currently loaded
And the phrase  if not deleted  makes sure the  write  takes effect only if the record is one that has not been deleted. This was unnecessary in the first example because a result set never contains deleted records.
But you may want to do more with every record of the database than just output a few fields.
So, what if it is not possible to express the necessary action in one command like the  write ... in our example?
The pattern easily extends to a more versatile variant:
 
first #
:loop
if not deleted perform proc
next #
if yes jump loop
// here follows what is to be done after the loop terminates
end
:proc
// here we do all we need to do for every record, e.g., write  output
return

That  :proc  line is a jump mark, but what follows is in fact a subroutine. It ends in the  return line. Between  :proc  and  :return, there can be any number of command lines. You may also go to the :proc line with a jump command; in that case, the return will be ignored.
Programmers will ask: Can we have nested subroutines, or in other words: other performs inside a subroutine? It might be useful indeed, but the answer is no. (Another incident of advice 1.1.)

"How can I get an ordered result set?" will be the next question. There's more than one answer to that. The simplest is the use of the  order command. It depends on the format of the short title list. If it starts with the title, you get a list ordered alphabetically by title with the command  order a0, meaning "sort the list beginning at the 1st character, ascending" (order d0  would mean "descending").


For a list of all acon FLEX commands see the  Compact Command Table.


Part 4 : The Secret behind a35

We are turning now to the question of how exactly FLEX operates as part of the a35 Web interface. To explain this, we may begin with the "Hello World" paradigm again - the simplest possible piece of operable programming, now in the context of a35.
What we need to write is : just a little FLEX job, what else? And no more than one line in this case as well:

wri "_!_POP Hello world!" n

Put this script into the  scripts/jobs  subdirectory of your a35 installation and name it  hw.job.
Then, in any of your (potentially many) a35 database interfaces, enter this into the red frame:

X hw

and it will bring up a small "popup" window containing those two words. Replace POP by INF and the text appears in the INF quadrant (upper right).
The capital X is a command a35 understands. It means: "The following word (hw) is the name of a job. Go execute it!"
To be precise, a35 itself doesn't know what to do with it but sends it to the server where the database resides, using the avanti program as a messenger. avanti, in turn, hands it over to  acon  for execution. The output of  acon  goes all the way back via  avanti  and the webserver to the browser, and there the a35 page sits waiting for it, and knows what to do with the message, i.e. where and how to display "Hello world".

Now what's that "secret" of a35? From this example, you'll gather it must lie in that cryptic  "_!_POP" because that's the only thing that goes beyond the first example. And indeed, the character sequence  _!_  is used to mark 3-letter labels, of which POP is just one. Those labels are hidden beneath the surface of what you see in your browser when you use a35. There are numerous elements, mostly <div> elements, that have an id attribute with them. It always goes like this:  
<div id="XXX"> ... </div>
,
and one of these is id="POP". The most important ones are those for the four "Quadrants":
EXT : Display of one record (upper left)
INF : Help or info text (upper right)
ERG : Result set list (lower left)
REG : Index display (lower right)
Then, whenever one of the a35 FLEX jobs is started, this job will create a stream of HTML text, using the write command or sometimes the export command also. These commands insert labels  _!_XXX, like in the example, to mark those portions of the output that are meant for those various <div> parts. All the HTML that follows  _!_XXX in the long stream of output, up until the next _!_XXX occurs, will be put into that particular <div> or <span> with the  id="XXX". And that's all there is to that "secret".
Besides the four quadrants, there are other areas that can be written to by _!_ statements. You can discover the labels of these areas in the source file  a35-pc.php, looking for the  id="..."  attributes. Or simply type  h labels.txt  into the red frame...

All the necessary machinery that puts the HTML stuff into the various div's is in a35.js - you don't have to bother yourself with that.
You may even create new <div>'s inside the php scripts, like the startup file a35-pc.php, and then write jobs that address these to get something inserted there.
The "secret" is, in other words, just an application of the AJAX technique - without the need to familiarize with that or write any JavaScript yourself. And you need not write PHP for the backend either, since all the output you need to produce for the client's browser can be created in FLEX jobs.
There are three PHP scripts that contain all those id labels in the startup screen:
a35-pc-cont.php  for the PC variant of the a35 interface
a35-tab-cont.php for the Tablet variant
a35-app.php      for the smartphone PC variant

There's a picture of the PC variant here:    a35/a35-1.htm
It shows the important labels in red colour: EXT, INF, ERG, REG, FRE, FRR. But there are a few more, some of which not always visible, like POP, used to create popup messages.

By the way: If there is no database access involved, just HTML textual matter, you may put labeled HTML text into a simple text file. For instance, create a file hw.txt, containing this line:

_!_INF <h1>Hello world!</h1>

and then enter this into the red frame:  h hw.txt
The  h  is another command that a35 understands: it will load the file and interpret its contents as labeled HTML (no matter the filename extension).
A larger example for this method is  a35start.htm. Load it by inputting  h a35start.htm.
Do look into it, for with your new knowledge, you'll understand how it works and be able to modify it right away. This file is brought up automatically every time you call  a35-pc.php.


Part 5 : Forms in a35



FreeForm : An Easy Way to Generate Forms for a35
For data input and editing, any browser-based program uses HTML forms. The freeform technique invented for a35 provides a simple and flexible means to create such forms, and without the need to know anything about the HTML syntax.

A "freeform list" is a file of type .frf that specifies the fields to be presented in a form.
General rule: Remember a freeform list is not a Jobscript, but it gets converted into a form, the moment it is needed, by the script  freeform.job.  Besides command lines described below, the list may contain empty lines and comment lines that begin with //.

In the simplest case, a freeform list consists of just the list of tags for the fields to be edited,
like for example:

#20
#40
#90

which is three fields from the standard ($a.cfg) format. All the other fields will remain untouched.

As was already mentioned, a freeform list will be turned into a form and filled with the respective fields' contents from the current record. The form will normally have one line per field (but see below for other options).
Every field is automatically prefixed with the label that derives from the CFG file's field list:


Title: 

That's a bare minimum. However, for every field, a number of options may be set, following the tag on the same line of the list.

The shortest freeform list consists of just one line, like

#123

which will present a form with just one input field, holding the content of #123 for editing.
The input field will be preceded by the label for that tag as given in the CFG file.

Direct test without a list file is possible:  Enter 
X freeform&Dfields=123@@124
  in the red command frame.
In this syntax, the very short freeform list (fields #123 and #124) directly follows after Dfields=.
Or put those two tags into a file  test.frf  (placed in the data folder with the actual database) and then enter 
X freeform&Dform=test

 
If only subfield $a of #123 is to be edited, this is the list file:

#123$a
#124

However, if field #123 may have some content preceding all its subfields, and you want to edit just that, then write

#123$$

because $$ stands for "the subfield before all the subfields" - something that
MARC, for example, doesn't have but which may occur in allegro data, as in
  #74 PlaceName$gCountryCode
where  #74$$  denotes just the PlaceName, and #74$g holds the CountryCode

An additional # in front, like

##123 ...
has the effect that no form field will be created if #123 has no content.

A number of options may be added to every line, following the tag, separated by spaces. None of the options is mandatory.

First, the width of the input field may be specified by a number, meaning pixels:
 (the default is 400, set as $wid in  freeform.job)

#123 200

The width option must directly follow the tag, or omitted - defaulting to 400
If the text inside the input field is longer, it scrolls horizontally.
If the number is greater than 400, a multiline textarea is created.
There's another way to create a textarea:

Textarea
For a potentially long field, one may specify a <textarea> rather than an <input> field:
#98*10 54
would mean a <textarea> with 10 lines ("rows") of 54 columns to hold the contents of #98.


The other options have no prescribed order. Each option is to be delimited with a pair of special characters.

Label
If the CFG field label is not appropriate, define a  "label"  like this:

#123 500 "Title:"
  or without a width number as in
#123 "Title:"     then the default width will be used

The same works for subfields. But for them, the "label" option is necessary because it will not be provided automatically (the CFG doesn't specify labels for subfields, only for the entire field)

#123$s 300 "Language"

Default value
If the field is empty in the current record, a default value can be set, enclosed in colons :

#123$a 300 "Language" :eng:

It may happen that there's no value in #123$a and that in this case the value from another tag is to be used and put into #123$a, say #124$x. Then just add  #124$x to the end of the line:

#123$a 300 "Language" :eng: #124$x
With this, "eng" will only be used if in the current record there's no value in #124$x either.

Extra text
In some cases, extra text is needed following after the input field, like a helpful hint.
This text can be specified between exclamation marks:

#123$a :xyz: "Language:" !(Enter language code)!

Placeholder
A ;Placeholder; appears in the field, in a grey colour, while there is no input text.
This can also be used to show a helpful hint,for  as long as there is no content.

Attributes
Any other attributes can be added to an <input> field by adding them within %...%, like
... %readonly type=hidden%

Here's the complete order and syntax of elements to be observed:
(elements in [] are optional)

[+]#tag[$x][+] [width] ["label"] [!ExtraText!] [>Placeholder<] [:default:] [#TAG] %attributes%

There can be either  $x  or  +  directly following a #tag, not both.
The + sign before the #tag  serves to place that input element on the same line as the preceding element. (Adjust the widths to make sure they will fit.)
The + sign after the #tag indicates that multiple fields with the same #tag may exist,
and that these are to be presented for editing together in one edit box (an HTML <textarea>). In this case, the field tags will be visible and editable, whereas in regular input fields, the tags are invisible and unchangeable. This way, the order of the multifields can be rearranged manually.
The #TAG is used if #tag has no content in the record. It takes precedence over  the :default:. The input then goes into  #tag, however.

Here's an example of two fields to appear on the same line, both of width 180px:

#330 180 "Composer"
+#331 180 "Arranger"

Composer:    Arranger: 

Dropdown lists  (HTML <select>)
To create dropdown lists with a number of fixed options to choose from, there's a freeform option that can be used in addition to the others:

#nnn =listname= ...

To make this work, there needs to be an HTML <select> list. It has to be in an extra file which must be loaded after startup of a35. It must contain a section that looks like this:
(In the <option> lines, single quotes only, or you get errors.)

_!_VAR listname=
<select id="VVV" style="width:180px;">
<option value='c1'>Text1</option>       
<option value='c2'>Text2</option>                
...
<option value='cN'>TextN</option>                
</select>
_!_VAR ...    [the next list]

Here, VVV is mandatory, c1, c2, ... cN are the values that end up in #nnn and Text1,...,TextN the textual designations displayed in the selectdropdown list. Use exactly this as a template and just fill in the blue elements according to your own specifications. The style attribute is optional; without it, the longest option entry will determine the width.

Then, the input to #nnn can only be one of those values, the one belonging to the chosen Text line. This section together with more predefined sections has to be in a text file  filename.htm which must be read in by a35 with the command  h filename.htm in the red frame, or an appropriate link like  
<a href="javascript:reqHelp('name.htm');">Initialize</a>


Then, upon executing a freeform list containing a line  #nnn =listname= ..., all necessary steps will be automatically done to prepare the actual dropdown list and position it on the line containing the cx value that is already present in the #nnn of the current record, if there is one. All of this is done partly by  freeform.job  and partly by  a35.js in the receivE() function that processes the _!_ labels created by the job.

Variable settings
A freeform list can also contain a number of settings:
Setting  Default  Meaning
Button1=text1  Save  Text on the 1st button
Job1=name1  prsave  Job to execute by this button
Button2=text2  Test  Text on the 2nd button; 0: No Button2
Job2=name2  prtest  Job to execute by this button
Info=Text  F12 to see more  Text to appear to the right of the two buttons
Headline=Text  nothing  Text to appear as a headline for the form
T=any HTML  nothing  Text to insert in the current position
 (between any two lines in the list)

To create a new record by the use of a form, the call has to include the parameter
&Dfunc=new  or   &Dfunc=copy
In the case of copy, the form will not be all empty but fields of the current record will be used to populate it, plus any defaults defined in the freeform list.

Conditions
Sometimes, a form should be allowed to access only if the current record contains one field or other, or does not contain those fields. Then, add appropriate  if  or  if not conditions, but place them at the top of the list:

if #20      : do this entire form only if #20 is present in the record

if not #90  : ... only if #90 is not present

if #20 #40  : either #20 or #40 must be present, or both

if not #20 #40 : both #20 and #40 must not be present

if #20
if #40   : both #20 and #40 must be present (two lines!)

Jumps and jumpmarks
The presence of absence of any field in the record can be used to trigger jumps to other parts of the list, thereby skipping parts not relevant for the situation.
Any jump needs a jumpmark somewhere above or below in the list. If it is above, there is of course the danger of an eternal loop ...
There are two cases:

#123 >mark1< ...
After creating the form field for #123, continue below the line that begins  <mark1 .
If that line does not exist, the form creation ends at this point.

##123 >mark1< ...
If field #123 does not exist in the current record, do not create a form field for it.

>mark1
When this line is reached, the process will continue with the line under <mark1.


Test
How to test it outside of a35:

If xyz.frf is a freeform list, then for testing, try

http:.../db/ajax4.php?JOB=freeform&db=demo&Dform=xyz&VurN=nnn
with nnn being the internal number of any record on which to test the form.

The form will appear in the browser, filled with data from record nnn.
Then look at the HTML source code to see how it has been constructed by the freeform.job, after processing xyz.frf.


Necessary files:
The "generic" or universal files can be used without changes for every database.
For the various folders see "Basic Requirements"

PHP and HTML: (Location: HTML start directory of the database)

ajax4.php  using  ajax4ini.php  and  a35ini.php
handles all the traffic between the browser and the webserver.
Predefined HTML files containing the variable contents described in Part 5.

JavaScript: (Location: scripts)
a35.js : A generic file containing all JavaScript for normal purposes
jquery*.js : The free jQuery library of useful JavaScript functions. 

Jobs:  (Location:  scripts/jobs  on web server)
freeform.job : Generic for creating the HTML-Form out of any freeform list
prsave.job   : Generic job to save the record after being edited in the form
prtest.job   : The same, only without the actual saving,
                        but it will also display the index entries of the edited record

Freeform lists:   (Location: database folder)
*.frf : form specification files as described above.


For those who need to know all the details or are curious:
What happens when you enter  X freeform&Dform=test ?

1. The function reqJob() in a35.js receives the name of the job to call,
  freeform.job, and the variable Dform=test - so  freeform.job  is to be called to
    read the file test.frf. It also receives, automatically, the internal record number of
    the curent record and the timestamp of the current record, and the user's ID .

2. A command goes out to ajax4.php to do what is required:
   freeform.job will be started; it reads the record and the file test.frf,
   then produces the form with data from the record filled in. This is
   HTML code that goes back to the browser who displays it in the INF quadrant
   (which is what freeform.job is specifiying).

3. The user can now edit the data in the form. Then hit the "Save" or "Test" button.

4. The browser uses the JavaScript function  reqForm() (in a35.js)
    which collects the variables from the form and the other necessary things
    that must be transmitted, through ajax4.php, to the job.

5. On the webserver, ajax4.php will receive the variables that have been
   transmitted by the browser, including, automatically, the record number (VurN), 
  its timestamp and some other details, but also the name of the
   Jobfile to execute, in this case  prsave.job  or  prtest.job.

Inside every job called, a <form> variable like  Dxyz  or  V123  arrives as  $xyz or  #123,
and thus  VurN  as #urN (i.e.,  V  gets replaced by # and D by $).

The job is executed by the console program  acon. It produces the entire
output, using FLEX statements  write ... and  export ....
The output goes, without further modifications, through ajax.php straight
back to the browser. There, the function  receivE()  in a35.js  analyzes the output
into its constituent labeled parts and then places the content following each label in
the HTML  <div> or <span>  which carries that label as its  id  attribute.

a35-pc.php, sitting inside the browser all the time, receives the output
of acon, and the function  receivE()  in a35.js  interprets the labels  _!_XYZ  inside that output, in this case _!_INF.
The text following a label  _!_XYZ up until the next such label is then
copied into the area  <div id="XYZ"> </div>  inside  a35-pc.php. Also possible is span instead of div.

This same protocol governs all communication of a35 with the webserver.

How to call up a form via linking?
Any link like
<a href="javascript:reqJob('freeform&Dform=book');">Form for Book</a>
will bring up the form  book.frf  with the content taken from the current record and with buttons for "Save" and "Test".
Add &Dfunc=new if creation of a blank new record is desired, or &Dfunc=copy to say you want a copy of the current record to turn it into a new one. In the latter case, you may add  NOCOPY  to any field definition line if that particular field should not be copied. This way, you can create new records that adopt only selected fields from another record.
Add  $div=FRE  if you want the form to appear not in the default INF quadrant but in the popup field FRE in the middle.
This sort of links can be placed in HTML files like a35forms.htm or in the Menu file a35-menu-pc.php. Or, via the display parameters, in the record display itself.
It is possible to add parameters like  DInfo=...  to the link. The Dxyz arguments translate into $xyz inside the job script  freeform.job, just like the  Vnnn arguments translate into #nnn.

Extensions and improvements
The freeform.job is an open source script written in the FLEX language. It is long and complicated, but extensively commented - another example for the study and learning of FLEX.





Basic Requirements

Everything mentioned here is contained in these packages:

a35 for Windows (including the programs and demo database)

Executable programs for Linux, and all other files necessary or useful

Demo-Database (same as in "a35 for Windows", separately, usable under Linux)

To start learning allegro FLEX with UNIX/Linux, you'll need
  1. A program directory (ProgDir) like  /var/allegro  or whatever

  2. Therein, these files (among potentially many others):
    acon     The allegro console program (For Windows: acon.exe)
    uifseng  An editable text file containing the program's messages in English
    al.job   A useful FLEX job example. Start it on the demo base with  ./acon -jal
    avanti  
    Program through which a web server talks to acon to access a database
    avanti.con   Contains settings for avanti, esp. the list of accessible databases
    The latter two are needed only if access over the Web is required, not for local work with acon and FLEX. Like for instance, to use the  al.job  for some administrative purpose.

  3. A database directory (DbDir) with all the files that belong to your allegro database.
    For experiments, use the demo database package.
    For convenience, attach it to your ProgDir, though you may put it anywhere else.
    If you have none or prefer to learn with a demo database, you may use the one that comes with an allegro installation. For convenience, name it  demo2.
    It contains these files (analogous to your own one, if you have a database):

    a.cfg
    cat.api
    cat_1.ald
    cat.adx
    cat.aex
    cat.azx
    cat.tbl
    cat.stl
    cat.res

    o.apt

    i.apt
    ucodes.apt
    swl1.apt

    Konfiguration (List of data field specifications etc.) (or $a.cfg )
    Index parameters (which fields go to which indexes and in which way)
    Data file containing the records (There may be more: cat_2.ald ...)
    Main index file
    Additional index file containing the ALL index
    Special index file for persistent variables. Initially copied from demo database
    Table of record addresses in the data files
    Short titles of records (useful for result set displays)
    Qualifyer data for searches, like document type, language, and date

    Table of 1:1 character concordance (internal <-> external codes)
    Table of index values of characters  (A -> a, é -> e etc.)
    Table of translations Unicode (UTF-8) to internal code (optional)
    Table of stopwords (not-to-be-indexed words) (optional)

That's all? That's all, though there's some more files in the demo package.
And what about the database server? acon needs none, it can access the databases all by itself. The avanti Server does not itself access a database, it just feeds jobs coming from a webserver into acon, then receives the resulting output and hands it over to the webserver which in turn delivers it to the end-user's browser.
But can several people safely access the same database at the same time, for reading and writing? Yes.

The setup for Windows is analogous. Except there, you have a convenient graphic interface: a99. It needs the database on the same machine or on an accessible file directory.


Appendix

The Special FLEX Variables
You get this list if you start the  al.job  on the Demo database and then use the function  Admin / Settings.
It produces a text file  settings.txt  containing exactly this list, showing the current values of the "special variables" at that moment:

A : Access mode of current session : 3
B : Name of database : cat (Index parameters: cat.api)
D : Database pathname (includes terminating slash) : c:\allegro\demo2\
E : Name of current export file : settings.txt
G : Name of .log file (if not equal to default = c:\allegro\demo2\cat.log) : c:\allegro\demo2\cat.log
H : List of index headlines (from | lines in the index parameters) :
    |1=Namen von Personen (Sonderabteilungen D Diss., ...
I : Index list : Symbolic index names (I lines in the index parameters) :
    ALL e1=ALL-Wortregister^TPER 1=Personennamen^TTIT 3=...   [^T = Code 20]
K : Name of Configuration file : a.cfg
K1: first letter thereof : a
Kk: Values k and t from CFG : 2/4
M : Environment Variable TEMP (User has write access there) : \*.ald
N : Number of data file (cat_N.ald) for new records : 1
P : Name of program directory (with terminating slash) :
R : Restrictions : Symbolic names (R lines in index param)  :
    PYR=Erscheinungsjahr^TTYP=DokumentTyp^TBDT=Bestellda...
    (Separataor code for I and R is 20 ( ^T ))
S : Short title headline (from line |<=... of index parameters) :
    Titel                                  ·Verf.     ·Jahr·Signatur
T : Title of database (from line |a=... of index parameters) :
    'allegro'-Demo-Datenbank
U : Name of current result set : U
Z : Value of the internal number variable iZ : 1 (capital Z)
Z2: the same, rounded to 2 decimal places (k=0...9) : 1.00

b : Width of field tags , Position of field text : 2,4
cn: Date of Input of current record : #99n
ce: Date of latest edit in current record : #99e
cg: Field for automatic IdNumber : #00
ci: structure of the id nr: 9a?5
cf: Number of filler characters for every new record : 0
cN: Line N of UIF file, e.g., c175 : "TBL gesperrt - Speichern gelang nicht"
cl: Command line that started this run of acon : acon  -jal
ca: Workstore dimensions in bytes/Max.size//Number of fields/Max nr. : 255/42000//12,250
cr: Background dimensions in bytes/Max.size//Number of fields/Max nr. : 58/128000//1,1000
cp: Phrase store size/Max size//Number of phrases,max number : 539/18000//63,1200
cs: Internal Coding (D = DOS, W = Win, U = UTF-8)        : D
e : Name of current export parameters (DOS: Option -e) : e-w
i : internal nr. of curr. record : 960 (in : intern. numbers of curr. result set)
j : Number of .ald file of curr. record : 1
k0: Number of fields of current record : 12
k_1: First field of curr. record : 00 823056
k_2: Next field : 20 Optoelektronik : Bauelemente der Halbleiter-Optoelektronik
k_2: Next field : 30aelk
k_3: Last field : 99n20140814/07:27:23oddd
l : Size ('length') of curr. result set : 4
m : Name of program and version (z.B. acon ac-w v34.5) : ac-w v35.3
n : new line, equivalent: 13 10 :

p : Primary key of curr. record : |9823056
pid : PID of curr. program (acon): 808
r : relative Number of current rec in curr. result set : 1
s : Short line of curr. record (from .STL file) :
   
t : Total number of records in database : 963
u : Date and time : 20140821/13:14:30
z : Value of the internal counter : 1001




B.Eversberg, 2014-07-31 / 2020-06-08