It seems that everyone has an opinion about Miva Merchants and Miva Script, the proprietary server-side scripting behind the Miva Merchant and Miva Shopping Cart. Some say that Miva Merchant has too many security problems. Others say Miva Merchant is too slow or that its built-in dBase database is too buggy. But our opinion is that the security problems come from poor Miva script design, the dBase troubles can be tamed, and the speed can be greatly increased by compiling the Miva script and using a MySQL database.
Best of all, Miva provides one of the fastest server-side deployments of new features we have ever seen. Translation - it's faster and easier to add new, custom features to Miva Merchant than almost anything else. That really helps if your department is understaffed and you still have to get a lot done fast.
With that in mind, we realize that one of the biggest challenges facing the Miva Ecommerce web site designer is the lack of good information about writing Miva scripts. The Miva online manuals are a good start. But if you really want to make Miva "sing" you have to "play the right notes". Some of the resources that have really helped us are truXoft MIVO!, MvCODE Library, Miva Samples.
This is our contribution to the community of Miva Ecommerce Web Site Designers. It is is a collection of helpful functions and utilities. Let us know what you think. If you have anything you would like to add, send them to us and we'll include them here.
Thanks,
Music44.com/The Sheet Music Store Ecommerce Web Site Designers.
Miva Email Address Validation Function
This function determines if a list of comma delimited email addresses is valid or not.
<MvFUNCTION NAME = "Email_Validate" PARAMETERS = "addresses" STANDARDOUTPUTLEVEL = "">
<MvASSIGN NAME = "l.valid" VALUE = "(len(addresses) GT 0)">
<MvIF EXPR = "{l.valid}">
<MvASSIGN NAME = "l.valid" VALUE = "{substring(l.addresses, len(l.addresses), 1) NE ','}">
</MvIF>
<MvIF EXPR = "{l.valid}">
<MvASSIGN NAME = "l.valid" VALUE = "{substring(l.addresses, 1, 1) NE ','}">
</MvIF>
<MvIF EXPR = "{l.valid}">
<MvASSIGN NAME = "l.count" VALUE = "{split(l.addresses, ',', 0, 0, l.adrs_)}">
<MvASSIGN NAME = "l.valid" VALUE = "{l.count GT 0}">
</MvIF>
<MvIF EXPR = "{l.valid}">
<MvASSIGN NAME = "l.indx" VALUE = "{l.count}">
<MvWHILE EXPR = "{l.indx}">
<MvASSIGN NAME = "l.adrs_" INDEX = "{l.indx}" VALUE = "{trim(l.adrs_[l.indx])}">
<MvASSIGN NAME = "l.indx" VALUE = "{l.indx - 1}">
</MvWHILE>
</MvIF>
<MvASSIGN NAME = "l.indx" VALUE = "{l.count}">
<MvWHILE EXPR = "{l.indx AND l.valid}">
<MvIF EXPR = "{len(l.adrs_[l.indx])}">
<MvASSIGN NAME = "l.atpos" VALUE = "{'@' IN l.adrs_[l.indx]}">
<MvASSIGN NAME = "l.before" VALUE = "{substring(l.adrs_[l.indx], 1, l.atpos - 1)}">
<MvASSIGN NAME = "l.after" VALUE = "{substring(l.adrs_[l.indx], l.atpos + 1, len(l.adrs_[l.indx]) - l.atpos)}">
<MvASSIGN NAME = "l.valid" VALUE = "{l.atpos GT 1}">
<MvIF EXPR = "{l.valid}">
<MvASSIGN NAME = "l.valid" VALUE = "{('.' IN l.adrs_[l.indx]) GT 1}">
</MvIF>
<MvIF EXPR = "{l.valid}">
<MvASSIGN NAME = "l.valid" VALUE = "{substring(l.adrs_[l.indx], len(l.adrs_[l.indx]), 1) NE '.'}">
</MvIF>
<MvIF EXPR = "{l.valid}">
<MvASSIGN NAME = "l.valid" VALUE = "{('..' IN l.adrs_[l.indx]) EQ 0}">
</MvIF>
<MvIF EXPR = "{l.valid}">
<MvASSIGN NAME = "l.valid" VALUE = "{('@' IN l.after) EQ 0}">
</MvIF>
<MvIF EXPR = "{l.valid}">
<MvASSIGN NAME = "l.valid" VALUE = "{(('.' IN l.after) GT 1) AND (len(l.after) GT 3)}">
</MvIF>
<MvIF EXPR = "{l.valid}">
<MvASSIGN NAME = "l.valid" VALUE = "{('_' IN l.after) EQ 0}">
</MvIF>
<MvIF EXPR = "{l.valid}">
<MvASSIGN NAME = "l.valid" VALUE = "{('~' IN l.after) EQ 0}">
</MvIF>
<MvIF EXPR = "{l.valid}">
<MvASSIGN NAME = "l.valid" VALUE = "{(' ' IN l.adrs_[l.indx]) EQ 0}">
</MvIF>
<MvIF EXPR = "{l.valid}">
<MvASSIGN NAME = "l.valid" VALUE = "{('@.' IN l.adrs_[l.indx]) EQ 0}">
</MvIF>
<MvIF EXPR = "{l.valid}">
<MvASSIGN NAME = "l.valid" VALUE = "{('.@' IN l.adrs_[l.indx]) EQ 0}">
</MvIF>
<MvIF EXPR = "{l.valid}">
<MvASSIGN NAME = "l.valid" VALUE = "{(';' IN l.adrs_[l.indx]) EQ 0}">
</MvIF>
<MvIF EXPR = "{l.valid}">
<MvASSIGN NAME = "l.valid" VALUE = "{('`' IN l.adrs_[l.indx]) EQ 0}">
</MvIF>
<MvIF EXPR = "{l.valid}">
<MvASSIGN NAME = "l.valid" VALUE = "{('!' IN l.adrs_[l.indx]) EQ 0}">
</MvIF>
<MvIF EXPR = "{l.valid}">
<MvASSIGN NAME = "l.valid" VALUE = "{('`' IN l.adrs_[l.indx]) EQ 0}">
</MvIF>
<MvELSE>
<MvASSIGN NAME = "l.valid" VALUE = "0">
</MvIF>
<MvASSIGN NAME = "l.indx" VALUE = "{l.indx - 1}">
</MvWHILE>
<MvFUNCTIONRETURN VALUE = "{l.valid}">
</MvFUNCTION>
Miva Right Function (a keyword Borrowed from Visual Basic)
This function provides the same result as the "right" string processing function in Microsoft visual basic.
<MvFUNCTION NAME = "right" PARAMETERS = "work, lngth" STANDARDOUTPUTLEVEL = "">
<MvCOMMENT>Returns a string containing a specified number of characters from the right side of a string.</MvCOMMENT>
<MvASSIGN NAME = "l.chars" VALUE = "{len(l.work)}">
<MvIF EXPR = "{l.chars GT l.lngth}">
<MvASSIGN NAME = "l.rght" VALUE = "{substring(l.work, l.chars - l.lngth + 1, l.lngth)}">
<MvELSE>
<MvASSIGN NAME = "l.rght" VALUE = "{l.work}">
</MvIF>
<MvFUNCTIONRETURN VALUE = "{l.rght}">
<MvCOMMENT>written by Gary Osborne for The Sheet Music Store</MvCOMMENT>
</MvFUNCTION>
Miva InStr Function (a keyword Borrowed from Visual Basic)
This function is similar to the "InStr" string processing function in Microsoft visual basic.
<MvFUNCTION NAME = "instr" PARAMETERS = "start, input_string, compare_string, case_sensitive" STANDARDOUTPUTLEVEL = "">
<MvCOMMENT> Returns a number specifying the position of the first occurrence of compare_string within input_string. </MvCOMMENT>
<MvASSIGN NAME = "l.pos" VALUE = "0">
<MvIF EXPR = "{len(l.input_string)}">
<MvIF EXPR = "{len(l.compare_string)}">
<MvIF EXPR = "{l.start GT 1}">
<MvIF EXPR = "{l.start LE len(l.input_string)}">
<MvASSIGN NAME = "l.string2" VALUE = "{substring(l.input_string, l.start, len(l.input_string))}">
<MvIF EXPR = "{l.case_sensitive}">
<MvASSIGN NAME = "l.pos" VALUE = "{l.compare_string IN l.string2}">
<MvELSE>
<MvASSIGN NAME = "l.pos" VALUE = "{l.compare_string CIN l.string2}">
</MvIF>
<MvIF EXPR = "{l.pos}">
<MvASSIGN NAME = "l.pos" VALUE = "{l.pos + l.start - 1}">
</MvIF>
</MvIF>
<MvELSE>
<MvIF EXPR = "{l.case_sensitive}">
<MvASSIGN NAME = "l.pos" VALUE = "{l.compare_string IN l.input_string}">
<MvELSE>
<MvASSIGN NAME = "l.pos" VALUE = "{l.compare_string CIN l.input_string}">
</MvIF>
</MvIF>
<MvELSE>
<MvASSIGN NAME = "l.pos" VALUE = "1">
</MvIF>
</MvIF>
<MvFUNCTIONRETURN VALUE = "{l.pos}">
<MvCOMMENT>written by Gary Osborne for The Sheet Music Store</MvCOMMENT>
</MvFUNCTION>
Miva Split Function (a keyword Borrowed from Visual Basic)
This function is similar to the "Split" string processing function in Microsoft visual basic.
<MvFUNCTION NAME = "split" PARAMETERS = "input_string, delimiter, case_sensitive, max_dimensions, array_ VAR" STANDARDOUTPUTLEVEL = "">
<MvCOMMENT> Splits input_string into a number of substrings that are returned in array_ having array_ubound dimensions. </MvCOMMENT>
<MvIF EXPR = "{l.case_sensitive}">
<MvASSIGN NAME = "l.pos" VALUE = "{l.delimiter IN l.input_string}">
<MvELSE>
<MvASSIGN NAME = "l.pos" VALUE = "{l.delimiter CIN l.input_string}">
</MvIF>
<MvIF EXPR = "{len(l.delimiter) AND l.pos}">
<MvIF EXPR = "{l.pos EQ 1}">
<MvASSIGN NAME = "l.pos1" VALUE = "{len(l.delimiter) + 1}">
<MvASSIGN NAME = "l.pos" VALUE = "{instr(l.pos1, l.input_string, l.delimiter, l.case_sensitive)}">
<MvASSIGN NAME = "l.array_ubound" VALUE = "1">
<MvASSIGN NAME = "l.array_" INDEX = "{l.array_ubound}" VALUE = "">
<MvELSE>
<MvASSIGN NAME = "l.pos1" VALUE = "1">
<MvASSIGN NAME = "l.array_ubound" VALUE = "0">
</MvIF>
<MvWHILE EXPR = "{l.pos}">
<MvASSIGN NAME = "l.array_ubound" VALUE = "{l.array_ubound + 1}">
<MvASSIGN NAME = "l.tok" VALUE = "{substring(l.input_string, l.pos1, l.pos - l.pos1)}">
<MvASSIGN NAME = "l.array_" INDEX = "{l.array_ubound}" VALUE = "{l.tok}">
<MvIF EXPR = "{l.pos GE len(l.input_string)}">
<MvWHILESTOP>
</MvIF>
<MvASSIGN NAME = "l.pos1" VALUE = "{l.pos + len(l.delimiter)}">
<MvASSIGN NAME = "l.pos" VALUE = "{instr(l.pos1, l.input_string, l.delimiter, l.case_sensitive)}">
<MvIF EXPR = "{l.pos EQ 0}">
<MvWHILESTOP>
</MvIF>
<MvIF EXPR = "{(l.max_dimensions GT 0) AND (l.array_ubound GE l.max_dimensions)}">
<MvWHILESTOP>
</MvIF>
</MvWHILE>
<MvIF EXPR = "{l.pos EQ 0}">
<MvIF EXPR = "{(l.max_dimensions LE 0) OR (l.array_ubound LT l.max_dimensions)}">
<MvASSIGN NAME = "l.tok" VALUE = "{substring(l.input_string, l.pos1, len(l.input_string))}">
<MvIF EXPR = "{len(l.tok)}">
<MvASSIGN NAME = "l.array_ubound" VALUE = "{l.array_ubound + 1}">
<MvASSIGN NAME = "l.array_" INDEX = "{l.array_ubound}" VALUE = "{l.tok}">
</MvIF>
</MvIF>
</MvIF>
<MvELSE>
<MvASSIGN NAME = "l.array_ubound" VALUE = "1">
<MvASSIGN NAME = "l.array_" INDEX = "{l.array_ubound}" VALUE = "{l.input_string}">
</MvIF>
<MvFUNCTIONRETURN VALUE = "{l.array_ubound}">
</MvFUNCTION>
Here is an example of how to call this function. Notice that the resulting array's dimension is returned to l.count and the array itself is returned to l.adrs_.
<MvASSIGN NAME = "l.count" VALUE = "{split(l.addresses, ',', 0, 0, l.adrs_)}">
The first substring stored in the array can be accessed like this: <MvEVAL EXPR = "{l.adrs_[1]}">.
The last substring stored in the array can be accessed like this: <MvEVAL EXPR = "{l.adrs_[l.count]}">.
Miva String Sorting Function
To be blunt - forget about it. If you have a lot of string variables to sort, this function takes too much time. Instead, put your strings into an array and use the array_sort() function below. It's much faster.
<MvFUNCTION NAME = "string_sort" PARAMETERS = "var_base_name, count, decending, delimiter, runtime, case_sensitive" STANDARDOUTPUTLEVEL = "">
<MvCOMMENT>
Sorts a group of string variables named g.<var_base_name>X, where X is 1, 2, 3...count.
This function is slow. Use array_sort() instead if possible.
</MvCOMMENT>
<MvASSIGN NAME = "l.starttime" VALUE = "{s.dyn_time_t}">
<MvASSIGN NAME = "l.x" VALUE = "{l.count - 1}">
<MvWHILE EXPR = "{l.x}">
<MvASSIGN NAME = "l.done" VALUE = "1">
<MvASSIGN NAME = "l.y" VALUE = "1">
<MvWHILE EXPR = "{l.y LE l.x}">
<MvASSIGN NAME = "l.z" VALUE = "{l.y + 1}">
<MvASSIGN NAME = "l.v1" VALUE = "{miva_variable_value('g.' $ l.var_base_name $ l.y)}">
<MvASSIGN NAME = "l.v2" VALUE = "{miva_variable_value('g.' $ l.var_base_name $ l.z)}">
<MvIF EXPR = "{len(l.delimiter)}">
<MvASSIGN NAME = "l.v1" VALUE = "{gettoken(l.v1, l.delimiter, 1)}">
<MvASSIGN NAME = "l.v2" VALUE = "{gettoken(l.v2, l.delimiter, 1)}">
</MvIF>
<MvIF EXPR = "{NOT l.case_sensitive}">
<MvASSIGN NAME = "l.v1" VALUE = "{toupper(l.v1)}">
<MvASSIGN NAME = "l.v2" VALUE = "{toupper(l.v2)}">
</MvIF>
<MvASSIGN NAME = "l.flip" VALUE = "0">
<MvIF EXPR = "{l.decending}">
<MvASSIGN NAME = "l.flip" VALUE = "{l.v1 LT l.v2}">
<MvELSE>
<MvASSIGN NAME = "l.flip" VALUE = "{l.v1 GT l.v2}">
</MvIF>
<MvIF EXPR = "{l.flip}">
<MvASSIGN NAME = "l.w" VALUE = "{miva_variable_value('g.' $ l.var_base_name $ l.z)}">
<MvASSIGN NAME = "{'g.' $ l.var_base_name $ l.z}" VALUE = "{miva_variable_value('g.' $ l.var_base_name $ l.y)}">
<MvASSIGN NAME = "{'g.' $ l.var_base_name $ l.y}" VALUE = "{l.w}">
<MvASSIGN NAME = "l.done" VALUE = "0">
</MvIF>
<MvASSIGN NAME = "l.y" VALUE = "{l.y + 1}">
<MvIF EXPR = "{l.runtime GT 0}">
<MvIF EXPR = "{(s.dyn_time_t - l.starttime) GE l.runtime}">
<MvWHILESTOP>
</MvIF>
</MvIF>
</MvWHILE>
<MvIF EXPR = "{l.done}">
<MvWHILESTOP>
</MvIF>
<MvASSIGN NAME = "l.x" VALUE = "{l.x - 1}">
</MvWHILE>
</MvFUNCTION>
<MvFUNCTION NAME = "array_sort" PARAMETERS = "n, list VAR, ascending, case_sensitive" STANDARDOUTPUTLEVEL = "">
<MvCOMMENT>
list_ is the array to be sorted. This function overwrites list_ with the new, sorted array.
n is an integer that indicates how far to sort into the array. To sort the entire array, set n equal to
miva_array_max(list_).
ascending is a flag (1 or 0) that indicates to sort the array ascending (when 1) or descending (when 0).
case_sensitive is a flag (1 or 0) that indicates to ignore upper/lower case when sorting when 0.
</MvCOMMENT>
<MvIF EXPR = "{l.n GT 1}">
<MvASSIGN NAME = "l.x" VALUE = "{(l.n BITSR 1) + 1}">
<MvASSIGN NAME = "l.ir" VALUE = "{l.n}">
<MvWHILE EXPR = "1">
<MvIF EXPR = "{l.x GT 1}">
<MvASSIGN NAME = "l.x" VALUE = "{l.x - 1}">
<MvASSIGN NAME = "l.rra" VALUE = "{l.list[l.x]}">
<MvELSE>
<MvASSIGN NAME = "l.rra" VALUE = "{list[l.ir]}">
<MvASSIGN NAME = "l.list" INDEX = "{l.ir}" VALUE = "{l.list[1]}">
<MvASSIGN NAME = "l.ir" VALUE = "{l.ir - 1}">
<MvIF EXPR = "{l.ir EQ 1}">
<MvASSIGN NAME = "l.list" INDEX = "1" VALUE = "{l.rra}">
<MvFUNCRETURN>
</MvIF>
</MvIF>
<MvASSIGN NAME = "l.i" VALUE = "{l.x}">
<MvASSIGN NAME = "l.j" VALUE = "{l.x BITSL 1}">
<MvWHILE EXPR = "{l.j LE l.ir}">
<MvIF EXPR = "{l.case_sensitive}">
<MvIF EXPR = "{l.ascending}">
<MvASSIGN NAME = "l.test" VALUE = "{(l.j LT l.ir) AND (l.list[l.j] LT l.list[l.j + 1])}">
<MvELSE>
<MvASSIGN NAME = "l.test" VALUE = "{(l.j LT l.ir) AND (l.list[l.j] GT l.list[l.j + 1])}">
</MvIF>
<MvELSE>
<MvIF EXPR = "{l.ascending}">
<MvASSIGN NAME = "l.test" VALUE = "{(l.j LT l.ir) AND (toupper(l.list[l.j]) LT toupper(l.list[l.j + 1]))}">
<MvELSE>
<MvASSIGN NAME = "l.test" VALUE = "{(l.j LT l.ir) AND (toupper(l.list[l.j]) GT toupper(l.list[l.j + 1]))}">
</MvIF>
</MvIF>
<MvIF EXPR = "{l.test}">
<MvASSIGN NAME = "l.j" VALUE = "{l.j + 1}">
</MvIF>
<MvIF EXPR = "{l.case_sensitive}">
<MvIF EXPR = "{l.ascending}">
<MvASSIGN NAME = "l.test" VALUE = "{l.rra LT l.list[l.j]}">
<MvELSE>
<MvASSIGN NAME = "l.test" VALUE = "{l.rra GT l.list[l.j]}">
</MvIF>
<MvELSE>
<MvIF EXPR = "{l.ascending}">
<MvASSIGN NAME = "l.test" VALUE = "{toupper(l.rra) LT toupper(l.list[l.j])}">
<MvELSE>
<MvASSIGN NAME = "l.test" VALUE = "{toupper(l.rra) GT toupper(l.list[l.j])}">
</MvIF>
</MvIF>
<MvIF EXPR = "{l.test}">
<MvASSIGN NAME = "l.list" INDEX = "{l.i}" VALUE = "{l.list[l.j]}">
<MvASSIGN NAME = "l.i" VALUE = "{l.j}">
<MvASSIGN NAME = "l.j" VALUE = "{l.j + l.i}">
<MvELSE>
<MvASSIGN NAME = "l.j" VALUE = "{l.ir + 1}">
</MvIF>
<MvASSIGN NAME = "l.list" INDEX = "{l.i}" VALUE = "{l.rra}">
</MvWHILE>
</MvWHILE>
</MvIF>
<MvCOMMENT>written by Gary Osborne for The Sheet Music Store</MvCOMMENT>
</MvFUNCTION>
Miva Gettoken_Last Function (Gets The Last Token In a String)
Miva's built-in gettoken function is very useful. But it's not good for getting the last token of a string. Use the following two functions for that.
<MvFUNCTION NAME = "gettoken_last" PARAMETERS = "work, token" STANDARDOUTPUTLEVEL = "">
<MvASSIGN NAME = "l.last_token" VALUE = "">
<MvIF EXPR = "{len(l.work)}">
<MvIF EXPR = "{l.token IN l.work}">
<MvASSIGN NAME = "l.token_len" VALUE = "{len(l.token)}">
<MvIF EXPR = "{substring(l.work, len(l.work) - l.token_len + 1, l.token_len) EQ l.token}">
<MvASSIGN NAME = "l.work" VALUE = "{substring(l.work, 1, len(l.work) - l.token_len)}">
</MvIF>
<MvIF EXPR = "{len(l.work)}">
<MvASSIGN NAME = "l.pos" VALUE = "{instrrev(l.work, l.token)}">
<MvASSIGN NAME = "l.last_token" VALUE = "{substring(l.work, l.pos + l.token_len, len(l.work))}">
</MvIF>
</MvIF>
</MvIF>
<MvFUNCTIONRETURN VALUE = "{l.last_token}">
</MvFUNCTION>
Miva InStrRev Function (a keyword Borrowed from Visual Basic)
You will need this function for the previous one to work. It works the same as the InStrRev keyword in MS Visual Basic.
<MvFUNCTION NAME = "instrrev" PARAMETERS = "work, chars" STANDARDOUTPUTLEVEL = "">
<MvASSIGN NAME = "l.x" VALUE = "{l.work}">
<MvASSIGN NAME = "l.pos" VALUE = "0">
<MvASSIGN NAME = "l.y" VALUE = "{l.chars IN l.x}">
<MvWHILE EXPR = "{l.y}">
<MvASSIGN NAME = "l.x" VALUE = "{substring(l.x, l.y + len(chars), len(l.work))}">
<MvASSIGN NAME = "l.pos" VALUE = "{l.pos + l.y + len(chars) - 1}">
<MvASSIGN NAME = "l.y" VALUE = "{l.chars IN l.x}">
</MvWHILE>
<MvIF EXPR = "{l.pos}">
<MvASSIGN NAME = "l.pos" VALUE = "{l.pos - len(chars) + 1}">
</MvIF>
<MvFUNCTIONRETURN VALUE = "{l.pos}">
<MvCOMMENT>written by Gary Osborne for The Sheet Music Store</MvCOMMENT>
</MvFUNCTION>
Miva Escape Function
Here is an escape function that converts a string to its escaped value. Use this to insure that a string is free of all invalid characters so it can be transferred in a URL variable. Use Miva's built-in decodeattribute function to convert the escaped string back to its original form.
<MvFUNCTION NAME = "escape" PARAMETERS = "string" STANDARDOUTPUTLEVEL = "">
<MvIF EXPR = "{len(l.string)}">
<MvASSIGN NAME = "l.string" VALUE = "{encodeattribute(l.string)}">
<MvASSIGN NAME = "l.string" VALUE = "{glosub(l.string, '+', '%20')}">
<MvASSIGN NAME = "l.count" VALUE = "32">
<MvWHILE EXPR = "{l.count}">
<MvASSIGN NAME = "l.count" VALUE = "{l.count - 1}">
<MvASSIGN NAME = "l.string" VALUE = "{glosub(l.string, asciichar(l.count), '%' $ padl(dec2hex(l.count), 2, '0'))}">
</MvWHILE>
</MvIF>
<MvFUNCTIONRETURN VALUE = "{l.string}">
<MvCOMMENT>written by Gary Osborne for The Sheet Music Store</MvCOMMENT>
</MvFUNCTION>
Here is another function that does the same thing with using Miva's built-in encodeattribute fuction which can sometimes give erroneous results.
<MvFUNCTION NAME = "encodeattribute_all" PARAMETERS = "work" STANDARDOUTPUTLEVEL = "">
<MvASSIGN NAME = "l.count" VALUE = "1">
<MvWHILE EXPR = "{l.count LE len(work)}">
<MvASSIGN NAME = "l.char" VALUE = "{substring(l.work, l.count, 1)}">
<MvASSIGN NAME = "l.ascii" VALUE = "{asciivalue(l.char)}">
<MvASSIGN NAME = "l.encode" VALUE = "0">
<MvASSIGN NAME = "l.encode" VALUE = "{l.encode OR ((1 LE l.ascii) AND (l.ascii LE 41))}">
<MvASSIGN NAME = "l.encode" VALUE = "{l.encode OR (l.ascii EQ 43)}">
<MvASSIGN NAME = "l.encode" VALUE = "{l.encode OR (l.ascii EQ 44)}">
<MvASSIGN NAME = "l.encode" VALUE = "{l.encode OR (l.ascii EQ 47)}">
<MvASSIGN NAME = "l.encode" VALUE = "{l.encode OR ((58 LE l.ascii) AND (l.ascii LE 63))}">
<MvASSIGN NAME = "l.encode" VALUE = "{l.encode OR ((91 LE l.ascii) AND (l.ascii LE 94))}">
<MvASSIGN NAME = "l.encode" VALUE = "{l.encode OR (l.ascii EQ 96)}">
<MvASSIGN NAME = "l.encode" VALUE = "{l.encode OR ((123 LE l.ascii) AND (l.ascii LE 126))}">
<MvASSIGN NAME = "l.encode" VALUE = "{l.encode OR ((128 LE l.ascii) AND (l.ascii LE 255))}">
<MvIF EXPR = "{l.encode}">
<MvIF EXPR = "{l.ascii EQ 32}">
<MvASSIGN NAME = "l.result" VALUE = "{l.result $ '+'}">
<MvELSE>
<MvASSIGN NAME = "l.result" VALUE = "{l.result $ '%' $ padl(dec2hex(l.ascii), 2, '0')}">
</MvIF>
<MvELSE>
<MvASSIGN NAME = "l.result" VALUE = "{l.result $ l.char}">
</MvIF>
<MvASSIGN NAME = "l.count" VALUE = "{l.count + 1}">
</MvWHILE>
<MvFUNCTIONRETURN VALUE = "{l.result}">
</MvFUNCTION>
Miva URL Validation Function
This function determines if a URL string is valid or not. It does not try to get the URL. That would be even better but would take too long.
<MvFUNCTION NAME = "URL_Validate" PARAMETERS = "url" STANDARDOUTPUTLEVEL = "">
<MvASSIGN NAME = "l.valid" VALUE = "{len(l.url) GT 0}">
<MvIF EXPR = "{l.valid}">
<MvASSIGN NAME = "l.valid" VALUE = "0">
<MvASSIGN NAME = "l.valid" VALUE = "{l.valid OR (tolower(substring(l.url, 1, 7)) EQ 'http://')}">
<MvASSIGN NAME = "l.valid" VALUE = "{l.valid OR (tolower(substring(l.url, 1, 8)) EQ 'https://')}">
</MvIF>
<MvIF EXPR = "{l.valid}">
<MvASSIGN NAME = "l.valid" VALUE = "{len(l.url) GT 7}">
</MvIF>
<MvIF EXPR = "{l.valid}">
<MvASSIGN NAME = "l.valid" VALUE = "{('..' IN l.url) EQ 0}">
</MvIF>
<MvIF EXPR = "{l.valid}">
<MvASSIGN NAME = "l.valid" VALUE = "{(' ' IN l.url) EQ 0}">
</MvIF>
<MvFUNCTIONRETURN VALUE = "{l.valid}">
<MvCOMMENT>written by Gary Osborne for The Sheet Music Store</MvCOMMENT>
</MvFUNCTION>
Miva Email Sender Function
This function sends an email with (or without) an attached text file.
<MvFUNCTION NAME = "send_email" PARAMETERS = "from, to, cc, subject, content, attachment_content, attachment_name" STANDARDOUTPUTLEVEL = "">
<MvASSIGN NAME = "l.ok" VALUE = "1">
<MvIF EXPR = "{Domain.d.mail_angl}">
<MvASSIGN NAME = "l.from" VALUE = "{Email_Add_AngleBrackets(l.from)}">
<MvASSIGN NAME = "l.to" VALUE = "{Email_Add_AngleBrackets(l.to)}">
<MvASSIGN NAME = "l.cc" VALUE = "{Email_Add_AngleBrackets(l.cc)}">
</MvIF>
<MvASSIGN NAME = "l.content" VALUE = "{glosub(l.content, '=', '=3D')}">
<MvASSIGN NAME = "l.content_html" VALUE = "{l.content}">
<MvASSIGN NAME = "l.content_html" VALUE = "{glosub(l.content_html, asciichar(10), '')}">
<MvASSIGN NAME = "l.content_html" VALUE = "{glosub(l.content_html, asciichar(13), '<BR>' $ asciichar(13))}">
<MvASSIGN NAME = "l.crlf" VALUE = "{asciichar(13) $ asciichar(10)}">
<MvASSIGN NAME = "l.subject" VALUE =
"{ l.subject $ l.crlf
$ 'MIME-Version: 1.0' $ l.crlf
$ 'Content-Type: multipart/mixed;' $ l.crlf
$ 'boundary="----=_NextPart_000_0056_01C1853D.4E0479E0"' $ l.crlf $
'X-Priority: 3' $ l.crlf $
'X-MSMail-Priority: Normal' $ l.crlf $
'X-MimeOLE: Produced By Microsoft MimeOLE V5.00.2615.200'
}">
<MvSMTP FROM = "{l.from}" TO = "{l.to}" CC = "{l.cc}" SUBJECT = "{l.subject}" MAILHOST = "{Domain.d.mailhost}"><MIVA STANDARDOUTPUTLEVEL = "text, html">
This is a multi-part message in MIME format.
------=
_NextPart_000_0056_01C1853D.4E0479E0 Content-Type:
multipart/alternative;boundary="----=_NextPart_001_0057_01C1853D.4E0479E0"
------=_NextPart_001_0057_01C1853D.4E0479E0
Content-Type: text/plain;
charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable
<MvEVAL EXPR = "{l.content}">
------=_NextPart_001_0057_01C1853D.4E0479E0
Content-Type: text/html;
charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML><HEAD>
<META content=3D"text/html; charset=3Diso-8859-1" =
http-equiv=3DContent-Type>
<META content=3D"MSHTML 5.00.2614.3500" name=3DGENERATOR>
<STYLE></STYLE>
</HEAD>
<BODY bgColor=3D#ffffff>
<MvEVAL EXPR = "{l.content_html}"></BODY></HTML>
------=_NextPart_001_0057_01C1853D.4E0479E0--
<MvIF EXPR = "{len(l.attachment_name)}">
------=_NextPart_000_0056_01C1853D.4E0479E0
Content-Type: text/plain;
name="<MvEVAL EXPR = "{l.attachment_name}">"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
filename="<MvEVAL EXPR = "{l.attachment_name}">"
<MvEVAL EXPR = "{l.attachment_content}">
</MvIF>
------=_NextPart_000_0056_01C1853D.4E0479E0--
</MvSMTP><MIVA STANDARDOUTPUTLEVEL = "">
<MvIF EXPR = "{len(MvSMTP_ERROR)}">
<MvASSIGN NAME = "l.ok" VALUE = "{smtp_error_msg(l.subject, l.from, l.to, l.cc, l.content $ l.crlf $ l.crlf $ l.attachment_content)}">
</MvIF>
<MvFUNCTIONRETURN VALUE = "{l.ok}">
<MvCOMMENT>written by Gary Osborne for The Sheet Music Store</MvCOMMENT>
</MvFUNCTION>
Miva Hexadecimal to Decimal Converter Function
This function converts a hex number to a decimal number.
<MvFUNCTION NAME = "hex2dec" PARAMETERS = "hex" STANDARDOUTPUTLEVEL = "">
<MvASSIGN NAME = "l.dec" VALUE = "">
<MvASSIGN NAME = "l.count" VALUE = "{len(l.hex)}">
<MvWHILE EXPR = "{l.count}">
<MvASSIGN NAME = "l.mult" VALUE = "{power(16, len(l.hex) - l.count)}">
<MvASSIGN NAME = "l.char" VALUE = "{substring(l.hex, l.count, 1)}">
<MvIF EXPR = "{isdigit(l.char)}">
<MvASSIGN NAME = "l.dec" VALUE = "{l.dec + (l.mult * l.char)}">
<MvELSE>
<MvIF EXPR = "{('A' LE l.char) AND (l.char LE 'F')}">
<MvASSIGN NAME = "l.dec" VALUE = "{l.dec + (l.mult * (asciivalue(l.char) - 55))}">
<MvELSE>
<## Can't do conversion. ##>
<MvASSIGN NAME = "l.dec" VALUE = "">
<MvWHILESTOP>
</MvIF>
</MvIF>
<MvASSIGN NAME = "l.count" VALUE = "{l.count - 1}">
</MvWHILE>
<MvFUNCTIONRETURN VALUE = "{l.dec}">
<MvCOMMENT>written by Gary Osborne for The Sheet Music Store</MvCOMMENT>
</MvFUNCTION>
Miva Decimal to Hexadecimal Converter Function
These two functions convert a decimal number to a hex number.
<MvFUNCTION NAME = "dec2hex" PARAMETERS = "dec" STANDARDOUTPUTLEVEL = "">
<MvASSIGN NAME = "l.convert" VALUE = "{dec2baseX(l.dec, 16)}">
<MvFUNCTIONRETURN VALUE = "{l.convert}">
<MvCOMMENT>written by Gary Osborne for The Sheet Music Store</MvCOMMENT>
</MvFUNCTION>
<MvFUNCTION NAME = "dec2baseX" PARAMETERS = "dec, x" STANDARDOUTPUTLEVEL = "">
<MvASSIGN NAME = "l.convert" VALUE = "">
<MvIF EXPR = "{(2 LE l.x) AND (l.x LE 36)}">
<MvWHILE EXPR = "{l.dec}">
<MvIF EXPR = "{l.dec GE l.x}">
<MvASSIGN NAME = "l.mod" VALUE = "{l.dec MOD l.x}">
<MvASSIGN NAME = "l.dec" VALUE = "{int(l.dec / l.x)}">
<MvELSE>
<MvASSIGN NAME = "l.mod" VALUE = "{l.dec}">
<MvASSIGN NAME = "l.dec" VALUE = "0">
</MvIF>
<MvIF EXPR = "{l.mod GT 9}">
<MvASSIGN NAME = "l.mod" VALUE = "{asciichar(l.mod + 55)}">
</MvIF>
<MvASSIGN NAME = "l.convert" VALUE = "{l.mod $ l.convert}">
</MvWHILE>
</MvIF>
<MvFUNCTIONRETURN VALUE = "{l.convert}">
<MvCOMMENT>written by Gary Osborne for The Sheet Music Store</MvCOMMENT>
</MvFUNCTION>
Miva dBase Record Locking Principles
This is to be one of the worst problems. Failure to lock dBase files in multi-user situations can cause all kinds of problems with Miva. This is why the database files have to be rebuilt so often. It's also the main reason that Miva is considered not robust enough for really busy websites.
Luckily, you can now use a MySQL database instead of Miva's built-in dBase database. We suggest that you avoid the built-in dBase and use MySQL instead. But even MySQL can have record-locking conflicts on a busy website so you can adapt the script below to lock a MySQL table or just one record in a MySQL table.
The online Miva script manual says that you can lock an entire database file but not an individual record. Well, that's not good if the website is busy!
Thankfully it's not true. You can lock individual records!
<MvASSIGN NAME = "l.lock" VALUE = "{g._DB_Store_Directory $ encodeattribute(l.record_name)}">
<MvLOCKFILE FILE = "{l.lock}">
<## This is critical. Don't open the file until after lock is released because the script below could change an index. ##>
<MvASSIGN NAME = "l.ok" VALUE = "{_Open_Database()}">
<MvIF EXPR = "{l.ok}">
<## At this point, you have locked a single record in the database. ##>
<## You can use and change indexes now. ##>
<MvEVAL EXPR = "{_Close_Database()}">
</MvIF>
<MvCOMMENT>written by Gary Osborne for The Sheet Music Store</MvCOMMENT>
</MvLOCKFILE>
l.record_name can contain any string except a null string. Two processes
trying to open the same database with the same l.record_name will have to wait
until the first one is done..
Notice the order of locking, opening,
and closing the database. This is critical whether you are
locking an entire database or just one record.
You will also need to set some parameters in your miva.conf file. (That's the file on your server that Miva looks to for global parameters each time a script is run.) Here are the settings. Notice that globaltimeout is a few seconds less than lockexpiration. The values of these two are not as important (for file locking) as globaltimeout being a few seconds less than lockexpiration. Also, filetimeout=500 filedelay=600 seem to be good settings but we are not sure why.
globaltimeout=185 lockexpiration=187 filetimeout=500 filedelay=600
Miva IsNumber functions
<MvFUNCTION NAME = "isnumber_whole" PARAMETERS = "value" STANDARDOUTPUTLEVEL = "">
<MvCOMMENT> Returns 1 when value is a whole number. </MvCOMMENT>
<MvASSIGN NAME = "l.validate" VALUE = "{l.value}">
<MvIF EXPR = "{substring(l.value, 1, 1) EQ '-'}">
<MvASSIGN NAME = "l.validate" VALUE = "{substring(l.value, 2, len(l.value) - 1)}">
</MvIF>
<MvFUNCTIONRETURN VALUE = "{isdigit(l.validate)}">
</MvFUNCTION>
<MvFUNCTION NAME = "isnumber_float" PARAMETERS = "value" STANDARDOUTPUTLEVEL = "">
<MvCOMMENT> Returns 1 when value is a floating point number. </MvCOMMENT>
<MvASSIGN NAME = "l.temp" VALUE = "{glosub(l.value, '.', '')}">
<MvASSIGN NAME = "l.temp" VALUE = "{glosub(l.temp, '-', '')}">
<MvASSIGN NAME = "l.valid" VALUE = "{isnumber(l.temp)}">
<MvIF EXPR = "{l.valid}">
<MvASSIGN NAME = "l.len" VALUE = "{len(l.value)}">
<MvASSIGN NAME = "l.have_decimal" VALUE = "0">
<MvASSIGN NAME = "l.pos" VALUE = "1">
<MvIF EXPR = "{substring(l.value, 1, 1) EQ '-'}">
<MvASSIGN NAME = "l.pos" VALUE = "2">
</MvIF>
<MvWHILE EXPR = "{l.valid AND (l.pos LE l.len)}">
<MvASSIGN NAME = "l.char" VALUE = "{substring(l.value, l.pos, 1)}">
<MvIF EXPR = "{NOT isdigit(l.char)}">
<MvIF EXPR = "{l.char EQ '.'}">
<MvIF EXPR = "{l.have_decimal}">
<MvASSIGN NAME = "l.valid" VALUE = "0">
<MvELSE>
<MvASSIGN NAME = "l.have_decimal" VALUE = "1">
</MvIF>
<MvELSE>
<MvASSIGN NAME = "l.valid" VALUE = "0">
</MvIF>
</MvIF>
<MvASSIGN NAME = "l.pos" VALUE = "{l.pos + 1}">
</MvWHILE>
</MvIF>
<MvFUNCTIONRETURN VALUE = "{l.valid}">
</MvFUNCTION>
Merging an HTML page with a Miva page (Server Side Includes SSI)
Here is an interesting code snippet. It opens up a HTML page on your website, extracts the HTML script and JavaScript, ignores the <BODY> tag, then merges the content of the HTML page into your Miva page.
The HTML page used can be a Server Side Include page which is used for ordinary HTML pages. This enables you to use the same SSI page for Miva pages and HTML pages.
<MvASSIGN NAME = "l.pageurl" VALUE = "http://yourdomain.com/yourpage.html">
<MvASSIGN NAME = "l.reply_text" VALUE = "">
<MvCALL ACTION = "{l.pageurl}" METHOD = "GET" FIELDS = "" FILES = "">
<MvASSIGN NAME = "l.reply_text" VALUE = "{l.reply_text $ s.callvalue $ asciichar(13)}">
</MvCALL>
<MvASSIGN NAME = "l.reply_text" VALUE = "{glosub(l.reply_text, asciichar(7), '')}">
<MvIF EXPR = "{'<BODY ' CIN l.reply_text}">
<MvASSIGN NAME = "l.reply_body" VALUE = "{l.reply_text}">
<MvASSIGN NAME = "l.reply_body" VALUE = "{glosub(l.reply_body, '<BODY ', asciichar(7))}">
<MvASSIGN NAME = "l.reply_body" VALUE = "{glosub(l.reply_body, '<body ', asciichar(7))}">
<MvASSIGN NAME = "l.reply_body" VALUE = "{glosub(l.reply_body, '</BODY', asciichar(7))}">
<MvASSIGN NAME = "l.reply_body" VALUE = "{glosub(l.reply_body, '</body', asciichar(7))}">
<MvASSIGN NAME = "l.reply_body" VALUE = "{gettoken(l.reply_body, asciichar(7), 2)}">
<MvASSIGN NAME = "l.pos" VALUE = "{'>' IN l.reply_body}">
<MvASSIGN NAME = "l.reply_body" VALUE = "{substring(l.reply_body, l.pos + 1, len(l.reply_body))}">
<MvELSE>
<MvASSIGN NAME = "l.reply_body" VALUE = "{l.reply_text}">
</MvIF>
<MvIF EXPR = "{'<SCRIPT' CIN l.reply_text}">
<MvASSIGN NAME = "l.reply_script" VALUE = "{l.reply_text}">
<MvASSIGN NAME = "l.reply_script" VALUE = "{glosub(l.reply_text, asciichar(7), '')}">
<MvASSIGN NAME = "l.reply_script" VALUE = "{glosub(l.reply_script, '<SCRIPT', asciichar(7))}">
<MvASSIGN NAME = "l.reply_script" VALUE = "{glosub(l.reply_script, '<script', asciichar(7))}">
<MvASSIGN NAME = "l.reply_script" VALUE = "{glosub(l.reply_script, '</SCRIPT>', asciichar(7))}">
<MvASSIGN NAME = "l.reply_script" VALUE = "{glosub(l.reply_script, '</script>', asciichar(7))}">
<MvASSIGN NAME = "l.x" VALUE = "">
<MvASSIGN NAME = "l.count2" VALUE = "0">
<MvWHILE EXPR = "1">
<MvASSIGN NAME = "l.count2" VALUE = "{l.count2 + 2}">
<MvASSIGN NAME = "l.y" VALUE = "{gettoken(l.reply_script, asciichar(7), l.count2)}">
<MvIF EXPR = "{len(l.y)}">
<MvASSIGN NAME = "l.y" VALUE = "{'<script ' $ l.y $ '</script>' $ asciichar(13) $ asciichar(10)}">
<MvASSIGN NAME = "l.x" VALUE = "{l.x $ l.y}">
<MvELSE>
<MvWHILESTOP>
</MvIF>
</MvWHILE>
<MvASSIGN NAME = "l.reply_script" VALUE = "{l.x}">
</MvIF>
<MvEVAL EXPR = "{l.reply_script}">
<MvEVAL EXPR = "{l.reply_body}">
This source code is provided free of charge without any warranty expressed or implied as to its use, usefulness, merchantability, or compliance with
patent rights, copyrights, property rights, or local law.
By using any of this source code, you agree to assume any and all responsibility and liability arising from or attributed to your use of it.
This source code may be freely distributed provided that the distribution license is in compliance with
The Open Source Definition.